Esempio n. 1
0
 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
Esempio n. 2
0
 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 __init__(self, value: float, label: Tuple[str, float],
                 norm_value: float):
        super().__init__()
        color = QColor(*self.light_rgb)
        self.value = value
        self.norm_value = norm_value
        self.setPath(self.get_path())
        pen = QPen(color)
        pen.setWidth(2)
        self.setPen(pen)

        value = np.abs(value)
        self.value_item = item = QGraphicsSimpleTextItem(_str(value))
        item.setToolTip(_str(value, 3))
        font = item.font()
        font.setPixelSize(11)
        item.setFont(font)
        width = item.boundingRect().width()
        item.setX(StripeItem.WIDTH / 2 - width / 2)
        item.setPen(color)
        item.setBrush(color)

        self.label_item = QGraphicsSimpleTextItem(
            f"{label[0]} = {_str(label[1])}")
        self.label_item.setToolTip(f"{label[0]} = {_str(label[1], 3)}")
        self.label_item.setX(StripeItem.WIDTH + StripePlot.SPACING)
def make_pen(brush=Qt.black, width=1, style=Qt.SolidLine,
             cap_style=Qt.SquareCap, join_style=Qt.BevelJoin,
             cosmetic=False):
    pen = QPen(brush)
    pen.setWidth(width)
    pen.setStyle(style)
    pen.setCapStyle(cap_style)
    pen.setJoinStyle(join_style)
    pen.setCosmetic(cosmetic)
    return pen
Esempio n. 5
0
        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
Esempio n. 6
0
        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
Esempio n. 7
0
 def _change_pen(self):
     pen = QPen(self._pen)
     if self._in_subset and self._selected:
         color = QColor(self._pen.color())
         color.setAlpha(255)
         pen.setWidth(4)
         pen.setColor(color)
     elif not self._in_subset and self._selected:
         color = QColor(self._pen.color())
         color.setAlpha(255)
         pen.setColor(color)
     elif self._in_subset and not self._selected:
         pen.setWidth(4)
     self.setPen(pen)
     self.setSymbolPen(pen)
Esempio n. 8
0
 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 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)
Esempio n. 10
0
 def show_average(self):
     self.view_average_menu.setChecked(True)
     self.set_pen_colors()
     self.clear_graph()
     self.viewtype = AVERAGE
     if not self.data:
         return
     x = self.data_x
     if self.data:
         ysall = []
         cinfo = []
         selected_indices = np.full(self.data_size, False, dtype=bool)
         selected_indices[list(self.selected_indices)] = True
         dsplit = self._split_by_color_value(self.data)
         for colorv, indices in dsplit.items():
             for part in [None, "subset", "selection"]:
                 if part is None:
                     part_selection = indices
                     pen = self.pen_normal if np.any(
                         self.subset_indices) else self.pen_subset
                 elif part == "selection" and self.selection_type:
                     part_selection = indices & selected_indices
                     pen = self.pen_selected
                 elif part == "subset":
                     part_selection = indices & self.subset_indices
                     pen = self.pen_subset
                 if np.any(part_selection):
                     ys = self.data.X[part_selection]
                     std = np.nanstd(ys, axis=0)
                     mean = np.nanmean(ys, axis=0)
                     std = std[self.data_xsind]
                     mean = mean[self.data_xsind]
                     ysall.append(mean)
                     penc = QPen(pen[colorv])
                     penc.setWidth(3)
                     self.add_curve(x, mean, pen=penc)
                     self.add_fill_curve(x,
                                         mean + std,
                                         mean - std,
                                         pen=penc)
                     cinfo.append((colorv, part, part_selection))
         self.curves.append((x, np.array(ysall)))
         self.multiple_curves_info = cinfo
     self.curves_cont.update()
     self.plot.vb.set_mode_panning()
Esempio n. 11
0
 def paint(self, p, opt, widget):
     if self.xData is None or len(self.xData) == 0:
         return
     p.setRenderHint(p.Antialiasing, True)
     p.setCompositionMode(p.CompositionMode_SourceOver)
     if self.widths is None:
         p.setPen(self.pen)
         for x0, y0, x1, y1 in zip(self.xData[::2], self.yData[::2],
                                   self.xData[1::2], self.yData[1::2]):
             p.drawLine(QLineF(x0, y0, x1, y1))
     else:
         pen = QPen(self.pen)
         for x0, y0, x1, y1, w in zip(self.xData[::2], self.yData[::2],
                                      self.xData[1::2], self.yData[1::2],
                                      self.widths):
             pen.setWidth(w)
             p.setPen(pen)
             p.drawLine(QLineF(x0, y0, x1, y1))
Esempio n. 12
0
    def _change_pen(self):
        pen = QPen(self._pen)
        color = QColor(Qt.black) if self._selected and self.master.has_subset \
            else QColor(self._pen.color())

        if self._in_subset or self._selected:
            width = LinePlotStyle.SELECTED_LINE_WIDTH
            alpha = LinePlotStyle.SELECTED_LINE_ALPHA
        else:
            width = LinePlotStyle.UNSELECTED_LINE_WIDTH
            alpha = LinePlotStyle.UNSELECTED_LINE_ALPHA_WITH_SELECTION \
                if self.master.has_subset or self.master.has_selection else \
                LinePlotStyle.UNSELECTED_LINE_ALPHA

        pen.setWidth(width)
        color.setAlpha(alpha)
        pen.setColor(color)
        self.setPen(pen)
    def __init__(self, parent):
        super().__init__(parent)
        self.__range = None  # type: Tuple[float]
        self.__value_range = None  # type: Tuple[float]
        self.__model_output = None  # type: float
        self.__base_value = None  # type: float

        self.__group = QGraphicsItemGroup(self)
        low_color, high_color = QColor(*RGB_LOW), QColor(*RGB_HIGH)

        self.__low_item = QGraphicsRectItem()
        self.__low_item.setPen(QPen(low_color))
        self.__low_item.setBrush(QBrush(low_color))

        self.__high_item = QGraphicsRectItem()
        self.__high_item.setPen(QPen(high_color))
        self.__high_item.setBrush(QBrush(high_color))

        self.__low_cover_item = LowCoverItem()
        self.__high_cover_item = HighCoverItem()

        pen = QPen(IndicatorItem.COLOR)
        pen.setStyle(Qt.DashLine)
        pen.setWidth(1)
        self.__model_output_line = QGraphicsLineItem()
        self.__model_output_line.setPen(pen)
        self.__base_value_line = QGraphicsLineItem()
        self.__base_value_line.setPen(pen)

        self.__model_output_ind = IndicatorItem("Model prediction: {}")
        self.__base_value_ind = IndicatorItem("Base value: {}\nThe average "
                                              "prediction for selected class.")

        self.__group.addToGroup(self.__low_item)
        self.__group.addToGroup(self.__high_item)
        self.__group.addToGroup(self.__low_cover_item)
        self.__group.addToGroup(self.__high_cover_item)
        self.__group.addToGroup(self.__model_output_line)
        self.__group.addToGroup(self.__base_value_line)
        self.__group.addToGroup(self.__model_output_ind)
        self.__group.addToGroup(self.__base_value_ind)

        self.__low_parts = []  # type: List[LowPartItem]
        self.__high_parts = []  # type: List[HighPartItem]
Esempio n. 14
0
 def _change_pen(self):
     pen = QPen(self._pen)
     if self._in_subset and self._selected:
         color = QColor(self._pen.color())
         color.setAlpha(255)
         pen.setWidth(4)
         pen.setColor(color)
     elif not self._in_subset and self._selected:
         color = QColor(self._pen.color())
         color.setAlpha(255)
         pen.setColor(color)
     elif self._in_subset and not self._selected:
         color = QColor(self._pen.color())
         color.setAlpha(LinePlotColors.LIGHT_ALPHA)
         pen.setWidth(4)
         pen.setColor(color)
     else:
         color = QColor(self._pen.color())
         color.setAlpha(LinePlotColors.LIGHT_ALPHA)
         pen.setColor(color)
     self.setPen(pen)
Esempio n. 15
0
def update_pen(pen,
               brush=None,
               width=None,
               style=None,
               cap_style=None,
               join_style=None,
               cosmetic=None):
    pen = QPen(pen)
    if brush is not None:
        pen.setBrush(QBrush(brush))
    if width is not None:
        pen.setWidth(width)
    if style is not None:
        pen.setStyle(style)
    if cap_style is not None:
        pen.setCapStyle(cap_style)
    if join_style is not None:
        pen.setJoinStyle(join_style)
    if cosmetic is not None:
        pen.setCosmetic(cosmetic)
    return pen
Esempio n. 16
0
 def show_average(self):
     if not self.data:
         return
     self.viewtype = AVERAGE
     self.clear_graph()
     x = self.data_x
     if self.data:
         ysall = []
         subset_indices = [i for i, id in enumerate(self.data.ids) if id in self.subset_ids]
         dsplit = self._split_by_color_value(self.data)
         for colorv, indices in dsplit.items():
             for part in ["everything", "subset", "selection"]:
                 if part == "everything":
                     ys = self.data_ys[indices]
                     pen = self.pen_normal if subset_indices else self.pen_subset
                 elif part == "selection" and self.selection_type:
                     current_selected = sorted(set(self.selected_indices) & set(indices))
                     if not current_selected:
                         continue
                     ys = self.data_ys[current_selected]
                     pen = self.pen_selected
                 elif part == "subset":
                     current_subset = sorted(set(subset_indices) & set(indices))
                     if not current_subset:
                         continue
                     ys = self.data_ys[current_subset]
                     pen = self.pen_subset
                 std = np.std(ys, axis=0)
                 mean = np.mean(ys, axis=0)
                 ysall.append(mean)
                 penc = QPen(pen[colorv])
                 penc.setWidth(3)
                 self.add_curve(x, mean, pen=penc)
                 self.add_fill_curve(x, mean + std, mean - std, pen=penc)
         self.curves.append((x, np.array(ysall)))
     self.curves_cont.update()
Esempio n. 17
0
 def hoverLeaveEvent(self, event):
     pen = QPen(self.pen())
     pen.setWidth(2)
     self.setPen(pen)
     return RugItem.hoverLeaveEvent(self, event)
Esempio n. 18
0
    def replot_experiments(self):
        """Replot the whole quality plot.
        """
        self.scene.clear()
        labels = []

        max_dist = numpy.nanmax(list(filter(None, self.distances)))
        rug_widgets = []

        group_pen = QPen(Qt.black)
        group_pen.setWidth(2)
        group_pen.setCapStyle(Qt.RoundCap)
        background_pen = QPen(QColor(0, 0, 250, 150))
        background_pen.setWidth(1)
        background_pen.setCapStyle(Qt.RoundCap)

        main_widget = QGraphicsWidget()
        layout = QGraphicsGridLayout()
        attributes = self.data.domain.attributes
        if self.data is not None:
            for (group, indices), dist_vec in zip(self.groups, self.distances):
                indices_set = set(indices)
                rug_items = []
                if dist_vec is not None:
                    for i, attr in enumerate(attributes):
                        # Is this a within group distance or background
                        in_group = i in indices_set
                        if in_group:
                            rug_item = ClickableRugItem(dist_vec[i] / max_dist,
                                           1.0, self.on_rug_item_clicked)
                            rug_item.setPen(group_pen)
                            tooltip = experiment_description(attr)
                            rug_item.setToolTip(tooltip)
                            rug_item.group_index = indices.index(i)
                            rug_item.setZValue(rug_item.zValue() + 1)
                        else:
                            rug_item = ClickableRugItem(dist_vec[i] / max_dist,
                                           0.85, self.on_rug_item_clicked)
                            rug_item.setPen(background_pen)
                            tooltip = experiment_description(attr)
                            rug_item.setToolTip(tooltip)

                        rug_item.group = group
                        rug_item.index = i
                        rug_item.in_group = in_group

                        rug_items.append(rug_item)

                rug_widget = RugGraphicsWidget(parent=main_widget)
                rug_widget.set_rug(rug_items)

                rug_widgets.append(rug_widget)

                label = group_label(self.selected_split_by_labels(), group)
                label_item = QGraphicsSimpleTextItem(label, main_widget)
                label_item = GraphicsSimpleTextLayoutItem(label_item, parent=layout)
                label_item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
                labels.append(label_item)

        for i, (label, rug_w) in enumerate(zip(labels, rug_widgets)):
            layout.addItem(label, i, 0, Qt.AlignVCenter)
            layout.addItem(rug_w, i, 1)
            layout.setRowMaximumHeight(i, 30)

        main_widget.setLayout(layout)
        self.scene.addItem(main_widget)
        self.main_widget = main_widget
        self.rug_widgets = rug_widgets
        self.labels = labels
        self.on_view_resize(self.scene_view.size())
Esempio n. 19
0
    def _setup_plot(self):
        """Setup the plot with new curve data."""
        assert self.data is not None

        data, domain = self.data, self.data.domain
        if is_discrete(domain.class_var):
            class_col_data, _ = data.get_column_view(domain.class_var)

            group_indices = [
                np.flatnonzero(class_col_data == i)
                for i in range(len(domain.class_var.values))
            ]
        else:
            group_indices = [np.arange(len(data))]

        X = np.arange(1, len(domain.attributes) + 1)
        groups = []

        for i, indices in enumerate(group_indices):
            if self.classes:
                color = self.class_colors[i]
            else:
                color = QColor(Qt.darkGray)
            group_data = data[indices, :]
            plot_x, plot_y, connect = disconnected_curve_data(group_data.X,
                                                              x=X)

            color.setAlpha(200)
            lightcolor = QColor(color.lighter(factor=150))
            lightcolor.setAlpha(150)
            pen = QPen(color, 2)
            pen.setCosmetic(True)

            lightpen = QPen(lightcolor, 1)
            lightpen.setCosmetic(True)
            hoverpen = QPen(pen)
            hoverpen.setWidth(2)

            curve = pg.PlotCurveItem(
                x=plot_x,
                y=plot_y,
                connect=connect,
                pen=lightpen,
                symbolSize=2,
                antialias=True,
            )
            self.graph.addItem(curve)

            hovercurves = []
            for index, profile in zip(indices, group_data.X):
                hcurve = HoverCurve(x=X,
                                    y=profile,
                                    pen=hoverpen,
                                    antialias=True)
                hcurve.setToolTip('{}'.format(index))
                hcurve._data_index = index
                hovercurves.append(hcurve)
                self.graph.addItem(hcurve)

            mean = np.nanmean(group_data.X, axis=0)

            meancurve = pg.PlotDataItem(x=X,
                                        y=mean,
                                        pen=pen,
                                        size=5,
                                        symbol="o",
                                        pxMode=True,
                                        symbolSize=5,
                                        antialias=True)
            hoverpen = QPen(hoverpen)
            hoverpen.setWidth(5)

            hc = HoverCurve(x=X, y=mean, pen=hoverpen, antialias=True)
            hc.setFlag(QGraphicsItem.ItemIsSelectable, False)
            self.graph.addItem(hc)

            self.graph.addItem(meancurve)
            self.legend_items.append(meancurve)
            q1, q2, q3 = np.nanpercentile(group_data.X, [25, 50, 75], axis=0)
            # TODO: implement and use a box plot item
            errorbar = pg.ErrorBarItem(x=X,
                                       y=mean,
                                       bottom=np.clip(mean - q1, 0, mean - q1),
                                       top=np.clip(q3 - mean, 0, q3 - mean),
                                       beam=0.5)
            self.graph.addItem(errorbar)
            groups.append(
                namespace(data=group_data,
                          indices=indices,
                          profiles=curve,
                          hovercurves=hovercurves,
                          mean=meancurve,
                          boxplot=errorbar))

        self.__groups = groups
        self.__update_visibility()
        self.__update_tooltips()
Esempio n. 20
0
    def _setup_plot(self):
        """Setup the plot with new curve data."""
        assert self.data is not None

        data, domain = self.data, self.data.domain
        if is_discrete(domain.class_var):
            class_col_data, _ = data.get_column_view(domain.class_var)

            group_indices = [np.flatnonzero(class_col_data == i)
                             for i in range(len(domain.class_var.values))]
        else:
            group_indices = [np.arange(len(data))]

        X = np.arange(1, len(domain.attributes)+1)
        groups = []

        for i, indices in enumerate(group_indices):
            if self.classes:
                color = self.class_colors[i]
            else:
                color = QColor(Qt.darkGray)
            group_data = data[indices, :]
            plot_x, plot_y, connect = disconnected_curve_data(group_data.X, x=X)

            color.setAlpha(200)
            lightcolor = QColor(color.lighter(factor=150))
            lightcolor.setAlpha(150)
            pen = QPen(color, 2)
            pen.setCosmetic(True)

            lightpen = QPen(lightcolor, 1)
            lightpen.setCosmetic(True)
            hoverpen = QPen(pen)
            hoverpen.setWidth(2)

            curve = pg.PlotCurveItem(
                x=plot_x, y=plot_y, connect=connect,
                pen=lightpen, symbolSize=2, antialias=True,
            )
            self.graph.addItem(curve)

            hovercurves = []
            for index, profile in zip(indices, group_data.X):
                hcurve = HoverCurve(x=X, y=profile, pen=hoverpen,
                                    antialias=True)
                hcurve.setToolTip('{}'.format(index))
                hcurve._data_index = index
                hovercurves.append(hcurve)
                self.graph.addItem(hcurve)

            mean = np.nanmean(group_data.X, axis=0)

            meancurve = pg.PlotDataItem(
                x=X, y=mean, pen=pen, size=5, symbol="o", pxMode=True,
                symbolSize=5, antialias=True
            )
            hoverpen = QPen(hoverpen)
            hoverpen.setWidth(5)

            hc = HoverCurve(x=X, y=mean, pen=hoverpen, antialias=True)
            hc.setFlag(QGraphicsItem.ItemIsSelectable, False)
            self.graph.addItem(hc)

            self.graph.addItem(meancurve)
            self.legend_items.append(meancurve)
            q1, q2, q3 = np.nanpercentile(group_data.X, [25, 50, 75], axis=0)
            # TODO: implement and use a box plot item
            errorbar = pg.ErrorBarItem(
                x=X, y=mean,
                bottom=np.clip(mean - q1, 0, mean - q1),
                top=np.clip(q3 - mean, 0, q3 - mean),
                beam=0.5
            )
            self.graph.addItem(errorbar)
            groups.append(
                namespace(
                    data=group_data, indices=indices, profiles=curve,
                    hovercurves=hovercurves, mean=meancurve, boxplot=errorbar)
            )

        self.__groups = groups
        self.__update_visibility()
        self.__update_tooltips()
Esempio n. 21
0
    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))
Esempio n. 22
0
    def paint(self, p, opt, widget):
        def get_arrows():
            cos12 = 10 * np.cos(np.pi / 12)
            sin12 = 10 * np.sin(np.pi / 12)

            # cos(a ± 12) = cos(a) cos(12) ∓ sin(a) sin(12)
            tx = sins * (fx * sin12)
            xa1s = x1s - coss * (fx * cos12)
            xa2s = xa1s - tx
            xa1s += tx

            # sin(a ± 12) = sin(a) cos(12) ± sin(12) cos(a)
            ty = (fy * sin12) * coss
            ya1s = y1s + sins * (fy * cos12)
            ya2s = ya1s - ty
            ya1s += ty
            return xa1s, ya1s, xa2s, ya2s

        def get_angles():
            angles = np.arctan2(-(y1s - y0s) / fy, (x1s - x0s) / fx)
            return np.cos(angles), np.sin(angles)
            """
            # This below faster. Uncomment and check that it works
            diffx, diffy = (x1s - x0s) / fx, (y1s - y0s) / fy
            norm = np.sqrt(diffx ** 2 + diffy ** 2)
            self.coss = np.nan_to_num(diffx / norm)
            self.sins = np.nan_to_num(diffy / norm)
            """

        def shorter_edges():
            nonlocal x0s, x1s, y0s, y1s
            sizes0, sizes1 = self.sizes[::2], self.sizes[1::2]
            return (x0s + fx * sizes0 * coss, y0s - fy * sizes0 * sins,
                    x1s - fx * sizes1 * coss, y1s + fy * sizes1 * sins)

        if self.xData is None or len(self.xData) == 0:
            return
        x0s, x1s = self.xData[::2], self.xData[1::2]
        y0s, y1s = self.yData[::2], self.yData[1::2]
        fx = 1 / p.worldTransform().m11()
        fy = 1 / p.worldTransform().m22()
        coss, sins = get_angles()
        endpoints = x0s, y0s, x1s, y1s = shorter_edges()

        p.setRenderHint(p.Antialiasing, True)
        p.setCompositionMode(p.CompositionMode_SourceOver)
        if self.widths is None:
            p.setPen(self.pen)
            if self.directed:
                for x0, y0, x1, y1, xa1, ya1, xa2, ya2 in zip(
                        *endpoints, *get_arrows()):
                    p.drawLine(QLineF(x0, y0, x1, y1))
                    p.drawLine(QLineF(xa1, ya1, x1, y1))
                    p.drawLine(QLineF(xa2, ya2, x1, y1))
            else:
                for x0, y0, x1, y1 in zip(*endpoints):
                    p.drawLine(QLineF(x0, y0, x1, y1))
        else:
            pen = QPen(self.pen)
            if self.directed:
                for x0, y0, x1, y1, xa1, ya1, xa2, ya2, w in zip(
                        *endpoints, *get_arrows(), self.widths):
                    pen.setWidth(w)
                    p.setPen(pen)
                    p.drawLine(QLineF(x0, y0, x1, y1))
                    p.drawLine(QLineF(xa1, ya1, x1, y1))
                    p.drawLine(QLineF(xa2, ya2, x1, y1))
            else:
                for x0, y0, x1, y1, w in zip(*endpoints, self.widths):
                    pen.setWidth(w)
                    p.setPen(pen)
                    p.drawLine(QLineF(x0, y0, x1, y1))