def __init__(self, master, parent=None): self.selection = [] self.master: OWBarPlot = master self.state: int = SELECT self.bar_item: pg.BarGraphItem = None super().__init__( parent=parent, viewBox=BarPlotViewBox(self), background="w", enableMenu=False, axisItems={"bottom": AxisItem(orientation="bottom", rotate_ticks=True), "left": AxisItem(orientation="left")} ) self.hideAxis("left") self.hideAxis("bottom") self.getPlotItem().buttonsHidden = True self.getPlotItem().setContentsMargins(10, 0, 0, 10) self.getViewBox().setMouseMode(pg.ViewBox.PanMode) self.group_axis = AxisItem("bottom") self.group_axis.hide() self.group_axis.linkToView(self.getViewBox()) self.getPlotItem().layout.addItem(self.group_axis, 4, 1) self.legend = self._create_legend() self.tooltip_delegate = HelpEventDelegate(self.help_event) self.scene().installEventFilter(self.tooltip_delegate) self.parameter_setter = ParameterSetter(self) self.showGrid(y=self.parameter_setter.DEFAULT_SHOW_GRID, alpha=self.parameter_setter.DEFAULT_ALPHA_GRID / 255)
def axis_view(orientation): ax = AxisItem(orientation=orientation, maxTickLength=7) ax.mousePressed.connect(self._activate_cut_line) ax.mouseMoved.connect(self._activate_cut_line) ax.mouseReleased.connect(self._activate_cut_line) ax.setRange(1.0, 0.0) return ax
def __init__(self, parent: OWWidget): self.__data_bounds: Optional[Tuple[Tuple[float, float], Tuple[float, float]]] = None self.__pos_labels: Optional[List[str]] = None self.__neg_labels: Optional[List[str]] = None self.__tooltip_data: Optional[Table] = None self.__show_tooltips = True self.__highlight_feature = True self.__mouse_pressed = False self.__fill_items: List[pg.FillBetweenItem] = [] self.__text_items: List[pg.TextItem] = [] self.__dot_items: List[pg.ScatterPlotItem] = [] self.__vertical_line_item: Optional[pg.InfiniteLine] = None self.__selection: List = [] self.__selection_rect_items: List[SelectionRect] = [] view_box = ForcePlotViewBox() view_box.sigSelectionChanged.connect(self._update_selection) view_box.sigDeselect.connect(self._deselect) view_box.sigRangeChangedManually.connect(self.__on_range_changed_man) view_box.sigRangeChanged.connect(self.__on_range_changed) super().__init__(parent, viewBox=view_box, background="w", enableMenu=False, axisItems={"bottom": AxisItem("bottom", True), "left": AxisItem("left")}) self.setAntialiasing(True) self.getPlotItem().setContentsMargins(10, 10, 10, 10) self.getPlotItem().buttonsHidden = True self.getPlotItem().scene().sigMouseMoved.connect(self.__on_mouse_moved) self.parameter_setter = ParameterSetter(self)
def _clear_axes(self): self.setAxisItems({ "bottom": AxisItem(orientation="bottom"), "left": AxisItem(orientation="left") }) Updater.update_axes_titles_font( self.parameter_setter.axis_items, **self.parameter_setter.titles_settings) Updater.update_axes_ticks_font(self.parameter_setter.axis_items, **self.parameter_setter.ticks_settings) self.getAxis("bottom").setRotateTicks( self.parameter_setter.is_vertical_setting)
def __init__(self, parent): self.groups: List[ProfileGroup] = [] self.bottom_axis = BottomAxisItem(orientation="bottom") self.bottom_axis.setLabel("") left_axis = AxisItem(orientation="left") left_axis.setLabel("") super().__init__(parent, viewBox=LinePlotViewBox(), background="w", enableMenu=False, axisItems={"bottom": self.bottom_axis, "left": left_axis}) self.view_box = self.getViewBox() self.selection = set() self.legend = self._create_legend(((1, 0), (1, 0))) self.getPlotItem().buttonsHidden = True self.setRenderHint(QPainter.Antialiasing, True)
def __init__(self, parent: OWWidget, kernel: str, scale: int, orientation: Qt.Orientations, show_box_plot: bool, show_strip_plot: bool, show_rug_plot: bool, show_grid: bool, sort_items: bool): # data self.__values: Optional[np.ndarray] = None self.__value_var: Optional[ContinuousVariable] = None self.__group_values: Optional[np.ndarray] = None self.__group_var: Optional[DiscreteVariable] = None # settings self.__kernel = kernel self.__scale = scale self.__orientation = orientation self.__show_box_plot = show_box_plot self.__show_strip_plot = show_strip_plot self.__show_rug_plot = show_rug_plot self.__show_grid = show_grid self.__sort_items = sort_items # items self.__violin_items: List[ViolinItem] = [] self.__box_items: List[BoxItem] = [] self.__median_items: List[MedianItem] = [] self.__strip_items: List[pg.ScatterPlotItem] = [] # selection self.__selection: Set[int] = set() self.__selection_rects: List[SelectionRect] = [] view_box = ViolinPlotViewBox(self) super().__init__(parent, viewBox=view_box, enableMenu=False, axisItems={ "bottom": AxisItem("bottom"), "left": AxisItem("left") }) self.setAntialiasing(True) self.hideButtons() self.getPlotItem().setContentsMargins(10, 10, 10, 10) self.setMouseEnabled(False, False) view_box.sigSelectionChanged.connect(self._update_selection) view_box.sigDeselect.connect(self._deselect) self.parameter_setter = ParameterSetter(self)
def __init__(self): super().__init__() self.__violin_column_width = self.VIOLIN_COLUMN_WIDTH # type: int self.__range = None # type: Optional[Tuple[float, float]] self.__violin_items = [] # type: List[ViolinItem] self.__variable_items = [] # type: List[VariableItem] self.__bottom_axis = AxisItem(parent=self, orientation="bottom", maxTickLength=7, pen=QPen(Qt.black)) self.__bottom_axis.setLabel("Impact on model output") self.__vertical_line = QGraphicsLineItem(self.__bottom_axis) self.__vertical_line.setPen(QPen(Qt.gray)) self.__legend = Legend(self) self.__layout = QGraphicsGridLayout() self.__layout.addItem(self.__legend, 0, ViolinPlot.LEGEND_COLUMN) self.__layout.setVerticalSpacing(0) self.setLayout(self.__layout) self.parameter_setter = ParameterSetter(self)
def __init__(self): super().__init__() self._item_column_width = self.ITEM_COLUMN_WIDTH self._range: Optional[Tuple[float, float]] = None self._items: List[FeatureItem] = [] self._variable_items: List[VariableItem] = [] self._bottom_axis = AxisItem(parent=self, orientation="bottom", maxTickLength=7, pen=QPen(Qt.black)) self._bottom_axis.setLabel(self.BOTTOM_AXIS_LABEL) self._vertical_line = QGraphicsLineItem(self._bottom_axis) self._vertical_line.setPen(QPen(Qt.gray)) self._layout = QGraphicsGridLayout() self._layout.setVerticalSpacing(0) self.setLayout(self._layout) self.parameter_setter = BaseParameterSetter(self)
def __init__(self): super().__init__() self.results = None self.scores = None self.classifier_names = [] self.colors = [] self.line = None self._last_score_value = -1 box = gui.vBox(self.controlArea, box="Settings") self.target_cb = gui.comboBox(box, self, "target_index", label="Target:", orientation=Qt.Horizontal, callback=self.target_index_changed, contentsLength=8, searchable=True) gui.checkBox(box, self, "display_rug", "Show rug", callback=self._on_display_rug_changed) gui.checkBox(box, self, "fold_curves", "Curves for individual folds", callback=self._replot) self.classifiers_list_box = gui.listBox( self.controlArea, self, "selected_classifiers", "classifier_names", box="Classifier", selectionMode=QListWidget.ExtendedSelection, sizePolicy=(QSizePolicy.Preferred, QSizePolicy.Preferred), sizeHint=QSize(150, 40), callback=self._on_selection_changed) box = gui.vBox(self.controlArea, "Metrics") combo = gui.comboBox(box, self, "score", items=(metric.name for metric in Metrics), callback=self.score_changed) self.explanation = gui.widgetLabel(box, wordWrap=True, fixedWidth=combo.sizeHint().width()) self.explanation.setContentsMargins(8, 8, 0, 0) font = self.explanation.font() font.setPointSizeF(0.85 * font.pointSizeF()) self.explanation.setFont(font) gui.radioButtons(box, self, value="output_calibration", btnLabels=("Sigmoid calibration", "Isotonic calibration"), label="Output model calibration", callback=self.apply) self.info_box = gui.widgetBox(self.controlArea, "Info") self.info_label = gui.widgetLabel(self.info_box) gui.auto_apply(self.controlArea, self, "auto_commit", commit=self.apply) self.plotview = pg.GraphicsView(background="w") axes = { "bottom": AxisItem(orientation="bottom"), "left": AxisItem(orientation="left") } self.plot = pg.PlotItem(enableMenu=False, axisItems=axes) self.plot.parameter_setter = ParameterSetter(self.plot) self.plot.setMouseEnabled(False, False) self.plot.hideButtons() for axis_name in ("bottom", "left"): axis = self.plot.getAxis(axis_name) axis.setPen(pg.mkPen(color=0.0)) # Remove the condition (that is, allow setting this for bottom # axis) when pyqtgraph is fixed # Issue: https://github.com/pyqtgraph/pyqtgraph/issues/930 # Pull request: https://github.com/pyqtgraph/pyqtgraph/pull/932 if axis_name != "bottom": # remove if when pyqtgraph is fixed axis.setStyle(stopAxisAtTick=(True, True)) 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) self._set_explanation() VisualSettingsDialog(self, self.plot.parameter_setter.initial_settings)
class BarPlotGraph(PlotWidget): selection_changed = Signal(list) bar_width = 0.7 def __init__(self, master, parent=None): self.selection = [] self.master: OWBarPlot = master self.state: int = SELECT self.bar_item: pg.BarGraphItem = None super().__init__( parent=parent, viewBox=BarPlotViewBox(self), enableMenu=False, axisItems={ "bottom": AxisItem(orientation="bottom", rotate_ticks=True), "left": AxisItem(orientation="left"), }, ) self.hideAxis("left") self.hideAxis("bottom") self.getPlotItem().buttonsHidden = True self.getPlotItem().setContentsMargins(10, 0, 0, 10) self.getViewBox().setMouseMode(pg.ViewBox.PanMode) self.group_axis = AxisItem("bottom") self.group_axis.hide() self.group_axis.linkToView(self.getViewBox()) self.getPlotItem().layout.addItem(self.group_axis, 4, 1) self.legend = self._create_legend() self.tooltip_delegate = HelpEventDelegate(self.help_event) self.scene().installEventFilter(self.tooltip_delegate) self.parameter_setter = ParameterSetter(self) self.showGrid( y=self.parameter_setter.DEFAULT_SHOW_GRID, alpha=self.parameter_setter.DEFAULT_ALPHA_GRID / 255, ) def _create_legend(self): legend = LegendItem() legend.setParentItem(self.getViewBox()) legend.anchor((1, 0), (1, 0), offset=(-3, 1)) legend.hide() return legend def update_legend(self): self.legend.clear() self.legend.hide() for color, text in self.master.get_legend_data(): dot = pg.ScatterPlotItem(pen=pg.mkPen(color=color), brush=pg.mkBrush(color=color)) self.legend.addItem(dot, escape(text)) self.legend.show() Updater.update_legend_font(self.legend.items, **self.parameter_setter.legend_settings) def reset_graph(self): self.clear() self.update_bars() self.update_axes() self.update_group_lines() self.update_legend() self.reset_view() def update_bars(self): if self.bar_item is not None: self.removeItem(self.bar_item) self.bar_item = None values = self.master.get_values() if values is None: return self.bar_item = pg.BarGraphItem( x=np.arange(len(values)), height=values, width=self.bar_width, pen=pg.mkPen(QColor(Qt.white)), labels=self.master.get_labels(), brushes=self.master.get_colors(), ) self.addItem(self.bar_item) self.__select_bars() def update_axes(self): if self.bar_item is not None: self.showAxis("left") self.showAxis("bottom") self.group_axis.show() vals_label, group_label, annot_label = self.master.get_axes() self.setLabel(axis="left", text=vals_label) self.setLabel(axis="bottom", text=annot_label) self.group_axis.setLabel(group_label) ticks = [list(enumerate(self.master.get_labels()))] self.getAxis("bottom").setTicks(ticks) labels = np.array(self.master.get_group_labels()) _, indices, counts = np.unique(labels, return_index=True, return_counts=True) ticks = [[(i + (c - 1) / 2, labels[i]) for i, c in zip(indices, counts)]] self.group_axis.setTicks(ticks) if not group_label: self.group_axis.hide() elif not annot_label: self.hideAxis("bottom") else: self.hideAxis("left") self.hideAxis("bottom") self.group_axis.hide() def reset_view(self): if self.bar_item is None: return values = np.append(self.bar_item.opts["height"], 0) min_ = np.nanmin(values) max_ = -min_ + np.nanmax(values) rect = QRectF(-0.5, min_, len(values) - 1, max_) self.getViewBox().setRange(rect) def zoom_button_clicked(self): self.state = ZOOMING self.getViewBox().setMouseMode(pg.ViewBox.RectMode) def pan_button_clicked(self): self.state = PANNING self.getViewBox().setMouseMode(pg.ViewBox.PanMode) def select_button_clicked(self): self.state = SELECT self.getViewBox().setMouseMode(pg.ViewBox.RectMode) def reset_button_clicked(self): self.reset_view() def update_group_lines(self): if self.bar_item is None: return labels = np.array(self.master.get_group_labels()) if labels is None or len(labels) == 0: return _, indices = np.unique(labels, return_index=True) offset = self.bar_width / 2 + (1 - self.bar_width) / 2 for index in sorted(indices)[1:]: line = pg.InfiniteLine(pos=index - offset, angle=90) self.addItem(line) def select_by_rectangle(self, rect: QRectF): if self.bar_item is None: return x0, x1 = sorted((rect.topLeft().x(), rect.bottomRight().x())) y0, y1 = sorted((rect.topLeft().y(), rect.bottomRight().y())) x = self.bar_item.opts["x"] height = self.bar_item.opts["height"] d = self.bar_width / 2 # positive bars mask = (x0 <= x + d) & (x1 >= x - d) & (y0 <= height) & (y1 > 0) # negative bars mask |= (x0 <= x + d) & (x1 >= x - d) & (y0 <= 0) & (y1 > height) self.select_by_indices(list(np.flatnonzero(mask))) def select_by_click(self, p: QPointF): if self.bar_item is None: return index = self.__get_index_at(p) self.select_by_indices([index] if index is not None else []) def __get_index_at(self, p: QPointF): x = p.x() index = round(x) heights = self.bar_item.opts["height"] if 0 <= index < len(heights) and abs(x - index) <= self.bar_width / 2: height = heights[index] # pylint: disable=unsubscriptable-object if 0 <= p.y() <= height or height <= p.y() <= 0: return index return None def select_by_indices(self, indices: List): keys = QApplication.keyboardModifiers() if keys & Qt.ControlModifier: self.selection = list(set(self.selection) ^ set(indices)) elif keys & Qt.AltModifier: self.selection = list(set(self.selection) - set(indices)) elif keys & Qt.ShiftModifier: self.selection = list(set(self.selection) | set(indices)) else: self.selection = list(set(indices)) self.__select_bars() self.selection_changed.emit(self.selection) def __select_bars(self): if self.bar_item is None: return n = len(self.bar_item.opts["height"]) pens = np.full(n, pg.mkPen(QColor(Qt.white))) pen = pg.mkPen(QColor(Qt.black)) pen.setStyle(Qt.DashLine) pens[self.selection] = pen self.bar_item.setOpts(pens=pens) def help_event(self, ev: QGraphicsSceneHelpEvent): if self.bar_item is None: return False index = self.__get_index_at(self.bar_item.mapFromScene(ev.scenePos())) text = "" if index is not None: text = self.master.get_tooltip(index) if text: QToolTip.showText(ev.screenPos(), text, widget=self) return True else: return False
def __init__(self): super().__init__() self.results = None self.classifier_names = [] self.colors = [] self._points_hull: Dict[Tuple[int, int], PointsAndHull] = {} box = gui.vBox(self.controlArea, box="Curve") self.target_cb = gui.comboBox(box, self, "target_index", label="Target: ", orientation=Qt.Horizontal, callback=self._on_target_changed, contentsLength=8, searchable=True) gui.radioButtons(box, self, "curve_type", ("Lift Curve", "Cumulative Gains"), callback=self._on_curve_type_changed) self.classifiers_list_box = gui.listBox( self.controlArea, self, "selected_classifiers", "classifier_names", box="Models", selectionMode=QListView.MultiSelection, callback=self._on_classifiers_changed) self.classifiers_list_box.setMaximumHeight(100) gui.checkBox(self.controlArea, self, "display_convex_hull", "Show convex hull", box="Settings", callback=self._replot) gui.rubber(self.controlArea) self.plotview = pg.GraphicsView(background="w") self.plotview.setFrameStyle(QFrame.StyledPanel) axes = { "bottom": AxisItem(orientation="bottom"), "left": AxisItem(orientation="left") } self.plot = pg.PlotItem(enableMenu=False, axisItems=axes) self.plot.parameter_setter = ParameterSetter(self.plot) self.plot.curve_items = [] self.plot.hull_items = [] self.plot.default_line_item = None self.plot.display_convex_hull = self.display_convex_hull 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)) for pos, label in (("bottom", "P Rate"), ("left", "")): axis = self.plot.getAxis(pos) axis.setTickFont(tickfont) axis.setPen(pen) axis.setLabel(label) self._set_left_label() self.plot.showGrid(True, True, alpha=0.1) self.plotview.setCentralItem(self.plot) self.mainArea.layout().addWidget(self.plotview) VisualSettingsDialog(self, self.plot.parameter_setter.initial_settings)
class ViolinPlot(QGraphicsWidget): LABEL_COLUMN, VIOLIN_COLUMN, LEGEND_COLUMN = range(3) VIOLIN_COLUMN_WIDTH, OFFSET = 300, 80 MAX_N_ITEMS = 100 selection_cleared = Signal() selection_changed = Signal(float, float, str) resized = Signal() def __init__(self): super().__init__() self.__violin_column_width = self.VIOLIN_COLUMN_WIDTH # type: int self.__range = None # type: Optional[Tuple[float, float]] self.__violin_items = [] # type: List[ViolinItem] self.__variable_items = [] # type: List[VariableItem] self.__bottom_axis = AxisItem(parent=self, orientation="bottom", maxTickLength=7, pen=QPen(Qt.black)) self.__bottom_axis.setLabel("Impact on model output") self.__vertical_line = QGraphicsLineItem(self.__bottom_axis) self.__vertical_line.setPen(QPen(Qt.gray)) self.__legend = Legend(self) self.__layout = QGraphicsGridLayout() self.__layout.addItem(self.__legend, 0, ViolinPlot.LEGEND_COLUMN) self.__layout.setVerticalSpacing(0) self.setLayout(self.__layout) self.parameter_setter = ParameterSetter(self) @property def violin_column_width(self): return self.__violin_column_width @violin_column_width.setter def violin_column_width(self, view_width: int): j = ViolinPlot.LABEL_COLUMN w = max([self.__layout.itemAt(i, j).item.boundingRect().width() for i in range(len(self.__violin_items))] + [0]) width = view_width - self.legend.sizeHint().width() - self.OFFSET - w self.__violin_column_width = max(self.VIOLIN_COLUMN_WIDTH, width) @property def bottom_axis(self): return self.__bottom_axis @property def labels(self): return self.__variable_items @property def legend(self): return self.__legend def set_data(self, x: np.ndarray, colors: np.ndarray, names: List[str], n_attrs: float, view_width: int): self.violin_column_width = view_width abs_max = np.max(np.abs(x)) * 1.05 self.__range = (-abs_max, abs_max) self._set_violin_items(x, colors, names) self._set_labels(names) self._set_bottom_axis() self.set_n_visible(n_attrs) def set_n_visible(self, n: int): for i in range(len(self.__violin_items)): violin_item = self.__layout.itemAt(i, ViolinPlot.VIOLIN_COLUMN) violin_item.setVisible(i < n) text_item = self.__layout.itemAt(i, ViolinPlot.LABEL_COLUMN).item text_item.setVisible(i < n) self.set_vertical_line() def rescale(self, view_width: int): self.violin_column_width = view_width with temp_seed(0): for item in self.__violin_items: item.rescale(self.violin_column_width) self.__bottom_axis.setWidth(self.violin_column_width) x = self.violin_column_width / 2 self.__vertical_line.setLine(x, 0, x, self.__vertical_line.line().y2()) def show_legend(self, show: bool): self.__legend.setVisible(show) self.__bottom_axis.setWidth(self.violin_column_width) x = self.violin_column_width / 2 self.__vertical_line.setLine(x, 0, x, self.__vertical_line.line().y2()) def _set_violin_items(self, x: np.ndarray, colors: np.ndarray, labels: List[str]): with temp_seed(0): for i in range(x.shape[1]): item = ViolinItem(self, labels[i], self.__range, self.violin_column_width) item.set_data(x[:, i], colors[:, i]) item.selection_changed.connect(self.select) self.__violin_items.append(item) self.__layout.addItem(item, i, ViolinPlot.VIOLIN_COLUMN) if i == self.MAX_N_ITEMS: break def _set_labels(self, labels: List[str]): for i, (label, _) in enumerate(zip(labels, self.__violin_items)): text = VariableItem(self, label) item = SimpleLayoutItem(text) item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.__layout.addItem(item, i, ViolinPlot.LABEL_COLUMN, Qt.AlignRight | Qt.AlignVCenter) self.__variable_items.append(item) def _set_bottom_axis(self): self.__bottom_axis.setRange(*self.__range) self.__layout.addItem(self.__bottom_axis, len(self.__violin_items), ViolinPlot.VIOLIN_COLUMN) def set_vertical_line(self): x = self.violin_column_width / 2 height = 0 for i in range(len(self.__violin_items)): violin_item = self.__layout.itemAt(i, ViolinPlot.VIOLIN_COLUMN) text_item = self.__layout.itemAt(i, ViolinPlot.LABEL_COLUMN).item if violin_item.isVisible(): height += max(text_item.boundingRect().height(), violin_item.preferredSize().height()) self.__vertical_line.setLine(x, 0, x, -height) def deselect(self): self.selection_cleared.emit() def select(self, *args): self.selection_changed.emit(*args) def select_from_settings(self, x1: float, x2: float, attr_name: str): point_r_diff = 2 * self.__range[1] / (self.violin_column_width / 2) for item in self.__violin_items: if item.attr_name == attr_name: item.add_selection_rect(x1 - point_r_diff, x2 + point_r_diff) break self.select(x1, x2, attr_name) def apply_visual_settings(self, settings: Dict): for key, value in settings.items(): self.parameter_setter.set_parameter(key, value)
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) foreground = self.palette().brush(QPalette.WindowText) ax = AxisItem(parent=self, orientation="top", maxTickLength=7) ax.setRange(smin, smax) self.__topScale = ax layout = self.__layout assert layout is self.layout() 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) layout.addItem(silhouettegroup, i + 1, 2) if group.label: layout.addItem(Line(orientation=Qt.Vertical), i + 1, 1) text = group.label if group.scores.size: text += f" ({np.mean(group.scores):.3f})" label = QGraphicsSimpleTextItem(text, self) label.setBrush(foreground) label.setPen(QPen(Qt.NoPen)) label.setRotation(-90) item = SimpleLayoutItem( label, anchor=(0., 1.0), anchorItem=(0., 0.), ) item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) layout.addItem(item, i + 1, 0, Qt.AlignCenter) textlist = _SilhouettePlotTextListWidget(self, font=font, elideMode=Qt.ElideRight, alignment=Qt.AlignLeft | Qt.AlignVCenter) textlist.setMaximumWidth(750) textlist.setFlag(TextListWidget.ItemClipsChildrenToShape, False) sp = textlist.sizePolicy() sp.setVerticalPolicy(QSizePolicy.Ignored) textlist.setSizePolicy(sp) if group.rownames is not None: textlist.setItems(group.items) textlist.setVisible(self.__rowNamesVisible) else: textlist.setVisible(False) layout.addItem(textlist, i + 1, 3) ax = AxisItem(parent=self, orientation="bottom", maxTickLength=7) ax.setRange(smin, smax) self.__bottomScale = ax layout.addItem(ax, len(self.__groups) + 1, 2)
class FeaturesPlot(QGraphicsWidget): BOTTOM_AXIS_LABEL = "Feature Importance" LABEL_COLUMN, ITEM_COLUMN = range(2) ITEM_COLUMN_WIDTH, OFFSET = 300, 80 selection_cleared = Signal() selection_changed = Signal(object) resized = Signal() def __init__(self): super().__init__() self._item_column_width = self.ITEM_COLUMN_WIDTH self._range: Optional[Tuple[float, float]] = None self._items: List[FeatureItem] = [] self._variable_items: List[VariableItem] = [] self._bottom_axis = AxisItem(parent=self, orientation="bottom", maxTickLength=7, pen=QPen(Qt.black)) self._bottom_axis.setLabel(self.BOTTOM_AXIS_LABEL) self._vertical_line = QGraphicsLineItem(self._bottom_axis) self._vertical_line.setPen(QPen(Qt.gray)) self._layout = QGraphicsGridLayout() self._layout.setVerticalSpacing(0) self.setLayout(self._layout) self.parameter_setter = BaseParameterSetter(self) @property def item_column_width(self) -> int: return self._item_column_width @item_column_width.setter def item_column_width(self, view_width: int): j = FeaturesPlot.LABEL_COLUMN w = max([ self._layout.itemAt(i, j).item.boundingRect().width() for i in range(len(self._items)) ] + [0]) width = view_width - self.OFFSET - w self._item_column_width = max(self.ITEM_COLUMN_WIDTH, width) @property def x0_scaled(self) -> float: min_max = self._range[1] - self._range[0] return -self._range[0] * self.item_column_width / min_max @property def bottom_axis(self) -> AxisItem: return self._bottom_axis @property def labels(self) -> List[VariableItem]: return self._variable_items def set_data(self, x: np.ndarray, names: List[str], n_attrs: int, view_width: int, *plot_args): self.item_column_width = view_width self._set_range(x, *plot_args) self._set_items(x, names, *plot_args) self._set_labels(names) self._set_bottom_axis() self.set_n_visible(n_attrs) def _set_range(self, *_): raise NotImplementedError def _set_items(self, *_): raise NotImplementedError def set_n_visible(self, n: int): for i in range(len(self._items)): item = self._layout.itemAt(i, FeaturesPlot.ITEM_COLUMN) item.setVisible(i < n) text_item = self._layout.itemAt(i, FeaturesPlot.LABEL_COLUMN).item text_item.setVisible(i < n) self.set_vertical_line() def rescale(self, view_width: int): self.item_column_width = view_width for item in self._items: item.rescale(self.item_column_width) self._bottom_axis.setWidth(self.item_column_width) x = self.x0_scaled self._vertical_line.setLine(x, 0, x, self._vertical_line.line().y2()) self.updateGeometry() def set_height(self, height: float): for i in range(len(self._items)): item = self._layout.itemAt(i, FeaturesPlot.ITEM_COLUMN) item.set_height(height) self.set_vertical_line() self.updateGeometry() def _set_labels(self, labels: List[str]): for i, (label, _) in enumerate(zip(labels, self._items)): text = VariableItem(self, label) item = SimpleLayoutItem(text) item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self._layout.addItem(item, i, FeaturesPlot.LABEL_COLUMN, Qt.AlignRight | Qt.AlignVCenter) self._variable_items.append(item) def _set_bottom_axis(self): self._bottom_axis.setRange(*self._range) self._layout.addItem(self._bottom_axis, len(self._items), FeaturesPlot.ITEM_COLUMN) def set_vertical_line(self): height = 0 for i in range(len(self._items)): item = self._layout.itemAt(i, FeaturesPlot.ITEM_COLUMN) text_item = self._layout.itemAt(i, FeaturesPlot.LABEL_COLUMN).item if item.isVisible(): height += max(text_item.boundingRect().height(), item.preferredSize().height()) self._vertical_line.setLine(self.x0_scaled, 0, self.x0_scaled, -height) def deselect(self): self.selection_cleared.emit() def select(self, *args): self.selection_changed.emit(*args) def select_from_settings(self, *_): raise NotImplementedError def apply_visual_settings(self, settings: Dict): for key, value in settings.items(): self.parameter_setter.set_parameter(key, value)