def rect(self): if getattr(self, "_rect", QRectF()).isValid(): return self._rect else: return QRectF(QPointF(0, 0), self.document().size()) | \ getattr(self, "_rect", QRectF(0, 0, 1, 1))
def setup_plot(self, scoreindex, scores, nulldist=None): """ Setup the score histogram plot Parameters ---------- scoreindex : int Score index (into OWFeatureSelection.Scores) scores : (N, ) array The scores obtained nulldist (P, N) array optional The scores obtained under P permutations of labels. """ score_name, side, test_type, _ = self.Scores[scoreindex] low, high = self.thresholds.get(score_name, (-np.inf, np.inf)) validmask = np.isfinite(scores) validscores = scores[validmask] nbins = int(max(np.ceil(np.sqrt(len(validscores))), 20)) freq, edges = np.histogram(validscores, bins=nbins) self.histogram.setHistogramCurve( pg.PlotCurveItem(x=edges, y=freq, stepMode=True, pen=pg.mkPen("b", width=2))) if nulldist is not None: nulldist = nulldist.ravel() validmask = np.isfinite(nulldist) validnulldist = nulldist[validmask] nullbins = edges # XXX: extend to the full range of nulldist nullfreq, _ = np.histogram(validnulldist, bins=nullbins) nullfreq = nullfreq * (freq.sum() / nullfreq.sum()) nullitem = pg.PlotCurveItem(x=nullbins, y=nullfreq, stepMode=True, pen=pg.mkPen((50, 50, 50, 100))) # Ensure it stacks behind the main curve nullitem.setZValue(nullitem.zValue() - 10) self.histogram.addItem(nullitem) # Restore saved thresholds eps = np.finfo(float).eps minx, maxx = edges[0] - eps, edges[-1] + eps low, high = max(low, minx), min(high, maxx) if side == OWFeatureSelection.LowTail: mode = Histogram.Low elif side == OWFeatureSelection.HighTail: mode = Histogram.High elif side == OWFeatureSelection.TwoTail: mode = Histogram.TwoSided else: assert False self.histogram.setSelectionMode(mode) self.histogram.setBoundary(low, high) # If this is a two sample test add markers to the left and right # plot indicating which group is over-expressed in that part if test_type == OWFeatureSelection.TwoSampleTest and \ side == OWFeatureSelection.TwoTail: maxy = np.max(freq) # XXX: Change use of integer constant if scoreindex == 0: # fold change is centered on 1.0 x1, y1 = (minx + 1) / 2, maxy x2, y2 = (maxx + 1) / 2, maxy else: x1, y1 = minx / 2, maxy x2, y2 = maxx / 2, maxy grp, selected_indices = self.selected_split() values = grp.values selected_values = [values[i] for i in selected_indices] left = ", ".join(v for v in values if v not in selected_values) right = ", ".join(v for v in selected_values) labelitem = pg.TextItem(left, color=(40, 40, 40)) labelitem.setPos(x1, y1) self.histogram.addItem(labelitem) labelitem = pg.TextItem(right, color=(40, 40, 40)) labelitem.setPos(x2, y2) self.histogram.addItem(labelitem) miny, maxy = 0.0, np.max(freq) if nulldist is not None: maxy = max(np.max(nullfreq), maxy) if not np.any(np.isnan([maxx, minx, maxy])): self.histogram.setRange(QRectF(minx, miny, maxx - minx, maxy - miny), padding=0.05)
def _select_data(self): self.widget.graph.select_by_rectangle(QRectF(4, 3, 3, 1)) return self.widget.graph.get_selection()
def boundingRect(self): return QRectF(-self.r, -self.r, 2 * self.r, 2 * self.r)
def boundingRect(self): return QRectF(0, 0, 40 + self.text_width, 20 + self.scale.bins * 15)
def _ensureSceneRect(self, scene): r = scene.addRect(QRectF(0, 0, 400, 400)) scene.sceneRect() scene.removeItem(r)
def update_data(self, attr_x, attr_y, reset_view=True): self.master.Warning.missing_coords.clear() self.master.Information.missing_coords.clear() self._clear_plot_widget() self.shown_x, self.shown_y = attr_x, attr_y if self.jittered_data is None or not len(self.jittered_data): self.valid_data = None else: index_x = self.domain.index(attr_x) index_y = self.domain.index(attr_y) self.valid_data = self.get_valid_list([index_x, index_y]) if not np.any(self.valid_data): self.valid_data = None if self.valid_data is None: self.selection = None self.n_points = 0 self.master.Warning.missing_coords( self.shown_x.name, self.shown_y.name) return x_data, y_data = self.get_xy_data_positions( attr_x, attr_y, self.valid_data) self.n_points = len(x_data) if reset_view: min_x, max_x = np.nanmin(x_data), np.nanmax(x_data) min_y, max_y = np.nanmin(y_data), np.nanmax(y_data) self.view_box.setRange( QRectF(min_x, min_y, max_x - min_x, max_y - min_y), padding=0.025) self.view_box.init_history() self.view_box.tag_history() [min_x, max_x], [min_y, max_y] = self.view_box.viewRange() for axis, name, index in (("bottom", attr_x, index_x), ("left", attr_y, index_y)): self.set_axis_title(axis, name) var = self.domain[index] if var.is_discrete: self.set_labels(axis, get_variable_values_sorted(var)) else: self.set_labels(axis, None) color_data, brush_data = self.compute_colors() color_data_sel, brush_data_sel = self.compute_colors_sel() size_data = self.compute_sizes() shape_data = self.compute_symbols() if self.should_draw_density(): rgb_data = [pen.color().getRgb()[:3] for pen in color_data] self.density_img = classdensity.class_density_image( min_x, max_x, min_y, max_y, self.resolution, x_data, y_data, rgb_data) self.plot_widget.addItem(self.density_img) data_indices = np.flatnonzero(self.valid_data) if len(data_indices) != self.original_data.shape[1]: self.master.Information.missing_coords( self.shown_x.name, self.shown_y.name) self.scatterplot_item = ScatterPlotItem( x=x_data, y=y_data, data=data_indices, symbol=shape_data, size=size_data, pen=color_data, brush=brush_data ) self.scatterplot_item_sel = ScatterPlotItem( x=x_data, y=y_data, data=data_indices, symbol=shape_data, size=size_data + SELECTION_WIDTH, pen=color_data_sel, brush=brush_data_sel ) self.plot_widget.addItem(self.scatterplot_item_sel) self.plot_widget.addItem(self.scatterplot_item) self.scatterplot_item.selected_points = [] self.scatterplot_item.sigClicked.connect(self.select_by_click) self.update_labels() self.make_legend() self.plot_widget.replot()
def setSelectionRect(self, rect): if self._selection_rect != rect: self._selection_rect = QRectF(rect) self._item.setRect(self._selection_rect)
def _reset(self): self.setSelectionRect(QRectF()) self._item.setVisible(False) self._mouse_dragging = False
def basic_scene(): scene = QGraphicsScene() scene.addRect(QRectF(0, 0, 100, 100)) return scene
def rect(self): return QRectF(self.pos(), QSizeF(*self.size()))
def boundingRect(self): return QRectF(0, 0, self.width, self.height)
def clear_scene(self): self.scene.clear() self.scene.setSceneRect(QRectF())
def boundingRect(self): return self._rect if getattr(self, "_rect", QRectF()).isValid() \ else super().boundingRect()
def boundingRect(self): return QRectF()
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 set_view_box_range(self): self.view_box.setRange(QRectF(-1.05, -1.05, 2.1, 2.1))
def clear_scene(self): self.scene.clear() self.scene.setSceneRect(QRectF()) self.view.setSceneRect(QRectF()) self._stripe_plot = None
def adjustLeft(rect): rect = QRectF(rect) rect.setLeft(geom.left()) return rect
def update_data(self, attr_x, attr_y, reset_view=True): self.master.Warning.missing_coords.clear() self.master.Information.missing_coords.clear() self._clear_plot_widget() if self.shown_y != attr_y: # 'reset' the axis text width estimation. Without this the left # axis tick labels space only ever expands yaxis = self.plot_widget.getAxis("left") yaxis.textWidth = 30 self.shown_x, self.shown_y = attr_x, attr_y if attr_x not in self.data.domain or attr_y not in self.data.domain: data = self.sparse_to_dense() self.set_data(data) if self.jittered_data is None or not len(self.jittered_data): self.valid_data = None else: self.valid_data = self.get_valid_list([attr_x, attr_y]) if not np.any(self.valid_data): self.valid_data = None if self.valid_data is None: self.selection = None self.n_points = 0 self.master.Warning.missing_coords(self.shown_x.name, self.shown_y.name) return x_data, y_data = self.get_xy_data_positions(attr_x, attr_y, self.valid_data) self.n_points = len(x_data) if reset_view: min_x, max_x = np.nanmin(x_data), np.nanmax(x_data) min_y, max_y = np.nanmin(y_data), np.nanmax(y_data) self.view_box.setRange(QRectF(min_x, min_y, max_x - min_x, max_y - min_y), padding=0.025) self.view_box.init_history() self.view_box.tag_history() [min_x, max_x], [min_y, max_y] = self.view_box.viewRange() for axis, var in (("bottom", attr_x), ("left", attr_y)): self.set_axis_title(axis, var) if var.is_discrete: self.set_labels(axis, get_variable_values_sorted(var)) else: self.set_labels(axis, None) color_data, brush_data = self.compute_colors() color_data_sel, brush_data_sel = self.compute_colors_sel() size_data = self.compute_sizes() shape_data = self.compute_symbols() if self.should_draw_density(): rgb_data = [pen.color().getRgb()[:3] for pen in color_data] self.density_img = classdensity.class_density_image( min_x, max_x, min_y, max_y, self.resolution, x_data, y_data, rgb_data) self.plot_widget.addItem(self.density_img) self.data_indices = np.flatnonzero(self.valid_data) if len(self.data_indices) != len(self.data): self.master.Information.missing_coords(self.shown_x.name, self.shown_y.name) self.scatterplot_item = ScatterPlotItem(x=x_data, y=y_data, data=self.data_indices, symbol=shape_data, size=size_data, pen=color_data, brush=brush_data) self.scatterplot_item_sel = ScatterPlotItem(x=x_data, y=y_data, data=self.data_indices, symbol=shape_data, size=size_data + SELECTION_WIDTH, pen=color_data_sel, brush=brush_data_sel) self.plot_widget.addItem(self.scatterplot_item_sel) self.plot_widget.addItem(self.scatterplot_item) self.scatterplot_item.selected_points = [] self.scatterplot_item.sigClicked.connect(self.select_by_click) if self.show_reg_line: _x_data = self.data.get_column_view(self.shown_x)[0] _y_data = self.data.get_column_view(self.shown_y)[0] _x_data = _x_data[self.valid_data] _y_data = _y_data[self.valid_data] assert _x_data.size assert _y_data.size self.draw_regression_line(_x_data, _y_data, np.min(_x_data), np.max(_y_data)) self.update_labels() self.make_legend() self.plot_widget.replot()
def _select_data(self): rect = QRectF(QPointF(-20, -20), QPointF(20, 20)) self.widget.graph.select_by_rectangle(rect) return self.widget.graph.get_selection()
def update_scene(self): self.clear_scene() if self.domain is None or not len(self.points[0]): return n_attrs = self.n_attributes if self.display_index else int(1e10) attr_inds, attributes = zip(*self.get_ordered_attributes()[:n_attrs]) name_items = [QGraphicsTextItem(attr.name) for attr in attributes] point_text = QGraphicsTextItem("Points") probs_text = QGraphicsTextItem("Probabilities (%)") all_items = name_items + [point_text, probs_text] name_offset = -max(t.boundingRect().width() for t in all_items) - 10 w = self.view.viewport().rect().width() max_width = w + name_offset - 30 points = [self.points[i][self.target_class_index] for i in attr_inds] if self.align == OWNomogram.ALIGN_LEFT: points = [p - p.min() for p in points] max_ = np.nan_to_num(max(max(abs(p)) for p in points)) d = 100 / max_ if max_ else 1 minimums = [p[self.target_class_index].min() for p in self.points] if self.scale == OWNomogram.POINT_SCALE: points = [p * d for p in points] if self.align == OWNomogram.ALIGN_LEFT: self.scale_marker_values = lambda x: (x - minimums) * d else: self.scale_marker_values = lambda x: x * d else: if self.align == OWNomogram.ALIGN_LEFT: self.scale_marker_values = lambda x: x - minimums else: self.scale_marker_values = lambda x: x point_item, nomogram_head = self.create_main_nomogram( attributes, attr_inds, name_items, points, max_width, point_text, name_offset) probs_item, nomogram_foot = self.create_footer_nomogram( probs_text, d, minimums, max_width, name_offset) for item in self.feature_items.values(): item.dot.point_dot = point_item.dot item.dot.probs_dot = probs_item.dot item.dot.vertical_line = self.hidden_vertical_line self.nomogram = nomogram = NomogramItem() nomogram.add_items([nomogram_head, self.nomogram_main, nomogram_foot]) self.scene.addItem(nomogram) self.set_feature_marker_values() rect = QRectF(self.scene.itemsBoundingRect().x(), self.scene.itemsBoundingRect().y(), self.scene.itemsBoundingRect().width(), self.nomogram.preferredSize().height()).adjusted( 10, 0, 20, 0) self.scene.setSceneRect(rect) # Clip top and bottom (60 and 150) parts from the main view self.view.setSceneRect(rect.x(), rect.y() + 80, rect.width() - 10, rect.height() - 160) self.view.viewport().setMaximumHeight(rect.height() - 160) # Clip main part from top/bottom views # below point values are imprecise (less/more than required) but this # is not a problem due to clipped scene content still being drawn self.top_view.setSceneRect(rect.x(), rect.y() + 3, rect.width() - 10, 20) self.bottom_view.setSceneRect(rect.x(), rect.height() - 110, rect.width() - 10, 30)
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 paint(self, p, opt, widget): def get_arrows(): # Compute (n, 4) array of coordinates of arrows' ends # Arrows are at 15 degrees; length is 10, clipped to edge length x0, y0, x1, y1 = edge_coords.T arr_len = np.clip(lengths - sizes1 - w3, 0, 10) cos12 = arr_len * np.cos(np.pi / 12) sin12 = arr_len * np.sin(np.pi / 12) # cos(a ± 15) = cos(a) cos(15) ∓ sin(a) sin(15) tx = sins * (fx * sin12) x1a = x1 - coss * (fx * cos12) x2a = x1a - tx x1a += tx # sin(a ± 15) = sin(a) cos(15) ± sin(15) cos(a) ty = (fy * sin12) * coss y1a = y1 + sins * (fy * cos12) y2a = y1a - ty y1a += ty return np.vstack((x1a, y1a, x2a, y2a)).T def get_short_edge_coords(): # Compute the target-side coordinates of edges with arrows # Such edges are shorted by 8 pixels + width / 3 off = 8 + w3 return edge_coords[:, 2:] + (off * np.vstack((-fxcos, fysin))).T if self.xData is None or len(self.xData) == 0: return # Widths of edges, divided by 3; used for adjusting sizes w3 = (self.widths if self.widths is not None else self.pen.width()) / 3 # Sizes of source and target nodes; they are used for adjusting the # edge lengths, so we increase the sizes by edge widths / 3 sizes0, sizes1 = self.sizes[::2] + w3, self.sizes[1::2] + w3 # Coordinates of vertices for all end points (in real world) x0s, x1s = self.xData[::2], self.xData[1::2] y0s, y1s = self.yData[::2], self.yData[1::2] # Factors for transforming real-worlds coordinates into pixels fx = 1 / p.worldTransform().m11() fy = 1 / p.worldTransform().m22() # Computations of angles (lengths are also used to clip the arrows) # We need sine and cosine of angles, and never the actual angles. # Sine and cosine are compute as ratios in triangles rather than with # trigonometric functions diffx, diffy = (x1s - x0s) / fx, -(y1s - y0s) / fy lengths = np.sqrt(diffx**2 + diffy**2) arcs = lengths == 0 coss, sins = np.nan_to_num(diffx / lengths), np.nan_to_num(diffy / lengths) # A slower version of the above, with trigonometry # angles = np.arctan2(-(y1s - y0s) / fy, (x1s - x0s) / fx) # return np.cos(angles), np.sin(angles) # Sin and cos are mostly used as mulitplied with fx and fy; precompute fxcos, fysin = fx * coss, fy * sins # Coordinates of edges' end points: coordinates of vertices, adjusted # by sizes. When drawing arraws, the target coordinate is used for # the tip of the arrow, not the edge edge_coords = np.vstack((x0s + fxcos * sizes0, y0s - fysin * sizes0, x1s - fxcos * sizes1, y1s + fysin * sizes1)).T pen = QPen(self.pen) p.setRenderHint(p.Antialiasing, True) p.setCompositionMode(p.CompositionMode_SourceOver) if self.widths is None: p.setPen(pen) if self.directed: for (x0, y0, x1, y1), (x1w, y1w), (xa1, ya1, xa2, ya2), arc in zip(edge_coords, get_short_edge_coords(), get_arrows(), arcs): if not arc: p.drawLine(QLineF(x0, y0, x1w, y1w)) p.drawLine(QLineF(xa1, ya1, x1, y1)) p.drawLine(QLineF(xa2, ya2, x1, y1)) else: for ecoords in edge_coords[~arcs]: p.drawLine(QLineF(*ecoords)) else: if self.directed: for (x0, y0, x1, y1), (x1w, y1w), (xa1, ya1, xa2, ya2), w, arc in zip(edge_coords, get_short_edge_coords(), get_arrows(), self.widths, arcs): if not arc: pen.setWidth(w) p.setPen(pen) p.drawLine(QLineF(x0, y0, x1w, y1w)) p.drawLine(QLineF(xa1, ya1, x1, y1)) p.drawLine(QLineF(xa2, ya2, x1, y1)) else: for ecoords, w in zip(edge_coords[~arcs], self.widths[~arcs]): pen.setWidth(w) p.setPen(pen) p.drawLine(QLineF(*ecoords)) # This part is not so optimized because there can't be that many loops if np.any(arcs): xs, ys = self.xData[::2][arcs], self.yData[1::2][arcs] sizes = self.sizes[::2][arcs] sizes += w3 if isinstance(w3, float) else w3[arcs] # if radius of loop would be size, then distance betwween # vertex and loop centers would be # d = np.sqrt(size ** 2 - r ** 2 / 2) + r / np.sqrt(2) + r / 2 ds = sizes * (1 + np.sqrt(2) / 4) rxs = xs - ds * fx rys = ys - ds * fy rfxs = sizes * fx rfys = sizes * fy ax0o = 6 * np.cos(np.pi * 5 / 6) * fx ax1o = 6 * np.cos(np.pi * 7 / 12) * fx ay0o = 6 * np.sin(np.pi * 5 / 6) * fy ay1o = 6 * np.sin(np.pi * 7 / 12) * fy if self.widths is None: widths = np.full(len(rxs), pen.width()) else: widths = self.widths[arcs] for rx, ry, rfx, rfy, w in zip(rxs, rys, rfxs, rfys, widths): rect = QRectF(rx, ry, rfx, rfy) pen.setWidth(w) p.setPen(pen) p.drawArc(rect, 100 * 16, 250 * 16) if self.directed: rx += 1.1 * rfx ry += rfy / 2 p.drawLine(QLineF(rx, ry, rx + ax0o, ry - ay0o)) p.drawLine(QLineF(rx, ry, rx + ax1o, ry - ay1o))
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 get_mapped_rect(): p1, p2 = ev.buttonDownPos(ev.button()), ev.pos() p1 = self.mapToView(p1) p2 = self.mapToView(p2) return QRectF(p1, p2)
def extend_horizontal(rect): # type: (QRectF) -> QRectF rect = QRectF(rect) rect.setLeft(geom.left()) rect.setRight(geom.right()) return rect
def boundingRect(self): return QRectF(0, 0, 25 + self.text_width + self.bin_height, 20 + self.scale.bins * self.bin_height)
def boundingRect(self): # type: () -> QRectF return QRectF()
def set_rect(self, rect): self.prepareGeometryChange() rect = QRectF() if rect is None else rect self._rect = rect self.update_contents() self.update()