def _add_circle_item(self): if self._points is None: return self._circle_item = QGraphicsEllipseItem() self._circle_item.setRect(QRectF(-1., -1., 2., 2.)) self._circle_item.setPen(pg.mkPen(QColor(0, 0, 0), width=2)) self.plot_widget.addItem(self._circle_item)
def update_circle(self): if self.scatterplot_item is not None and not self.circle_item: self.circle_item = QGraphicsEllipseItem() self.circle_item.setRect(QRectF(-1, -1, 2, 2)) color = self.plot_widget.palette().color(QPalette.Text) self.circle_item.setPen(pg.mkPen(color, width=2)) self.plot_widget.addItem(self.circle_item)
def __init__(self, pen = QPen(Qt.black), brush = QBrush(Qt.NoBrush), xCenter = 0.0, yCenter = 0.0, radius = 1.0): OWCurve.__init__(self) self._item = QGraphicsEllipseItem(self) self.center = xCenter, yCenter self.radius = radius self._rect = QRectF(xCenter-radius, yCenter-radius, 2*radius, 2*radius) self.set_pen(pen) self.set_brush(brush)
def __init__(self, color, parent): super().__init__(parent) height, width = self.SIZE.height(), self.SIZE.width() self.__circle = QGraphicsEllipseItem(0, 0, height, width) self.__circle.setBrush(QBrush(color)) self.__circle.setPen(QPen(QColor(0, 0, 0, 0))) self.__circle.setParentItem(self)
def _add_circle_item(self): if not len(self._points): return r = self.radius / 100 + 1e-5 pen = pg.mkPen(QColor(Qt.lightGray), width=1, cosmetic=True) self._circle_item = QGraphicsEllipseItem() self._circle_item.setRect(QRectF(-r, -r, 2 * r, 2 * r)) self._circle_item.setPen(pen) self.plot_widget.addItem(self._circle_item)
class LegendItemCircle(ColorIndicator): """Legend circle item. The legend circle item is a small colored circle image that can be plugged into the legend in front of the text object. This should only really be used in conjunction with ˙LegendItem˙. Parameters ---------- color : QColor The color of the square. parent : QGraphicsItem See Also -------- LegendItemSquare """ SIZE = QSizeF(12, 12) _size_hint = SIZE def __init__(self, color, parent): super().__init__(parent) height, width = self.SIZE.height(), self.SIZE.width() self.__circle = QGraphicsEllipseItem(0, 0, height, width) self.__circle.setBrush(QBrush(color)) self.__circle.setPen(QPen(QColor(0, 0, 0, 0))) self.__circle.setParentItem(self) self._size_hint = QSizeF(self.__circle.boundingRect().size()) def sizeHint(self, size_hint, size_constraint=None, *args, **kwargs): return self._size_hint
def test_anchoritem(self): anchoritem = NodeAnchorItem(None) self.scene.addItem(anchoritem) path = QPainterPath() path.addEllipse(0, 0, 100, 100) anchoritem.setAnchorPath(path) anchor = AnchorPoint() anchoritem.addAnchor(anchor) ellipse1 = QGraphicsEllipseItem(-3, -3, 6, 6) ellipse2 = QGraphicsEllipseItem(-3, -3, 6, 6) self.scene.addItem(ellipse1) self.scene.addItem(ellipse2) anchor.scenePositionChanged.connect(ellipse1.setPos) with self.assertRaises(ValueError): anchoritem.addAnchor(anchor) anchor1 = AnchorPoint() anchoritem.addAnchor(anchor1) anchor1.scenePositionChanged.connect(ellipse2.setPos) self.assertSequenceEqual(anchoritem.anchorPoints(), [anchor, anchor1]) self.assertSequenceEqual(anchoritem.anchorPositions(), [0.5, 0.5]) anchoritem.setAnchorPositions([0.5, 0.0]) self.assertSequenceEqual(anchoritem.anchorPositions(), [0.5, 0.0]) def advance(): t = anchoritem.anchorPositions() t = [(t + 0.05) % 1.0 for t in t] anchoritem.setAnchorPositions(t) timer = QTimer(anchoritem, interval=10) timer.start() timer.timeout.connect(advance) self.qWait() timer.stop()
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 _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 pie_chart(self, x, y, r, dist, colors): start_angle = 0 dist = np.asarray(dist) spans = dist / (float(np.sum(dist)) or 1) * 360 * 16 for span, color in zip(spans, colors): if not span: continue if self.explode: mid_ang = (start_angle + span / 2) / 360 / 16 * 2 * pi dx = r / 30 * cos(mid_ang) dy = r / 30 * sin(mid_ang) else: dx = dy = 0 ellipse = QGraphicsEllipseItem(x - r / 2 + dx, y - r / 2 - dy, r, r) if len(spans) > 1: ellipse.setStartAngle(start_angle) ellipse.setSpanAngle(span) ellipse.setBrush(QColor(*color)) self.scene.addItem(ellipse) start_angle += span
class CircleCurve(OWCurve): """ Displays a circle on the plot :param pen: The pen used to draw the outline of the circle :type pen: QPen :param brush: The brush used to paint the inside of the circle :type brush: QBrush :param xCenter: The x coordinate of the circle's center :type xCenter: float :param yCenter: The y coordinate of the circle's center :type yCenter: float :param radius: The circle's radius :type radius: float """ def __init__(self, pen = QPen(Qt.black), brush = QBrush(Qt.NoBrush), xCenter = 0.0, yCenter = 0.0, radius = 1.0): OWCurve.__init__(self) self._item = QGraphicsEllipseItem(self) self.center = xCenter, yCenter self.radius = radius self._rect = QRectF(xCenter-radius, yCenter-radius, 2*radius, 2*radius) self.set_pen(pen) self.set_brush(brush) def update_properties(self): self._item.setRect(self.graph_transform().mapRect(self.data_rect())) self._item.setPen(self.pen()) self._item.setBrush(self.brush()) def data_rect(self): x, y = self.center r = self.radius return QRectF(x-r, y-r, 2*r, 2*r)
def place_point(_x, _y, _c): item = QGraphicsEllipseItem() item.setX(_x) item.setY(_y) item.setRect(0, 0, self.POINT_R, self.POINT_R) color = QColor(*_c) item.setPen(QPen(color)) item.setBrush(QBrush(color)) self._group.addToGroup(item)
class OWFreeVizGraph(OWVizGraph): radius = settings.Setting(0) def __init__(self, scatter_widget, parent): super().__init__(scatter_widget, parent) self._points = [] self._point_items = [] def update_radius(self): if self._circle_item is None: return r = self.radius / 100 + 1e-5 for point, axitem in zip(self._points, self._point_items): axitem.setVisible(np.linalg.norm(point) > r) self._circle_item.setRect(QRectF(-r, -r, 2 * r, 2 * r)) def set_view_box_range(self): self.view_box.setRange(RANGE) def can_show_indicator(self, pos): if not len(self._points): return False, None r = self.radius / 100 + 1e-5 mask = np.zeros((len(self._points)), dtype=bool) mask[np.linalg.norm(self._points, axis=1) > r] = True distances = distance.cdist([[pos.x(), pos.y()]], self._points)[0] distances = distances[mask] if len(distances) and np.min(distances) < self.DISTANCE_DIFF: return True, np.flatnonzero(mask)[np.argmin(distances)] return False, None def _remove_point_items(self): for item in self._point_items: self.plot_widget.removeItem(item) self._point_items = [] def _add_point_items(self): r = self.radius / 100 + 1e-5 for point, var in zip(self._points, self._attributes): axitem = AnchorItem(line=QLineF(0, 0, *point), text=var.name) axitem.setVisible(np.linalg.norm(point) > r) axitem.setPen(pg.mkPen((100, 100, 100))) self.plot_widget.addItem(axitem) self._point_items.append(axitem) def _add_circle_item(self): if not len(self._points): return r = self.radius / 100 + 1e-5 pen = pg.mkPen(QColor(Qt.lightGray), width=1, cosmetic=True) self._circle_item = QGraphicsEllipseItem() self._circle_item.setRect(QRectF(-r, -r, 2 * r, 2 * r)) self._circle_item.setPen(pen) self.plot_widget.addItem(self._circle_item) def _add_indicator_item(self, point_i): x, y = self._points[point_i] dx = (self.view_box.childGroup.mapToDevice(QPoint(1, 0)) - self.view_box.childGroup.mapToDevice(QPoint(-1, 0))).x() self._indicator_item = MoveIndicator(x, y, 600 / dx) self.plot_widget.addItem(self._indicator_item)
def __init__(self): super().__init__() self.data = None self.subset_data = None self._subset_mask = None self._selection = None # np.array self.__replot_requested = False self._new_plotdata() self.variable_x = ContinuousVariable("radviz-x") self.variable_y = ContinuousVariable("radviz-y") box = gui.vBox(self.mainArea, True, margin=0) self.graph = OWRadvizGraph(self, box, "Plot", view_box=RadvizInteractiveViewBox) self.graph.hide_axes() box.layout().addWidget(self.graph.plot_widget) plot = self.graph.plot_widget SIZE_POLICY = (QSizePolicy.Minimum, QSizePolicy.Maximum) self.variables_selection = VariablesSelection() self.model_selected = VariableListModel(enable_dnd=True) self.model_other = VariableListModel(enable_dnd=True) self.variables_selection(self, self.model_selected, self.model_other) self.vizrank, self.btn_vizrank = RadvizVizRank.add_vizrank( self.controlArea, self, "Suggest features", self.vizrank_set_attrs ) self.btn_vizrank.setSizePolicy(*SIZE_POLICY) self.variables_selection.add_remove.layout().addWidget(self.btn_vizrank) self.viewbox = plot.getViewBox() self.replot = None g = self.graph.gui pp_box = g.point_properties_box(self.controlArea) pp_box.setSizePolicy(*SIZE_POLICY) self.models = g.points_models box = gui.vBox(self.controlArea, "Plot Properties") box.setSizePolicy(*SIZE_POLICY) g.add_widget(g.JitterSizeSlider, box) g.add_widgets([g.ShowLegend, g.ClassDensity, g.LabelOnlySelected], box) zoom_select = self.graph.box_zoom_select(self.controlArea) zoom_select.setSizePolicy(*SIZE_POLICY) self.icons = gui.attributeIconDict p = self.graph.plot_widget.palette() self.graph.set_palette(p) gui.auto_commit( self.controlArea, self, "auto_commit", "Send Selection", auto_label="Send Automatically", ) self.graph.zoom_actions(self) self._circle = QGraphicsEllipseItem() self._circle.setRect(QRectF(-1.0, -1.0, 2.0, 2.0)) self._circle.setPen(pg.mkPen(QColor(0, 0, 0), width=2))
class OWRadviz(widget.OWWidget): name = "Radviz" description = "Radviz" icon = "icons/Radviz.svg" priority = 240 class Inputs: data = Input("Data", Table, default=True) data_subset = Input("Data Subset", Table) class Outputs: selected_data = Output("Selected Data", Table, default=True) annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table) components = Output("Components", Table) settings_version = 1 settingsHandler = settings.DomainContextHandler() variable_state = settings.ContextSetting({}) auto_commit = settings.Setting(True) graph = settings.SettingProvider(OWRadvizGraph) vizrank = settings.SettingProvider(RadvizVizRank) jitter_sizes = [0, 0.1, 0.5, 1.0, 2.0] ReplotRequest = QEvent.registerEventType() graph_name = "graph.plot_widget.plotItem" class Information(widget.OWWidget.Information): sql_sampled_data = widget.Msg("Data has been sampled") class Warning(widget.OWWidget.Warning): no_features = widget.Msg("At least 2 features have to be chosen") class Error(widget.OWWidget.Error): sparse_data = widget.Msg("Sparse data is not supported") no_features = widget.Msg( "At least 3 numeric or categorical variables are required" ) no_instances = widget.Msg("At least 2 data instances are required") def __init__(self): super().__init__() self.data = None self.subset_data = None self._subset_mask = None self._selection = None # np.array self.__replot_requested = False self._new_plotdata() self.variable_x = ContinuousVariable("radviz-x") self.variable_y = ContinuousVariable("radviz-y") box = gui.vBox(self.mainArea, True, margin=0) self.graph = OWRadvizGraph(self, box, "Plot", view_box=RadvizInteractiveViewBox) self.graph.hide_axes() box.layout().addWidget(self.graph.plot_widget) plot = self.graph.plot_widget SIZE_POLICY = (QSizePolicy.Minimum, QSizePolicy.Maximum) self.variables_selection = VariablesSelection() self.model_selected = VariableListModel(enable_dnd=True) self.model_other = VariableListModel(enable_dnd=True) self.variables_selection(self, self.model_selected, self.model_other) self.vizrank, self.btn_vizrank = RadvizVizRank.add_vizrank( self.controlArea, self, "Suggest features", self.vizrank_set_attrs ) self.btn_vizrank.setSizePolicy(*SIZE_POLICY) self.variables_selection.add_remove.layout().addWidget(self.btn_vizrank) self.viewbox = plot.getViewBox() self.replot = None g = self.graph.gui pp_box = g.point_properties_box(self.controlArea) pp_box.setSizePolicy(*SIZE_POLICY) self.models = g.points_models box = gui.vBox(self.controlArea, "Plot Properties") box.setSizePolicy(*SIZE_POLICY) g.add_widget(g.JitterSizeSlider, box) g.add_widgets([g.ShowLegend, g.ClassDensity, g.LabelOnlySelected], box) zoom_select = self.graph.box_zoom_select(self.controlArea) zoom_select.setSizePolicy(*SIZE_POLICY) self.icons = gui.attributeIconDict p = self.graph.plot_widget.palette() self.graph.set_palette(p) gui.auto_commit( self.controlArea, self, "auto_commit", "Send Selection", auto_label="Send Automatically", ) self.graph.zoom_actions(self) self._circle = QGraphicsEllipseItem() self._circle.setRect(QRectF(-1.0, -1.0, 2.0, 2.0)) self._circle.setPen(pg.mkPen(QColor(0, 0, 0), width=2)) def resizeEvent(self, event): self._update_points_labels() def keyPressEvent(self, event): super().keyPressEvent(event) self.graph.update_tooltip(event.modifiers()) def keyReleaseEvent(self, event): super().keyReleaseEvent(event) self.graph.update_tooltip(event.modifiers()) def vizrank_set_attrs(self, attrs): if not attrs: return self.variables_selection.display_none() self.model_selected[:] = attrs[:] self.model_other[:] = [v for v in self.model_other if v not in attrs] def _new_plotdata(self): self.plotdata = namespace( valid_mask=None, embedding_coords=None, points=None, arcarrows=[], point_labels=[], rand=None, data=None, ) def update_colors(self): self._vizrank_color_change() self.cb_class_density.setEnabled(self.graph.can_draw_density()) def sizeHint(self): return QSize(800, 500) def clear(self): """ Clear/reset the widget state """ self.data = None self.model_selected.clear() self.model_other.clear() self._clear_plot() def _clear_plot(self): self._new_plotdata() self.graph.plot_widget.clear() def invalidate_plot(self): """ Schedule a delayed replot. """ if not self.__replot_requested: self.__replot_requested = True QApplication.postEvent( self, QEvent(self.ReplotRequest), Qt.LowEventPriority - 10 ) def init_attr_values(self): self.graph.set_domain(self.data) def _vizrank_color_change(self): attr_color = self.graph.attr_color is_enabled = ( self.data is not None and not self.data.is_sparse() and (len(self.model_other) + len(self.model_selected)) > 3 and len(self.data) > 1 ) self.btn_vizrank.setEnabled( is_enabled and attr_color is not None and not np.isnan( self.data.get_column_view(attr_color)[0].astype(float) ).all() ) self.vizrank.initialize() @Inputs.data def set_data(self, data): """ Set the input dataset and check if data is valid. Args: data (Orange.data.table): data instances """ def sql(data): self.Information.sql_sampled_data.clear() if isinstance(data, SqlTable): if data.approx_len() < 4000: data = Table(data) else: self.Information.sql_sampled_data() data_sample = data.sample_time(1, no_cache=True) data_sample.download_data(2000, partial=True) data = Table(data_sample) return data def settings(data): # get the default encoded state, replacing the position with Inf state = VariablesSelection.encode_var_state( [list(self.model_selected), list(self.model_other)] ) state = { key: (source_ind, np.inf) for key, (source_ind, _) in state.items() } self.openContext(data.domain) selected_keys = [ key for key, (sind, _) in self.variable_state.items() if sind == 0 ] if set(selected_keys).issubset(set(state.keys())): pass # update the defaults state (the encoded state must contain # all variables in the input domain) state.update(self.variable_state) # ... and restore it with saved positions taking precedence over # the defaults selected, other = VariablesSelection.decode_var_state( state, [list(self.model_selected), list(self.model_other)] ) return selected, other def is_sparse(data): if data.is_sparse(): self.Error.sparse_data() data = None return data def are_features(data): domain = data.domain vars = [ var for var in chain(domain.class_vars, domain.metas, domain.attributes) if var.is_primitive() ] if len(vars) < 3: self.Error.no_features() data = None return data def are_instances(data): if len(data) < 2: self.Error.no_instances() data = None return data self.clear_messages() self.btn_vizrank.setEnabled(False) self.closeContext() self.clear() self.information() self.Error.clear() for f in [sql, is_sparse, are_features, are_instances]: if data is None: break data = f(data) if data is not None: self.data = data self.init_attr_values() domain = data.domain vars = [ v for v in chain(domain.metas, domain.attributes) if v.is_primitive() ] self.model_selected[:] = vars[:5] self.model_other[:] = vars[5:] + list(domain.class_vars) self.model_selected[:], self.model_other[:] = settings(data) self._selection = np.zeros(len(data), dtype=np.uint8) self.invalidate_plot() else: self.data = None @Inputs.data_subset def set_subset_data(self, subset): """ Set the supplementary input subset dataset. Args: subset (Orange.data.table): subset of data instances """ self.subset_data = subset self._subset_mask = None self.controls.graph.alpha_value.setEnabled(subset is None) def handleNewSignals(self): if self.data is not None: self._clear_plot() if self.subset_data is not None and self._subset_mask is None: dataids = self.data.ids.ravel() subsetids = np.unique(self.subset_data.ids) self._subset_mask = np.in1d(dataids, subsetids, assume_unique=True) self.setup_plot(reset_view=True) self.cb_class_density.setEnabled(self.graph.can_draw_density()) else: self.init_attr_values() self.graph.new_data(None) self._vizrank_color_change() self.commit() def customEvent(self, event): if event.type() == OWRadviz.ReplotRequest: self.__replot_requested = False self._clear_plot() self.setup_plot(reset_view=True) else: super().customEvent(event) def closeContext(self): self.variable_state = VariablesSelection.encode_var_state( [list(self.model_selected), list(self.model_other)] ) super().closeContext() def prepare_radviz_data(self, variables): ec, points, valid_mask = radviz(self.data, variables, self.plotdata.points) self.plotdata.embedding_coords = ec self.plotdata.points = points self.plotdata.valid_mask = valid_mask def setup_plot(self, reset_view=True): if self.data is None: return self.graph.jitter_continuous = True self.__replot_requested = False variables = list(self.model_selected) if len(variables) < 2: self.Warning.no_features() self.graph.new_data(None) return self.Warning.clear() self.prepare_radviz_data(variables) if self.plotdata.embedding_coords is None: return domain = self.data.domain new_metas = domain.metas + (self.variable_x, self.variable_y) domain = Domain( attributes=domain.attributes, class_vars=domain.class_vars, metas=new_metas ) mask = self.plotdata.valid_mask array = np.zeros((len(self.data), 2), dtype=np.float) array[mask] = self.plotdata.embedding_coords data = self.data.transform(domain) data[:, self.variable_x] = array[:, 0].reshape(-1, 1) data[:, self.variable_y] = array[:, 1].reshape(-1, 1) subset_data = ( data[self._subset_mask & mask] if self._subset_mask is not None and len(self._subset_mask) else None ) self.plotdata.data = data self.graph.new_data(data[mask], subset_data) if self._selection is not None: self.graph.selection = self._selection[self.plotdata.valid_mask] self.graph.update_data(self.variable_x, self.variable_y, reset_view=reset_view) self.graph.plot_widget.addItem(self._circle) self.graph.scatterplot_points = ScatterPlotItem( x=self.plotdata.points[:, 0], y=self.plotdata.points[:, 1] ) self._update_points_labels() self.graph.plot_widget.addItem(self.graph.scatterplot_points) def randomize_indices(self): ec = self.plotdata.embedding_coords self.plotdata.rand = ( np.random.choice(len(ec), MAX_POINTS, replace=False) if len(ec) > MAX_POINTS else None ) def manual_move(self): self.__replot_requested = False if self.plotdata.rand is not None: rand = self.plotdata.rand valid_mask = self.plotdata.valid_mask data = self.data[valid_mask] selection = self._selection[valid_mask] selection = selection[rand] ec, _, valid_mask = radviz( data, list(self.model_selected), self.plotdata.points ) assert sum(valid_mask) == len(data) data = data[rand] ec = ec[rand] data_x = data.X data_y = data.Y data_metas = data.metas else: self.prepare_radviz_data(list(self.model_selected)) ec = self.plotdata.embedding_coords valid_mask = self.plotdata.valid_mask data_x = self.data.X[valid_mask] data_y = self.data.Y[valid_mask] data_metas = self.data.metas[valid_mask] selection = self._selection[valid_mask] attributes = (self.variable_x, self.variable_y) + self.data.domain.attributes domain = Domain( attributes=attributes, class_vars=self.data.domain.class_vars, metas=self.data.domain.metas, ) data = Table.from_numpy( domain, X=np.hstack((ec, data_x)), Y=data_y, metas=data_metas ) self.graph.new_data(data, None) self.graph.selection = selection self.graph.update_data(self.variable_x, self.variable_y, reset_view=True) self.graph.plot_widget.addItem(self._circle) self.graph.scatterplot_points = ScatterPlotItem( x=self.plotdata.points[:, 0], y=self.plotdata.points[:, 1] ) self._update_points_labels() self.graph.plot_widget.addItem(self.graph.scatterplot_points) def _update_points_labels(self): if self.plotdata.points is None: return for point_label in self.plotdata.point_labels: self.graph.plot_widget.removeItem(point_label) self.plotdata.point_labels = [] sx, sy = self.graph.view_box.viewPixelSize() for row in self.plotdata.points: ti = TextItem() metrics = QFontMetrics(ti.textItem.font()) text_width = ((RANGE.width()) / 2.0 - np.abs(row[0])) / sx name = row[2].name ti.setText(name) ti.setTextWidth(text_width) ti.setColor(QColor(0, 0, 0)) br = ti.boundingRect() width = ( metrics.width(name) if metrics.width(name) < br.width() else br.width() ) width = sx * (width + 5) height = sy * br.height() ti.setPos(row[0] - (row[0] < 0) * width, row[1] + (row[1] > 0) * height) self.plotdata.point_labels.append(ti) self.graph.plot_widget.addItem(ti) def _update_jitter(self): self.invalidate_plot() def reset_graph_data(self, *_): if self.data is not None: self.graph.rescale_data() self._update_graph() def _update_graph(self, reset_view=True, **_): self.graph.zoomStack = [] if self.graph.data is None: return self.graph.update_data(self.variable_x, self.variable_y, reset_view=reset_view) def update_density(self): self._update_graph(reset_view=True) def selection_changed(self): if self.graph.selection is not None: self._selection[self.plotdata.valid_mask] = self.graph.selection self.commit() def prepare_data(self): pass def commit(self): selected = annotated = components = None graph = self.graph if self.plotdata.data is not None: name = self.data.name data = self.plotdata.data mask = self.plotdata.valid_mask.astype(int) mask[mask == 1] = ( graph.selection if graph.selection is not None else [False * len(mask)] ) selection = ( np.array([], dtype=np.uint8) if mask is None else np.flatnonzero(mask) ) if len(selection): selected = data[selection] selected.name = name + ": selected" selected.attributes = self.data.attributes if graph.selection is not None and np.max(graph.selection) > 1: annotated = create_groups_table(data, mask) else: annotated = create_annotated_table(data, selection) annotated.attributes = self.data.attributes annotated.name = name + ": annotated" comp_domain = Domain( self.plotdata.points[:, 2], metas=[StringVariable(name="component")] ) metas = np.array([["RX"], ["RY"], ["angle"]]) angle = np.arctan2( np.array(self.plotdata.points[:, 1].T, dtype=float), np.array(self.plotdata.points[:, 0].T, dtype=float), ) components = Table.from_numpy( comp_domain, X=np.row_stack((self.plotdata.points[:, :2].T, angle)), metas=metas, ) components.name = name + ": components" self.Outputs.selected_data.send(selected) self.Outputs.annotated_data.send(annotated) self.Outputs.components.send(components) def send_report(self): if self.data is None: return def name(var): return var and var.name caption = report.render_items_vert( ( ("Color", name(self.graph.attr_color)), ("Label", name(self.graph.attr_label)), ("Shape", name(self.graph.attr_shape)), ("Size", name(self.graph.attr_size)), ( "Jittering", self.graph.jitter_size != 0 and "{} %".format(self.graph.jitter_size), ), ) ) self.report_plot() if caption: self.report_caption(caption)
def put_point(_x, _y): item = QGraphicsEllipseItem() item.setX(_x) item.setY(_y) item.setRect(0, 0, self.POINT_R, self.POINT_R) color = QColor(*colors.pop().astype(int)) item.setPen(QPen(color)) item.setBrush(QBrush(color)) self.__group.addToGroup(item)
class OWGraphWithAnchors(OWScatterPlotBase, ParameterSetter): """ Graph for projections in which dimensions can be manually moved Class is used as a graph base class for OWFreeViz and OWRadviz.""" DISTANCE_DIFF = 0.08 def __init__(self, scatter_widget, parent, view_box=DraggableItemsViewBox): super().__init__(scatter_widget, parent, view_box) self.anchor_items = None self.circle_item = None self.indicator_item = None self._tooltip_delegate = MouseEventDelegate(self.help_event, self.show_indicator_event) self.plot_widget.scene().installEventFilter(self._tooltip_delegate) def clear(self): super().clear() self.anchor_items = None self.circle_item = None self.indicator_item = None def update_coordinates(self): super().update_coordinates() if self.scatterplot_item is not None: self.update_anchors() self.update_circle() self.set_view_box_range() self.view_box.setAspectLocked(True, 1) def update_anchors(self): raise NotImplementedError def update_circle(self): if self.scatterplot_item is not None and not self.circle_item: self.circle_item = QGraphicsEllipseItem() self.circle_item.setRect(QRectF(-1, -1, 2, 2)) self.circle_item.setPen(pg.mkPen(QColor(0, 0, 0), width=2)) self.plot_widget.addItem(self.circle_item) def reset_button_clicked(self): self.set_view_box_range() def set_view_box_range(self): raise NotImplementedError def closest_draggable_item(self, pos): return None def show_indicator(self, anchor_idx): self._update_indicator_item(anchor_idx) def show_indicator_event(self, ev): scene = self.plot_widget.scene() if self.scatterplot_item is None or scene.drag_tooltip.isVisible(): return False if self.view_box.mouse_state == 1: return True pos = self.scatterplot_item.mapFromScene(ev.scenePos()) anchor_idx = self.closest_draggable_item(pos) if anchor_idx is not None: self._update_indicator_item(anchor_idx) if self.view_box.mouse_state == 0: self.view_box.setCursor(Qt.OpenHandCursor) else: if self.indicator_item is not None: self.plot_widget.removeItem(self.indicator_item) self.indicator_item = None self.view_box.setCursor(Qt.ArrowCursor) return True def _update_indicator_item(self, anchor_idx): if self.indicator_item is not None: self.plot_widget.removeItem(self.indicator_item) self._add_indicator_item(anchor_idx) def _add_indicator_item(self, anchor_idx): pass
def update_circle(self): if self.scatterplot_item is not None and not self.circle_item: self.circle_item = QGraphicsEllipseItem() self.circle_item.setRect(QRectF(-1, -1, 2, 2)) self.circle_item.setPen(pg.mkPen(QColor(0, 0, 0), width=2)) self.plot_widget.addItem(self.circle_item)
def test_anchoritem(self): anchoritem = NodeAnchorItem(None) anchoritem.setAnimationEnabled(False) self.scene.addItem(anchoritem) path = QPainterPath() path.addEllipse(0, 0, 100, 100) anchoritem.setAnchorPath(path) anchor = AnchorPoint() anchoritem.addAnchor(anchor) ellipse1 = QGraphicsEllipseItem(-3, -3, 6, 6) ellipse2 = QGraphicsEllipseItem(-3, -3, 6, 6) self.scene.addItem(ellipse1) self.scene.addItem(ellipse2) anchor.scenePositionChanged.connect(ellipse1.setPos) with self.assertRaises(ValueError): anchoritem.addAnchor(anchor) anchor1 = AnchorPoint() anchoritem.addAnchor(anchor1) anchor1.scenePositionChanged.connect(ellipse2.setPos) self.assertSequenceEqual(anchoritem.anchorPoints(), [anchor, anchor1]) self.assertSequenceEqual(anchoritem.anchorPositions(), [2 / 3, 1 / 3]) anchoritem.setAnchorPositions([0.5, 0.0]) self.assertSequenceEqual(anchoritem.anchorPositions(), [0.5, 0.0]) def advance(): t = anchoritem.anchorPositions() t = [(t + 0.05) % 1.0 for t in t] anchoritem.setAnchorPositions(t) timer = QTimer(anchoritem, interval=10) timer.start() timer.timeout.connect(advance) self.qWait() timer.stop() anchoritem.setAnchorOpen(True) anchoritem.setHovered(True) self.assertEqual(*[p.scenePos() for p in anchoritem.anchorPoints()]) anchoritem.setAnchorOpen(False) self.assertNotEqual(*[p.scenePos() for p in anchoritem.anchorPoints()]) anchoritem.setAnchorOpen(False) anchoritem.setHovered(True) self.assertNotEqual(*[p.scenePos() for p in anchoritem.anchorPoints()]) anchoritem = NodeAnchorItem(None) anchoritem.setSignals([ InputSignal("first", "object", "set_first"), InputSignal("second", "object", "set_second") ]) self.assertListEqual( anchoritem._NodeAnchorItem__pathStroker.dashPattern(), list(anchoritem._NodeAnchorItem__unanchoredDash)) anchoritem.setAnchorOpen(True) anchoritem.setHovered(True) self.assertListEqual( anchoritem._NodeAnchorItem__pathStroker.dashPattern(), list(anchoritem._NodeAnchorItem__channelDash))
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
class OWRadvizGraph(OWVizGraph): def __init__(self, scatter_widget, parent): super().__init__(scatter_widget, parent, RadvizInteractiveViewBox) self._text_items = [] def set_point(self, i, x, y): angle = np.arctan2(y, x) super().set_point(i, np.cos(angle), np.sin(angle)) def set_view_box_range(self): self.view_box.setRange(RANGE, padding=0.025) def can_show_indicator(self, pos): if self._points is None: return False, None np_pos = np.array([[pos.x(), pos.y()]]) distances = distance.cdist(np_pos, self._points[:, :2])[0] if len(distances) and np.min(distances) < self.DISTANCE_DIFF: return True, np.argmin(distances) return False, None def update_items(self): super().update_items() self._update_text_items() def _update_text_items(self): self._remove_text_items() self._add_text_items() def _remove_text_items(self): for item in self._text_items: self.plot_widget.removeItem(item) self._text_items = [] def _add_text_items(self): if self._points is None: return for point in self._points: ti = TextItem() ti.setText(point[2].name) ti.setColor(QColor(0, 0, 0)) ti.setPos(point[0], point[1]) self._text_items.append(ti) self.plot_widget.addItem(ti) def _add_point_items(self): if self._points is None: return x, y = self._points[:, 0], self._points[:, 1] self._point_items = ScatterPlotItem(x=x, y=y) self.plot_widget.addItem(self._point_items) def _add_circle_item(self): if self._points is None: return self._circle_item = QGraphicsEllipseItem() self._circle_item.setRect(QRectF(-1., -1., 2., 2.)) self._circle_item.setPen(pg.mkPen(QColor(0, 0, 0), width=2)) self.plot_widget.addItem(self._circle_item) def _add_indicator_item(self, point_i): if point_i is None: return x, y = self._points[point_i][:2] col = self.view_box.mouse_state dx = (self.view_box.childGroup.mapToDevice(QPoint(1, 0)) - self.view_box.childGroup.mapToDevice(QPoint(-1, 0))).x() self._indicator_item = MoveIndicator(np.arctan2(y, x), col, 6000 / dx) self.plot_widget.addItem(self._indicator_item)