def _draw_border(point_1, point_2, border_width, parent): pen = QPen(QColor(self.border_color)) pen.setCosmetic(True) pen.setWidth(border_width) line = QGraphicsLineItem(QLineF(point_1, point_2), parent) line.setPen(pen) return line
def _anchor_circle(self): # minimum visible anchor radius (radius) minradius = self.radius / 100 + 1e-5 for item in chain(self.plotdata.anchoritem, self.plotdata.items): self.viewbox.removeItem(item) self.plotdata.anchoritem = [] self.plotdata.items = [] for anchor, var in zip(self.plotdata.anchors, self.data.domain.attributes): if True or np.linalg.norm(anchor) > minradius: axitem = AnchorItem( line=QLineF(0, 0, *anchor), text=var.name,) axitem.setVisible(np.linalg.norm(anchor) > minradius) axitem.setPen(pg.mkPen((100, 100, 100))) axitem.setArrowVisible(True) self.plotdata.anchoritem.append(axitem) self.viewbox.addItem(axitem) hidecircle = QGraphicsEllipseItem() hidecircle.setRect( QRectF(-minradius, -minradius, 2 * minradius, 2 * minradius)) _pen = QPen(Qt.lightGray, 1) _pen.setCosmetic(True) hidecircle.setPen(_pen) self.viewbox.addItem(hidecircle) self.plotdata.items.append(hidecircle) self.plotdata.hidecircle = hidecircle
def __init__(self, source, dest): super().__init__() self.setAcceptedMouseButtons(Qt.LeftButton) self.setCacheMode(self.DeviceCoordinateCache) # Without this, burn thy CPU self.setZValue(1) pen = QPen(Edge.Color.DEFAULT[0], 1) pen.setJoinStyle(Qt.MiterJoin) self.setPen(pen) self.arrowHead = QPolygonF() self._selected = False self._weights = [] self._labels = [] self.squares = GroupOfSquares(self) self.source = source self.dest = dest if source is dest: source.edges.append(self) else: source.edges.insert(0, self) dest.edges.insert(0, self) # Add text labels label = self.label = TextItem('', self) label.setFont(Edge.Font.DEFAULT) label.setZValue(3) self.adjust()
def __init__(self, tree_node, parent=None, **kwargs): self.tree_node = tree_node super().__init__(self._get_rect_attributes(), parent) self.tree_node.graphics_item = self self.setTransformOriginPoint(self.boundingRect().center()) self.setRotation(degrees(self.tree_node.square.angle)) self.setBrush(kwargs.get('brush', QColor('#297A1F'))) # The border should be invariant to scaling pen = QPen(QColor(Qt.black)) pen.setWidthF(0.75) pen.setCosmetic(True) self.setPen(pen) self.setAcceptHoverEvents(True) self.setZValue(kwargs.get('zvalue', 0)) self.z_step = Z_STEP # calculate the correct z values based on the parent if self.tree_node.parent != TreeAdapter.ROOT_PARENT: p = self.tree_node.parent # override root z step num_children = len(p.children) own_index = [1 if c.label == self.tree_node.label else 0 for c in p.children].index(1) self.z_step = int(p.graphics_item.z_step / num_children) base_z = p.graphics_item.zValue() self.setZValue(base_z + own_index * self.z_step)
def _anchor_circle(self, variables): # minimum visible anchor radius (radius) min_radius = self._get_min_radius() axisitems = [] for anchor, var in zip(self.plotdata.axes, variables[:]): axitem = AnchorItem(line=QLineF(0, 0, *anchor), text=var.name,) axitem.setVisible(np.linalg.norm(anchor) > min_radius) axitem.setPen(pg.mkPen((100, 100, 100))) axitem.setArrowVisible(True) self.viewbox.addItem(axitem) axisitems.append(axitem) self.plotdata.axisitems = axisitems if self.placement == self.Placement.Circular: return hidecircle = QGraphicsEllipseItem() hidecircle.setRect(QRectF(-min_radius, -min_radius, 2 * min_radius, 2 * min_radius)) _pen = QPen(Qt.lightGray, 1) _pen.setCosmetic(True) hidecircle.setPen(_pen) self.viewbox.addItem(hidecircle) self.plotdata.hidecircle = hidecircle
def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: downPos = event.buttonDownPos(Qt.LeftButton) if not self.__tmpLine and self.__dragStartItem and \ (downPos - event.pos()).manhattanLength() > \ QApplication.instance().startDragDistance(): # Start a line drag line = QGraphicsLineItem(self) start = self.__dragStartItem.boundingRect().center() start = self.mapFromItem(self.__dragStartItem, start) line.setLine(start.x(), start.y(), event.pos().x(), event.pos().y()) pen = QPen(Qt.black, 4) pen.setCapStyle(Qt.RoundCap) line.setPen(pen) line.show() self.__tmpLine = line if self.__tmpLine: # Update the temp line line = self.__tmpLine.line() line.setP2(event.pos()) self.__tmpLine.setLine(line) QGraphicsWidget.mouseMoveEvent(self, event)
def paint(self, painter, option, widget=None): painter.save() palette = self.palette() border = palette.brush(QPalette.Mid) pen = QPen(border, 1) pen.setCosmetic(True) painter.setPen(pen) painter.setBrush(palette.brush(QPalette.Window)) brect = self.boundingRect() painter.drawRoundedRect(brect, 4, 4) painter.restore()
class LinkLineItem(QGraphicsLineItem): """ A line connecting two Channel Anchors. """ def __init__(self, parent=None): QGraphicsLineItem.__init__(self, parent) self.setAcceptHoverEvents(True) self.__shape = None self.__default_pen = QPen(QColor('#383838'), 4) self.__default_pen.setCapStyle(Qt.RoundCap) self.__hover_pen = QPen(QColor('#000000'), 4) self.__hover_pen.setCapStyle(Qt.RoundCap) self.setPen(self.__default_pen) self.__shadow = QGraphicsDropShadowEffect( blurRadius=10, color=QColor('#9CACB4'), offset=QPointF(0, 0) ) self.setGraphicsEffect(self.__shadow) self.prepareGeometryChange() self.__shadow.setEnabled(False) def setLine(self, *args, **kwargs): super().setLine(*args, **kwargs) # extends mouse hit area stroke_path = QPainterPathStroker() stroke_path.setCapStyle(Qt.RoundCap) stroke_path.setWidth(10) self.__shape = stroke_path.createStroke(super().shape()) def shape(self): if self.__shape is None: return QGraphicsLineItem.shape(self) return self.__shape def hoverEnterEvent(self, event): self.prepareGeometryChange() self.__shadow.setEnabled(True) self.setPen(self.__hover_pen) self.setZValue(1.0) QGraphicsLineItem.hoverEnterEvent(self, event) def hoverLeaveEvent(self, event): self.prepareGeometryChange() self.__shadow.setEnabled(False) self.setPen(self.__default_pen) self.setZValue(0.0) QGraphicsLineItem.hoverLeaveEvent(self, event)
def show_pearson(rect, pearson, pen_width): """ Color the given rectangle according to its corresponding standardized Pearson residual. Args: rect (QRect): the rectangle being drawn pearson (float): signed standardized pearson residual pen_width (int): pen width (bolder pen is used for selection) """ r = rect.rect() x, y, w, h = r.x(), r.y(), r.width(), r.height() if w == 0 or h == 0: return r = b = 255 if pearson > 0: r = g = max(255 - 20 * pearson, 55) elif pearson < 0: b = g = max(255 + 20 * pearson, 55) else: r = g = b = 224 rect.setBrush(QBrush(QColor(r, g, b))) pen_color = QColor(255 * (r == 255), 255 * (g == 255), 255 * (b == 255)) pen = QPen(pen_color, pen_width) rect.setPen(pen) if pearson > 0: pearson = min(pearson, 10) dist = 20 - 1.6 * pearson else: pearson = max(pearson, -10) dist = 20 - 8 * pearson pen.setWidth(1) def _offseted_line(ax, ay): r = QGraphicsLineItem(x + ax, y + ay, x + (ax or w), y + (ay or h)) self.canvas.addItem(r) r.setPen(pen) ax = dist while ax < w: _offseted_line(ax, 0) ax += dist ay = dist while ay < h: _offseted_line(0, ay) ay += dist
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 _setup_plot(self): target = self.target_index selected = self.selected_classifiers curves = [self.plot_curves(target, clf_idx) for clf_idx in selected] for curve in curves: self.plot.addItem(curve.curve_item) if self.display_convex_hull: hull = convex_hull([c.curve.hull for c in curves]) self.plot.plot(hull[0], hull[1], pen="y", antialias=True) pen = QPen(QColor(100, 100, 100, 100), 1, Qt.DashLine) pen.setCosmetic(True) self.plot.plot([0, 1], [0, 1], pen=pen, antialias=True)
def __init__(self, size=None, offset=None, pen=None, brush=None): super().__init__(size, offset) self.layout.setContentsMargins(5, 5, 5, 5) self.layout.setHorizontalSpacing(15) self.layout.setColumnAlignment(1, Qt.AlignLeft | Qt.AlignVCenter) if pen is None: pen = QPen(QColor(196, 197, 193, 200), 1) pen.setCosmetic(True) self.__pen = pen if brush is None: brush = QBrush(QColor(232, 232, 232, 100)) self.__brush = brush
def create_main_nomogram(self, attributes, attr_inds, name_items, points, max_width, point_text, name_offset): cls_index = self.target_class_index min_p = min(p.min() for p in points) max_p = max(p.max() for p in points) values = self.get_ruler_values(min_p, max_p, max_width) min_p, max_p = min(values), max(values) diff_ = np.nan_to_num(max_p - min_p) scale_x = max_width / diff_ if diff_ else max_width nomogram_header = NomogramItem() point_item = RulerItem(point_text, values, scale_x, name_offset, - scale_x * min_p) point_item.setPreferredSize(point_item.preferredWidth(), 35) nomogram_header.add_items([point_item]) self.nomogram_main = NomogramItem() cont_feature_item_class = ContinuousFeature2DItem if \ self.cont_feature_dim_index else ContinuousFeatureItem feature_items = [ DiscreteFeatureItem( name_item, attr.values, point, scale_x, name_offset, - scale_x * min_p) if attr.is_discrete else cont_feature_item_class( name_item, self.log_reg_cont_data_extremes[i][cls_index], self.get_ruler_values( point.min(), point.max(), scale_x * point.ptp(), False), scale_x, name_offset, - scale_x * min_p) for i, attr, name_item, point in zip(attr_inds, attributes, name_items, points)] self.nomogram_main.add_items(feature_items) self.feature_items = OrderedDict(sorted(zip(attr_inds, feature_items))) x = - scale_x * min_p y = self.nomogram_main.layout().preferredHeight() + 10 self.vertical_line = QGraphicsLineItem(x, -6, x, y) self.vertical_line.setPen(QPen(Qt.DotLine)) self.vertical_line.setParentItem(point_item) self.hidden_vertical_line = QGraphicsLineItem(x, -6, x, y) pen = QPen(Qt.DashLine) pen.setBrush(QColor(Qt.red)) self.hidden_vertical_line.setPen(pen) self.hidden_vertical_line.setParentItem(point_item) return point_item, nomogram_header
def crosshairs(color, radius=24, circle=False): radius = max(radius, 16) pixmap = QPixmap(radius, radius) pixmap.fill(Qt.transparent) painter = QPainter() painter.begin(pixmap) painter.setRenderHints(QPainter.Antialiasing) pen = QPen(QBrush(color), 1) pen.setWidthF(1.5) painter.setPen(pen) if circle: painter.drawEllipse(2, 2, radius - 2, radius - 2) painter.drawLine(radius / 2, 7, radius / 2, radius / 2 - 7) painter.drawLine(7, radius / 2, radius / 2 - 7, radius / 2) painter.end() return pixmap
def paint(self, painter, option, widget=None): # Override the default selected appearance if self.isSelected(): option.state ^= QStyle.State_Selected rect = self.rect() # this must render before overlay due to order in which it's drawn super().paint(painter, option, widget) painter.save() pen = QPen(QColor(Qt.black)) pen.setWidth(4) pen.setJoinStyle(Qt.MiterJoin) painter.setPen(pen) painter.drawRect(rect.adjusted(2, 2, -2, -2)) painter.restore() else: super().paint(painter, option, widget)
def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: downPos = event.buttonDownPos(Qt.LeftButton) if not self.__tmpLine and self.__dragStartItem and \ (downPos - event.pos()).manhattanLength() > \ QApplication.instance().startDragDistance(): # Start a line drag line = LinkLineItem(self) start = self.__dragStartItem.boundingRect().center() start = self.mapFromItem(self.__dragStartItem, start) eventPos = event.pos() line.setLine(start.x(), start.y(), eventPos.x(), eventPos.y()) pen = QPen(self.palette().color(QPalette.Foreground), 4) pen.setCapStyle(Qt.RoundCap) line.setPen(pen) line.show() self.__tmpLine = line if self.__dragStartItem in self.sourceNodeWidget.channelAnchors: for anchor in self.sinkNodeWidget.channelAnchors: self.__updateAnchorState(anchor, [self.__dragStartItem]) else: for anchor in self.sourceNodeWidget.channelAnchors: self.__updateAnchorState(anchor, [self.__dragStartItem]) if self.__tmpLine: # Update the temp line line = self.__tmpLine.line() maybe_anchor = find_item_at(self.scene(), event.scenePos(), type=ChannelAnchor) # If hovering over anchor if maybe_anchor is not None and maybe_anchor.isEnabled(): target_pos = maybe_anchor.boundingRect().center() target_pos = self.mapFromItem(maybe_anchor, target_pos) line.setP2(target_pos) else: target_pos = event.pos() line.setP2(target_pos) self.__tmpLine.setLine(line) QGraphicsWidget.mouseMoveEvent(self, event)
def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.setFlag(QGraphicsObject.ItemSendsGeometryChanges) self.__path = QPainterPath() self.__brush = QBrush(Qt.NoBrush) self.__pen = QPen() self.__boundingRect = None
def generate_pens(basecolor): pen = QPen(basecolor, 1) pen.setCosmetic(True) shadow_pen = QPen(pen.color().lighter(160), 2.5) shadow_pen.setCosmetic(True) return pen, shadow_pen
def __paint(self): picture = QPicture() painter = QPainter(picture) pen = QPen(QBrush(Qt.white), 0.5) pen.setCosmetic(True) painter.setPen(pen) geom = self.geometry x, y = geom.x(), geom.y() w, h = geom.width(), geom.height() wsingle = w / len(self.dist) for d, c in zip(self.dist, self.colors): painter.setBrush(QBrush(c)) painter.drawRect(QRectF(x, y, wsingle, d * h)) x += wsingle painter.end() self.__picture = picture
def display_distribution(self): dist = self.distributions var = self.var if dist is None or not len(dist): return self.plot.clear() self.plot_prob.clear() self.ploti.hideAxis('right') self.tooltip_items = [] bottomaxis = self.ploti.getAxis("bottom") bottomaxis.setLabel(var.name) bottomaxis.resizeEvent() self.set_left_axis_name() if var and var.is_continuous: bottomaxis.setTicks(None) if not len(dist[0]): return edges, curve = ash_curve(dist, None, m=OWDistributions.ASH_HIST, smoothing_factor=self.smoothing_factor) edges = edges + (edges[1] - edges[0])/2 edges = edges[:-1] if self.cumulative_distr: dx = edges[1] - edges[0] curve = numpy.cumsum(curve) * dx item = pg.PlotCurveItem() pen = QPen(QBrush(Qt.white), 3) pen.setCosmetic(True) item.setData(edges, curve, antialias=True, stepMode=False, fillLevel=0, brush=QBrush(Qt.gray), pen=pen) self.plot.addItem(item) item.tooltip = "Density" self.tooltip_items.append((self.plot, item)) else: bottomaxis.setTicks([list(enumerate(var.values))]) for i, w in enumerate(dist): geom = QRectF(i - 0.33, 0, 0.66, w) item = DistributionBarItem(geom, [1.0], [QColor(128, 128, 128)]) self.plot.addItem(item) item.tooltip = "Frequency for %s: %r" % (var.values[i], w) self.tooltip_items.append((self.plot, item))
def setPen(self, pen): """Set the items outline `pen` (:class:`QPen`). """ if not isinstance(pen, QPen): pen = QPen(pen) if self.__pen != pen: self.prepareGeometryChange() self.__pen = QPen(pen) self.__boundingRect = None self.update()
def __init__(self, parent=None): QGraphicsLineItem.__init__(self, parent) self.setAcceptHoverEvents(True) self.__shape = None self.__default_pen = QPen(QColor('#383838'), 4) self.__default_pen.setCapStyle(Qt.RoundCap) self.__hover_pen = QPen(QColor('#000000'), 4) self.__hover_pen.setCapStyle(Qt.RoundCap) self.setPen(self.__default_pen) self.__shadow = QGraphicsDropShadowEffect( blurRadius=10, color=QColor('#9CACB4'), offset=QPointF(0, 0) ) self.setGraphicsEffect(self.__shadow) self.prepareGeometryChange() self.__shadow.setEnabled(False)
def addLink(self, output, input): """ Add a link between `output` (:class:`OutputSignal`) and `input` (:class:`InputSignal`). """ if not compatible_channels(output, input): return if output not in self.source.output_channels(): raise ValueError("%r is not an output channel of %r" % \ (output, self.source)) if input not in self.sink.input_channels(): raise ValueError("%r is not an input channel of %r" % \ (input, self.sink)) if input.single: # Remove existing link if it exists. for s1, s2, _ in self.__links: if s2 == input: self.removeLink(s1, s2) line = QGraphicsLineItem(self) source_anchor = self.sourceNodeWidget.anchor(output) sink_anchor = self.sinkNodeWidget.anchor(input) source_pos = source_anchor.boundingRect().center() source_pos = self.mapFromItem(source_anchor, source_pos) sink_pos = sink_anchor.boundingRect().center() sink_pos = self.mapFromItem(sink_anchor, sink_pos) line.setLine(source_pos.x(), source_pos.y(), sink_pos.x(), sink_pos.y()) pen = QPen(Qt.black, 4) pen.setCapStyle(Qt.RoundCap) line.setPen(pen) self.__links.append(_Link(output, input, line))
def shape(self): if self.__shape is None: path = self.curvePath() pen = QPen(self.pen()) pen.setWidthF(max(pen.widthF(), 7.0)) pen.setStyle(Qt.SolidLine) self.__shape = stroke_path(path, pen) return self.__shape
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 _setup_plot(self): target = self.target_index selected = self.selected_classifiers curves = [self.plot_curves(target, clf_idx) for clf_idx in selected] for curve in curves: self.plot.addItem(curve.curve_item) if self.display_convex_hull: hull = convex_hull([c.curve.hull for c in curves]) self.plot.plot(hull[0], hull[1], pen="y", antialias=True) pen = QPen(QColor(100, 100, 100, 100), 1, Qt.DashLine) pen.setCosmetic(True) self.plot.plot([0, 1], [0, 1], pen=pen, antialias=True) warning = "" if not all(c.curve.is_valid for c in curves): if any(c.curve.is_valid for c in curves): warning = "Some lift curves are undefined" else: warning = "All lift curves are undefined" self.warning(warning)
def __init__(self, scene, num_of_atr=3, space=35, offset_y=10, rect_height=40): self.scene = scene self.num_of_atr = num_of_atr self.space = space self.graph_space = 80 self.offset_y = offset_y self.black_pen = QPen(Qt.black, 2) self.gray_pen = QPen(Qt.gray, 1) self.light_gray_pen = QPen(QColor("#DFDFDF"), 1) self.light_gray_pen.setStyle(Qt.DashLine) self.brush = QBrush(QColor(0x33, 0x88, 0xff, 0xc0)) self.blue_pen = QPen(QBrush(QColor(0x33, 0x00, 0xff)), 2) """placeholders""" self.rect_height = rect_height self.max_contrib = None self.atr_area_h = None self.atr_area_w = None self.scale = None
def __updateStyleState(self): """ Update the arrows' brush, pen, ... based on it's state """ if self.isSelected(): color = self.__color.darker(150) pen = QPen(QColor(96, 158, 215), Qt.DashDotLine) pen.setWidthF(1.25) pen.setCosmetic(True) self.__shadow.setColor(pen.color().darker(150)) else: color = self.__color pen = QPen(Qt.NoPen) self.__shadow.setColor(QColor(63, 63, 63, 180)) self.__arrowItem.setBrush(color) self.__arrowItem.setPen(pen)
def plot_curves(self, target, clf_idx): if (target, clf_idx) not in self._curve_data: curve = liftCurve_from_results(self.results, clf_idx, target) color = self.colors[clf_idx] pen = QPen(color, 1) pen.setCosmetic(True) shadow_pen = QPen(pen.color().lighter(160), 2.5) shadow_pen.setCosmetic(True) item = pg.PlotDataItem( curve.points[0], curve.points[1], pen=pen, shadowPen=shadow_pen, symbol="+", symbolSize=3, symbolPen=shadow_pen, antialias=True ) hull_item = pg.PlotDataItem( curve.hull[0], curve.hull[1], pen=pen, antialias=True ) self._curve_data[target, clf_idx] = \ PlotCurve(curve, item, hull_item) return self._curve_data[target, clf_idx]
def framePen(self): # type: () -> QPen """Return the frame pen. """ return QPen(self.__framePen)
def pen(self): if self.__hasPen: return super().pen() else: # bypass pg.AxisItem return QPen(self.palette().brush(QPalette.Text), 1)
def add_main_layout(self): self.data = None self.preprocessors = None self.learner = None self.scatterplot_item = None self.plot_item = None self.x_label = 'x' self.y_label = 'y' box = gui.vBox(self.controlArea, "Variables") self.x_var_model = itemmodels.VariableListModel() self.comboBoxAttributesX = gui.comboBox(box, self, value='x_var_index', label="Input: ", orientation=Qt.Horizontal, callback=self.apply, contentsLength=12) self.comboBoxAttributesX.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) self.comboBoxAttributesX.setModel(self.x_var_model) gui.doubleSpin(gui.indentedBox(box), self, "polynomialexpansion", 0, 10, label="Polynomial expansion:", callback=self.apply) gui.separator(box, height=8) self.y_var_model = itemmodels.VariableListModel() self.comboBoxAttributesY = gui.comboBox(box, self, value="y_var_index", label="Target: ", orientation=Qt.Horizontal, callback=self.apply, contentsLength=12) self.comboBoxAttributesY.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) self.comboBoxAttributesY.setModel(self.y_var_model) gui.rubber(self.controlArea) # main area GUI self.plotview = pg.PlotWidget(background="w") self.plot = self.plotview.getPlotItem() axis_color = self.palette().color(QPalette.Text) axis_pen = QPen(axis_color) tickfont = QFont(self.font()) tickfont.setPixelSize(max(int(tickfont.pixelSize() * 2 // 3), 11)) axis = self.plot.getAxis("bottom") axis.setLabel(self.x_label) axis.setPen(axis_pen) axis.setTickFont(tickfont) axis = self.plot.getAxis("left") axis.setLabel(self.y_label) axis.setPen(axis_pen) axis.setTickFont(tickfont) self.plot.setRange(xRange=(0.0, 1.0), yRange=(0.0, 1.0), disableAutoRange=True) self.mainArea.layout().addWidget(self.plotview)
def __init__(self, *args): QGraphicsEllipseItem.__init__(self, *args) self.setRect(-3.5, -3.5, 7., 7.) self.setPen(QPen(Qt.NoPen)) self.setBrush(QBrush(QColor("#9CACB4"))) self.__hover = False
def makeline(pos): pen = QPen(Qt.darkGray, 1) pen.setCosmetic(True) line = InfiniteLine(angle=90, pos=pos, pen=pen, movable=True) line.setCursor(Qt.SizeHorCursor) return line
def pen(self): return QPen(self.__pen)
def __paintEventNoStyle(self): p = QPainter(self) opt = QStyleOptionToolButton() self.initStyleOption(opt) fm = QFontMetrics(opt.font) palette = opt.palette # highlight brush is used as the background for the icon and background # when the tab is expanded and as mouse hover color (lighter). brush_highlight = palette.highlight() if opt.state & QStyle.State_Sunken: # State 'down' pressed during a mouse press (slightly darker). background_brush = brush_darker(brush_highlight, 110) elif opt.state & QStyle.State_MouseOver: background_brush = brush_darker(brush_highlight, 95) elif opt.state & QStyle.State_On: background_brush = brush_highlight else: # The default button brush. background_brush = palette.button() rect = opt.rect icon_area_rect = QRect(rect) icon_area_rect.setRight(int(icon_area_rect.height() * 1.26)) text_rect = QRect(rect) text_rect.setLeft(icon_area_rect.right() + 10) # Background (TODO: Should the tab button have native # toolbutton shape, drawn using PE_PanelButtonTool or even # QToolBox tab shape) # Default outline pen pen = QPen(palette.color(QPalette.Mid)) p.save() p.setPen(Qt.NoPen) p.setBrush(QBrush(background_brush)) p.drawRect(rect) # Draw the background behind the icon if the background_brush # is different. if not opt.state & QStyle.State_On: p.setBrush(brush_highlight) p.drawRect(icon_area_rect) # Line between the icon and text p.setPen(pen) p.drawLine(icon_area_rect.topRight(), icon_area_rect.bottomRight()) if opt.state & QStyle.State_HasFocus: # Set the focus frame pen and draw the border pen = QPen(QColor(FOCUS_OUTLINE_COLOR)) p.setPen(pen) p.setBrush(Qt.NoBrush) # Adjust for pen rect = rect.adjusted(0, 0, -1, -1) p.drawRect(rect) else: p.setPen(pen) # Draw the top/bottom border if self.position == QStyleOptionToolBox.OnlyOneTab or \ self.position == QStyleOptionToolBox.Beginning or \ self.selected & \ QStyleOptionToolBox.PreviousIsSelected: p.drawLine(rect.topLeft(), rect.topRight()) p.drawLine(rect.bottomLeft(), rect.bottomRight()) p.restore() p.save() text = fm.elidedText(opt.text, Qt.ElideRight, text_rect.width()) p.setPen(QPen(palette.color(QPalette.ButtonText))) p.setFont(opt.font) p.drawText(text_rect, int(Qt.AlignVCenter | Qt.AlignLeft) | \ int(Qt.TextSingleLine), text) if not opt.icon.isNull(): if opt.state & QStyle.State_Enabled: mode = QIcon.Normal else: mode = QIcon.Disabled if opt.state & QStyle.State_On: state = QIcon.On else: state = QIcon.Off icon_area_rect = icon_area_rect icon_rect = QRect(QPoint(0, 0), opt.iconSize) icon_rect.moveCenter(icon_area_rect.center()) opt.icon.paint(p, icon_rect, Qt.AlignCenter, mode, state) p.restore()
def __init__(self): super().__init__() self.results = None self.classifier_names = [] self.perf_line = None self.colors = [] self._curve_data = {} self._plot_curves = {} self._rocch = None self._perf_line = None self._tooltip_cache = None box = gui.vBox(self.controlArea, "Plot") self.target_cb = gui.comboBox(box, self, "target_index", label="Target", orientation=Qt.Horizontal, callback=self._on_target_changed, contentsLength=8, searchable=True) gui.widgetLabel(box, "Classifiers") line_height = 4 * QFontMetrics(self.font()).lineSpacing() self.classifiers_list_box = gui.listBox( box, self, "selected_classifiers", "classifier_names", selectionMode=QListView.MultiSelection, callback=self._on_classifiers_changed, sizeHint=QSize(0, line_height)) abox = gui.vBox(self.controlArea, "Curves") gui.comboBox(abox, self, "roc_averaging", items=[ "Merge Predictions from Folds", "Mean TP Rate", "Mean TP and FP at Threshold", "Show Individual Curves" ], callback=self._replot) gui.checkBox(abox, self, "display_convex_curve", "Show convex ROC curves", callback=self._replot) gui.checkBox(abox, self, "display_convex_hull", "Show ROC convex hull", callback=self._replot) box = gui.vBox(self.controlArea, "Analysis") gui.checkBox(box, self, "display_def_threshold", "Default threshold (0.5) point", callback=self._on_display_def_threshold_changed) gui.checkBox(box, self, "display_perf_line", "Show performance line", callback=self._on_display_perf_line_changed) grid = QGridLayout() gui.indentedBox(box, orientation=grid) sp = gui.spin(box, self, "fp_cost", 1, 1000, 10, alignment=Qt.AlignRight, callback=self._on_display_perf_line_changed) grid.addWidget(QLabel("FP Cost:"), 0, 0) grid.addWidget(sp, 0, 1) sp = gui.spin(box, self, "fn_cost", 1, 1000, 10, alignment=Qt.AlignRight, callback=self._on_display_perf_line_changed) grid.addWidget(QLabel("FN Cost:")) grid.addWidget(sp, 1, 1) self.target_prior_sp = gui.spin(box, self, "target_prior", 1, 99, alignment=Qt.AlignRight, callback=self._on_target_prior_changed) self.target_prior_sp.setSuffix(" %") self.target_prior_sp.addAction(QAction("Auto", sp)) grid.addWidget(QLabel("Prior probability:")) grid.addWidget(self.target_prior_sp, 2, 1) self.plotview = pg.GraphicsView(background="w") self.plotview.setFrameStyle(QFrame.StyledPanel) self.plotview.scene().sigMouseMoved.connect(self._on_mouse_moved) self.plot = pg.PlotItem(enableMenu=False) self.plot.setMouseEnabled(False, False) self.plot.hideButtons() pen = QPen(self.palette().color(QPalette.Text)) tickfont = QFont(self.font()) tickfont.setPixelSize(max(int(tickfont.pixelSize() * 2 // 3), 11)) axis = self.plot.getAxis("bottom") axis.setTickFont(tickfont) axis.setPen(pen) axis.setLabel("FP Rate (1-Specificity)") axis = self.plot.getAxis("left") axis.setTickFont(tickfont) axis.setPen(pen) axis.setLabel("TP Rate (Sensitivity)") self.plot.showGrid(True, True, alpha=0.1) self.plot.setRange(xRange=(0.0, 1.0), yRange=(0.0, 1.0), padding=0.05) self.plotview.setCentralItem(self.plot) self.mainArea.layout().addWidget(self.plotview)
def __init__(self): super().__init__() box = gui.vBox(self.mainArea, True, margin=0) self.graph = OWScatterPlotGraph(self, box, "ScatterPlot") box.layout().addWidget(self.graph.plot_widget) plot = self.graph.plot_widget axispen = QPen(self.palette().color(QPalette.Text)) axis = plot.getAxis("bottom") axis.setPen(axispen) axis = plot.getAxis("left") axis.setPen(axispen) self.data = None # Orange.data.Table self.subset_data = None # Orange.data.Table self.data_metas_X = None # self.data, where primitive metas are moved to X self.sql_data = None # Orange.data.sql.table.SqlTable self.attribute_selection_list = None # list of Orange.data.Variable self.__timer = QTimer(self, interval=1200) self.__timer.timeout.connect(self.add_data) common_options = dict( labelWidth=50, orientation=Qt.Horizontal, sendSelectedValue=True, valueType=str) box = gui.vBox(self.controlArea, "Axis Data") dmod = DomainModel self.xy_model = DomainModel(dmod.MIXED, valid_types=dmod.PRIMITIVE) self.cb_attr_x = gui.comboBox( box, self, "attr_x", label="Axis x:", callback=self.update_attr, model=self.xy_model, **common_options) self.cb_attr_y = gui.comboBox( box, self, "attr_y", label="Axis y:", callback=self.update_attr, model=self.xy_model, **common_options) vizrank_box = gui.hBox(box) gui.separator(vizrank_box, width=common_options["labelWidth"]) self.vizrank, self.vizrank_button = ScatterPlotVizRank.add_vizrank( vizrank_box, self, "Find Informative Projections", self.set_attr) gui.separator(box) gui.valueSlider( box, self, value='graph.jitter_size', label='Jittering: ', values=self.jitter_sizes, callback=self.reset_graph_data, labelFormat=lambda x: "None" if x == 0 else ("%.1f %%" if x < 1 else "%d %%") % x) gui.checkBox( gui.indentedBox(box), self, 'graph.jitter_continuous', 'Jitter numeric values', callback=self.reset_graph_data) self.sampling = gui.auto_commit( self.controlArea, self, "auto_sample", "Sample", box="Sampling", callback=self.switch_sampling, commit=lambda: self.add_data(1)) self.sampling.setVisible(False) g = self.graph.gui g.point_properties_box(self.controlArea) self.models = [self.xy_model] + g.points_models box = gui.vBox(self.controlArea, "Plot Properties") g.add_widgets([g.ShowLegend, g.ShowGridLines], box) gui.checkBox( box, self, value='graph.tooltip_shows_all', label='Show all data on mouse hover') self.cb_class_density = gui.checkBox( box, self, value='graph.class_density', label='Show class density', callback=self.update_density) self.cb_reg_line = gui.checkBox( box, self, value='graph.show_reg_line', label='Show regression line', callback=self.update_regression_line) gui.checkBox( box, self, 'graph.label_only_selected', 'Label only selected points', callback=self.graph.update_labels) self.zoom_select_toolbar = g.zoom_select_toolbar( gui.vBox(self.controlArea, "Zoom/Select"), nomargin=True, buttons=[g.StateButtonsBegin, g.SimpleSelect, g.Pan, g.Zoom, g.StateButtonsEnd, g.ZoomReset] ) buttons = self.zoom_select_toolbar.buttons buttons[g.Zoom].clicked.connect(self.graph.zoom_button_clicked) buttons[g.Pan].clicked.connect(self.graph.pan_button_clicked) buttons[g.SimpleSelect].clicked.connect(self.graph.select_button_clicked) buttons[g.ZoomReset].clicked.connect(self.graph.reset_button_clicked) self.controlArea.layout().addStretch(100) self.icons = gui.attributeIconDict p = self.graph.plot_widget.palette() self.graph.set_palette(p) gui.auto_commit(self.controlArea, self, "auto_send_selection", "Send Selection", "Send Automatically") def zoom(s): """Zoom in/out by factor `s`.""" viewbox = plot.getViewBox() # scaleBy scales the view's bounds (the axis range) viewbox.scaleBy((1 / s, 1 / s)) def fit_to_view(): viewbox = plot.getViewBox() viewbox.autoRange() zoom_in = QAction( "Zoom in", self, triggered=lambda: zoom(1.25) ) zoom_in.setShortcuts([QKeySequence(QKeySequence.ZoomIn), QKeySequence(self.tr("Ctrl+="))]) zoom_out = QAction( "Zoom out", self, shortcut=QKeySequence.ZoomOut, triggered=lambda: zoom(1 / 1.25) ) zoom_fit = QAction( "Fit in view", self, shortcut=QKeySequence(Qt.ControlModifier | Qt.Key_0), triggered=fit_to_view ) self.addActions([zoom_in, zoom_out, zoom_fit])
def setPen(self, pen): if self.__pen != pen: self.prepareGeometryChange() self.__pen = QPen(pen) self.__shape = None super(LinkCurveItem, self).setPen(self.__pen)
def framePen(self): """Return the frame pen. """ return QPen(self.__framePen)
def make_pen(color, width=1): pen = QPen(color, width) pen.setCosmetic(True) return pen
def paintEvent(self, event): painter = QStylePainter(self) rect = self._subControlRect(QStyle.SC_SliderGroove) is_horizontal = self.orientation() == Qt.Horizontal minpos, maxpos = self.minimumPosition(), self.maximumPosition() span = rect.width() if is_horizontal else rect.height() x1 = QStyle.sliderPositionFromValue( self.minimum(), self.maximum(), minpos, span, self.invertedAppearance()) x2 = QStyle.sliderPositionFromValue( self.minimum(), self.maximum(), maxpos, span, self.invertedAppearance()) # Background painter.fillRect(rect, Qt.white) # Highlight painter.setOpacity(.7) if is_horizontal: painter.fillRect(x1, rect.y(), x2 - x1, rect.height(), Qt.yellow) else: painter.fillRect(rect.x(), x1, rect.width(), x2 - x1, Qt.yellow) painter.setOpacity(1) # Histogram if self._pixmap: painter.drawPixmap(rect, self._pixmap, self._pixmap.rect()) # Frame painter.setPen(QPen(QBrush(Qt.darkGray), 2)) painter.drawRect(rect) # Handles painter.setPen(QPen(QBrush(self._HANDLE_COLOR), self._HANDLE_WIDTH)) painter.setOpacity(9) if is_horizontal: painter.drawLine(x1, rect.y(), x1, rect.y() + rect.height()) painter.drawLine(x2, rect.y(), x2, rect.y() + rect.height()) else: painter.drawLine(rect.x(), x1, rect.x() + rect.width(), x1) painter.drawLine(rect.x(), x2, rect.x() + rect.width(), x2) painter.setOpacity(1) if self._show_text: painter.setFont(QFont('Monospace', 7, QFont.Bold)) strMin, strMax = self.formatValues(minpos, maxpos) widthMin = painter.fontMetrics().width(strMin) widthMax = painter.fontMetrics().width(strMax) height = painter.fontMetrics().height() is_enough_space = x2 - x1 > 3 + (max(widthMax, widthMin) if is_horizontal else (2 * height + self._HANDLE_WIDTH)) if is_enough_space: if is_horizontal: painter.drawText(x1 + 3, rect.y() + height, strMin) painter.drawText(x2 - widthMax - 2, rect.y() + rect.height() - 3, strMax) else: painter.drawText(rect.x() + 1, x1 + height, strMin) painter.drawText(rect.x() + rect.width() - widthMax - 1, x2 - 2, strMax) # Tooltip if self._showControlTooltip\ and (not self._show_text or not is_enough_space): # (show control-drag tooltip) painter.setFont(QFont('Monospace', 10, QFont.Normal)) text = "Hold {} to move time interval" \ .format("Cmd" if sys.platform == "darwin" else "Ctrl") w = painter.fontMetrics().width(text) h = painter.fontMetrics().height() brush = QColor(224, 224, 224, 212) pen = QPen(Qt.NoPen) rect = QRect(4, 4, w + 8, h + 4) painter.setBrush(brush) painter.setPen(pen) painter.drawRect(rect) painter.setPen(Qt.black) painter.drawText(8, 4 + h, text)
def add_points(): nonlocal cur, image_token if image_token != self._image_token: return batch = visible[cur:cur + self.N_POINTS_PER_ITER] batch_lat = lat[batch] batch_lon = lon[batch] x, y = self.Projection.latlon_to_easting_northing(batch_lat, batch_lon) x, y = self.Projection.easting_northing_to_pixel(x, y, zoom, origin, map_pane_pos) if self._jittering: dx, dy = self._jittering_offsets[batch].T x, y = x + dx, y + dy colors = (self._colorgen.getRGB(self._scaled_color_values[batch]).tolist() if self._color_attr else repeat((0xff, 0, 0))) sizes = self._size_coef * \ (self._sizes[batch] if self._size_attr else np.tile(10, len(batch))) opacity_subset, opacity_rest = self._opacity, int(.8 * self._opacity) for x, y, is_selected, size, color, _in_subset in \ zip(x, y, selected[batch], sizes, colors, in_subset[batch]): pensize2, selpensize2 = (.35, 1.5) if size >= 5 else (.15, .7) pensize2 *= self._size_coef selpensize2 *= self._size_coef size2 = size / 2 if is_selected: painter.setPen(QPen(QBrush(Qt.green), 2 * selpensize2)) painter.drawEllipse(x - size2 - selpensize2, y - size2 - selpensize2, size + selpensize2, size + selpensize2) color = QColor(*color) if _in_subset: color.setAlpha(opacity_subset) painter.setBrush(QBrush(color)) painter.setPen(QPen(QBrush(color.darker(180)), 2 * pensize2)) else: color.setAlpha(opacity_rest) painter.setBrush(Qt.NoBrush) painter.setPen(QPen(QBrush(color.lighter(120)), 2 * pensize2)) painter.drawEllipse(x - size2 - pensize2, y - size2 - pensize2, size + pensize2, size + pensize2) im.save(self._overlay_image_path, 'PNG') self.evalJS('markersImageLayer.setUrl("{}#{}"); 0;' .format(self.toFileURL(self._overlay_image_path), np.random.random())) cur += self.N_POINTS_PER_ITER if cur < len(visible): QTimer.singleShot(10, add_points) self._owwidget.progressBarAdvance(100 / n_iters, None) else: self._owwidget.progressBarFinished(None)
def line(x1, y1, x2, y2): r = QGraphicsLineItem(x1, y1, x2, y2, None) self.canvas.addItem(r) r.setPen(QPen(Qt.white, 2)) r.setZValue(30)
def update_selection_rects(self): pens = (QPen(), QPen(Qt.black, 3, Qt.DotLine)) for i, (_, _, area) in enumerate(self.areas): area.setPen(pens[i in self.selection])
def __init__(self, name, data_extremes, values, scale, name_offset, offset, coef): super().__init__() data_start, data_stop = data_extremes[0], data_extremes[1] self.name = name.toPlainText() self.diff = (data_stop - data_start) * coef labels = [ str( np.round( data_start + (data_stop - data_start) * i / (self.n_tck - 1), 1)) for i in range(self.n_tck) ] # leading label font = name.document().defaultFont() name.setFont(font) name.setPos(name_offset, -10) name.setParentItem(self) # labels ascending = data_start < data_stop y_start, y_stop = (self.y_diff, 0) if ascending else (0, self.y_diff) for i in range(self.n_tck): text = QGraphicsSimpleTextItem(labels[i]) w = text.boundingRect().width() y = y_start + (y_stop - y_start) / (self.n_tck - 1) * i text.setPos(-5 - w, y - 8) text.setParentItem(self) tick = QGraphicsLineItem(-2, y, 2, y) tick.setParentItem(self) # prediction marker self.dot = Continuous2DMovableDotItem(self.dot_r, scale, offset, values[0], values[-1], y_start, y_stop) self.dot.tooltip_labels = labels self.dot.tooltip_values = values self.dot.setParentItem(self) h_line = QGraphicsLineItem(values[0] * scale + offset, self.y_diff / 2, values[-1] * scale + offset, self.y_diff / 2) pen = QPen(Qt.DashLine) pen.setBrush(QColor(Qt.red)) h_line.setPen(pen) h_line.setParentItem(self) self.dot.horizontal_line = h_line # line line = QGraphicsLineItem(values[0] * scale + offset, y_start, values[-1] * scale + offset, y_stop) line.setParentItem(self) # ticks for value in values: diff_ = np.nan_to_num(values[-1] - values[0]) k = (value - values[0]) / diff_ if diff_ else 0 y_tick = (y_stop - y_start) * k + y_start - self.tick_height / 2 x_tick = value * scale - self.tick_width / 2 + offset tick = QGraphicsRectItem(x_tick, y_tick, self.tick_width, self.tick_height) tick.setBrush(QColor(Qt.black)) tick.setParentItem(self) # rect rect = QGraphicsRectItem(values[0] * scale + offset, -self.y_diff * 0.125, values[-1] * scale + offset, self.y_diff * 1.25) pen = QPen(Qt.DotLine) pen.setBrush(QColor(50, 150, 200, 255)) rect.setPen(pen) rect.setParentItem(self) self.setPreferredSize(self.preferredWidth(), self.y_diff * 1.5)
class OWBoxPlot(widget.OWWidget): """ Here's how the widget's functions call each other: - `set_data` is a signal handler fills the list boxes and calls `grouping_changed`. - `grouping_changed` handles changes of grouping attribute: it enables or disables the box for ordering, orders attributes and calls `attr_changed`. - `attr_changed` handles changes of attribute. It recomputes box data by calling `compute_box_data`, shows the appropriate display box (discrete/continuous) and then calls`layout_changed` - `layout_changed` constructs all the elements for the scene (as lists of QGraphicsItemGroup) and calls `display_changed`. It is called when the attribute or grouping is changed (by attr_changed) and on resize event. - `display_changed` puts the elements corresponding to the current display settings on the scene. It is called when the elements are reconstructed (layout is changed due to selection of attributes or resize event), or when the user changes display settings or colors. For discrete attributes, the flow is a bit simpler: the elements are not constructed in advance (by layout_changed). Instead, layout_changed and display_changed call display_changed_disc that draws everything. """ name = "箱形图" description = "在方框图中可视化特征值的分布。" icon = "icons/BoxPlot.svg" priority = 100 keywords = ["whisker"] class Inputs: data = Input("数据", Orange.data.Table) class Outputs: selected_data = Output("所选数据", Orange.data.Table, default=True) annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Orange.data.Table) #: Comparison types for continuous variables CompareNone, CompareMedians, CompareMeans = 0, 1, 2 settingsHandler = DomainContextHandler() conditions = ContextSetting([]) attribute = ContextSetting(None) order_by_importance = Setting(False) group_var = ContextSetting(None) show_annotations = Setting(True) compare = Setting(CompareMeans) stattest = Setting(0) sig_threshold = Setting(0.05) stretched = Setting(True) show_labels = Setting(True) sort_freqs = Setting(False) auto_commit = Setting(True) _sorting_criteria_attrs = { CompareNone: "", CompareMedians: "median", CompareMeans: "mean" } _pen_axis_tick = QPen(Qt.white, 5) _pen_axis = QPen(Qt.darkGray, 3) _pen_median = QPen(QBrush(QColor(0xff, 0xff, 0x00)), 2) _pen_paramet = QPen(QBrush(QColor(0x33, 0x00, 0xff)), 2) _pen_dotted = QPen(QBrush(QColor(0x33, 0x00, 0xff)), 1) _pen_dotted.setStyle(Qt.DotLine) _post_line_pen = QPen(Qt.lightGray, 2) _post_grp_pen = QPen(Qt.lightGray, 4) for pen in (_pen_paramet, _pen_median, _pen_dotted, _pen_axis, _pen_axis_tick, _post_line_pen, _post_grp_pen): pen.setCosmetic(True) pen.setCapStyle(Qt.RoundCap) pen.setJoinStyle(Qt.RoundJoin) _pen_axis_tick.setCapStyle(Qt.FlatCap) _box_brush = QBrush(QColor(0x33, 0x88, 0xff, 0xc0)) _axis_font = QFont() _axis_font.setPixelSize(12) _label_font = QFont() _label_font.setPixelSize(11) _attr_brush = QBrush(QColor(0x33, 0x00, 0xff)) graph_name = "盒式布景" def __init__(self): super().__init__() self.stats = [] self.dataset = None self.posthoc_lines = [] self.label_txts = self.mean_labels = self.boxes = self.labels = \ self.label_txts_all = self.attr_labels = self.order = [] self.p = -1.0 self.scale_x = self.scene_min_x = self.scene_width = 0 self.label_width = 0 self.attrs = VariableListModel() view = gui.listView(self.controlArea, self, "attribute", box="变量", model=self.attrs, callback=self.attr_changed) view.setMinimumSize(QSize(30, 30)) # Any other policy than Ignored will let the QListBox's scrollbar # set the minimal height (see the penultimate paragraph of # http://doc.qt.io/qt-4.8/qabstractscrollarea.html#addScrollBarWidget) view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored) gui.separator(view.box, 6, 6) self.cb_order = gui.checkBox(view.box, self, "order_by_importance", "按相关性排序", tooltip="由𝜒²或方差对子群排序", callback=self.apply_sorting) self.group_vars = DomainModel(placeholder="无", separators=False, valid_types=Orange.data.DiscreteVariable) self.group_view = view = gui.listView(self.controlArea, self, "group_var", box="子群", model=self.group_vars, callback=self.grouping_changed) view.setEnabled(False) view.setMinimumSize(QSize(30, 30)) # See the comment above view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored) # TODO: move Compare median/mean to grouping box # The vertical size policy is needed to let only the list views expand self.display_box = gui.vBox(self.controlArea, "Display", sizePolicy=(QSizePolicy.Minimum, QSizePolicy.Maximum), addSpace=False) gui.checkBox(self.display_box, self, "show_annotations", "Annotate", callback=self.display_changed) self.compare_rb = gui.radioButtonsInBox( self.display_box, self, 'compare', btnLabels=["无比较", "中位数比较", "均值比较"], callback=self.layout_changed) # The vertical size policy is needed to let only the list views expand self.stretching_box = box = gui.vBox(self.controlArea, box="显示", sizePolicy=(QSizePolicy.Minimum, QSizePolicy.Fixed)) self.stretching_box.sizeHint = self.display_box.sizeHint gui.checkBox(box, self, 'stretched', "拉杆", callback=self.display_changed) gui.checkBox(box, self, 'show_labels', "显示框标签", callback=self.display_changed) self.sort_cb = gui.checkBox(box, self, 'sort_freqs', "按子组频率排序", callback=self.display_changed) gui.rubber(box) gui.auto_commit(self.controlArea, self, "auto_commit", "选中发送", "自动发送") gui.vBox(self.mainArea, addSpace=True) self.box_scene = QGraphicsScene() self.box_scene.selectionChanged.connect(self.commit) self.box_view = QGraphicsView(self.box_scene) self.box_view.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform) self.box_view.viewport().installEventFilter(self) self.mainArea.layout().addWidget(self.box_view) e = gui.hBox(self.mainArea, addSpace=False) self.infot1 = gui.widgetLabel(e, "<center>没有测试结果。</center>") self.mainArea.setMinimumWidth(600) self.stats = self.dist = self.conts = [] self.is_continuous = False self.update_display_box() def sizeHint(self): return QSize(100, 500) # Vertical size is regulated by mainArea def eventFilter(self, obj, event): if obj is self.box_view.viewport() and \ event.type() == QEvent.Resize: self.layout_changed() return super().eventFilter(obj, event) def reset_attrs(self, domain): self.attrs[:] = [ var for var in chain(domain.class_vars, domain.metas, domain.attributes) if var.is_primitive() ] # noinspection PyTypeChecker @Inputs.data def set_data(self, dataset): if dataset is not None and (not bool(dataset) or not len(dataset.domain) and not any(var.is_primitive() for var in dataset.domain.metas)): dataset = None self.closeContext() self.dataset = dataset self.dist = self.stats = self.conts = [] self.group_var = None self.attribute = None if dataset: domain = dataset.domain self.group_vars.set_domain(domain) self.group_view.setEnabled(len(self.group_vars) > 1) self.reset_attrs(domain) self.select_default_variables(domain) self.openContext(self.dataset) self.grouping_changed() else: self.reset_all_data() self.commit() def select_default_variables(self, domain): # visualize first non-class variable, group by class (if present) if len(self.attrs) > len(domain.class_vars): self.attribute = self.attrs[len(domain.class_vars)] elif self.attrs: self.attribute = self.attrs[0] if domain.class_var and domain.class_var.is_discrete: self.group_var = domain.class_var else: self.group_var = None # Reset to trigger selection via callback def apply_sorting(self): def compute_score(attr): if attr is group_var: return 3 if attr.is_continuous: # One-way ANOVA col = data.get_column_view(attr)[0].astype(float) groups = (col[group_col == i] for i in range(n_groups)) groups = (col[~np.isnan(col)] for col in groups) groups = [group for group in groups if len(group)] p = f_oneway(*groups)[1] if len(groups) > 1 else 2 else: # Chi-square with the given distribution into groups # (see degrees of freedom in computation of the p-value) if not attr.values or not group_var.values: return 2 observed = np.array( contingency.get_contingency(data, group_var, attr)) observed = observed[observed.sum(axis=1) != 0, :] observed = observed[:, observed.sum(axis=0) != 0] if min(observed.shape) < 2: return 2 expected = \ np.outer(observed.sum(axis=1), observed.sum(axis=0)) / \ np.sum(observed) p = chisquare(observed.ravel(), f_exp=expected.ravel(), ddof=n_groups - 1)[1] if math.isnan(p): return 2 return p data = self.dataset if data is None: return domain = data.domain attribute = self.attribute group_var = self.group_var if self.order_by_importance and group_var is not None: n_groups = len(group_var.values) group_col = data.get_column_view(group_var)[0] if \ domain.has_continuous_attributes( include_class=True, include_metas=True) else None self.attrs.sort(key=compute_score) else: self.reset_attrs(domain) self.attribute = attribute def reset_all_data(self): self.clear_scene() self.infot1.setText("") self.attrs.clear() self.group_vars.set_domain(None) self.group_view.setEnabled(False) self.is_continuous = False self.update_display_box() def grouping_changed(self): self.cb_order.setEnabled(self.group_var is not None) self.apply_sorting() self.attr_changed() def select_box_items(self): temp_cond = self.conditions.copy() for box in self.box_scene.items(): if isinstance(box, FilterGraphicsRectItem): box.setSelected( box.filter.conditions in [c.conditions for c in temp_cond]) def attr_changed(self): self.compute_box_data() self.update_display_box() self.layout_changed() if self.is_continuous: heights = 90 if self.show_annotations else 60 self.box_view.centerOn(self.scene_min_x + self.scene_width / 2, -30 - len(self.stats) * heights / 2 + 45) else: self.box_view.centerOn(self.scene_width / 2, -30 - len(self.boxes) * 40 / 2 + 45) def compute_box_data(self): attr = self.attribute if not attr: return dataset = self.dataset self.is_continuous = attr.is_continuous if dataset is None or not self.is_continuous and not attr.values or \ self.group_var and not self.group_var.values: self.stats = self.dist = self.conts = [] return if self.group_var: self.dist = [] self.conts = contingency.get_contingency(dataset, attr, self.group_var) if self.is_continuous: stats, label_texts = [], [] for i, cont in enumerate(self.conts): if np.sum(cont[1]): stats.append(BoxData(cont, attr, i, self.group_var)) label_texts.append(self.group_var.values[i]) self.stats = stats self.label_txts_all = label_texts else: self.label_txts_all = \ [v for v, c in zip(self.group_var.values, self.conts) if np.sum(c) > 0] else: self.dist = distribution.get_distribution(dataset, attr) self.conts = [] if self.is_continuous: self.stats = [BoxData(self.dist, attr, None)] self.label_txts_all = [""] self.label_txts = [ txts for stat, txts in zip(self.stats, self.label_txts_all) if stat.n > 0 ] self.stats = [stat for stat in self.stats if stat.n > 0] def update_display_box(self): if self.is_continuous: self.stretching_box.hide() self.display_box.show() self.compare_rb.setEnabled(self.group_var is not None) else: self.stretching_box.show() self.display_box.hide() self.sort_cb.setEnabled(self.group_var is not None) def clear_scene(self): self.closeContext() self.box_scene.clearSelection() self.box_scene.clear() self.box_view.viewport().update() self.attr_labels = [] self.labels = [] self.boxes = [] self.mean_labels = [] self.posthoc_lines = [] self.openContext(self.dataset) def layout_changed(self): attr = self.attribute if not attr: return self.clear_scene() if self.dataset is None or len(self.conts) == len(self.dist) == 0: return if not self.is_continuous: self.display_changed_disc() return self.mean_labels = [ self.mean_label(stat, attr, lab) for stat, lab in zip(self.stats, self.label_txts) ] self.draw_axis() self.boxes = [self.box_group(stat) for stat in self.stats] self.labels = [ self.label_group(stat, attr, mean_lab) for stat, mean_lab in zip(self.stats, self.mean_labels) ] self.attr_labels = [ QGraphicsSimpleTextItem(lab) for lab in self.label_txts ] for it in chain(self.labels, self.attr_labels): self.box_scene.addItem(it) self.display_changed() def display_changed(self): if self.dataset is None: return if not self.is_continuous: self.display_changed_disc() return self.order = list(range(len(self.stats))) criterion = self._sorting_criteria_attrs[self.compare] if criterion: vals = [getattr(stat, criterion) for stat in self.stats] overmax = max((val for val in vals if val is not None), default=0) \ + 1 vals = [val if val is not None else overmax for val in vals] self.order = sorted(self.order, key=vals.__getitem__) heights = 90 if self.show_annotations else 60 for row, box_index in enumerate(self.order): y = (-len(self.stats) + row) * heights + 10 for item in self.boxes[box_index]: self.box_scene.addItem(item) item.setY(y) labels = self.labels[box_index] if self.show_annotations: labels.show() labels.setY(y) else: labels.hide() label = self.attr_labels[box_index] label.setY(y - 15 - label.boundingRect().height()) if self.show_annotations: label.hide() else: stat = self.stats[box_index] if self.compare == OWBoxPlot.CompareMedians and \ stat.median is not None: pos = stat.median + 5 / self.scale_x elif self.compare == OWBoxPlot.CompareMeans or stat.q25 is None: pos = stat.mean + 5 / self.scale_x else: pos = stat.q25 label.setX(pos * self.scale_x) label.show() r = QRectF(self.scene_min_x, -30 - len(self.stats) * heights, self.scene_width, len(self.stats) * heights + 90) self.box_scene.setSceneRect(r) self.compute_tests() self.show_posthoc() self.select_box_items() 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() def __draw_group_labels(self, y, row): """Draw group labels Parameters ---------- y: int vertical offset of bars row: int row index """ label = self.attr_labels[row] b = label.boundingRect() label.setPos(-b.width() - 10, y - b.height() / 2) self.box_scene.addItem(label) def __draw_row_counts(self, y, row): """Draw row counts Parameters ---------- y: int vertical offset of bars row: int row index """ assert not self.is_continuous 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) def __draw_bar_labels(self, y, bars, labels): """Draw bar labels Parameters ---------- y: int vertical offset of bars bars: List[FilterGraphicsRectItem] list of bars being drawn labels: List[QGraphicsTextItem] list of labels for corresponding bars """ label = bar_part = None for text_item, bar_part in zip(labels, bars): label = self.Label(text_item.toPlainText()) label.setPos(bar_part.boundingRect().x(), y - label.boundingRect().height() - 8) label.setMaxWidth(bar_part.boundingRect().width()) self.box_scene.addItem(label) def __draw_bars(self, y, bars): """Draw bars Parameters ---------- y: int vertical offset of bars bars: List[FilterGraphicsRectItem] list of bars to draw """ for item in bars: item.setPos(0, y) self.box_scene.addItem(item) # noinspection PyPep8Naming def compute_tests(self): # The t-test and ANOVA are implemented here since they efficiently use # the widget-specific data in self.stats. # The non-parametric tests can't do this, so we use statistics.tests def stat_ttest(): d1, d2 = self.stats if d1.n == 0 or d2.n == 0: return np.nan, np.nan pooled_var = d1.var / d1.n + d2.var / d2.n df = pooled_var ** 2 / \ ((d1.var / d1.n) ** 2 / (d1.n - 1) + (d2.var / d2.n) ** 2 / (d2.n - 1)) if pooled_var == 0: return np.nan, np.nan t = abs(d1.mean - d2.mean) / math.sqrt(pooled_var) p = 2 * (1 - scipy.special.stdtr(df, t)) return t, p # TODO: Check this function # noinspection PyPep8Naming def stat_ANOVA(): if any(stat.n == 0 for stat in self.stats): return np.nan, np.nan n = sum(stat.n for stat in self.stats) grand_avg = sum(stat.n * stat.mean for stat in self.stats) / n var_between = sum(stat.n * (stat.mean - grand_avg)**2 for stat in self.stats) df_between = len(self.stats) - 1 var_within = sum(stat.n * stat.var for stat in self.stats) df_within = n - len(self.stats) F = (var_between / df_between) / (var_within / df_within) p = 1 - scipy.special.fdtr(df_between, df_within, F) return F, p if self.compare == OWBoxPlot.CompareNone or len(self.stats) < 2: t = "" elif any(s.n <= 1 for s in self.stats): t = "At least one group has just one instance," \ "cannot compute significance" elif len(self.stats) == 2: if self.compare == OWBoxPlot.CompareMedians: t = "" # z, self.p = tests.wilcoxon_rank_sum( # self.stats[0].dist, self.stats[1].dist) # t = "Mann-Whitney's z: %.1f (p=%.3f)" % (z, self.p) else: t, self.p = stat_ttest() t = "Student's t: %.3f (p=%.3f)" % (t, self.p) else: if self.compare == OWBoxPlot.CompareMedians: t = "" # U, self.p = -1, -1 # t = "Kruskal Wallis's U: %.1f (p=%.3f)" % (U, self.p) else: F, self.p = stat_ANOVA() t = "ANOVA: %.3f (p=%.3f)" % (F, self.p) self.infot1.setText("<center>%s</center>" % t) def mean_label(self, stat, attr, val_name): label = QGraphicsItemGroup() t = QGraphicsSimpleTextItem( "%.*f" % (attr.number_of_decimals + 1, stat.mean), label) t.setFont(self._label_font) bbox = t.boundingRect() w2, h = bbox.width() / 2, bbox.height() t.setPos(-w2, -h) tpm = QGraphicsSimpleTextItem( " \u00b1 " + "%.*f" % (attr.number_of_decimals + 1, stat.dev), label) tpm.setFont(self._label_font) tpm.setPos(w2, -h) if val_name: vnm = QGraphicsSimpleTextItem(val_name + ": ", label) vnm.setFont(self._label_font) vnm.setBrush(self._attr_brush) vb = vnm.boundingRect() label.min_x = -w2 - vb.width() vnm.setPos(label.min_x, -h) else: label.min_x = -w2 return label def draw_axis(self): """Draw the horizontal axis and sets self.scale_x""" misssing_stats = not self.stats stats = self.stats or [BoxData(np.array([[0.], [1.]]), self.attribute)] mean_labels = self.mean_labels or [ self.mean_label(stats[0], self.attribute, "") ] bottom = min(stat.a_min for stat in stats) top = max(stat.a_max for stat in stats) first_val, step = compute_scale(bottom, top) while bottom <= first_val: first_val -= step bottom = first_val no_ticks = math.ceil((top - first_val) / step) + 1 top = max(top, first_val + no_ticks * step) gbottom = min(bottom, min(stat.mean - stat.dev for stat in stats)) gtop = max(top, max(stat.mean + stat.dev for stat in stats)) bv = self.box_view viewrect = bv.viewport().rect().adjusted(15, 15, -15, -30) self.scale_x = scale_x = viewrect.width() / (gtop - gbottom) # In principle we should repeat this until convergence since the new # scaling is too conservative. (No chance am I doing this.) mlb = min(stat.mean + mean_lab.min_x / scale_x for stat, mean_lab in zip(stats, mean_labels)) if mlb < gbottom: gbottom = mlb self.scale_x = scale_x = viewrect.width() / (gtop - gbottom) self.scene_min_x = gbottom * scale_x self.scene_width = (gtop - gbottom) * scale_x val = first_val decimals = max(3, 4 - int(math.log10(step))) while True: l = self.box_scene.addLine(val * scale_x, -1, val * scale_x, 1, self._pen_axis_tick) l.setZValue(100) t = self.box_scene.addSimpleText( repr(round(val, decimals)) if not misssing_stats else "?", self._axis_font) t.setFlags(t.flags() | QGraphicsItem.ItemIgnoresTransformations) r = t.boundingRect() t.setPos(val * scale_x - r.width() / 2, 8) if val >= top: break val += step self.box_scene.addLine(bottom * scale_x - 4, 0, top * scale_x + 4, 0, self._pen_axis) def draw_axis_disc(self): """ Draw the horizontal axis and sets self.scale_x for discrete attributes """ assert not self.is_continuous if self.stretched: if not self.attr_labels: return step = steps = 10 else: if self.group_var: max_box = max(float(np.sum(dist)) for dist in self.conts) else: max_box = float(np.sum(self.dist)) if max_box == 0: self.scale_x = 1 return _, step = compute_scale(0, max_box) step = int(step) if step > 1 else 1 steps = int(math.ceil(max_box / step)) max_box = step * steps bv = self.box_view viewrect = bv.viewport().rect().adjusted(15, 15, -15, -30) self.scene_width = viewrect.width() lab_width = max(lab.boundingRect().width() for lab in self.attr_labels) lab_width = max(lab_width, 40) lab_width = min(lab_width, self.scene_width / 3) self.label_width = lab_width right_offset = 0 # offset for the right label if not self.stretched and self.labels: if self.group_var: rows = list(zip(self.conts, self.labels)) else: rows = [(self.dist, self.labels[0])] # available space left of the 'group labels' available = self.scene_width - lab_width - 10 scale_x = (available - right_offset) / max_box max_right = max( sum(dist) * scale_x + 10 + lbl.boundingRect().width() for dist, lbl in rows) right_offset = max(0, max_right - max_box * scale_x) self.scale_x = scale_x = \ (self.scene_width - lab_width - 10 - right_offset) / max_box self.box_scene.addLine(0, 0, max_box * scale_x, 0, self._pen_axis) for val in range(0, step * steps + 1, step): l = self.box_scene.addLine(val * scale_x, -1, val * scale_x, 1, self._pen_axis_tick) l.setZValue(100) t = self.box_scene.addSimpleText(str(val), self._axis_font) t.setPos(val * scale_x - t.boundingRect().width() / 2, 8) if self.stretched: self.scale_x *= 100 def label_group(self, stat, attr, mean_lab): def centered_text(val, pos): t = QGraphicsSimpleTextItem( "%.*f" % (attr.number_of_decimals + 1, val), labels) t.setFont(self._label_font) bbox = t.boundingRect() t.setPos(pos - bbox.width() / 2, 22) return t def line(x, down=1): QGraphicsLineItem(x, 12 * down, x, 20 * down, labels) def move_label(label, frm, to): label.setX(to) to += t_box.width() / 2 path = QPainterPath() path.lineTo(0, 4) path.lineTo(to - frm, 4) path.lineTo(to - frm, 8) p = QGraphicsPathItem(path) p.setPos(frm, 12) labels.addToGroup(p) labels = QGraphicsItemGroup() labels.addToGroup(mean_lab) m = stat.mean * self.scale_x mean_lab.setPos(m, -22) line(m, -1) if stat.median is not None: msc = stat.median * self.scale_x med_t = centered_text(stat.median, msc) med_box_width2 = med_t.boundingRect().width() / 2 line(msc) if stat.q25 is not None: x = stat.q25 * self.scale_x t = centered_text(stat.q25, x) t_box = t.boundingRect() med_left = msc - med_box_width2 if x + t_box.width() / 2 >= med_left - 5: move_label(t, x, med_left - t_box.width() - 5) else: line(x) if stat.q75 is not None: x = stat.q75 * self.scale_x t = centered_text(stat.q75, x) t_box = t.boundingRect() med_right = msc + med_box_width2 if x - t_box.width() / 2 <= med_right + 5: move_label(t, x, med_right + 5) else: line(x) return labels def box_group(self, stat, height=20): def line(x0, y0, x1, y1, *args): return QGraphicsLineItem(x0 * scale_x, y0, x1 * scale_x, y1, *args) scale_x = self.scale_x box = [] whisker1 = line(stat.a_min, -1.5, stat.a_min, 1.5) whisker2 = line(stat.a_max, -1.5, stat.a_max, 1.5) vert_line = line(stat.a_min, 0, stat.a_max, 0) mean_line = line(stat.mean, -height / 3, stat.mean, height / 3) for it in (whisker1, whisker2, mean_line): it.setPen(self._pen_paramet) vert_line.setPen(self._pen_dotted) var_line = line(stat.mean - stat.dev, 0, stat.mean + stat.dev, 0) var_line.setPen(self._pen_paramet) box.extend([whisker1, whisker2, vert_line, mean_line, var_line]) if stat.q25 is not None and stat.q75 is not None: mbox = FilterGraphicsRectItem(stat.conditions, stat.q25 * scale_x, -height / 2, (stat.q75 - stat.q25) * scale_x, height) mbox.setBrush(self._box_brush) mbox.setPen(QPen(Qt.NoPen)) mbox.setZValue(-200) box.append(mbox) if stat.median is not None: median_line = line(stat.median, -height / 2, stat.median, height / 2) median_line.setPen(self._pen_median) median_line.setZValue(-150) box.append(median_line) 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 commit(self): self.conditions = [ item.filter for item in self.box_scene.selectedItems() if item.filter ] selected, selection = None, [] if self.conditions: selected = Values(self.conditions, conjunction=False)(self.dataset) selection = np.in1d(self.dataset.ids, selected.ids, assume_unique=True).nonzero()[0] self.Outputs.selected_data.send(selected) self.Outputs.annotated_data.send( create_annotated_table(self.dataset, selection)) def show_posthoc(self): def line(y0, y1): it = self.box_scene.addLine(x, y0, x, y1, self._post_line_pen) it.setZValue(-100) self.posthoc_lines.append(it) while self.posthoc_lines: self.box_scene.removeItem(self.posthoc_lines.pop()) if self.compare == OWBoxPlot.CompareNone or len(self.stats) < 2: return if self.compare == OWBoxPlot.CompareMedians: crit_line = "median" else: crit_line = "mean" xs = [] height = 90 if self.show_annotations else 60 y_up = -len(self.stats) * height + 10 for pos, box_index in enumerate(self.order): stat = self.stats[box_index] x = getattr(stat, crit_line) if x is None: continue x *= self.scale_x xs.append(x * self.scale_x) by = y_up + pos * height line(by + 12, 3) line(by - 12, by - 25) used_to = [] last_to = to = 0 for frm, frm_x in enumerate(xs[:-1]): for to in range(frm + 1, len(xs)): if xs[to] - frm_x > 1.5: to -= 1 break if to in (last_to, frm): continue for rowi, used in enumerate(used_to): if used < frm: used_to[rowi] = to break else: rowi = len(used_to) used_to.append(to) y = -6 - rowi * 6 it = self.box_scene.addLine(frm_x - 2, y, xs[to] + 2, y, self._post_grp_pen) self.posthoc_lines.append(it) last_to = to def get_widget_name_extension(self): return self.attribute.name if self.attribute else None def send_report(self): self.report_plot() text = "" if self.attribute: text += "Box plot for attribute '{}' ".format(self.attribute.name) if self.group_var: text += "grouped by '{}'".format(self.group_var.name) if text: self.report_caption(text) class Label(QGraphicsSimpleTextItem): """Boxplot Label with settable maxWidth""" # Minimum width to display label text MIN_LABEL_WIDTH = 25 # padding bellow the text PADDING = 3 __max_width = None def maxWidth(self): return self.__max_width def setMaxWidth(self, max_width): self.__max_width = max_width def paint(self, painter, option, widget): """Overrides QGraphicsSimpleTextItem.paint If label text is too long, it is elided to fit into the allowed region """ if self.__max_width is None: width = option.rect.width() else: width = self.__max_width if width < self.MIN_LABEL_WIDTH: # if space is too narrow, no label return fm = painter.fontMetrics() text = fm.elidedText(self.text(), Qt.ElideRight, width) painter.drawText( option.rect.x(), option.rect.y() + self.boundingRect().height() - self.PADDING, text)
def pen(self): """Pen used to draw the legend frame.""" return QPen(self.__pen)
def _setup_plot(self): def merge_averaging(): for curve in curves: graphics = curve.merge() curve = graphics.curve self.plot.addItem(graphics.curve_item) if self.display_convex_curve: self.plot.addItem(graphics.hull_item) if self.display_def_threshold and curve.is_valid: points = curve.points ind = np.argmin(np.abs(points.thresholds - 0.5)) item = pg.TextItem(text="{:.3f}".format( points.thresholds[ind]), ) item.setPos(points.fpr[ind], points.tpr[ind]) self.plot.addItem(item) hull_curves = [curve.merged.hull for curve in selected] if hull_curves: self._rocch = convex_hull(hull_curves) iso_pen = QPen(QColor(Qt.black), 1) iso_pen.setCosmetic(True) self._perf_line = InfiniteLine(pen=iso_pen, antialias=True) self.plot.addItem(self._perf_line) return hull_curves def vertical_averaging(): for curve in curves: graphics = curve.avg_vertical() self.plot.addItem(graphics.curve_item) self.plot.addItem(graphics.confint_item) return [curve.avg_vertical.hull for curve in selected] def threshold_averaging(): for curve in curves: graphics = curve.avg_threshold() self.plot.addItem(graphics.curve_item) self.plot.addItem(graphics.confint_item) return [curve.avg_threshold.hull for curve in selected] def no_averaging(): for curve in curves: graphics = curve.folds() for fold in graphics: self.plot.addItem(fold.curve_item) if self.display_convex_curve: self.plot.addItem(fold.hull_item) return [fold.hull for curve in selected for fold in curve.folds] averagings = { OWROCAnalysis.Merge: merge_averaging, OWROCAnalysis.Vertical: vertical_averaging, OWROCAnalysis.Threshold: threshold_averaging, OWROCAnalysis.NoAveraging: no_averaging } target = self.target_index selected = self.selected_classifiers curves = [self.plot_curves(target, i) for i in selected] selected = [self.curve_data(target, i) for i in selected] hull_curves = averagings[self.roc_averaging]() if self.display_convex_hull and hull_curves: hull = convex_hull(hull_curves) hull_pen = QPen(QColor(200, 200, 200, 100), 2) hull_pen.setCosmetic(True) item = self.plot.plot(hull.fpr, hull.tpr, pen=hull_pen, brush=QBrush(QColor(200, 200, 200, 50)), fillLevel=0) item.setZValue(-10000) pen = QPen(QColor(100, 100, 100, 100), 1, Qt.DashLine) pen.setCosmetic(True) self.plot.plot([0, 1], [0, 1], pen=pen, antialias=True) if self.roc_averaging == OWROCAnalysis.Merge: self._update_perf_line() warning = "" if not all(c.is_valid for c in hull_curves): if any(c.is_valid for c in hull_curves): warning = "Some ROC curves are undefined" else: warning = "All ROC curves are undefined" self.warning(warning)
def make_pen(color, width): p = QPen(color, width) p.setCosmetic(True) return p
def setPen(self, pen): pen = QPen(pen) if self.__pen != pen: self.__pen = pen for item in self.__items: item.setPen(pen)
def pen(color): pen = QPen(color, 1) pen.setCosmetic(True) return pen
def __setup(self): # Setup the subwidgets/groups/layout smax = max( (np.nanmax(g.scores) for g in self.__groups if g.scores.size), default=1) smax = 1 if np.isnan(smax) else smax smin = min( (np.nanmin(g.scores) for g in self.__groups if g.scores.size), default=-1) smin = -1 if np.isnan(smin) else smin smin = min(smin, 0) font = self.font() font.setPixelSize(self.__barHeight) axispen = QPen(Qt.black) ax = pg.AxisItem(parent=self, orientation="top", maxTickLength=7, pen=axispen) ax.setRange(smin, smax) self.layout().addItem(ax, 0, 2) for i, group in enumerate(self.__groups): silhouettegroup = BarPlotItem(parent=self) silhouettegroup.setBrush(QBrush(QColor(*group.color))) silhouettegroup.setPen(self.__pen) silhouettegroup.setDataRange(smin, smax) silhouettegroup.setPlotData(group.scores) silhouettegroup.setPreferredBarSize(self.__barHeight) silhouettegroup.setData(0, group.indices) self.layout().addItem(silhouettegroup, i + 1, 2) if group.label: self.layout().addItem(Line(orientation=Qt.Vertical), i + 1, 1) label = QGraphicsSimpleTextItem(self) label.setText("{} ({})".format(escape(group.label), len(group.scores))) item = WrapperLayoutItem(label, Qt.Vertical, parent=self) self.layout().addItem(item, i + 1, 0, Qt.AlignCenter) textlist = TextListWidget(self, font=font) sp = textlist.sizePolicy() sp.setVerticalPolicy(QSizePolicy.Ignored) textlist.setSizePolicy(sp) textlist.setParent(self) if group.rownames is not None: textlist.setItems(group.items) textlist.setVisible(self.__rowNamesVisible) else: textlist.setVisible(False) self.layout().addItem(textlist, i + 1, 3) ax = pg.AxisItem(parent=self, orientation="bottom", maxTickLength=7, pen=axispen) ax.setRange(smin, smax) self.layout().addItem(ax, len(self.__groups) + 1, 2)
def _init_ui(self): namesBox = gui.vBox(self.controlArea, "Names") hbox = gui.hBox(namesBox, margin=0, spacing=0) gui.lineEdit(hbox, self, "attr1", "Variable X: ", controlWidth=80, orientation=Qt.Horizontal, callback=self._attr_name_changed) gui.separator(hbox, 21) hbox = gui.hBox(namesBox, margin=0, spacing=0) attr2 = gui.lineEdit(hbox, self, "attr2", "Variable Y: ", controlWidth=80, orientation=Qt.Horizontal, callback=self._attr_name_changed) gui.separator(hbox) gui.checkBox(hbox, self, "hasAttr2", '', disables=attr2, labelWidth=0, callback=self.set_dimensions) gui.widgetLabel(namesBox, "Labels") self.classValuesView = listView = gui.ListViewWithSizeHint( preferred_size=(-1, 30)) listView.setModel(self.class_model) itemmodels.select_row(listView, 0) namesBox.layout().addWidget(listView) self.addClassLabel = QAction("+", self, toolTip="Add new class label", triggered=self.add_new_class_label) self.removeClassLabel = QAction( unicodedata.lookup("MINUS SIGN"), self, toolTip="Remove selected class label", triggered=self.remove_selected_class_label) actionsWidget = itemmodels.ModelActionsWidget( [self.addClassLabel, self.removeClassLabel], self) actionsWidget.layout().addStretch(10) actionsWidget.layout().setSpacing(1) namesBox.layout().addWidget(actionsWidget) tBox = gui.vBox(self.buttonsArea, "Tools") toolsBox = gui.widgetBox(tBox, orientation=QGridLayout()) self.toolActions = QActionGroup(self) self.toolActions.setExclusive(True) self.toolButtons = [] for i, (name, tooltip, tool, icon) in enumerate(self.TOOLS): action = QAction( name, self, toolTip=tooltip, checkable=tool.checkable, icon=QIcon(icon), ) action.triggered.connect(partial(self.set_current_tool, tool)) button = QToolButton(iconSize=QSize(24, 24), toolButtonStyle=Qt.ToolButtonTextUnderIcon, sizePolicy=QSizePolicy( QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)) button.setDefaultAction(action) self.toolButtons.append((button, tool)) toolsBox.layout().addWidget(button, i / 3, i % 3) self.toolActions.addAction(action) for column in range(3): toolsBox.layout().setColumnMinimumWidth(column, 10) toolsBox.layout().setColumnStretch(column, 1) undo = self.undo_stack.createUndoAction(self) redo = self.undo_stack.createRedoAction(self) undo.setShortcut(QKeySequence.Undo) redo.setShortcut(QKeySequence.Redo) self.addActions([undo, redo]) self.undo_stack.indexChanged.connect(self.invalidate) indBox = gui.indentedBox(tBox, sep=8) form = QFormLayout(formAlignment=Qt.AlignLeft, labelAlignment=Qt.AlignLeft, fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow) indBox.layout().addLayout(form) slider = gui.hSlider(indBox, self, "brushRadius", minValue=1, maxValue=100, createLabel=False, addToLayout=False) form.addRow("Radius:", slider) slider = gui.hSlider(indBox, self, "density", None, minValue=1, maxValue=100, createLabel=False, addToLayout=False) form.addRow("Intensity:", slider) slider = gui.hSlider(indBox, self, "symbol_size", None, minValue=1, maxValue=100, createLabel=False, callback=self.set_symbol_size, addToLayout=False) form.addRow("Symbol:", slider) self.btResetToInput = gui.button(tBox, self, "Reset to Input Data", self.reset_to_input) self.btResetToInput.setDisabled(True) gui.auto_send(self.buttonsArea, self, "autocommit") # main area GUI viewbox = PaintViewBox(enableMouse=False) self.plotview = pg.PlotWidget(background="w", viewBox=viewbox) self.plotview.sizeHint = lambda: QSize( 200, 100) # Minimum size for 1-d painting self.plot = self.plotview.getPlotItem() axis_color = self.palette().color(QPalette.Text) axis_pen = QPen(axis_color) tickfont = QFont(self.font()) tickfont.setPixelSize(max(int(tickfont.pixelSize() * 2 // 3), 11)) axis = self.plot.getAxis("bottom") axis.setLabel(self.attr1) axis.setPen(axis_pen) axis.setTickFont(tickfont) axis = self.plot.getAxis("left") axis.setLabel(self.attr2) axis.setPen(axis_pen) axis.setTickFont(tickfont) if not self.hasAttr2: self.plot.hideAxis('left') self.plot.hideButtons() self.plot.setXRange(0, 1, padding=0.01) self.mainArea.layout().addWidget(self.plotview) # enable brush tool self.toolActions.actions()[0].setChecked(True) self.set_current_tool(self.TOOLS[0][2]) self.set_dimensions()
def setData(self, data, nsamples, sample_range=None, color=Qt.magenta): assert np.all(np.isfinite(data)) if data.size > 0: xmin, xmax = np.min(data), np.max(data) else: xmin = xmax = 0.0 if sample_range is None: xrange = xmax - xmin sample_min = xmin - xrange * 0.025 sample_max = xmax + xrange * 0.025 else: sample_min, sample_max = sample_range sample = np.linspace(sample_min, sample_max, nsamples) if data.size < 2: est = np.full( sample.size, 1. / sample.size, ) else: try: density = stats.gaussian_kde(data) est = density.evaluate(sample) except np.linalg.LinAlgError: est = np.zeros(sample.size) item = QGraphicsPathItem(violin_shape(sample, est)) color = QColor(color) color.setAlphaF(0.5) item.setBrush(QBrush(color)) pen = QPen(self.palette().color(QPalette.Shadow)) pen.setCosmetic(True) item.setPen(pen) est_max = np.max(est) x = np.random.RandomState(0xD06F00D).uniform(-est_max, est_max, size=data.size) dots = ScatterPlotItem( x=x, y=data, size=3, ) dots.setVisible(self.__dataPointsVisible) pen = QPen(self.palette().color(QPalette.Shadow), 1) hoverPen = QPen(self.palette().color(QPalette.Highlight), 1.5) cmax = SelectionLine(angle=0, pos=xmax, movable=True, bounds=(sample_min, sample_max), pen=pen, hoverPen=hoverPen) cmin = SelectionLine(angle=0, pos=xmin, movable=True, bounds=(sample_min, sample_max), pen=pen, hoverPen=hoverPen) cmax.setCursor(Qt.SizeVerCursor) cmin.setCursor(Qt.SizeVerCursor) selection_item = QGraphicsRectItem( QRectF(-est_max, xmin, est_max * 2, xmax - xmin)) selection_item.setPen(QPen(Qt.NoPen)) selection_item.setBrush(QColor(0, 250, 0, 50)) def update_selection_rect(): mode = self.__selectionMode p = selection_item.parentItem() # type: Optional[QGraphicsItem] while p is not None and not isinstance(p, pg.ViewBox): p = p.parentItem() if p is not None: viewbox = p # type: pg.ViewBox else: viewbox = None rect = selection_item.rect() # type: QRectF if mode & ViolinPlot.High: rect.setTop(cmax.value()) elif viewbox is not None: rect.setTop(viewbox.viewRect().bottom()) else: rect.setTop(cmax.maxRange[1]) if mode & ViolinPlot.Low: rect.setBottom(cmin.value()) elif viewbox is not None: rect.setBottom(viewbox.viewRect().top()) else: rect.setBottom(cmin.maxRange[0]) selection_item.setRect(rect.normalized()) cmax.sigPositionChanged.connect(update_selection_rect) cmin.sigPositionChanged.connect(update_selection_rect) cmax.visibleChanged.connect(update_selection_rect) cmin.visibleChanged.connect(update_selection_rect) def setupper(line): ebound = self.__effectiveBoundary() elower, eupper = ebound mode = self.__selectionMode if not mode & ViolinPlot.High: return upper = line.value() lower = min(elower, upper) if lower != elower and mode & ViolinPlot.Low: self.__min = lower cmin.setValue(lower) if upper != eupper: self.__max = upper if ebound != self.__effectiveBoundary(): self.selectionEdited.emit() self.selectionChanged.emit() def setlower(line): ebound = self.__effectiveBoundary() elower, eupper = ebound mode = self.__selectionMode if not mode & ViolinPlot.Low: return lower = line.value() upper = max(eupper, lower) if upper != eupper and mode & ViolinPlot.High: self.__max = upper cmax.setValue(upper) if lower != elower: self.__min = lower if ebound != self.__effectiveBoundary(): self.selectionEdited.emit() self.selectionChanged.emit() cmax.sigPositionChanged.connect(setupper) cmin.sigPositionChanged.connect(setlower) selmode = self.__selectionMode cmax.setVisible(selmode & ViolinPlot.High) cmin.setVisible(selmode & ViolinPlot.Low) selection_item.setVisible(selmode) self.addItem(dots) self.addItem(item) self.addItem(cmax) self.addItem(cmin) self.addItem(selection_item) self.setRange( QRectF(-est_max, np.min(sample), est_max * 2, np.ptp(sample))) self._plotitems = SimpleNamespace(pointsitem=dots, densityitem=item, cmax=cmax, cmin=cmin, selection_item=selection_item) self.__min = xmin self.__max = xmax
def __init__(self): super().__init__() self.data = None self.distributions = None self.contingencies = None self.var = self.cvar = None varbox = gui.vBox(self.controlArea, "Variable") self.varmodel = itemmodels.VariableListModel() self.groupvarmodel = [] self.varview = QListView( selectionMode=QListView.SingleSelection, uniformItemSizes=True ) self.varview.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) self.varview.setModel(self.varmodel) self.varview.setSelectionModel( itemmodels.ListSingleSelectionModel(self.varmodel) ) self.varview.selectionModel().selectionChanged.connect( self._on_variable_idx_changed ) varbox.layout().addWidget(self.varview) box = gui.vBox(self.controlArea, "Precision") gui.separator(self.controlArea, 4, 4) box2 = gui.hBox(box) self.l_smoothing_l = gui.widgetLabel(box2, "Smooth") gui.hSlider( box2, self, "smoothing_index", minValue=0, maxValue=len(self.smoothing_facs) - 1, callback=self._on_set_smoothing, createLabel=False, ) self.l_smoothing_r = gui.widgetLabel(box2, "Precise") self.cb_disc_cont = gui.checkBox( gui.indentedBox(box, sep=4), self, "disc_cont", "Bin numeric variables", callback=self._on_groupvar_idx_changed, tooltip="Show numeric variables as categorical.", ) box = gui.vBox(self.controlArea, "Group by") self.icons = gui.attributeIconDict self.groupvarview = gui.comboBox( box, self, "groupvar_idx", callback=self._on_groupvar_idx_changed, valueType=str, contentsLength=12, ) box2 = gui.indentedBox(box, sep=4) self.cb_rel_freq = gui.checkBox( box2, self, "relative_freq", "Show relative frequencies", callback=self._on_relative_freq_changed, tooltip="Normalize probabilities so that probabilities " "for each group-by value sum to 1.", ) gui.separator(box2) self.cb_prob = gui.comboBox( box2, self, "show_prob", label="Show probabilities:", orientation=Qt.Horizontal, callback=self._on_relative_freq_changed, tooltip="Show probabilities for a chosen group-by value " "(at each point probabilities for all group-by values sum to 1).", ) self.plotview = pg.PlotWidget(background=None) self.plotview.setRenderHint(QPainter.Antialiasing) self.mainArea.layout().addWidget(self.plotview) w = QLabel() w.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.mainArea.layout().addWidget(w, Qt.AlignCenter) self.ploti = pg.PlotItem() self.plot = self.ploti.vb self.ploti.hideButtons() self.plotview.setCentralItem(self.ploti) self.plot_prob = pg.ViewBox() self.ploti.hideAxis("right") self.ploti.scene().addItem(self.plot_prob) self.ploti.getAxis("right").linkToView(self.plot_prob) self.ploti.getAxis("right").setLabel("Probability") self.plot_prob.setZValue(10) self.plot_prob.setXLink(self.ploti) self.update_views() self.ploti.vb.sigResized.connect(self.update_views) self.plot_prob.setRange(yRange=[0, 1]) def disable_mouse(plot): plot.setMouseEnabled(False, False) plot.setMenuEnabled(False) disable_mouse(self.plot) disable_mouse(self.plot_prob) self.tooltip_items = [] self.plot.scene().installEventFilter(HelpEventDelegate(self.help_event, self)) pen = QPen(self.palette().color(QPalette.Text)) for axis in ("left", "bottom"): self.ploti.getAxis(axis).setPen(pen) self._legend = LegendItem() self._legend.setParentItem(self.plot) self._legend.hide() self._legend.anchor((1, 0), (1, 0))
def display_contingency(self): """ Set the contingency to display. """ cont = self.contingencies var, cvar = self.var, self.cvar if cont is None or not len(cont): return self.plot.clear() self.plot_prob.clear() self._legend.clear() self.tooltip_items = [] if self.show_prob: self.ploti.showAxis("right") else: self.ploti.hideAxis("right") bottomaxis = self.ploti.getAxis("bottom") bottomaxis.setLabel(var.name) bottomaxis.resizeEvent() cvar_values = cvar.values colors = [QColor(*col) for col in cvar.colors] if var and var.is_continuous: bottomaxis.setTicks(None) weights, cols, cvar_values, curves = [], [], [], [] for i, dist in enumerate(cont): v, W = dist if len(v): weights.append(numpy.sum(W)) cols.append(colors[i]) cvar_values.append(cvar.values[i]) curves.append( ash_curve( dist, cont, m=OWDistributions.ASH_HIST, smoothing_factor=self.smoothing_factor, ) ) weights = numpy.array(weights) sumw = numpy.sum(weights) weights /= sumw colors = cols curves = [(X, Y * w) for (X, Y), w in zip(curves, weights)] curvesline = [] # from histograms to lines for X, Y in curves: X = X + (X[1] - X[0]) / 2 X = X[:-1] X = numpy.array(X) Y = numpy.array(Y) curvesline.append((X, Y)) for t in ["fill", "line"]: curve_data = list(zip(curvesline, colors, weights, cvar_values)) for (X, Y), color, w, cval in reversed(curve_data): item = pg.PlotCurveItem() pen = QPen(QBrush(color), 3) pen.setCosmetic(True) color = QColor(color) color.setAlphaF(0.2) item.setData( X, Y / (w if self.relative_freq else 1), antialias=True, stepMode=False, fillLevel=0 if t == "fill" else None, brush=QBrush(color), pen=pen, ) self.plot.addItem(item) if t == "line": item.tooltip = "{}\n{}={}".format( "Normalized density " if self.relative_freq else "Density ", cvar.name, cval, ) self.tooltip_items.append((self.plot, item)) if self.show_prob: all_X = numpy.array( numpy.unique(numpy.hstack([X for X, _ in curvesline])) ) inter_X = numpy.array( numpy.linspace(all_X[0], all_X[-1], len(all_X) * 2) ) curvesinterp = [numpy.interp(inter_X, X, Y) for (X, Y) in curvesline] sumprob = numpy.sum(curvesinterp, axis=0) legal = sumprob > 0.05 * numpy.max(sumprob) i = len(curvesinterp) + 1 show_all = self.show_prob == i for Y, color, cval in reversed( list(zip(curvesinterp, colors, cvar_values)) ): i -= 1 if show_all or self.show_prob == i: item = pg.PlotCurveItem() pen = QPen(QBrush(color), 3, style=Qt.DotLine) pen.setCosmetic(True) prob = Y[legal] / sumprob[legal] item.setData( inter_X[legal], prob, antialias=True, stepMode=False, fillLevel=None, brush=None, pen=pen, ) self.plot_prob.addItem(item) item.tooltip = "Probability that \n" + cvar.name + "=" + cval self.tooltip_items.append((self.plot_prob, item)) elif var and var.is_discrete: bottomaxis.setTicks([list(enumerate(var.values))]) cont = numpy.array(cont) maxh = 0 # maximal column height maxrh = 0 # maximal relative column height scvar = cont.sum(axis=1) # a cvar with sum=0 with allways have distribution counts 0, # therefore we can divide it by anything scvar[scvar == 0] = 1 for i, (value, dist) in enumerate(zip(var.values, cont.T)): maxh = max(maxh, max(dist)) maxrh = max(maxrh, max(dist / scvar)) for i, (value, dist) in enumerate(zip(var.values, cont.T)): dsum = sum(dist) geom = QRectF( i - 0.333, 0, 0.666, maxrh if self.relative_freq else maxh ) if self.show_prob: prob = dist / dsum ci = 1.96 * numpy.sqrt(prob * (1 - prob) / dsum) else: ci = None item = DistributionBarItem( geom, dist / scvar / maxrh if self.relative_freq else dist / maxh, colors, ) self.plot.addItem(item) tooltip = "\n".join( "%s: %.*f" % (n, 3 if self.relative_freq else 1, v) for n, v in zip( cvar_values, dist / scvar if self.relative_freq else dist ) ) item.tooltip = "{} ({}={}):\n{}".format( "Normalized frequency " if self.relative_freq else "Frequency ", cvar.name, value, tooltip, ) self.tooltip_items.append((self.plot, item)) if self.show_prob: item.tooltip += "\n\nProbabilities:" for ic, a in enumerate(dist): if self.show_prob - 1 != ic and self.show_prob - 1 != len(dist): continue position = -0.333 + ((ic + 0.5) * 0.666 / len(dist)) if dsum < 1e-6: continue prob = a / dsum if not 1e-6 < prob < 1 - 1e-6: continue ci = 1.96 * sqrt(prob * (1 - prob) / dsum) item.tooltip += "\n%s: %.3f ± %.3f" % ( cvar_values[ic], prob, ci, ) mark = pg.ScatterPlotItem() errorbar = pg.ErrorBarItem() pen = QPen(QBrush(QColor(0)), 1) pen.setCosmetic(True) errorbar.setData( x=[i + position], y=[prob], bottom=min(numpy.array([ci]), prob), top=min(numpy.array([ci]), 1 - prob), beam=numpy.array([0.05]), brush=QColor(1), pen=pen, ) mark.setData( [i + position], [prob], antialias=True, symbol="o", fillLevel=None, pxMode=True, size=10, brush=QColor(colors[ic]), pen=pen, ) self.plot_prob.addItem(errorbar) self.plot_prob.addItem(mark) for color, name in zip(colors, cvar_values): self._legend.addItem( ScatterPlotItem(pen=color, brush=color, size=10, shape="s"), escape(name), ) self._legend.show()
def hoverLeaveEvent(self, event): pen = QPen(self.pen()) pen.setWidth(2) self.setPen(pen) return RugItem.hoverLeaveEvent(self, event)
def __init__(self): super().__init__() self.results = None self.classifier_names = [] self.colors = [] self._curve_data = {} box = gui.vBox(self.controlArea, "Plot") tbox = gui.vBox(box, "Target Class") tbox.setFlat(True) self.target_cb = gui.comboBox(tbox, self, "target_index", callback=self._on_target_changed, contentsLength=8) cbox = gui.vBox(box, "Classifiers") cbox.setFlat(True) self.classifiers_list_box = gui.listBox( cbox, self, "selected_classifiers", "classifier_names", selectionMode=QListView.MultiSelection, callback=self._on_classifiers_changed) gui.checkBox(box, self, "display_convex_hull", "Show lift convex hull", callback=self._replot) self.plotview = pg.GraphicsView(background="w") self.plotview.setFrameStyle(QFrame.StyledPanel) self.plot = pg.PlotItem(enableMenu=False) self.plot.setMouseEnabled(False, False) self.plot.hideButtons() pen = QPen(self.palette().color(QPalette.Text)) tickfont = QFont(self.font()) tickfont.setPixelSize(max(int(tickfont.pixelSize() * 2 // 3), 11)) axis = self.plot.getAxis("bottom") axis.setTickFont(tickfont) axis.setPen(pen) axis.setLabel("P Rate") axis = self.plot.getAxis("left") axis.setTickFont(tickfont) axis.setPen(pen) axis.setLabel("TP Rate") self.plot.showGrid(True, True, alpha=0.1) self.plot.setRange(xRange=(0.0, 1.0), yRange=(0.0, 1.0), padding=0.05) self.plotview.setCentralItem(self.plot) self.mainArea.layout().addWidget(self.plotview)