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 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)
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))
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 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 VennIntersectionArea(QGraphicsPathItem): 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 setText(self, text): if self._text != text: self._text = text self.text.setPlainText(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 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 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()
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()