def drawValue(self, p: QPainter, baseRect: QRectF, value: float, delta: float):
     if value == self.m_min:
         return
     if self.m_barStyle == self.BarStyle.EXPAND:
         p.setBrush(self.palette().highlight())
         p.setPen(QPen(self.palette().shadow().color(), self.m_dataPenWidth))
         radius = (baseRect.height() / 2) / delta
         p.drawEllipse(baseRect.center(), radius, radius)
         return
     if self.m_barStyle == self.BarStyle.LINE:
         p.setPen(QPen(self.palette().highlight().color(), self.m_dataPenWidth))
         p.setBrush(Qt.NoBrush)
         if value == self.m_max:
             p.drawEllipse(baseRect.adjusted(self.m_outlinePenWidth / 2, self.m_outlinePenWidth / 2,
                                             -self.m_outlinePenWidth / 2, -self.m_outlinePenWidth / 2))
         else:
             arcLength = 360 / delta
             p.drawArc(baseRect.adjusted(self.m_outlinePenWidth / 2, self.m_outlinePenWidth / 2,
                                         -self.m_outlinePenWidth / 2, -self.m_outlinePenWidth / 2),
                       int(self.m_nullPosition * 16),
                       int(-arcLength * 16))
         return
     dataPath = QPainterPath()
     dataPath.setFillRule(Qt.WindingFill)
     if value == self.m_max:
         dataPath.addEllipse(baseRect)
     else:
         arcLength = 360 / delta
         dataPath.moveTo(baseRect.center())
         dataPath.arcTo(baseRect, self.m_nullPosition, -arcLength)
         dataPath.lineTo(baseRect.center())
     p.setBrush(self.palette().highlight())
     p.setPen(QPen(self.palette().shadow().color(), self.m_dataPenWidth))
     p.drawPath(dataPath)
Пример #2
0
    def paintEvent(self, a0: QPaintEvent) -> None:
        super().paintEvent(a0)
        painter = QPainter(self)
        painter.save()
        painter.setRenderHint(QPainter.Antialiasing)
        rect = QRectF(self.margin, self.margin,
                      self.width() - self.margin * 2,
                      self.height() - 2 * self.margin)
        painter.setBrush(Qt.white)
        painter.setPen(Qt.white)
        painter.drawEllipse(rect)

        painter.restore()
        painter.save()
        painter.setRenderHint(QPainter.Antialiasing)
        pen = QPen()
        pen.setWidth(2)
        painter.setPen(pen)
        mid_point = QPointF(a0.rect().width() / 2, a0.rect().height() / 2)
        radius = min(a0.rect().height(), a0.rect().width()) / 3
        rays_num = 10
        for i in range(rays_num):
            point = QPointF(
                math.sin(math.pi / (rays_num / 2) * i) * radius,
                math.cos(math.pi / (rays_num / 2) * i) * radius)
            painter.drawLine(mid_point + (point * 0.4), mid_point + point)
        painter.restore()
Пример #3
0
    def draw_filled_connection_points(painter: QPainter, geom: NodeGeometry,
                                      state: NodeState, model: NodeDataModel,
                                      node_style: NodeStyle,
                                      connection_style: ConnectionStyle):
        """
        Draw filled connection points

        Parameters
        ----------
        painter : QPainter
        geom : NodeGeometry
        state : NodeState
        model : NodeDataModel
        node_style : NodeStyle
        connection_style : ConnectionStyle
        """
        diameter = node_style.connection_point_diameter
        for port in state.ports:
            if not port.connections:
                continue

            scene_pos = port.scene_position
            if connection_style.use_data_defined_colors:
                c = connection_style.get_normal_color(port.data_type.id)
            else:
                c = node_style.filled_connection_point_color
            painter.setPen(c)
            painter.setBrush(c)
            painter.drawEllipse(scene_pos, diameter * 0.4, diameter * 0.4)
Пример #4
0
    def paint(painter: QPainter, connection: ConnectionBase,
              style: ConnectionStyle):
        """
        Paint

        Parameters
        ----------
        painter : QPainter
        connection : Connection
        style : ConnectionStyle
        """
        draw_hovered_or_selected(painter, connection, style)
        draw_sketch_line(painter, connection, style)
        draw_normal_line(painter, connection, style)
        if use_debug_drawing:
            debug_drawing(painter, connection)

        # draw end points
        geom = connection.geometry
        source, sink = geom.source, geom.sink

        point_diameter = style.point_diameter
        painter.setPen(style.construction_color)
        painter.setBrush(style.construction_color)
        point_radius = point_diameter / 2.0
        painter.drawEllipse(source, point_radius, point_radius)
        painter.drawEllipse(sink, point_radius, point_radius)
Пример #5
0
 def paintEvent(self, event: QPaintEvent):
     painter = QPainter(self)
     painter.save()
     painter.setRenderHint(QPainter.Antialiasing)
     size = min(self.width(), self.height())
     rect = QRect(0, 0, size, size)
     painter.setBrush(self.background_color)
     painter.setPen(self.background_color)
     painter.drawEllipse(rect)
     painter.setBrush(self.main_color)
     painter.setPen(self.main_color)
     factor = self.nominator / self.denominator
     radius = size / 2
     if factor > 0.5:
         painter.drawChord(rect, 0, 16 * 360 * 0.5)
         painter.drawChord(rect, 16 * 180, 16 * 360 * (factor - 0.5))
         zero_point = QPointF(0, radius)
     else:
         painter.drawChord(rect, 0, 16 * 360 * factor)
         zero_point = QPointF(size, radius)
     mid_point = QPointF(radius, radius)
     point = mid_point + QPointF(
         math.cos(math.pi * (factor * 2)) * radius, -math.sin(math.pi * (factor * 2)) * radius
     )
     polygon = QPolygonF()
     polygon += mid_point
     polygon += zero_point
     polygon += point
     painter.drawPolygon(polygon)
     painter.restore()
Пример #6
0
class PyDMBitIndicator(QWidget):
    """
    A QWidget which draws a colored circle or rectangle

    Parameters
    ----------
    parent : QWidget
        The parent widget for the Label

    """
    def __init__(self, parent=None, circle=False):
        super(PyDMBitIndicator, self).__init__(parent)
        self.setAutoFillBackground(True)
        self.circle = circle
        self._painter = QPainter()
        self._brush = QBrush(Qt.SolidPattern)
        self._pen = QPen(Qt.SolidLine)

    def paintEvent(self, event):
        """
        Paint events are sent to widgets that need to update themselves,
        for instance when part of a widget is exposed because a covering
        widget was moved.

        Parameters
        ----------
        event : QPaintEvent
        """
        self._painter.begin(self)
        opt = QStyleOption()
        opt.initFrom(self)
        self.style().drawPrimitive(QStyle.PE_Widget, opt, self._painter, self)
        self._painter.setRenderHint(QPainter.Antialiasing)
        self._painter.setBrush(self._brush)
        self._painter.setPen(self._pen)
        if self.circle:
            rect = event.rect()
            w = rect.width()
            h = rect.height()
            r = min(w, h) / 2.0 - 2.0 * max(self._pen.widthF(), 1.0)
            self._painter.drawEllipse(QPoint(w / 2.0, h / 2.0), r, r)
        else:
            self._painter.drawRect(event.rect())
        self._painter.end()

    def setColor(self, color):
        """
        Property for the color to be used when drawing

        Parameters
        ----------
        QColor
        """
        self._brush.setColor(color)
        self.update()

    def minimumSizeHint(self):
        fm = QFontMetrics(self.font())
        return QSize(fm.height(), fm.height())
Пример #7
0
class PyDMBitIndicator(QWidget):
    """
    A QWidget which draws a colored circle or rectangle

    Parameters
    ----------
    parent : QWidget
        The parent widget for the Label

    """
    def __init__(self, parent=None, circle=False):
        super(PyDMBitIndicator, self).__init__(parent)
        self.setAutoFillBackground(True)
        self.circle = circle
        self._painter = QPainter()
        self._brush = QBrush(Qt.SolidPattern)
        self._pen = QPen(Qt.SolidLine)

    def paintEvent(self, event):
        """
        Paint events are sent to widgets that need to update themselves,
        for instance when part of a widget is exposed because a covering
        widget was moved.

        Parameters
        ----------
        event : QPaintEvent
        """
        self._painter.begin(self)
        opt = QStyleOption()
        opt.initFrom(self)
        self.style().drawPrimitive(QStyle.PE_Widget, opt, self._painter, self)
        self._painter.setRenderHint(QPainter.Antialiasing)
        self._painter.setBrush(self._brush)
        self._painter.setPen(self._pen)
        if self.circle:
            rect = self.rect()
            w = rect.width()
            h = rect.height()
            r = min(w, h) / 2.0 - 2.0 * max(self._pen.widthF(), 1.0)
            self._painter.drawEllipse(QPoint(w / 2.0, h / 2.0), r, r)
        else:
            self._painter.drawRect(self.rect())
        self._painter.end()

    def setColor(self, color):
        """
        Property for the color to be used when drawing

        Parameters
        ----------
        QColor
        """
        self._brush.setColor(color)
        self.update()

    def minimumSizeHint(self):
        fm = QFontMetrics(self.font())
        return QSize(fm.height(), fm.height())
Пример #8
0
    def draw_connection_points(painter: QPainter, geom: NodeGeometry,
                               state: NodeState, model: NodeDataModel,
                               scene: FlowSceneBase, node_style: NodeStyle,
                               connection_style: ConnectionStyle
                               ):
        """
        Draw connection points

        Parameters
        ----------
        painter : QPainter
        geom : NodeGeometry
        state : NodeState
        model : NodeDataModel
        scene : FlowScene
        connection_style : ConnectionStyle
        """
        diameter = node_style.connection_point_diameter
        reduced_diameter = diameter * 0.6
        for port in state.ports:
            scene_pos = port.scene_position
            can_connect = port.can_connect
            port_type = port.port_type
            data_type = port.data_type

            r = 1.0
            if state.is_reacting and can_connect and port_type == state.reacting_port_type:
                diff = geom.dragging_pos - scene_pos
                dist = math.sqrt(QPointF.dotProduct(diff, diff))

                registry = scene.registry
                dtype1, dtype2 = state.reacting_data_type, data_type
                if port_type != PortType.input:
                    dtype2, dtype1 = dtype1, dtype2

                type_convertable = registry.get_type_converter(dtype1, dtype2) is not None
                if dtype1.id == dtype2.id or type_convertable:
                    thres = 40.0
                    r = ((2.0 - dist / thres)
                         if dist < thres
                         else 1.0)
                else:
                    thres = 80.0
                    r = ((dist / thres)
                         if dist < thres
                         else 1.0)

            if connection_style.use_data_defined_colors:
                brush = connection_style.get_normal_color(data_type.id)
            else:
                brush = node_style.connection_point_color

            painter.setBrush(brush)
            painter.drawEllipse(scene_pos, reduced_diameter * r, reduced_diameter * r)
Пример #9
0
def circle_pixmap(size):
    """Create a white/black hollow circle pixmap. For use as labels cursor."""
    pixmap = QPixmap(QSize(size, size))
    pixmap.fill(Qt.transparent)
    painter = QPainter(pixmap)
    painter.setPen(Qt.white)
    painter.drawEllipse(0, 0, size - 1, size - 1)
    painter.setPen(Qt.black)
    painter.drawEllipse(1, 1, size - 3, size - 3)
    painter.end()
    return pixmap
Пример #10
0
    def paintEvent(self, event):
        painter = QPainter()
        x = 0
        y = 0
        if self._alignment & Qt.AlignLeft:
            x = 0
        elif self._alignment & Qt.AlignRight:
            x = self.width() - self._diameter
        elif self._alignment & Qt.AlignHCenter:
            x = (self.width() - self._diameter) / 2
        elif self._alignment & Qt.AlignJustify:
            x = 0

        if self._alignment & Qt.AlignTop:
            y = 0
        elif self._alignment & Qt.AlignBottom:
            y = self.height() - self._diameter
        elif self._alignment & Qt.AlignVCenter:
            y = (self.height() - self._diameter) / 2

        gradient = QRadialGradient(x + self._diameter / 2,
                                   y + self._diameter / 2,
                                   self._diameter * 0.3, self._diameter * 0.1,
                                   self._diameter * 0.1)
        gradient.setColorAt(0, Qt.white)

        # ensure the border/halo is same color as gradient
        draw_color = QColor(self._color)

        if not self._state:
            # cut to black @ 70% for darker effect
            draw_color = QColor(Qt.black)

        if not self.isEnabled():
            draw_color.setAlpha(30)

        pen_color = draw_color
        gradient.setColorAt(0.7, draw_color)

        painter.begin(self)
        brush = QBrush(gradient)
        painter.setPen(pen_color)
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.setBrush(brush)
        painter.drawEllipse(x + 1, y + 1, self._diameter - 2,
                            self._diameter - 2)

        if self._flashRate > 0 and self._flashing:
            self._timer.start(self._flashRate)
        else:
            self._timer.stop()

        painter.end()
Пример #11
0
    def draw_resize_rect(painter: QPainter, geom: NodeGeometry, model: NodeDataModel):
        """
        Draw resize rect

        Parameters
        ----------
        painter : QPainter
        geom : NodeGeometry
        model : NodeDataModel
        """
        if model.resizable():
            painter.setBrush(Qt.gray)
            painter.drawEllipse(geom.resize_rect)
Пример #12
0
    def paintEvent(self, event):
        """Paint the background, range bar and splitters.

        Parameters
        ----------
        event : qtpy.QEvent
            Event from the Qt context.
        """
        painter, w, h = QPainter(self), self.width(), self.height()

        half_width = self.slider_width / 2
        halfdiff = w / 2 - half_width
        # Background
        painter.setPen(self.background_color)
        painter.setBrush(self.background_color)
        painter.drawRect(halfdiff, 0, self.slider_width, h)

        # Range Bar
        painter.setPen(self.bar_color)
        painter.setBrush(self.bar_color)
        if self.collapsed:
            painter.drawRect(
                halfdiff,
                h - self.display_max,
                self.slider_width,
                self.display_max,
            )
        else:
            painter.drawRect(
                halfdiff,
                h - self.display_max,
                self.slider_width,
                self.display_max - self.display_min,
            )

        painter.setRenderHints(QPainter.Antialiasing)
        # Splitters
        painter.setPen(self.handle_border_color)
        painter.setBrush(self.handle_color)
        painter.drawEllipse(
            w / 2 - self.handle_radius,
            h - self.display_min - self.handle_radius,
            self.handle_width,
            self.handle_width,
        )  # upper
        painter.drawEllipse(
            w / 2 - self.handle_radius,
            h - self.display_max - self.handle_radius,
            self.handle_width,
            self.handle_width,
        )  # lower
Пример #13
0
    def paintEvent(self, event):
        painter = QPainter()
        x = 0
        y = 0
        if self._alignment & Qt.AlignLeft:
            x = 0
        elif self._alignment & Qt.AlignRight:
            x = self.width() - self._diameter
        elif self._alignment & Qt.AlignHCenter:
            x = (self.width() - self._diameter) / 2
        elif self._alignment & Qt.AlignJustify:
            x = 0

        if self._alignment & Qt.AlignTop:
            y = 0
        elif self._alignment & Qt.AlignBottom:
            y = self.height() - self._diameter
        elif self._alignment & Qt.AlignVCenter:
            y = (self.height() - self._diameter) / 2

        # get the fill draw color set from QTDesigner
        draw_color = QColor(self._color)
        # set the pen color, we want to use this even if the state is OFF
        pen_color = draw_color

        if not self._state:
            # LED state is OFF, set fill
            draw_color = QColor(Qt.black)

        # dim if control is not enabled
        if not self.isEnabled():
            draw_color.setAlpha(30)

        # Start actual painting process
        painter.begin(self)
        brush = QBrush(draw_color)
        painter.setPen(pen_color)
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.setBrush(brush)
        painter.drawEllipse(x + 1, y + 1, self._diameter - 2,
                            self._diameter - 2)

        # Does flashing make sense for Monokrom style?
        if self._flashRate > 0 and self._flashing:
            self._timer.start(self._flashRate)
        else:
            self._timer.stop()

        painter.end()
Пример #14
0
    def paintEvent(self, event):
        """Paint the background, range bar and splitters.

        Parameters
        ----------
        event : qtpy.QEvent
            Event from the Qt context.
        """
        painter, w, h = QPainter(self), self.width(), self.height()

        half_width = self.slider_width / 2 - 1
        halfdiff = int(h / 2 - half_width)

        # Background
        painter.setPen(self.background_color)
        painter.setBrush(self.background_color)
        painter.drawRoundedRect(0, halfdiff, w, self.slider_width, 2, 2)

        # Range Bar
        painter.setPen(self.bar_color)
        painter.setBrush(self.bar_color)
        if self.collapsed:
            painter.drawRect(0, halfdiff, self.display_max, self.slider_width)
        else:
            painter.drawRect(
                self.display_min,
                halfdiff,
                self.display_max - self.display_min,
                self.slider_width,
            )

        painter.setRenderHints(QPainter.Antialiasing)
        # Splitters
        painter.setPen(self.handle_border_color)
        painter.setBrush(self.handle_color)
        painter.drawEllipse(
            self.display_min - self.handle_radius,
            int(h / 2 - self.handle_radius + 1),
            self.handle_width - 1,
            self.handle_width - 1,
        )  # left
        painter.drawEllipse(
            self.display_max - self.handle_radius,
            int(h / 2 - self.handle_radius + 1),
            self.handle_width - 1,
            self.handle_width - 1,
        )  # right
Пример #15
0
    def paintEvent(self, a0: QPaintEvent) -> None:
        painter = QPainter(self)
        margin = 10
        width = self.width() - 2 * margin
        rect = QRect(margin, margin, width, self.height() - 2 * margin)
        painter.drawImage(rect, self.image)
        painter.save()

        for pos_factor in self.position_list:
            pos = width * pos_factor
            point = QPointF(pos + margin, self.height() / 2)
            painter.setBrush(QBrush(Qt.black))
            painter.drawEllipse(point, 5, 5)
            painter.setBrush(QBrush(Qt.white))
            painter.drawEllipse(point, 3, 3)

        painter.restore()
Пример #16
0
    def updatePicture(self):
        self._picture = QPicture()
        p = QPainter(self._picture)
        # self._generatePicture(painter)
        # painter.end()

        # def _generatePicture(self, p=QPainter()):
        #     a = self.colormap(self.normalize(self.z))
        #     color = QColor.fromRgbF(a[0], a[1], a[2], a[3])
        color = self.color_assignment_callback(self.z)

        p.setPen(QPen(color))
        p.setBrush(QBrush(color))
        # p.drawRect(self.b_rect)
        p.drawEllipse(QPointF(0, 0), self.w, self.w)

        # self._generatePicture(painter)
        p.end()
Пример #17
0
    def paintEvent(self, event):
        painter = QPainter()
        painter.begin(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.fillRect(event.rect(), QBrush(QColor(255, 255, 255, 127)))
        painter.setPen(QPen(QtCore.Qt.NoPen))

        for i in range(6):
            if (self.counter / 5) % 6 == i:
                painter.setBrush(QBrush(QColor(127 + (self.counter % 5) * 32, 127, 127)))
            else:
                painter.setBrush(QBrush(QColor(127, 127, 127)))
            painter.drawEllipse(
                self.width() / 2 + 30 * np.cos(2 * np.pi * i / 6.0) - 10,
                self.height() / 2 + 30 * np.sin(2 * np.pi * i / 6.0) - 10,
                20, 20
            )

        painter.end()
 def drawBase(self, p: QPainter, baseRect: QRectF):
     if self.m_barStyle == self.BarStyle.DONUT:
         p.setPen(QPen(self.palette().shadow().color(), self.m_outlinePenWidth))
         p.setBrush(self.palette().base())
         p.drawEllipse(baseRect)
     elif self.m_barStyle == self.BarStyle.LINE:
         p.setPen(QPen(self.palette().base().color(), self.m_outlinePenWidth))
         p.setBrush(Qt.NoBrush)
         p.drawEllipse(baseRect.adjusted(self.m_outlinePenWidth / 2, self.m_outlinePenWidth / 2,
                                         -self.m_outlinePenWidth / 2, -self.m_outlinePenWidth / 2))
     elif self.m_barStyle in (self.BarStyle.PIE, self.BarStyle.EXPAND):
         p.setPen(QPen(self.palette().base().color(), self.m_outlinePenWidth))
         p.setBrush(self.palette().base())
         p.drawEllipse(baseRect)
Пример #19
0
def graph2icon(g: Graph,
               width: int,
               node_mode: bool,
               show_label: bool,
               monochrome: bool,
               *,
               except_node: Optional[int] = None,
               engine: str = "",
               pos: Optional[_Pos] = None) -> QIcon:
    """Draw a generalized chain graph."""
    if engine:
        pos = engine_picker(g, engine, node_mode)
    if pos is None:
        raise ValueError("no engine selected")
    if not pos:
        pixmap = QPixmap(width, width)
        pixmap.fill(Qt.transparent)
        return QIcon(pixmap)

    width_bound = -float('inf')
    for x, y in pos.values():
        if abs(x) > width_bound:
            width_bound = x
        if abs(y) > width_bound:
            width_bound = y
    width_bound *= 2.5
    image = QImage(QSize(int(width_bound), int(width_bound)),
                   QImage.Format_ARGB32_Premultiplied)
    image.fill(Qt.transparent)
    painter = QPainter(image)
    painter.translate(image.width() / 2, image.height() / 2)
    pen = QPen()
    r = int(width_bound / 50)
    pen.setWidth(r)
    painter.setPen(pen)
    _font.setPixelSize(r * 6)
    painter.setFont(_font)

    # Draw edges
    if node_mode:
        for l1, l2 in g.edges:
            if except_node in {l1, l2}:
                pen.setColor(Qt.gray)
            else:
                pen.setColor(Qt.black)
            painter.setPen(pen)

            painter.drawLine(QPointF(pos[l1][0], -pos[l1][1]),
                             QPointF(pos[l2][0], -pos[l2][1]))
    else:
        color = color_qt('dark-gray') if monochrome else LINK_COLOR
        color.setAlpha(150)
        painter.setBrush(QBrush(color))
        for link in g.vertices:
            if link == except_node:
                pen.setColor(Qt.gray)
            else:
                pen.setColor(Qt.black)
            painter.setPen(pen)

            painter.drawPolygon(*convex_hull([(pos[n][0], -pos[n][1])
                                              for n, edge in edges_view(g)
                                              if link in edge],
                                             as_qpoint=True))

    # Draw vertices
    for k, (x, y) in pos.items():
        if node_mode:
            color = color_num(len(list(g.neighbors(k))) - 1)
            if k == except_node:
                color.setAlpha(150)
        else:
            if monochrome:
                color = Qt.black
            elif except_node in dict(edges_view(g))[k]:
                color = color_qt('green')
            else:
                color = color_qt('blue')
        pen.setColor(color)
        painter.setPen(pen)
        painter.setBrush(QBrush(color))
        point = QPointF(x, -y)
        painter.drawEllipse(point, r, r)
        if show_label:
            pen.setColor(Qt.darkMagenta)
            painter.setPen(pen)
            painter.drawText(point, str(k))
    painter.end()
    return QIcon(QPixmap.fromImage(image).scaledToWidth(width))
Пример #20
0
class BaseCanvas(QWidget, metaclass=QABCMeta):
    """The subclass can draw a blank canvas more easier."""
    @abstractmethod
    def __init__(self, parent: QWidget):
        """Set the parameters for drawing."""
        super(BaseCanvas, self).__init__(parent)
        self.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
        self.setFocusPolicy(Qt.StrongFocus)
        self.setMouseTracking(True)
        self.painter = QPainter()
        # Origin coordinate
        self.ox = self.width() / 2
        self.oy = self.height() / 2
        # Canvas zoom rate
        self.zoom = 1.
        # Joint size
        self.joint_size = 5
        # Canvas line width
        self.link_width = 3
        self.path_width = 3
        # Font size
        self.font_size = 15
        # Show point mark or dimension
        self.show_ticks = _TickMark.SHOW
        self.show_point_mark = True
        self.show_dimension = True
        # Path track
        self.path = _PathOption()
        # Path solving
        self.ranges: Dict[str, QRectF] = {}
        self.target_path: Dict[int, Sequence[_Coord]] = {}
        self.show_target_path = False
        # Background
        self.background = QImage()
        self.background_opacity = 1.
        self.background_scale = 1.
        self.background_offset = QPointF(0, 0)
        # Monochrome mode
        self.monochrome = False
        # Grab mode
        self.__grab_mode = False

    def switch_grab(self) -> None:
        """Start grab mode."""
        self.__grab_mode = not self.__grab_mode

    @staticmethod
    def zoom_factor(width: int, height: int, x_right: float, x_left: float,
                    y_top: float, y_bottom: float) -> float:
        """Calculate the zoom factor."""
        x_diff = x_left - x_right
        y_diff = y_top - y_bottom
        x_diff = x_diff if x_diff else 1.
        y_diff = y_diff if y_diff else 1.
        if width / x_diff < height / y_diff:
            return width / x_diff
        else:
            return height / y_diff

    @abstractmethod
    def paintEvent(self, event: QPaintEvent) -> None:
        """Using a QPainter under 'self',
        so just change QPen or QBrush before painting.
        """
        if not self.__grab_mode:
            self.painter.begin(self)
            self.painter.fillRect(event.rect(), QBrush(Qt.white))
        # Translation
        self.painter.translate(self.ox, self.oy)
        # Background
        if not self.background.isNull():
            rect = self.background.rect()
            self.painter.setOpacity(self.background_opacity)
            self.painter.drawImage(
                QRectF(
                    self.background_offset * self.zoom,
                    QSizeF(rect.width(), rect.height()) *
                    self.background_scale * self.zoom), self.background,
                QRectF(rect))
            self.painter.setOpacity(1)
        # Show frame
        pen = QPen(Qt.blue)
        pen.setWidth(1)
        self.painter.setPen(pen)
        self.painter.setFont(QFont("Arial", self.font_size))
        # Draw origin lines
        if self.show_ticks not in {_TickMark.SHOW, _TickMark.SHOW_NUM}:
            return
        pen.setColor(Qt.gray)
        self.painter.setPen(pen)
        x_l = -self.ox
        x_r = self.width() - self.ox
        self.painter.drawLine(QPointF(x_l, 0), QPointF(x_r, 0))
        y_t = self.height() - self.oy
        y_b = -self.oy
        self.painter.drawLine(QPointF(0, y_b), QPointF(0, y_t))

        def indexing(v: float) -> int:
            """Draw tick."""
            return int(v / self.zoom - v / self.zoom % 5)

        # Draw tick
        for x in range(indexing(x_l), indexing(x_r) + 1, 5):
            if x == 0:
                continue
            is_ten = x % 10 == 0
            end = QPointF(x * self.zoom, -10 if is_ten else -5)
            self.painter.drawLine(QPointF(x, 0) * self.zoom, end)
            if self.show_ticks == _TickMark.SHOW_NUM and is_ten:
                self.painter.drawText(end + QPointF(0, 3), f"{x}")
        for y in range(indexing(y_b), indexing(y_t) + 1, 5):
            if y == 0:
                continue
            is_ten = y % 10 == 0
            end = QPointF(10 if is_ten else 5, y * self.zoom)
            self.painter.drawLine(QPointF(0, y) * self.zoom, end)
            if self.show_ticks == _TickMark.SHOW_NUM and is_ten:
                self.painter.drawText(end + QPointF(3, 0), f"{-y}")
        # Please to call the "end" method when ending paint event.

    def draw_circle(self, p: QPointF, r: float) -> None:
        """Draw circle."""
        self.painter.drawEllipse(p, r, r)

    def draw_point(self,
                   i: int,
                   cx: float,
                   cy: float,
                   fixed: bool,
                   color: Optional[Tuple[int, int, int]],
                   mul: int = 1) -> None:
        """Draw a joint."""
        if self.monochrome or color is None:
            color = Qt.black
        else:
            color = QColor(*color)
        pen = QPen(color)
        pen.setWidth(2)
        self.painter.setPen(pen)
        x = cx * self.zoom
        y = cy * -self.zoom
        if fixed:
            # Draw a triangle below
            self.painter.drawPolygon(
                QPointF(x, y),
                QPointF(x - self.joint_size, y + 2 * self.joint_size),
                QPointF(x + self.joint_size, y + 2 * self.joint_size))
        r = self.joint_size
        for _ in range(1 if mul < 1 else mul):
            self.draw_circle(QPointF(x, y), r)
            r += 5
        if not self.show_point_mark:
            return
        pen.setColor(Qt.darkGray)
        pen.setWidth(2)
        self.painter.setPen(pen)
        text = f"[Point{i}]"
        if self.show_dimension:
            text += f":({cx:.02f}, {cy:.02f})"
        self.painter.drawText(QPointF(x, y) + QPointF(6, -6), text)

    def draw_ranges(self) -> None:
        """Draw rectangle ranges."""
        pen = QPen()
        pen.setWidth(5)
        for i, (tag, rect) in enumerate(self.ranges.items()):
            range_color = QColor(color_num(i + 1))
            range_color.setAlpha(30)
            self.painter.setBrush(range_color)
            range_color.setAlpha(255)
            pen.setColor(range_color)
            self.painter.setPen(pen)
            cx = rect.x() * self.zoom
            cy = rect.y() * -self.zoom
            if rect.width():
                self.painter.drawRect(
                    QRectF(QPointF(cx, cy),
                           QSizeF(rect.width(), rect.height()) * self.zoom))
            else:
                self.draw_circle(QPointF(cx, cy), 3)
            range_color.setAlpha(255)
            pen.setColor(range_color)
            self.painter.setPen(pen)
            self.painter.drawText(QPointF(cx, cy) + QPointF(6, -6), tag)
            self.painter.setBrush(Qt.NoBrush)

    def draw_target_path(self) -> None:
        """Draw solving path."""
        pen = QPen()
        pen.setWidth(self.path_width)
        for i, n in enumerate(sorted(self.target_path)):
            path = self.target_path[n]
            if self.monochrome:
                line, dot = target_path_style(0)
            else:
                line, dot = target_path_style(i + 1)
            pen.setColor(line)
            self.painter.setPen(pen)
            if len(path) == 1:
                x, y = path[0]
                p = QPointF(x, -y) * self.zoom
                self.painter.drawText(p + QPointF(6, -6), f"P{n}")
                pen.setColor(dot)
                self.painter.setPen(pen)
                self.draw_circle(p, self.joint_size)
            else:
                painter_path = QPainterPath()
                for j, (x, y) in enumerate(path):
                    p = QPointF(x, -y) * self.zoom
                    self.draw_circle(p, self.joint_size)
                    if j == 0:
                        self.painter.drawText(p + QPointF(6, -6), f"P{n}")
                        painter_path.moveTo(p)
                    else:
                        x2, y2 = path[j - 1]
                        self.__draw_arrow(x, -y, x2, -y2, zoom=True)
                        painter_path.lineTo(p)
                pen.setColor(line)
                self.painter.setPen(pen)
                self.painter.drawPath(painter_path)
                for x, y in path:
                    pen.setColor(dot)
                    self.painter.setPen(pen)
                    self.draw_circle(
                        QPointF(x, -y) * self.zoom, self.joint_size)
        self.painter.setBrush(Qt.NoBrush)

    def __draw_arrow(self,
                     x1: float,
                     y1: float,
                     x2: float,
                     y2: float,
                     *,
                     zoom: bool = False,
                     text: str = '') -> None:
        """Front point -> Back point"""
        if zoom:
            x1 *= self.zoom
            y1 *= self.zoom
            x2 *= self.zoom
            y2 *= self.zoom
        a = atan2(y2 - y1, x2 - x1)
        x1 = (x1 + x2) / 2 - 7.5 * cos(a)
        y1 = (y1 + y2) / 2 - 7.5 * sin(a)
        first_point = QPointF(x1, y1)
        self.painter.drawLine(
            first_point,
            QPointF(x1 + 15 * cos(a + radians(20)),
                    y1 + 15 * sin(a + radians(20))))
        self.painter.drawLine(
            first_point,
            QPointF(x1 + 15 * cos(a - radians(20)),
                    y1 + 15 * sin(a - radians(20))))
        if not text:
            return
        # Font
        font = self.painter.font()
        font_copy = QFont(font)
        font.setBold(True)
        font.setPointSize(font.pointSize() + 8)
        self.painter.setFont(font)
        # Color
        pen = self.painter.pen()
        color = pen.color()
        pen.setColor(color.darker())
        self.painter.setPen(pen)
        self.painter.drawText(first_point, text)
        pen.setColor(color)
        self.painter.setPen(pen)
        self.painter.setFont(font_copy)

    def draw_curve(self, path: Sequence[_Coord]) -> None:
        """Draw path as curve."""
        if len(set(path)) < 2:
            return
        painter_path = QPainterPath()
        error = False
        for i, (x, y) in enumerate(path):
            if isnan(x):
                error = True
                self.painter.drawPath(painter_path)
                painter_path = QPainterPath()
            else:
                p = QPointF(x, -y) * self.zoom
                if i == 0:
                    painter_path.moveTo(p)
                    self.draw_circle(p, 2)
                    continue
                if error:
                    painter_path.moveTo(p)
                    error = False
                else:
                    painter_path.lineTo(p)
        self.painter.drawPath(painter_path)

    def draw_dot(self, path: Sequence[_Coord]) -> None:
        """Draw path as dots."""
        if len(set(path)) < 2:
            return
        for i, (x, y) in enumerate(path):
            if isnan(x):
                continue
            p = QPointF(x, -y) * self.zoom
            if i == 0:
                self.draw_circle(p, 2)
            else:
                self.painter.drawPoint(p)

    def solution_polygon(
            self, func: str, args: Sequence[str], target: str,
            pos: Sequence[VPoint]) -> Tuple[List[QPointF], QColor]:
        """Get solution polygon."""
        if func == 'PLLP':
            color = QColor(121, 171, 252)
            params = [args[0], args[-1]]
        elif func == 'PLAP':
            color = QColor(249, 84, 216)
            params = [args[0]]
        else:
            if func == 'PLPP':
                color = QColor(94, 255, 185)
            else:
                # PXY
                color = QColor(249, 175, 27)
            params = [args[0]]
        params.append(target)
        tmp_list = []
        for name in params:
            try:
                index = int(name.replace('P', ''))
            except ValueError:
                continue
            else:
                vpoint = pos[index]
                tmp_list.append(QPointF(vpoint.cx, -vpoint.cy) * self.zoom)
        return tmp_list, color

    def draw_solution(self, func: str, args: Sequence[str], target: str,
                      pos: Sequence[VPoint]) -> None:
        """Draw the solution triangle."""
        points, color = self.solution_polygon(func, args, target, pos)
        color.setAlpha(150)
        pen = QPen(color)
        pen.setWidth(self.joint_size)
        self.painter.setPen(pen)

        def draw_arrow(index: int, text: str) -> None:
            """Draw arrow."""
            self.__draw_arrow(points[-1].x(),
                              points[-1].y(),
                              points[index].x(),
                              points[index].y(),
                              text=text)

        draw_arrow(0, args[1])
        if func == 'PLLP':
            draw_arrow(1, args[2])
        color.setAlpha(30)
        self.painter.setBrush(QBrush(color))
        self.painter.drawPolygon(QPolygonF(points))
        self.painter.setBrush(Qt.NoBrush)

    @Slot(int)
    def set_show_ticks(self, show: int):
        """Set the appearance of tick mark."""
        self.show_ticks = _TickMark(show + 1)
        self.update()

    @Slot(bool)
    def set_monochrome_mode(self, monochrome: bool) -> None:
        """Set monochrome mode."""
        self.monochrome = monochrome
        self.update()
 def drawInnerBackground(self, p: QPainter, innerRect: QRectF):
     if self.m_barStyle == self.BarStyle.DONUT:
         p.setBrush(self.palette().base())
         p.drawEllipse(innerRect)