class LegendItemTitle(QGraphicsWidget): """Legend item title - the text displayed in the legend. This should only really be used in conjunction with ˙LegendItem˙. Parameters ---------- text : str parent : QGraphicsItem font : QFont This """ _size_hint = QSizeF(100, 10) def __init__(self, text, parent, font): super().__init__(parent) self.__text = QGraphicsTextItem(text.title()) self.__text.setParentItem(self) self.__text.setFont(font) self._size_hint = QSizeF(self.__text.boundingRect().size()) def sizeHint(self, size_hint, size_constraint=None, *args, **kwargs): return self._size_hint
def __init__(self, *args): self.__boundingRect = None QGraphicsObject.__init__(self, *args) self.setFlag(QGraphicsItem.ItemHasNoContents, True) self.setAcceptedMouseButtons(Qt.RightButton | Qt.LeftButton) self.setAcceptHoverEvents(True) self.setZValue(self.Z_VALUE) self.sourceItem = None self.sourceAnchor = None self.sinkItem = None self.sinkAnchor = None self.curveItem = LinkCurveItem(self) self.sourceIndicator = LinkAnchorIndicator(self) self.sinkIndicator = LinkAnchorIndicator(self) self.sourceIndicator.hide() self.sinkIndicator.hide() self.linkTextItem = QGraphicsTextItem(self) self.linkTextItem.setAcceptedMouseButtons(Qt.NoButton) self.linkTextItem.setAcceptHoverEvents(False) self.__sourceName = "" self.__sinkName = "" self.__dynamic = False self.__dynamicEnabled = False self.__state = LinkItem.NoState self.hover = False self.prepareGeometryChange() self.__updatePen() self.__boundingRect = None
def __init__(self, text, parent=None, **kwargs): super().__init__(parent, **kwargs) self.labelItem = QGraphicsTextItem(self) self.setHtml(text) self.labelItem.document().documentLayout().documentSizeChanged.connect( self.onLayoutChanged)
def update_scene(self): if not self.repaint: return self.clear_scene() if self.domain is None or not len(self.points[0]): return name_items = [QGraphicsTextItem(a.name) for a in self.domain.attributes] point_text = QGraphicsTextItem("Points") probs_text = QGraphicsTextItem("Probabilities (%)") all_items = name_items + [point_text, probs_text] name_offset = -max(t.boundingRect().width() for t in all_items) - 50 w = self.view.viewport().rect().width() max_width = w + name_offset - 100 points = [pts[self.target_class_index] for pts in self.points] minimums = [min(p) for p in points] if self.align == OWNomogram.ALIGN_LEFT: points = [p - m for m, p in zip(minimums, points)] max_ = np.nan_to_num(max(max(abs(p)) for p in points)) d = 100 / max_ if max_ else 1 if self.scale == OWNomogram.POINT_SCALE: points = [p * d for p in points] if self.scale == OWNomogram.POINT_SCALE and \ self.align == OWNomogram.ALIGN_LEFT: self.scale_back = lambda x: [p / d + m for m, p in zip(minimums, x)] self.scale_forth = lambda x: [(p - m) * d for m, p in zip(minimums, x)] if self.scale == OWNomogram.POINT_SCALE and \ self.align != OWNomogram.ALIGN_LEFT: self.scale_back = lambda x: [p / d for p in x] self.scale_forth = lambda x: [p * d for p in x] if self.scale != OWNomogram.POINT_SCALE and \ self.align == OWNomogram.ALIGN_LEFT: self.scale_back = lambda x: [p + m for m, p in zip(minimums, x)] self.scale_forth = lambda x: [p - m for m, p in zip(minimums, x)] if self.scale != OWNomogram.POINT_SCALE and \ self.align != OWNomogram.ALIGN_LEFT: self.scale_back = lambda x: x self.scale_forth = lambda x: x point_item, nomogram_head = self.create_main_nomogram( name_items, points, max_width, point_text, name_offset) probs_item, nomogram_foot = self.create_footer_nomogram( probs_text, d, minimums, max_width, name_offset) for item in self.feature_items: item.dot.point_dot = point_item.dot item.dot.probs_dot = probs_item.dot item.dot.vertical_line = self.hidden_vertical_line self.nomogram = nomogram = NomogramItem() nomogram.add_items([nomogram_head, self.nomogram_main, nomogram_foot]) self.scene.addItem(nomogram) self.set_feature_marker_values() rect = QRectF(self.scene.itemsBoundingRect().x(), self.scene.itemsBoundingRect().y(), self.scene.itemsBoundingRect().width(), self.nomogram.preferredSize().height()) self.scene.setSceneRect(rect.adjusted(0, 0, 70, 70))
def __init__(self, parent=None, **kwargs): # type: (Optional[QGraphicsItem], Any) -> None self.__boundingRect = None # type: Optional[QRectF] super().__init__(parent, **kwargs) self.setAcceptedMouseButtons(Qt.RightButton | Qt.LeftButton) self.setAcceptHoverEvents(True) self.setZValue(self.Z_VALUE) self.sourceItem = None # type: Optional[NodeItem] self.sourceAnchor = None # type: Optional[AnchorPoint] self.sinkItem = None # type: Optional[NodeItem] self.sinkAnchor = None # type: Optional[AnchorPoint] self.curveItem = LinkCurveItem(self) self.linkTextItem = QGraphicsTextItem(self) self.linkTextItem.setAcceptedMouseButtons(Qt.NoButton) self.linkTextItem.setAcceptHoverEvents(False) self.__sourceName = "" self.__sinkName = "" self.__dynamic = False self.__dynamicEnabled = False self.__state = LinkItem.NoState self.hover = False self.prepareGeometryChange() self.__updatePen() self.__updatePalette() self.__updateFont()
def _adjust_scale(attributes, points, max_width, diff, attr_inds, log_reg_cont_data_extremes, cls_index): if not diff: return max_width def offset(name, point): text_ = QGraphicsTextItem(name).boundingRect() return scale * point + text_.width() / 2 lr = log_reg_cont_data_extremes scale = max_width / diff names = list(chain.from_iterable( [_get_labels(a, lr and lr[i] and lr[i][0] and lr[i][cls_index], OWNomogram.get_ruler_values(p.min(), p.max(), scale * p.ptp(), False)) for i, a, p in zip(attr_inds, attributes, points)])) points = list(chain.from_iterable(points)) old_scale = scale + 1 while old_scale > scale: old_scale = scale offsets = [offset(n, p) for n, p in zip(names, points)] most_right_name = names[np.argmax(offsets)] text = QGraphicsTextItem(most_right_name).boundingRect() scale = (max_width - text.width() / 2) / diff return scale
def __init__(self, scene, text="", x=0, y=0, alignment=Qt.AlignLeft | Qt.AlignTop, bold=False, font=None, z=0, html_text=None, tooltip=None, show=True, vertical=False): QGraphicsTextItem.__init__(self, text, None) if font: self.setFont(font) if bold: font = self.font() font.setBold(bold) self.setFont(font) if html_text: self.setHtml(html_text) self.alignment = alignment self.vertical = vertical if vertical: self.setRotation(-90) self.setPos(x, y) self.x, self.y = x, y self.setZValue(z) if tooltip: self.setToolTip(tooltip) if show: self.show() else: self.hide() if scene is not None: scene.addItem(self)
def __init__(self, text, parent, font): super().__init__(parent) self.__text = QGraphicsTextItem(text.title()) self.__text.setParentItem(self) self.__text.setFont(font) self._size_hint = QSizeF(self.__text.boundingRect().size())
def display_changed_disc(self): self.clear_scene() self.attr_labels = [ QGraphicsSimpleTextItem(lab) for lab in self.label_txts_all ] if not self.stretched: if self.group_var: self.labels = [ QGraphicsTextItem("{}".format(int(sum(cont)))) for cont in self.conts ] else: self.labels = [QGraphicsTextItem(str(int(sum(self.dist))))] self.draw_axis_disc() if self.group_var: self.boxes = [ self.strudel(cont, i) for i, cont in enumerate(self.conts) ] else: self.boxes = [self.strudel(self.dist)] for row, box in enumerate(self.boxes): y = (-len(self.boxes) + row) * 40 + 10 label = self.attr_labels[row] b = label.boundingRect() label.setPos(-b.width() - 10, y - b.height() / 2) self.box_scene.addItem(label) if not self.stretched: label = self.labels[row] b = label.boundingRect() if self.group_var: right = self.scale_x * sum(self.conts[row]) else: right = self.scale_x * sum(self.dist) label.setPos(right + 10, y - b.height() / 2) self.box_scene.addItem(label) if self.attribute is not self.group_var: for text_item, bar_part in zip(box.childItems()[1::2], box.childItems()[::2]): label = QGraphicsSimpleTextItem(text_item.toPlainText()) label.setPos(bar_part.boundingRect().x(), y - label.boundingRect().height() - 8) self.box_scene.addItem(label) for text_item in box.childItems()[1::2]: box.removeFromGroup(text_item) for item in box.childItems(): self.box_scene.addItem(item) item.setPos(0, y) self.box_scene.setSceneRect(-self.label_width - 5, -30 - len(self.boxes) * 40, self.scene_width, len(self.boxes * 40) + 90) self.infot1.setText("") self.select_box_items()
def _display_changed_disc(self): self.clear_scene() self.attr_labels = [ QGraphicsSimpleTextItem(lab) for lab in self.label_txts_all ] if not self.show_stretched: if self.group_var: self.labels = [ QGraphicsTextItem("{}".format(int(sum(cont)))) for cont in self.conts.array_with_unknowns if np.sum(cont) > 0 ] else: self.labels = [QGraphicsTextItem(str(int(sum(self.dist))))] self.order = list(range(len(self.attr_labels))) self.draw_axis_disc() if self.group_var: conts = self.conts.array_with_unknowns self.boxes = [ self.strudel(cont, val) for cont, val in zip(conts, self.group_var.values + ("", )) if np.sum(cont) > 0 ] sums_ = np.sum(conts, axis=1) sums_ = sums_[sums_ > 0] # only bars with sum > 0 are shown if self.sort_freqs: # pylint: disable=invalid-unary-operand-type self.order = sorted(self.order, key=(-sums_).__getitem__) else: conts = self.dist.array_with_unknowns self.boxes = [self.strudel(conts)] sums_ = [np.sum(conts)] for row, box_index in enumerate(self.order): y = (-len(self.boxes) + row) * 40 + 10 box = self.boxes[box_index] bars, labels = box[::2], box[1::2] self.__draw_group_labels(y, box_index) if not self.show_stretched: self.__draw_row_counts(y, self.labels[box_index], sums_[box_index]) if self.show_labels and self.attribute is not self.group_var: self.__draw_bar_labels(y, bars, labels) self.__draw_bars(y, bars) self.box_scene.setSceneRect( -self.label_width - 5, -30 - len(self.boxes) * 40, self.scene_width, len(self.boxes * 40) + 90, ) self._compute_tests_disc()
def __init__(self, text, parent): QGraphicsObject.__init__(self, parent) self.text_item = QGraphicsTextItem(text + ':', self) f = self.text_item.font() f.setBold(True) self.text_item.setFont(f) self.rect_item = QGraphicsRectItem(self.text_item.boundingRect(), self) self.rect_item.setPen(QPen(Qt.NoPen)) self.rect_item.stackBefore(self.text_item)
def __init__(self, name, values, scale, name_offset, offset, get_points, title, get_probabilities): super().__init__() self.scale = scale self.offset = offset self.get_points = get_points self.min_val = min(values) self.max_val = max(values) # leading labels font = name.document().defaultFont() font.setWeight(QFont.Bold) name_total = QGraphicsTextItem("Total", self) name_total.setFont(font) name_total.setPos(name_offset, -25) name.setFont(font) name.setPos(name_offset, 10) name.setParentItem(self) # prediction marker self.dot = ProbabilitiesDotItem( self.DOT_RADIUS, scale, offset, values[0], values[-1], title, get_probabilities) self.dot.setPos(0, (- self.DOT_RADIUS + self.y_diff) / 2) self.dot.setParentItem(self) # pylint: disable=unused-variable # two lines t_line = QGraphicsLineItem(self.min_val * scale + offset, 0, self.max_val * scale + offset, 0, self) p_line = QGraphicsLineItem(self.min_val * scale + offset, self.y_diff, self.max_val * scale + offset, self.y_diff, self) # ticks and labels old_x_tick = values[0] * scale + offset for i, value in enumerate(values[1:]): x_tick = value * scale + offset x = x_tick - (x_tick - old_x_tick) / 2 half_tick = QGraphicsLineItem(x, - self.tick_height / 2, x, 0, self) old_x_tick = x_tick if i == len(values) - 2: break text = QGraphicsTextItem(str(abs(value) if value == -0 else value), self) x_text = value * scale - text.boundingRect().width() / 2 + offset y_text = - text.boundingRect().height() - self.DOT_RADIUS * 0.7 text.setPos(x_text, y_text) tick = QGraphicsLineItem(x_tick, -self.tick_height, x_tick, 0, self) self.prob_items = [ (i / 10, QGraphicsTextItem(" " + str(i * 10) + " "), QGraphicsLineItem(0, 0, 0, 0)) for i in range(1, 10)]
def __init__(self, parent=None, text=""): super().__init__(parent) self.setAcceptHoverEvents(True) self.setPen(QPen(Qt.NoPen)) self.text = QGraphicsTextItem(self) layout = self.text.document().documentLayout() layout.documentSizeChanged.connect(self._onLayoutChanged) self._text = text self._anchor = QPointF()
def __init__(self, text, x, y, align, bold = 0, color = None, brushColor = None, size=None): orangeqt.PlotItem.__init__(self) self.setFlag(QGraphicsItem.ItemIgnoresTransformations, True) self._item = QGraphicsTextItem(text, parent=self) self._data_point = QPointF(x,y) f = self._item.font() f.setBold(bold) if size: f.setPointSize(size) self._item.setFont(f) self._item.setPos(x, y)
def __init__(self, id, title='', title_above=False, title_location=AxisMiddle, line=None, arrows=0, plot=None, bounds=None): QGraphicsItem.__init__(self) self.setFlag(QGraphicsItem.ItemHasNoContents) self.setZValue(AxisZValue) self.id = id self.title = title self.title_location = title_location self.data_line = line self.plot = plot self.graph_line = None self.size = None self.scale = None self.tick_length = (10, 5, 0) self.arrows = arrows self.title_above = title_above self.line_item = QGraphicsLineItem(self) self.title_item = QGraphicsTextItem(self) self.end_arrow_item = None self.start_arrow_item = None self.show_title = False self.scale = None path = QPainterPath() path.setFillRule(Qt.WindingFill) path.moveTo(0, 3.09) path.lineTo(0, -3.09) path.lineTo(9.51, 0) path.closeSubpath() self.arrow_path = path self.label_items = [] self.label_bg_items = [] self.tick_items = [] self._ticks = [] self.zoom_transform = QTransform() self.labels = None self.values = None self._bounds = bounds self.auto_range = None self.auto_scale = True self.zoomable = False self.update_callback = None self.max_text_width = 50 self.text_margin = 5 self.always_horizontal_text = False
def display_changed_disc(self): assert not self.is_continuous self.clear_scene() self.attr_labels = [ QGraphicsSimpleTextItem(lab) for lab in self.label_txts_all ] if not self.stretched: if self.group_var: self.labels = [ QGraphicsTextItem("{}".format(int(sum(cont)))) for cont in self.conts if np.sum(cont) > 0 ] else: self.labels = [QGraphicsTextItem(str(int(sum(self.dist))))] self.order = list(range(len(self.attr_labels))) self.draw_axis_disc() if self.group_var: self.boxes = \ [self.strudel(cont, i) for i, cont in enumerate(self.conts) if np.sum(cont) > 0] self.conts = self.conts[np.sum(np.array(self.conts), axis=1) > 0] if self.sort_freqs: # pylint: disable=invalid-unary-operand-type self.order = sorted( self.order, key=(-np.sum(self.conts, axis=1)).__getitem__) else: self.boxes = [self.strudel(self.dist)] for row, box_index in enumerate(self.order): y = (-len(self.boxes) + row) * 40 + 10 box = self.boxes[box_index] bars, labels = box[::2], box[1::2] self.__draw_group_labels(y, box_index) if not self.stretched: self.__draw_row_counts(y, box_index) if self.show_labels and self.attribute is not self.group_var: self.__draw_bar_labels(y, bars, labels) self.__draw_bars(y, bars) self.box_scene.setSceneRect(-self.label_width - 5, -30 - len(self.boxes) * 40, self.scene_width, len(self.boxes * 40) + 90) self.infot1.setText("") self.select_box_items()
class GraphicsTextWidget(QGraphicsWidget): def __init__(self, text, parent=None, **kwargs): super().__init__(parent, **kwargs) self.labelItem = QGraphicsTextItem(self) self.setHtml(text) self.labelItem.document().documentLayout().documentSizeChanged.connect( self.onLayoutChanged ) def setGeometry(self, rect): self.prepareGeometryChange() super().setGeometry(rect) def onLayoutChanged(self, *args): self.updateGeometry() def sizeHint(self, which, constraint=QSizeF()): if which == Qt.MinimumSize: return self.labelItem.boundingRect().size() else: return self.labelItem.boundingRect().size() def setTextWidth(self, width): self.labelItem.setTextWidth(width) def setHtml(self, text): self.labelItem.setHtml(text)
class GraphicsTextWidget(QGraphicsWidget): def __init__(self, text, parent=None, **kwargs): super().__init__(parent, **kwargs) self.labelItem = QGraphicsTextItem(self) self.setHtml(text) self.labelItem.document().documentLayout().documentSizeChanged.connect( self.onLayoutChanged) def setGeometry(self, rect): self.prepareGeometryChange() super().setGeometry(rect) def onLayoutChanged(self, *args): self.updateGeometry() def sizeHint(self, which, constraint=QSizeF()): if which == Qt.MinimumSize: return self.labelItem.boundingRect().size() else: return self.labelItem.boundingRect().size() def setTextWidth(self, width): self.labelItem.setTextWidth(width) def setHtml(self, text): self.labelItem.setHtml(text)
def __init__(self, parent, *args, **kwargs): QGraphicsTextItem.__init__(self, *args) GraphNode.__init__(self, **kwargs) self._background_brush = None self._rect = None self.parent = parent font = self.font() font.setPointSize(10) self.setFont(font) self.droplet = GraphicsDroplet(-5, 0, 10, 10, self) self.droplet.setPos(self.rect().center().x(), self.rect().height()) self.document().contentsChanged.connect(self.update_contents) self.isOpen = True self.setFlag(QGraphicsItem.ItemIsSelectable, True)
def setItems(self, items): if self._items: self.clear() self._items = list(items) for item in self._items: item.setParentItem(self) item.setVisible(True) fmt = self.TitleFormat.format font = self.font() font.setPixelSize(14) for item in items: text = GraphicsTextEdit(self) text.setFont(font) text.setDefaultTextColor(QColor("#333")) text.setHtml(fmt(escape(item.text), item.informativeText)) text.adjustSize() text.editingStarted.connect(self._on_editingStarted) text.editingFinished.connect(self._on_editingFinished) text.documentSizeChanged.connect(self._on_itemTextSizeChanged) self._textitems.append(text) self._vennareas = [ VennIntersectionArea(parent=self) for i in range(2**len(items)) ] self._subsettextitems = [ QGraphicsTextItem(parent=self) for i in range(2**len(items)) ] self._updateLayout()
def __init__(self, *args): self.__boundingRect = None QGraphicsObject.__init__(self, *args) self.setFlag(QGraphicsItem.ItemHasNoContents, True) self.setAcceptedMouseButtons(Qt.RightButton | Qt.LeftButton) self.setAcceptHoverEvents(True) self.setZValue(self.Z_VALUE) self.sourceItem = None self.sourceAnchor = None self.sinkItem = None self.sinkAnchor = None self.curveItem = LinkCurveItem(self) self.sourceIndicator = LinkAnchorIndicator(self) self.sinkIndicator = LinkAnchorIndicator(self) self.sourceIndicator.hide() self.sinkIndicator.hide() self.linkTextItem = QGraphicsTextItem(self) self.__sourceName = "" self.__sinkName = "" self.__dynamic = False self.__dynamicEnabled = False self.__state = LinkItem.Empty self.hover = False self.prepareGeometryChange() self.__updatePen() self.__boundingRect = None
def strudel(self, dist): attr = self.attribute ss = np.sum(dist) box = QGraphicsItemGroup() if ss < 1e-6: QGraphicsRectItem(0, -10, 1, 10, box) cum = 0 for i, v in enumerate(dist): if v < 1e-6: continue if self.stretched: v /= ss v *= self.scale_x rect = QGraphicsRectItem(cum + 1, -6, v - 2, 12, box) rect.setBrush(QBrush(QColor(*attr.colors[i]))) rect.setPen(QPen(Qt.NoPen)) if self.stretched: tooltip = "{}: {:.2f}%".format(attr.values[i], 100 * dist[i] / sum(dist)) else: tooltip = "{}: {}".format(attr.values[i], int(dist[i])) rect.setToolTip(tooltip) text = QGraphicsTextItem(attr.values[i]) box.addToGroup(text) cum += v return box
def paint(self, painter, option, widget=None): rect = self.rect() if self.isSelected(): option.state ^= QStyle.State_Selected font = self.document().defaultFont() painter.setFont(font) if self.parent: draw_text = self.node_inst.description if self.parent.x() > self.x(): # node is to the left fm = QFontMetrics(font) x = rect.width() / 2 - fm.width(draw_text) - 4 else: x = rect.width() / 2 + 4 painter.drawText(QPointF(x, -self.line_descent - 1), draw_text) painter.save() painter.setBrush(self.backgroundBrush) painter.setPen(QPen(Qt.black, 3 if self.isSelected() else 0)) adjrect = rect.adjusted(-3, 0, 0, 0) if not self.node_inst.children: painter.drawRoundedRect(adjrect, 4, 4) else: painter.drawRect(adjrect) painter.restore() painter.setClipRect(rect) return QGraphicsTextItem.paint(self, painter, option, widget)
def strudel(self, dist, group_val=None): attr = self.attribute ss = np.sum(dist) box = [] if ss < 1e-6: cond = DiscDataRange(None, group_val) box.append(FilterGraphicsRectItem(cond, 0, -10, 1, 10)) cum = 0 missing_val_str = f"missing '{attr.name}'" values = attr.values + ("", ) colors = attr.palette.qcolors_w_nan total = sum(dist) for freq, value, color in zip(dist, values, colors): if freq < 1e-6: continue v = freq if self.show_stretched: v /= ss v *= self.scale_x cond = DiscDataRange(value, group_val) rect = FilterGraphicsRectItem(cond, cum + 1, -6, v - 2, 12) rect.setBrush(QBrush(color)) rect.setPen(QPen(Qt.NoPen)) value = value or missing_val_str if self.show_stretched: tooltip = f"{value}: {100 * freq / total:.2f}%" else: tooltip = f"{value}: ({int(freq)})" rect.setToolTip(tooltip) text = QGraphicsTextItem(value) box.append(rect) box.append(text) cum += v return box
def strudel(self, dist, group_val_index=None): attr = self.attribute ss = np.sum(dist) box = [] if ss < 1e-6: cond = [FilterDiscrete(attr, None)] if group_val_index is not None: cond.append(FilterDiscrete(self.group_var, [group_val_index])) box.append(FilterGraphicsRectItem(cond, 0, -10, 1, 10)) cum = 0 for i, v in enumerate(dist): if v < 1e-6: continue if self.stretched: v /= ss v *= self.scale_x cond = [FilterDiscrete(attr, [i])] if group_val_index is not None: cond.append(FilterDiscrete(self.group_var, [group_val_index])) rect = FilterGraphicsRectItem(cond, cum + 1, -6, v - 2, 12) rect.setBrush(QBrush(QColor(*attr.colors[i]))) rect.setPen(QPen(Qt.NoPen)) if self.stretched: tooltip = "{}: {:.2f}%".format(attr.values[i], 100 * dist[i] / sum(dist)) else: tooltip = "{}: {}".format(attr.values[i], int(dist[i])) rect.setToolTip(tooltip) text = QGraphicsTextItem(attr.values[i]) box.append(rect) box.append(text) cum += v return box
def _create_drag_tooltip(self, scene): tip_parts = [(Qt.ShiftModifier, "Shift: Add group"), (Qt.ShiftModifier + Qt.ControlModifier, "Shift-{}: Append to group".format( "Cmd" if sys.platform == "darwin" else "Ctrl")), (Qt.AltModifier, "Alt: Remove")] all_parts = ", ".join(part for _, part in tip_parts) self.tiptexts = { int(modifier): all_parts.replace(part, "<b>{}</b>".format(part)) for modifier, part in tip_parts } self.tiptexts[0] = all_parts self.tip_textitem = text = QGraphicsTextItem() # Set to the longest text text.setHtml(self.tiptexts[Qt.ShiftModifier + Qt.ControlModifier]) text.setPos(4, 2) r = text.boundingRect() rect = QGraphicsRectItem(0, 0, r.width() + 8, r.height() + 4) rect.setBrush(QColor(224, 224, 224, 212)) rect.setPen(QPen(Qt.NoPen)) self.update_tooltip(Qt.NoModifier) scene.drag_tooltip = scene.createItemGroup([rect, text]) scene.drag_tooltip.hide()
def paint(self, painter, option, widget=None): rect = self.rect() if self.isSelected(): option.state ^= QStyle.State_Selected font = self.document().defaultFont() painter.setFont(font) if self.parent: draw_text = str(self.tree_adapter.short_rule(self.node_inst)) if self.parent.x() > self.x(): # node is to the left fm = QFontMetrics(font) x = rect.width() / 2 - fm.width(draw_text) - 4 else: x = rect.width() / 2 + 4 painter.drawText(QPointF(x, -self.line_descent - 1), draw_text) painter.save() painter.setBrush(self.backgroundBrush) painter.setPen(QPen(Qt.black, 3 if self.isSelected() else 0)) adjrect = rect.adjusted(-3, 0, 0, 0) if not self.tree_adapter.has_children(self.node_inst): painter.drawRoundedRect(adjrect, 4, 4) else: painter.drawRect(adjrect) painter.restore() painter.setClipRect(rect) return QGraphicsTextItem.paint(self, painter, option, widget)
def setPos(self, x, y): """setPos with adjustment for alignment""" self.x, self.y = x, y rect = QGraphicsTextItem.boundingRect(self) if self.vertical: h, w = rect.height(), rect.width() rect.setWidth(h) rect.setHeight(-w) if int(self.alignment & Qt.AlignRight): x -= rect.width() elif int(self.alignment & Qt.AlignHCenter): x -= rect.width() / 2. if int(self.alignment & Qt.AlignBottom): y -= rect.height() elif int(self.alignment & Qt.AlignVCenter): y -= rect.height() / 2. QGraphicsTextItem.setPos(self, x, y)
def __init__(self, text, parent=None, **kwargs): super().__init__(parent, **kwargs) self.labelItem = QGraphicsTextItem(self) self.setHtml(text) self.labelItem.document().documentLayout().documentSizeChanged.connect( self.onLayoutChanged )
def paint(self, painter, option, widget=None): QGraphicsTextItem.paint(self, painter, option, widget) # Draw placeholder text if necessary if not (self.toPlainText() and self.toHtml()) and \ self.__placeholderText and \ not (self.hasFocus() and \ self.textInteractionFlags() & Qt.TextEditable): brect = self.boundingRect() painter.setFont(self.font()) metrics = painter.fontMetrics() text = metrics.elidedText(self.__placeholderText, Qt.ElideRight, brect.width()) color = self.defaultTextColor() color.setAlpha(min(color.alpha(), 150)) painter.setPen(QPen(color)) painter.drawText(brect, Qt.AlignTop | Qt.AlignLeft, text)
def paint(self, painter, option, widget=0): painter.save() painter.setBrush(self.backgroundBrush) painter.setPen(QPen(Qt.gray)) rect = self.rect() painter.drawRoundedRect(rect, 4, 4) painter.restore() painter.setClipRect(rect) return QGraphicsTextItem.paint(self, painter, option, widget)
class OWLegendTitle(QGraphicsObject): """ A legend item that shows ``text`` with a bold font and no symbol. """ def __init__(self, text, parent): QGraphicsObject.__init__(self, parent) self.text_item = QGraphicsTextItem(text + ':', self) f = self.text_item.font() f.setBold(True) self.text_item.setFont(f) self.rect_item = QGraphicsRectItem(self.text_item.boundingRect(), self) self.rect_item.setPen(QPen(Qt.NoPen)) self.rect_item.stackBefore(self.text_item) def boundingRect(self): return self.text_item.boundingRect() def paint(self, painter, option, widget): pass
class OWLegendItem(QGraphicsObject): """ Represents a legend item with a title and a point symbol. :param name: The text to display :type name: str :param point: The point symbol :type point: :obj:`.OWPoint` :param parent: The parent item, passed to QGraphicsItem :type parent: :obj:`QGraphicsItem` .. seealso:: :meth:`.OWLegend.add_item`, :meth:`.OWLegend.add_curve`. """ def __init__(self, name, point, parent): QGraphicsObject.__init__(self, parent) self.text_item = QGraphicsTextItem(name, self) if point: s = point.size() height = max(2 * s, self.text_item.boundingRect().height()) else: height = self.text_item.boundingRect().height() p = 0.5 * height self.text_item.setPos(height, 0) self.point_item = point if point: self.point_item.setParentItem(self) self.point_item.setPos(p, p) self._rect = QRectF(0, 0, height + self.text_item.boundingRect().width(), height) self.rect_item = QGraphicsRectItem(self._rect, self) self.rect_item.setPen(QPen(Qt.NoPen)) self.rect_item.stackBefore(self.text_item) if self.point_item: self.rect_item.stackBefore(self.point_item) def boundingRect(self): return self._rect def paint(self, painter, option, widget): pass
def display_changed_disc(self): assert not self.is_continuous self.clear_scene() self.attr_labels = [ QGraphicsSimpleTextItem(lab) for lab in self.label_txts_all ] if not self.stretched: if self.group_var: self.labels = [ QGraphicsTextItem("{}".format(int(sum(cont)))) for cont in self.conts if np.sum(cont) > 0 ] else: self.labels = [QGraphicsTextItem(str(int(sum(self.dist))))] self.draw_axis_disc() if self.group_var: self.boxes = \ [self.strudel(cont, i) for i, cont in enumerate(self.conts) if np.sum(cont) > 0] self.conts = self.conts[np.sum(np.array(self.conts), axis=1) > 0] else: self.boxes = [self.strudel(self.dist)] for row, box in enumerate(self.boxes): y = (-len(self.boxes) + row) * 40 + 10 bars, labels = box[::2], box[1::2] self.__draw_group_labels(y, row) if not self.stretched: self.__draw_row_counts(y, row) if self.show_labels and self.attribute is not self.group_var: self.__draw_bar_labels(y, bars, labels) self.__draw_bars(y, bars) self.box_scene.setSceneRect(-self.label_width - 5, -30 - len(self.boxes) * 40, self.scene_width, len(self.boxes * 40) + 90) self.infot1.setText("") self.select_box_items()
def __init__(self, parent=None, textItem=None): super().__init__(parent) if textItem is None: textItem = QGraphicsTextItem() self.__textItem = textItem self.__textItem.setParentItem(self) self.__textItem.setPos(0, 0) doc_layout = self.document().documentLayout() doc_layout.documentSizeChanged.connect(self._onDocumentSizeChanged)
def __init__(self, parent=None, text=""): super(QGraphicsPathItem, self).__init__(parent) self.setAcceptHoverEvents(True) self.setPen(QPen(Qt.NoPen)) self.text = QGraphicsTextItem(self) layout = self.text.document().documentLayout() layout.documentSizeChanged.connect(self._onLayoutChanged) self._text = "" self._anchor = QPointF()
class OWLegendItem(QGraphicsObject): """ Represents a legend item with a title and a point symbol. :param name: The text to display :type name: str :param point: The point symbol :type point: :obj:`.OWPoint` :param parent: The parent item, passed to QGraphicsItem :type parent: :obj:`QGraphicsItem` .. seealso:: :meth:`.OWLegend.add_item`, :meth:`.OWLegend.add_curve`. """ def __init__(self, name, point, parent): QGraphicsObject.__init__(self, parent) self.text_item = QGraphicsTextItem(name, self) if point: s = point.size() height = max(2*s, self.text_item.boundingRect().height()) else: height = self.text_item.boundingRect().height() p = 0.5 * height self.text_item.setPos(height, 0) self.point_item = point if point: self.point_item.setParentItem(self) self.point_item.setPos(p, p) self._rect = QRectF(0, 0, height + self.text_item.boundingRect().width(), height ) self.rect_item = QGraphicsRectItem(self._rect, self) self.rect_item.setPen(QPen(Qt.NoPen)) self.rect_item.stackBefore(self.text_item) if self.point_item: self.rect_item.stackBefore(self.point_item) def boundingRect(self): return self._rect def paint(self, painter, option, widget): pass
class Marker(orangeqt.PlotItem): """ Displays a text marker on the plot. :param text: The text to display. It can be HTML-formatted :type tex: str :param x: The x coordinate of the marker's position :type x: float :param y: The y coordinate of the marker's position :type y: float :param align: The text alignment :type align: :param bold: If ``True``, the text will be show bold. :type bold: int :param color: The text color :type color: QColor :param brushColor: The color of the brush user to paint the background :type color: QColor :param size: Font size :type size: int Markers have the QGraphicsItem.ItemIgnoresTransformations flag set by default, so text remains the same size when zooming. There is no need to scale the manually. """ def __init__(self, text, x, y, align, bold = 0, color = None, brushColor = None, size=None): orangeqt.PlotItem.__init__(self) self.setFlag(QGraphicsItem.ItemIgnoresTransformations, True) self._item = QGraphicsTextItem(text, parent=self) self._data_point = QPointF(x,y) f = self._item.font() f.setBold(bold) if size: f.setPointSize(size) self._item.setFont(f) self._item.setPos(x, y) def update_properties(self): self._item.setPos(self.graph_transform().map(self._data_point))
def __init__(self, name, point, parent): QGraphicsObject.__init__(self, parent) self.text_item = QGraphicsTextItem(name, self) if point: s = point.size() height = max(2*s, self.text_item.boundingRect().height()) else: height = self.text_item.boundingRect().height() p = 0.5 * height self.text_item.setPos(height, 0) self.point_item = point if point: self.point_item.setParentItem(self) self.point_item.setPos(p, p) self._rect = QRectF(0, 0, height + self.text_item.boundingRect().width(), height ) self.rect_item = QGraphicsRectItem(self._rect, self) self.rect_item.setPen(QPen(Qt.NoPen)) self.rect_item.stackBefore(self.text_item) if self.point_item: self.rect_item.stackBefore(self.point_item)
def __init__(self, *args): self.__boundingRect = None super().__init__(*args) self.setAcceptedMouseButtons(Qt.RightButton | Qt.LeftButton) self.setAcceptHoverEvents(True) self.setZValue(self.Z_VALUE) self.sourceItem = None self.sourceAnchor = None self.sinkItem = None self.sinkAnchor = None self.curveItem = LinkCurveItem(self) self.sourceIndicator = LinkAnchorIndicator(self) self.sinkIndicator = LinkAnchorIndicator(self) self.sourceIndicator.hide() self.sinkIndicator.hide() self.linkTextItem = QGraphicsTextItem(self) self.linkTextItem.setAcceptedMouseButtons(Qt.NoButton) self.linkTextItem.setAcceptHoverEvents(False) self.__sourceName = "" self.__sinkName = "" self.__dynamic = False self.__dynamicEnabled = False self.__state = LinkItem.NoState self.hover = False self.prepareGeometryChange() self.__updatePen() self.__boundingRect = None self.__updatePalette() self.__updateFont()
def __init__(self, *args, **kwargs): QGraphicsTextItem.__init__(self, *args, **kwargs) self.setAcceptHoverEvents(True) self.__placeholderText = "" self.__editing = False # text editing in progress
def __init__(self, *args, **kwargs): QGraphicsTextItem.__init__(self, *args, **kwargs) self.__placeholderText = ""
class VennIntersectionArea(QGraphicsPathItem): def __init__(self, parent=None, text=""): super(QGraphicsPathItem, self).__init__(parent) self.setAcceptHoverEvents(True) self.setPen(QPen(Qt.NoPen)) self.text = QGraphicsTextItem(self) layout = self.text.document().documentLayout() layout.documentSizeChanged.connect(self._onLayoutChanged) self._text = "" self._anchor = QPointF() def setText(self, text): if self._text != text: self._text = text self.text.setPlainText(text) def text(self): return self._text def setTextAnchor(self, pos): if self._anchor != pos: self._anchor = pos self._updateTextAnchor() def hoverEnterEvent(self, event): self.setZValue(self.zValue() + 1) return QGraphicsPathItem.hoverEnterEvent(self, event) def hoverLeaveEvent(self, event): self.setZValue(self.zValue() - 1) return QGraphicsPathItem.hoverLeaveEvent(self, event) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: if event.modifiers() & Qt.AltModifier: self.setSelected(False) elif event.modifiers() & Qt.ControlModifier: self.setSelected(not self.isSelected()) elif event.modifiers() & Qt.ShiftModifier: self.setSelected(True) else: for area in self.parentWidget().vennareas(): area.setSelected(False) self.setSelected(True) def mouseReleaseEvent(self, event): pass def paint(self, painter, option, widget=None): painter.save() path = self.path() brush = QBrush(self.brush()) pen = QPen(self.pen()) if option.state & QStyle.State_Selected: pen.setColor(Qt.red) brush.setStyle(Qt.DiagCrossPattern) brush.setColor(QColor(40, 40, 40, 100)) elif option.state & QStyle.State_MouseOver: pen.setColor(Qt.blue) if option.state & QStyle.State_MouseOver: brush.setColor(QColor(100, 100, 100, 100)) if brush.style() == Qt.NoBrush: # Make sure the highlight is actually visible. brush.setStyle(Qt.SolidPattern) painter.setPen(pen) painter.setBrush(brush) painter.drawPath(path) painter.restore() def itemChange(self, change, value): if change == QGraphicsPathItem.ItemSelectedHasChanged: self.setZValue(self.zValue() + (1 if value else -1)) return QGraphicsPathItem.itemChange(self, change, value) def _updateTextAnchor(self): rect = self.text.boundingRect() pos = anchor_rect(rect, self._anchor) self.text.setPos(pos) def _onLayoutChanged(self): self._updateTextAnchor()
class LinkItem(QGraphicsWidget): """ A Link item in the canvas that connects two :class:`.NodeItem`\s in the canvas. The link curve connects two `Anchor` items (see :func:`setSourceItem` and :func:`setSinkItem`). Once the anchors are set the curve automatically adjusts its end points whenever the anchors move. An optional source/sink text item can be displayed above the curve's central point (:func:`setSourceName`, :func:`setSinkName`) """ #: Z value of the item Z_VALUE = 0 #: Runtime link state value #: These are pulled from SchemeLink.State for ease of binding to it's #: state State = SchemeLink.State #: The link has no associated state. NoState = SchemeLink.NoState #: Link is empty; the source node does not have any value on output Empty = SchemeLink.Empty #: Link is active; the source node has a valid value on output Active = SchemeLink.Active #: The link is pending; the sink node is scheduled for update Pending = SchemeLink.Pending def __init__(self, *args): self.__boundingRect = None super().__init__(*args) self.setFlag(QGraphicsItem.ItemHasNoContents, True) self.setAcceptedMouseButtons(Qt.RightButton | Qt.LeftButton) self.setAcceptHoverEvents(True) self.setZValue(self.Z_VALUE) self.sourceItem = None self.sourceAnchor = None self.sinkItem = None self.sinkAnchor = None self.curveItem = LinkCurveItem(self) self.sourceIndicator = LinkAnchorIndicator(self) self.sinkIndicator = LinkAnchorIndicator(self) self.sourceIndicator.hide() self.sinkIndicator.hide() self.linkTextItem = QGraphicsTextItem(self) self.linkTextItem.setAcceptedMouseButtons(Qt.NoButton) self.linkTextItem.setAcceptHoverEvents(False) self.__sourceName = "" self.__sinkName = "" self.__dynamic = False self.__dynamicEnabled = False self.__state = LinkItem.NoState self.hover = False self.prepareGeometryChange() self.__updatePen() self.__boundingRect = None self.__updatePalette() self.__updateFont() def setSourceItem(self, item, anchor=None): """ Set the source `item` (:class:`.NodeItem`). Use `anchor` (:class:`.AnchorPoint`) as the curve start point (if ``None`` a new output anchor will be created using ``item.newOutputAnchor()``). Setting item to ``None`` and a valid anchor is a valid operation (for instance while mouse dragging one end of the link). """ if item is not None and anchor is not None: if anchor not in item.outputAnchors(): raise ValueError("Anchor must be belong to the item") if self.sourceItem != item: if self.sourceAnchor: # Remove a previous source item and the corresponding anchor self.sourceAnchor.scenePositionChanged.disconnect( self._sourcePosChanged ) if self.sourceItem is not None: self.sourceItem.removeOutputAnchor(self.sourceAnchor) self.sourceItem = self.sourceAnchor = None self.sourceItem = item if item is not None and anchor is None: # Create a new output anchor for the item if none is provided. anchor = item.newOutputAnchor() # Update the visibility of the start point indicator. self.sourceIndicator.setVisible(bool(item)) if anchor != self.sourceAnchor: if self.sourceAnchor is not None: self.sourceAnchor.scenePositionChanged.disconnect( self._sourcePosChanged ) self.sourceAnchor = anchor if self.sourceAnchor is not None: self.sourceAnchor.scenePositionChanged.connect( self._sourcePosChanged ) self.__updateCurve() def setSinkItem(self, item, anchor=None): """ Set the sink `item` (:class:`.NodeItem`). Use `anchor` (:class:`.AnchorPoint`) as the curve end point (if ``None`` a new input anchor will be created using ``item.newInputAnchor()``). Setting item to ``None`` and a valid anchor is a valid operation (for instance while mouse dragging one and of the link). """ if item is not None and anchor is not None: if anchor not in item.inputAnchors(): raise ValueError("Anchor must be belong to the item") if self.sinkItem != item: if self.sinkAnchor: # Remove a previous source item and the corresponding anchor self.sinkAnchor.scenePositionChanged.disconnect( self._sinkPosChanged ) if self.sinkItem is not None: self.sinkItem.removeInputAnchor(self.sinkAnchor) self.sinkItem = self.sinkAnchor = None self.sinkItem = item if item is not None and anchor is None: # Create a new input anchor for the item if none is provided. anchor = item.newInputAnchor() # Update the visibility of the end point indicator. self.sinkIndicator.setVisible(bool(item)) if self.sinkAnchor != anchor: if self.sinkAnchor is not None: self.sinkAnchor.scenePositionChanged.disconnect( self._sinkPosChanged ) self.sinkAnchor = anchor if self.sinkAnchor is not None: self.sinkAnchor.scenePositionChanged.connect( self._sinkPosChanged ) self.__updateCurve() def setChannelNamesVisible(self, visible): """ Set the visibility of the channel name text. """ self.linkTextItem.setVisible(visible) def setSourceName(self, name): """ Set the name of the source (used in channel name text). """ if self.__sourceName != name: self.__sourceName = name self.__updateText() def sourceName(self): """ Return the source name. """ return self.__sourceName def setSinkName(self, name): """ Set the name of the sink (used in channel name text). """ if self.__sinkName != name: self.__sinkName = name self.__updateText() def sinkName(self): """ Return the sink name. """ return self.__sinkName def _sinkPosChanged(self, *arg): self.__updateCurve() def _sourcePosChanged(self, *arg): self.__updateCurve() def __updateCurve(self): self.prepareGeometryChange() self.__boundingRect = None if self.sourceAnchor and self.sinkAnchor: source_pos = self.sourceAnchor.anchorScenePos() sink_pos = self.sinkAnchor.anchorScenePos() source_pos = self.curveItem.mapFromScene(source_pos) sink_pos = self.curveItem.mapFromScene(sink_pos) # Adaptive offset for the curve control points to avoid a # cusp when the two points have the same y coordinate # and are close together delta = source_pos - sink_pos dist = math.sqrt(delta.x() ** 2 + delta.y() ** 2) cp_offset = min(dist / 2.0, 60.0) # TODO: make the curve tangent orthogonal to the anchors path. path = QPainterPath() path.moveTo(source_pos) path.cubicTo(source_pos + QPointF(cp_offset, 0), sink_pos - QPointF(cp_offset, 0), sink_pos) self.curveItem.setCurvePath(path) self.sourceIndicator.setPos(source_pos) self.sinkIndicator.setPos(sink_pos) self.__updateText() else: self.setHoverState(False) self.curveItem.setPath(QPainterPath()) def __updateText(self): self.prepareGeometryChange() self.__boundingRect = None if self.__sourceName or self.__sinkName: if self.__sourceName != self.__sinkName: text = ("<nobr>{0}</nobr> \u2192 <nobr>{1}</nobr>" .format(escape(self.__sourceName), escape(self.__sinkName))) else: # If the names are the same show only one. # Is this right? If the sink has two input channels of the # same type having the name on the link help elucidate # the scheme. text = escape(self.__sourceName) else: text = "" self.linkTextItem.setHtml('<div align="center">{0}</div>' .format(text)) path = self.curveItem.curvePath() # Constrain the text width if it is too long to fit on a single line # between the two ends if not path.isEmpty(): # Use the distance between the start/end points as a measure of # available space diff = path.pointAtPercent(0.0) - path.pointAtPercent(1.0) available_width = math.sqrt(diff.x() ** 2 + diff.y() ** 2) # Get the ideal text width if it was unconstrained doc = self.linkTextItem.document().clone(self) doc.setTextWidth(-1) idealwidth = doc.idealWidth() doc.deleteLater() # Constrain the text width but not below a certain min width minwidth = 100 textwidth = max(minwidth, min(available_width, idealwidth)) self.linkTextItem.setTextWidth(textwidth) else: # Reset the fixed width self.linkTextItem.setTextWidth(-1) if not path.isEmpty(): center = path.pointAtPercent(0.5) angle = path.angleAtPercent(0.5) brect = self.linkTextItem.boundingRect() transform = QTransform() transform.translate(center.x(), center.y()) transform.rotate(-angle) # Center and move above the curve path. transform.translate(-brect.width() / 2, -brect.height()) self.linkTextItem.setTransform(transform) def removeLink(self): self.setSinkItem(None) self.setSourceItem(None) self.__updateCurve() def setHoverState(self, state): if self.hover != state: self.prepareGeometryChange() self.__boundingRect = None self.hover = state self.sinkIndicator.setHoverState(state) self.sourceIndicator.setHoverState(state) self.curveItem.setHoverState(state) self.__updatePen() def hoverEnterEvent(self, event): # Hover enter event happens when the mouse enters any child object # but we only want to show the 'hovered' shadow when the mouse # is over the 'curveItem', so we install self as an event filter # on the LinkCurveItem and listen to its hover events. self.curveItem.installSceneEventFilter(self) return super().hoverEnterEvent(event) def hoverLeaveEvent(self, event): # Remove the event filter to prevent unnecessary work in # scene event filter when not needed self.curveItem.removeSceneEventFilter(self) return super().hoverLeaveEvent(event) def changeEvent(self, event): if event.type() == QEvent.PaletteChange: self.__updatePalette() elif event.type() == QEvent.FontChange: self.__updateFont() super().changeEvent(event) def sceneEventFilter(self, obj, event): if obj is self.curveItem: if event.type() == QEvent.GraphicsSceneHoverEnter: self.setHoverState(True) elif event.type() == QEvent.GraphicsSceneHoverLeave: self.setHoverState(False) return super().sceneEventFilter(obj, event) def boundingRect(self): if self.__boundingRect is None: self.__boundingRect = self.childrenBoundingRect() return self.__boundingRect def shape(self): return self.curveItem.shape() def setEnabled(self, enabled): """ Reimplemented from :class:`QGraphicWidget` Set link enabled state. When disabled the link is rendered with a dashed line. """ # This getter/setter pair override a property from the base class. # They should be renamed to e.g. setLinkEnabled/linkEnabled self.curveItem.setLinkEnabled(enabled) def isEnabled(self): return self.curveItem.isLinkEnabled() def setDynamicEnabled(self, enabled): """ Set the link's dynamic enabled state. If the link is `dynamic` it will be rendered in red/green color respectively depending on the state of the dynamic enabled state. """ if self.__dynamicEnabled != enabled: self.__dynamicEnabled = enabled if self.__dynamic: self.__updatePen() def isDynamicEnabled(self): """ Is the link dynamic enabled. """ return self.__dynamicEnabled def setDynamic(self, dynamic): """ Mark the link as dynamic (i.e. it responds to :func:`setDynamicEnabled`). """ if self.__dynamic != dynamic: self.__dynamic = dynamic self.__updatePen() def isDynamic(self): """ Is the link dynamic. """ return self.__dynamic def setRuntimeState(self, state): """ Style the link appropriate to the LinkItem.State Parameters ---------- state : LinkItem.State """ if self.__state != state: self.__state = state if state & LinkItem.Pending: self.sinkIndicator.setBrush(QBrush(Qt.red)) else: self.sinkIndicator.setBrush(QBrush(QColor("#9CACB4"))) self.__updatePen() def runtimeState(self): return self.__state def __updatePen(self): self.prepareGeometryChange() self.__boundingRect = None if self.__dynamic: if self.__dynamicEnabled: color = QColor(0, 150, 0, 150) else: color = QColor(150, 0, 0, 150) normal = QPen(QBrush(color), 2.0) hover = QPen(QBrush(color.darker(120)), 2.1) else: normal = QPen(QBrush(QColor("#9CACB4")), 2.0) hover = QPen(QBrush(QColor("#7D7D7D")), 2.1) if self.__state & LinkItem.Empty: pen_style = Qt.DashLine else: pen_style = Qt.SolidLine normal.setStyle(pen_style) hover.setStyle(pen_style) if self.hover: pen = hover else: pen = normal self.curveItem.setPen(pen) def __updatePalette(self): self.linkTextItem.setDefaultTextColor( self.palette().color(QPalette.Text)) def __updateFont(self): self.linkTextItem.setFont(self.font())
def __init__(self, text, parent, font): super().__init__(parent) self.__text = QGraphicsTextItem(text.title()) self.__text.setParentItem(self) self.__text.setFont(font)
class OWAxis(QGraphicsItem): Role = OWPalette.Axis def __init__(self, id, title='', title_above=False, title_location=AxisMiddle, line=None, arrows=0, plot=None, bounds=None): QGraphicsItem.__init__(self) self.setFlag(QGraphicsItem.ItemHasNoContents) self.setZValue(AxisZValue) self.id = id self.title = title self.title_location = title_location self.data_line = line self.plot = plot self.graph_line = None self.size = None self.scale = None self.tick_length = (10, 5, 0) self.arrows = arrows self.title_above = title_above self.line_item = QGraphicsLineItem(self) self.title_item = QGraphicsTextItem(self) self.end_arrow_item = None self.start_arrow_item = None self.show_title = False self.scale = None path = QPainterPath() path.setFillRule(Qt.WindingFill) path.moveTo(0, 3.09) path.lineTo(0, -3.09) path.lineTo(9.51, 0) path.closeSubpath() self.arrow_path = path self.label_items = [] self.label_bg_items = [] self.tick_items = [] self._ticks = [] self.zoom_transform = QTransform() self.labels = None self.values = None self._bounds = bounds self.auto_range = None self.auto_scale = True self.zoomable = False self.update_callback = None self.max_text_width = 50 self.text_margin = 5 self.always_horizontal_text = False @staticmethod def compute_scale(min, max): magnitude = int(3 * log10(abs(max - min)) + 1) if magnitude % 3 == 0: first_place = 1 elif magnitude % 3 == 1: first_place = 2 else: first_place = 5 magnitude = magnitude // 3 - 1 step = first_place * pow(10, magnitude) first_val = ceil(min / step) * step return first_val, step def update_ticks(self): self._ticks = [] major, medium, minor = self.tick_length if self.labels is not None and not self.auto_scale: values = self.values or range(len(self.labels)) for i, text in zip(values, self.labels): self._ticks.append((i, text, medium, 1)) else: if self.scale and not self.auto_scale: min, max, step = self.scale elif self.auto_range: min, max = self.auto_range if min is not None and max is not None: step = (max - min) / 10 else: return else: return if max == min: return val, step = self.compute_scale(min, max) while val <= max: self._ticks.append((val, "%.4g" % val, medium, step)) val += step def update_graph(self): if self.update_callback: self.update_callback() def update(self, zoom_only=False): self.update_ticks() line_color = self.plot.color(OWPalette.Axis) text_color = self.plot.color(OWPalette.Text) if not self.graph_line or not self.scene(): return self.line_item.setLine(self.graph_line) self.line_item.setPen(line_color) if self.title: self.title_item.setHtml('<b>' + self.title + '</b>') self.title_item.setDefaultTextColor(text_color) if self.title_location == AxisMiddle: title_p = 0.5 elif self.title_location == AxisEnd: title_p = 0.95 else: title_p = 0.05 title_pos = self.graph_line.pointAt(title_p) v = self.graph_line.normalVector().unitVector() dense_text = False if hasattr(self, 'title_margin'): offset = self.title_margin elif self._ticks: if self.should_be_expanded(): offset = 55 dense_text = True else: offset = 35 else: offset = 10 if self.title_above: title_pos += (v.p2() - v.p1()) * (offset + QFontMetrics(self.title_item.font()).height()) else: title_pos -= (v.p2() - v.p1()) * offset ## TODO: Move it according to self.label_pos self.title_item.setVisible(self.show_title) self.title_item.setRotation(-self.graph_line.angle()) c = self.title_item.mapToParent(self.title_item.boundingRect().center()) tl = self.title_item.mapToParent(self.title_item.boundingRect().topLeft()) self.title_item.setPos(title_pos - c + tl) ## Arrows if not zoom_only: if self.start_arrow_item: self.scene().removeItem(self.start_arrow_item) self.start_arrow_item = None if self.end_arrow_item: self.scene().removeItem(self.end_arrow_item) self.end_arrow_item = None if self.arrows & AxisStart: if not zoom_only or not self.start_arrow_item: self.start_arrow_item = QGraphicsPathItem(self.arrow_path, self) self.start_arrow_item.setPos(self.graph_line.p1()) self.start_arrow_item.setRotation(-self.graph_line.angle() + 180) self.start_arrow_item.setBrush(line_color) self.start_arrow_item.setPen(line_color) if self.arrows & AxisEnd: if not zoom_only or not self.end_arrow_item: self.end_arrow_item = QGraphicsPathItem(self.arrow_path, self) self.end_arrow_item.setPos(self.graph_line.p2()) self.end_arrow_item.setRotation(-self.graph_line.angle()) self.end_arrow_item.setBrush(line_color) self.end_arrow_item.setPen(line_color) ## Labels n = len(self._ticks) resize_plot_item_list(self.label_items, n, QGraphicsTextItem, self) resize_plot_item_list(self.label_bg_items, n, QGraphicsRectItem, self) resize_plot_item_list(self.tick_items, n, QGraphicsLineItem, self) test_rect = QRectF(self.graph_line.p1(), self.graph_line.p2()).normalized() test_rect.adjust(-1, -1, 1, 1) n_v = self.graph_line.normalVector().unitVector() if self.title_above: n_p = n_v.p2() - n_v.p1() else: n_p = n_v.p1() - n_v.p2() l_v = self.graph_line.unitVector() l_p = l_v.p2() - l_v.p1() for i in range(n): pos, text, size, step = self._ticks[i] hs = 0.5 * step tick_pos = self.map_to_graph(pos) if not test_rect.contains(tick_pos): self.tick_items[i].setVisible(False) self.label_items[i].setVisible(False) continue item = self.label_items[i] item.setVisible(True) if not zoom_only: if self.id in XAxes or getattr(self, 'is_horizontal', False): item.setHtml('<center>' + Qt.escape(text.strip()) + '</center>') else: item.setHtml(Qt.escape(text.strip())) item.setTextWidth(-1) text_angle = 0 if dense_text: w = min(item.boundingRect().width(), self.max_text_width) item.setTextWidth(w) if self.title_above: label_pos = tick_pos + n_p * (w + self.text_margin) + l_p * item.boundingRect().height() / 2 else: label_pos = tick_pos + n_p * self.text_margin + l_p * item.boundingRect().height() / 2 text_angle = -90 if self.title_above else 90 else: w = min(item.boundingRect().width(), QLineF(self.map_to_graph(pos - hs), self.map_to_graph(pos + hs)).length()) label_pos = tick_pos + n_p * self.text_margin + l_p * item.boundingRect().height() / 2 item.setTextWidth(w) if not self.always_horizontal_text: if self.title_above: item.setRotation(-self.graph_line.angle() - text_angle) else: item.setRotation(self.graph_line.angle() - text_angle) item.setPos(label_pos) item.setDefaultTextColor(text_color) self.label_bg_items[i].setRect(item.boundingRect()) self.label_bg_items[i].setPen(QPen(Qt.NoPen)) self.label_bg_items[i].setBrush(self.plot.color(OWPalette.Canvas)) item = self.tick_items[i] item.setVisible(True) tick_line = QLineF(v) tick_line.translate(-tick_line.p1()) tick_line.setLength(size) if self.title_above: tick_line.setAngle(tick_line.angle() + 180) item.setLine(tick_line) item.setPen(line_color) item.setPos(self.map_to_graph(pos)) @staticmethod def make_title(label, unit=None): lab = '<i>' + label + '</i>' if unit: lab = lab + ' [' + unit + ']' return lab def set_line(self, line): self.graph_line = line self.update() def set_title(self, title): self.title = title self.update() def set_show_title(self, b): self.show_title = b self.update() def set_labels(self, labels, values): self.labels = labels self.values = values self.graph_line = None self.auto_scale = False self.update_ticks() self.update_graph() def set_scale(self, min, max, step_size): self.scale = (min, max, step_size) self.graph_line = None self.auto_scale = False self.update_ticks() self.update_graph() def set_tick_length(self, minor, medium, major): self.tick_length = (minor, medium, major) self.update() def map_to_graph(self, x): min, max = self.plot.bounds_for_axis(self.id) if min == max: return QPointF() line_point = self.graph_line.pointAt((x - min) / (max - min)) end_point = line_point * self.zoom_transform return self.projection(end_point, self.graph_line) @staticmethod def projection(point, line): norm = line.normalVector() norm.translate(point - norm.p1()) p = QPointF() type = line.intersect(norm, p) return p def continuous_labels(self): min, max, step = self.scale magnitude = log10(abs(max - min)) def paint(self, painter, option, widget): pass def boundingRect(self): return QRectF() def ticks(self): if not self._ticks: self.update_ticks() return self._ticks def bounds(self): if self._bounds: return self._bounds if self.labels: return -0.2, len(self.labels) - 0.8 elif self.scale: min, max, _step = self.scale return min, max elif self.auto_range: return self.auto_range else: return 0, 1 def set_bounds(self, value): self._bounds = value def should_be_expanded(self): self.update_ticks() return self.id in YAxes or self.always_horizontal_text or sum( len(t[1]) for t in self._ticks) * 12 > self.plot.width()