Exemplo n.º 1
0
    def paintEvent(self, e):
        height = self.height()
        width = self.width()

        # draw background
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setPen(Qt.transparent)
        painter.setBrush(BACKGROUND)
        painter.setOpacity(1)
        path = QPainterPath()
        path.setFillRule(Qt.WindingFill)
        path.addRoundedRect(QRectF(8, 0, width - 8, height), 15, 15)
        painter.drawPath(path.simplified())

        # draw sidebar
        painter.setBrush(SIDEBAR)
        painter.setOpacity(1)
        path = QPainterPath()
        path.setFillRule(Qt.WindingFill)

        # we draw a fixed-width toolbar background
        path.addRoundedRect(QRectF(8, 0, 250 - 8, height), 15, 15)
        path.addRect(QRectF(200, 0, 50, 50))
        path.addRect(QRectF(200, height - 50, 50, 50))
        painter.drawPath(path.simplified())
Exemplo n.º 2
0
 def paintEvent(self, event):
     painter = QPainter(self)
     painter.setClipRegion(event.region())
     if (
         self.testPaintAttribute(self.BackingStore)
         and self.__data.backingStore is not None
     ):
         bs = self.__data.backingStore
         if QT_MAJOR_VERSION >= 5:
             pixelRatio = bs.devicePixelRatio()
         else:
             pixelRatio = 1.0
         if bs.size() != self.size() * pixelRatio:
             bs = QwtPainter.backingStore(self, self.size())
             if self.testAttribute(Qt.WA_StyledBackground):
                 p = QPainter(bs)
                 qwtFillBackground(p, self)
                 self.drawCanvas(p, True)
             else:
                 p = QPainter()
                 if self.__data.borderRadius <= 0.0:
                     #                        print('**DEBUG: QwtPlotCanvas.paintEvent')
                     QwtPainter.fillPixmap(self, bs)
                     p.begin(bs)
                     self.drawCanvas(p, False)
                 else:
                     p.begin(bs)
                     qwtFillBackground(p, self)
                     self.drawCanvas(p, True)
                 if self.frameWidth() > 0:
                     self.drawBorder(p)
                 p.end()
         painter.drawPixmap(0, 0, self.__data.backingStore)
     else:
         if self.testAttribute(Qt.WA_StyledBackground):
             if self.testAttribute(Qt.WA_OpaquePaintEvent):
                 qwtFillBackground(painter, self)
                 self.drawCanvas(painter, True)
             else:
                 self.drawCanvas(painter, False)
         else:
             if self.testAttribute(Qt.WA_OpaquePaintEvent):
                 if self.autoFillBackground():
                     qwtFillBackground(painter, self)
                     qwtDrawBackground(painter, self)
             else:
                 if self.borderRadius() > 0.0:
                     clipPath = QPainterPath()
                     clipPath.addRect(self.rect())
                     clipPath = clipPath.subtracted(self.borderPath(self.rect()))
                     painter.save()
                     painter.setClipPath(clipPath, Qt.IntersectClip)
                     qwtFillBackground(painter, self)
                     qwtDrawBackground(painter, self)
                     painter.restore()
             self.drawCanvas(painter, False)
             if self.frameWidth() > 0:
                 self.drawBorder(painter)
     if self.hasFocus() and self.focusIndicator() == self.CanvasFocusIndicator:
         self.drawFocusIndicator(painter)
Exemplo n.º 3
0
 def get_line_path(points, shape_type):
     line_path = QPainterPath()
     if shape_type == 'rectangle' and len(points) == 2:
         rectangle = Shape.getRectFromLine(*points)
         line_path.addRect(rectangle)
     elif shape_type == 'circle' and len(points) == 2:
         rectangle = Shape.getCircleRectFromLine(points)
         line_path.addEllipse(rectangle)
     elif shape_type == 'linestrip' and points:
         line_path.moveTo(points[0])
         for p in points:
             line_path.lineTo(p)
     elif shape_type in ['curve', 'freeform'] and points:
         # Paint Bezier curve across given points.
         refined_points = BezierB(points).smooth()
         line_path.moveTo(refined_points[0])
         for p in refined_points:
             line_path.lineTo(p)
     elif points:
         line_path.moveTo(points[0])
         for p in points:
             line_path.lineTo(p)
         if shape_type == 'polygon':
             line_path.lineTo(points[0])
     return line_path
    def shape(self) -> QPainterPath:
        """
        Shape

        Returns
        -------
        value : QPainterPath
        """
        # TODO DEBUG_DRAWING
        if debug_drawing:
            path = QPainterPath()
            path.addRect(self.boundingRect())
            return path

        return ConnectionPainter.get_painter_stroke(self._geometry)
Exemplo n.º 5
0
 def paint_vertices(painter,
                    points,
                    scale,
                    highlight=None,
                    highlight_mode=None):
     path = QPainterPath()
     if highlight is not None:
         vertex_fill_color = Shape.hvertex_fill_color
     else:
         vertex_fill_color = Shape.vertex_fill_color
     for i, point in enumerate(points):
         d = Shape.point_size / scale
         shape = Shape.def_point_type
         if i == highlight:
             size, shape = Shape._highlightSettings[highlight_mode]
             d *= size
         if shape == Shape.P_SQUARE:
             path.addRect(point.x() - d / 2, point.y() - d / 2, d, d)
         elif shape == Shape.P_ROUND:
             path.addEllipse(point, d / 2.0, d / 2.0)
         else:
             assert False, 'unsupported vertex shape'
     painter.drawPath(path)
     painter.fillPath(path, vertex_fill_color)
Exemplo n.º 6
0
    def update_canvas(self):
        """ """
        w, h = self.parent.size().width(), self.parent.size().height()

        self.path_full = QPainterPath()
        self.path_subtract = QPainterPath()
        self.path_decoration = QPainterPath()
        self.region_mask = QRegion(0, 0, w, h)

        self.path_full.addRect(0, 0, w, h)

        # Add the path
        if self.widgets is not None:
            for widget in self.widgets:
                temp_path = QPainterPath()
                # if widget is not found... find more general way to handle
                if widget is not None:
                    widget.raise_()
                    widget.show()
                    geo = widget.frameGeometry()
                    width, height = geo.width(), geo.height()
                    point = widget.mapTo(self.parent, QPoint(0, 0))
                    x, y = point.x(), point.y()

                    temp_path.addRect(QRectF(x, y, width, height))

                    temp_region = QRegion(x, y, width, height)

                if self.interaction_on:
                    self.region_mask = self.region_mask.subtracted(temp_region)
                self.path_subtract = self.path_subtract.united(temp_path)

            self.path_current = self.path_full.subtracted(self.path_subtract)
        else:
            self.path_current = self.path_full

        if self.decoration is not None:
            for widget in self.decoration:
                temp_path = QPainterPath()
                widget.raise_()
                widget.show()
                geo = widget.frameGeometry()
                width, height = geo.width(), geo.height()
                point = widget.mapTo(self.parent, QPoint(0, 0))
                x, y = point.x(), point.y()
                temp_path.addRect(QRectF(x, y, width, height))

                temp_region_1 = QRegion(x - 1, y - 1, width + 2, height + 2)
                temp_region_2 = QRegion(x + 1, y + 1, width - 2, height - 2)
                temp_region = temp_region_1.subtracted(temp_region_2)

                if self.interaction_on:
                    self.region_mask = self.region_mask.united(temp_region)

                self.path_decoration = self.path_decoration.united(temp_path)
        else:
            self.path_decoration.addRect(0, 0, 0, 0)

        # Add a decoration stroke around widget
        self.setMask(self.region_mask)
        self.update()
        self.repaint()
Exemplo n.º 7
0
class FadingCanvas(FadingDialog):
    """The black semi transparent canvas that covers the application"""
    def __init__(self,
                 parent,
                 opacity,
                 duration,
                 easing_curve,
                 color,
                 tour=None):
        """Create a black semi transparent canvas that covers the app."""
        super(FadingCanvas, self).__init__(parent, opacity, duration,
                                           easing_curve)
        self.parent = parent
        self.tour = tour

        self.color = color  # Canvas color
        self.color_decoration = Qt.red  # Decoration color
        self.stroke_decoration = 2  # width in pixels for decoration

        self.region_mask = None
        self.region_subtract = None
        self.region_decoration = None

        self.widgets = None  # The widget to uncover
        self.decoration = None  # The widget to draw decoration
        self.interaction_on = False

        self.path_current = None
        self.path_subtract = None
        self.path_full = None
        self.path_decoration = None

        # widget setup
        self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setAttribute(Qt.WA_TransparentForMouseEvents)
        self.setModal(False)
        self.setFocusPolicy(Qt.NoFocus)

        self.set_funcs_before_fade_in([self.update_canvas])
        self.set_funcs_after_fade_out([
            lambda: self.update_widgets(None),
            lambda: self.update_decoration(None)
        ])

    def set_interaction(self, value):
        """ """
        self.interaction_on = value

    def update_canvas(self):
        """ """
        w, h = self.parent.size().width(), self.parent.size().height()

        self.path_full = QPainterPath()
        self.path_subtract = QPainterPath()
        self.path_decoration = QPainterPath()
        self.region_mask = QRegion(0, 0, w, h)

        self.path_full.addRect(0, 0, w, h)

        # Add the path
        if self.widgets is not None:
            for widget in self.widgets:
                temp_path = QPainterPath()
                # if widget is not found... find more general way to handle
                if widget is not None:
                    widget.raise_()
                    widget.show()
                    geo = widget.frameGeometry()
                    width, height = geo.width(), geo.height()
                    point = widget.mapTo(self.parent, QPoint(0, 0))
                    x, y = point.x(), point.y()

                    temp_path.addRect(QRectF(x, y, width, height))

                    temp_region = QRegion(x, y, width, height)

                if self.interaction_on:
                    self.region_mask = self.region_mask.subtracted(temp_region)
                self.path_subtract = self.path_subtract.united(temp_path)

            self.path_current = self.path_full.subtracted(self.path_subtract)
        else:
            self.path_current = self.path_full

        if self.decoration is not None:
            for widget in self.decoration:
                temp_path = QPainterPath()
                widget.raise_()
                widget.show()
                geo = widget.frameGeometry()
                width, height = geo.width(), geo.height()
                point = widget.mapTo(self.parent, QPoint(0, 0))
                x, y = point.x(), point.y()
                temp_path.addRect(QRectF(x, y, width, height))

                temp_region_1 = QRegion(x - 1, y - 1, width + 2, height + 2)
                temp_region_2 = QRegion(x + 1, y + 1, width - 2, height - 2)
                temp_region = temp_region_1.subtracted(temp_region_2)

                if self.interaction_on:
                    self.region_mask = self.region_mask.united(temp_region)

                self.path_decoration = self.path_decoration.united(temp_path)
        else:
            self.path_decoration.addRect(0, 0, 0, 0)

        # Add a decoration stroke around widget
        self.setMask(self.region_mask)
        self.update()
        self.repaint()

    def update_widgets(self, widgets):
        """ """
        self.widgets = widgets

    def update_decoration(self, widgets):
        """ """
        self.decoration = widgets

    def paintEvent(self, event):
        """Override Qt method"""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        # Decoration
        painter.fillPath(self.path_current, QBrush(self.color))
        painter.strokePath(self.path_decoration,
                           QPen(self.color_decoration, self.stroke_decoration))


#        decoration_fill = QColor(self.color_decoration)
#        decoration_fill.setAlphaF(0.25)
#        painter.fillPath(self.path_decoration, decoration_fill)

    def reject(self):
        """Override Qt method"""
        if not self.is_fade_running():
            key = Qt.Key_Escape
            self.key_pressed = key
            self.sig_key_pressed.emit()

    def mousePressEvent(self, event):
        """Override Qt method"""
        pass

    def focusInEvent(self, event):
        """Override Qt method."""
        # To be used so tips do not appear outside spyder
        if self.hasFocus():
            self.tour.gain_focus()

    def focusOutEvent(self, event):
        """Override Qt method."""
        # To be used so tips do not appear outside spyder
        if self.tour.step_current != 0:
            self.tour.lost_focus()
Exemplo n.º 8
0
    def update_canvas(self):
        """ """
        w, h = self.parent.size().width(), self.parent.size().height()

        self.path_full = QPainterPath()
        self.path_subtract = QPainterPath()
        self.path_decoration = QPainterPath()
        self.region_mask = QRegion(0, 0, w, h)

        self.path_full.addRect(0, 0, w, h)

        # Add the path
        if self.widgets is not None:
            for widget in self.widgets:
                temp_path = QPainterPath()
                # if widget is not found... find more general way to handle
                if widget is not None:
                    widget.raise_()
                    widget.show()
                    geo = widget.frameGeometry()
                    width, height = geo.width(), geo.height()
                    point = widget.mapTo(self.parent, QPoint(0, 0))
                    x, y = point.x(), point.y()
    
                    temp_path.addRect(QRectF(x, y, width, height))
    
                    temp_region = QRegion(x, y, width, height)

                if self.interaction_on:
                    self.region_mask = self.region_mask.subtracted(temp_region)
                self.path_subtract = self.path_subtract.united(temp_path)

            self.path_current = self.path_full.subtracted(self.path_subtract)
        else:
            self.path_current = self.path_full

        if self.decoration is not None:
            for widget in self.decoration:
                temp_path = QPainterPath()
                widget.raise_()
                widget.show()
                geo = widget.frameGeometry()
                width, height = geo.width(), geo.height()
                point = widget.mapTo(self.parent, QPoint(0, 0))
                x, y = point.x(), point.y()
                temp_path.addRect(QRectF(x, y, width, height))

                temp_region_1 = QRegion(x-1, y-1, width+2, height+2)
                temp_region_2 = QRegion(x+1, y+1, width-2, height-2)
                temp_region = temp_region_1.subtracted(temp_region_2)

                if self.interaction_on:
                    self.region_mask = self.region_mask.united(temp_region)

                self.path_decoration = self.path_decoration.united(temp_path)
        else:
            self.path_decoration.addRect(0, 0, 0, 0)

        # Add a decoration stroke around widget
        self.setMask(self.region_mask)
        self.update()
        self.repaint()
Exemplo n.º 9
0
class FadingCanvas(FadingDialog):
    """The black semi transparent canvas that covers the application"""
    def __init__(self, parent, opacity, duration, easing_curve, color,
                 tour=None):
        """Create a black semi transparent canvas that covers the app."""
        super(FadingCanvas, self).__init__(parent, opacity, duration,
                                           easing_curve)
        self.parent = parent
        self.tour = tour

        self.color = color              # Canvas color
        self.color_decoration = Qt.red  # Decoration color
        self.stroke_decoration = 2      # width in pixels for decoration

        self.region_mask = None
        self.region_subtract = None
        self.region_decoration = None

        self.widgets = None             # The widget to uncover
        self.decoration = None          # The widget to draw decoration
        self.interaction_on = False

        self.path_current = None
        self.path_subtract = None
        self.path_full = None
        self.path_decoration = None

        # widget setup
        self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setAttribute(Qt.WA_TransparentForMouseEvents)
        self.setModal(False)
        self.setFocusPolicy(Qt.NoFocus)

        self.set_funcs_before_fade_in([self.update_canvas])
        self.set_funcs_after_fade_out([lambda: self.update_widgets(None),
                                       lambda: self.update_decoration(None)])

    def set_interaction(self, value):
        """ """
        self.interaction_on = value

    def update_canvas(self):
        """ """
        w, h = self.parent.size().width(), self.parent.size().height()

        self.path_full = QPainterPath()
        self.path_subtract = QPainterPath()
        self.path_decoration = QPainterPath()
        self.region_mask = QRegion(0, 0, w, h)

        self.path_full.addRect(0, 0, w, h)

        # Add the path
        if self.widgets is not None:
            for widget in self.widgets:
                temp_path = QPainterPath()
                # if widget is not found... find more general way to handle
                if widget is not None:
                    widget.raise_()
                    widget.show()
                    geo = widget.frameGeometry()
                    width, height = geo.width(), geo.height()
                    point = widget.mapTo(self.parent, QPoint(0, 0))
                    x, y = point.x(), point.y()
    
                    temp_path.addRect(QRectF(x, y, width, height))
    
                    temp_region = QRegion(x, y, width, height)

                if self.interaction_on:
                    self.region_mask = self.region_mask.subtracted(temp_region)
                self.path_subtract = self.path_subtract.united(temp_path)

            self.path_current = self.path_full.subtracted(self.path_subtract)
        else:
            self.path_current = self.path_full

        if self.decoration is not None:
            for widget in self.decoration:
                temp_path = QPainterPath()
                widget.raise_()
                widget.show()
                geo = widget.frameGeometry()
                width, height = geo.width(), geo.height()
                point = widget.mapTo(self.parent, QPoint(0, 0))
                x, y = point.x(), point.y()
                temp_path.addRect(QRectF(x, y, width, height))

                temp_region_1 = QRegion(x-1, y-1, width+2, height+2)
                temp_region_2 = QRegion(x+1, y+1, width-2, height-2)
                temp_region = temp_region_1.subtracted(temp_region_2)

                if self.interaction_on:
                    self.region_mask = self.region_mask.united(temp_region)

                self.path_decoration = self.path_decoration.united(temp_path)
        else:
            self.path_decoration.addRect(0, 0, 0, 0)

        # Add a decoration stroke around widget
        self.setMask(self.region_mask)
        self.update()
        self.repaint()

    def update_widgets(self, widgets):
        """ """
        self.widgets = widgets

    def update_decoration(self, widgets):
        """ """
        self.decoration = widgets

    def paintEvent(self, event):
        """Override Qt method"""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        # Decoration
        painter.fillPath(self.path_current, QBrush(self.color))
        painter.strokePath(self.path_decoration, QPen(self.color_decoration,
                                                      self.stroke_decoration))
#        decoration_fill = QColor(self.color_decoration)
#        decoration_fill.setAlphaF(0.25)
#        painter.fillPath(self.path_decoration, decoration_fill)

    def reject(self):
        """Override Qt method"""
        if not self.is_fade_running():
            key = Qt.Key_Escape
            self.key_pressed = key
            self.sig_key_pressed.emit()

    def mousePressEvent(self, event):
        """Override Qt method"""
        pass

    def focusInEvent(self, event):
        """Override Qt method."""
        # To be used so tips do not appear outside spyder
        if self.hasFocus():
            self.tour.gain_focus()

    def focusOutEvent(self, event):
        """Override Qt method."""
        # To be used so tips do not appear outside spyder
        if self.tour.step_current != 0:
            self.tour.lost_focus()
Exemplo n.º 10
0
 def drawFrame(
     self,
     painter,
     rect,
     palette,
     foregroundRole,
     frameWidth,
     midLineWidth,
     frameStyle,
 ):
     """
     Draw a rectangular frame
     
     :param QPainter painter: Painter
     :param QRectF rect: Frame rectangle
     :param QPalette palette: Palette
     :param QPalette.ColorRole foregroundRole: Palette
     :param int frameWidth: Frame width
     :param int midLineWidth: Used for `QFrame.Box`
     :param int frameStyle: bitwise OR´ed value of `QFrame.Shape` and `QFrame.Shadow`
     """
     if frameWidth <= 0 or rect.isEmpty():
         return
     shadow = frameStyle & QFrame.Shadow_Mask
     painter.save()
     if shadow == QFrame.Plain:
         outerRect = rect.adjusted(0.0, 0.0, -1.0, -1.0)
         innerRect = outerRect.adjusted(frameWidth, frameWidth, -frameWidth,
                                        -frameWidth)
         path = QPainterPath()
         path.addRect(outerRect)
         path.addRect(innerRect)
         painter.setPen(Qt.NoPen)
         painter.setBrush(palette.color(foregroundRole))
         painter.drawPath(path)
     else:
         shape = frameStyle & QFrame.Shape_Mask
         if shape == QFrame.Box:
             outerRect = rect.adjusted(0.0, 0.0, -1.0, -1.0)
             midRect1 = outerRect.adjusted(frameWidth, frameWidth,
                                           -frameWidth, -frameWidth)
             midRect2 = midRect1.adjusted(midLineWidth, midLineWidth,
                                          -midLineWidth, -midLineWidth)
             innerRect = midRect2.adjusted(frameWidth, frameWidth,
                                           -frameWidth, -frameWidth)
             path1 = QPainterPath()
             path1.moveTo(outerRect.bottomLeft())
             path1.lineTo(outerRect.topLeft())
             path1.lineTo(outerRect.topRight())
             path1.lineTo(midRect1.topRight())
             path1.lineTo(midRect1.topLeft())
             path1.lineTo(midRect1.bottomLeft())
             path2 = QPainterPath()
             path2.moveTo(outerRect.bottomLeft())
             path2.lineTo(outerRect.bottomRight())
             path2.lineTo(outerRect.topRight())
             path2.lineTo(midRect1.topRight())
             path2.lineTo(midRect1.bottomRight())
             path2.lineTo(midRect1.bottomLeft())
             path3 = QPainterPath()
             path3.moveTo(midRect2.bottomLeft())
             path3.lineTo(midRect2.topLeft())
             path3.lineTo(midRect2.topRight())
             path3.lineTo(innerRect.topRight())
             path3.lineTo(innerRect.topLeft())
             path3.lineTo(innerRect.bottomLeft())
             path4 = QPainterPath()
             path4.moveTo(midRect2.bottomLeft())
             path4.lineTo(midRect2.bottomRight())
             path4.lineTo(midRect2.topRight())
             path4.lineTo(innerRect.topRight())
             path4.lineTo(innerRect.bottomRight())
             path4.lineTo(innerRect.bottomLeft())
             path5 = QPainterPath()
             path5.addRect(midRect1)
             path5.addRect(midRect2)
             painter.setPen(Qt.NoPen)
             brush1 = palette.dark().color()
             brush2 = palette.light().color()
             if shadow == QFrame.Raised:
                 brush1, brush2 = brush2, brush1
             painter.setBrush(brush1)
             painter.drawPath(path1)
             painter.drawPath(path4)
             painter.setBrush(brush2)
             painter.drawPath(path2)
             painter.drawPath(path3)
             painter.setBrush(palette.mid())
             painter.drawPath(path5)
         else:
             outerRect = rect.adjusted(0.0, 0.0, -1.0, -1.0)
             innerRect = outerRect.adjusted(
                 frameWidth - 1.0,
                 frameWidth - 1.0,
                 -(frameWidth - 1.0),
                 -(frameWidth - 1.0),
             )
             path1 = QPainterPath()
             path1.moveTo(outerRect.bottomLeft())
             path1.lineTo(outerRect.topLeft())
             path1.lineTo(outerRect.topRight())
             path1.lineTo(innerRect.topRight())
             path1.lineTo(innerRect.topLeft())
             path1.lineTo(innerRect.bottomLeft())
             path2 = QPainterPath()
             path2.moveTo(outerRect.bottomLeft())
             path2.lineTo(outerRect.bottomRight())
             path2.lineTo(outerRect.topRight())
             path2.lineTo(innerRect.topRight())
             path2.lineTo(innerRect.bottomRight())
             path2.lineTo(innerRect.bottomLeft())
             painter.setPen(Qt.NoPen)
             brush1 = palette.dark().color()
             brush2 = palette.light().color()
             if shadow == QFrame.Raised:
                 brush1, brush2 = brush2, brush1
             painter.setBrush(brush1)
             painter.drawPath(path1)
             painter.setBrush(brush2)
             painter.drawPath(path2)
     painter.restore()
Exemplo n.º 11
0
 def shape(self):
     path = QPainterPath()
     path.addRect(self._bRect)
     return path
Exemplo n.º 12
0
class BarPlotItem(GraphicsObject):
    """
    Has a similar method signature as `matplotlib.axes.hist`.
    """

    def __init__(self):
        super().__init__()

        self.qpicture: Optional[QPicture] = None

    def setData(
        self,
        x,
        height,
        width=0.8,
        bottom=None,
        *,
        align="center",
        fillcolor=None,
        edgecolor=None,
    ) -> None:
        # assume vertical
        cbook._check_in_list(["center", "edge"], align=align)

        y = bottom
        if y is None:
            y = 0

        x, height, width, y = np.broadcast_arrays(np.atleast_1d(x), height, width, y)

        if align == "center":
            try:
                left = x - width / 2
            except TypeError as e:
                raise TypeError(
                    f"the dtypes of parameters x ({x.dtype}) "
                    f"and width ({width.dtype}) "
                    f"are incompatible"
                ) from e
        elif align == "edge":
            left = x
        else:
            raise RuntimeError(f"unknown align mode {align}")
        bottom = y

        # prepare to draw
        if edgecolor is None:
            edgecolor = (128, 128, 128)  # getConfigOption("foreground")
        pen = mkPen(edgecolor)

        if fillcolor is None:
            fillcolor = (128, 128, 128)
        brush = mkBrush(fillcolor)

        self.qpicture = QPicture()
        self.path = QPainterPath()

        p = QPainter(self.qpicture)
        p.setPen(pen)
        p.setBrush(brush)

        # draw
        rects = zip(left, bottom, width, height)
        for l, b, w, h in rects:
            rect = QRectF(l, b, w, h)
            p.drawRect(rect)
            self.path.addRect(rect)

        p.end()
        self.prepareGeometryChange()

    def paint(self, p: QPainter, *args) -> None:
        if self.qpicture is None:
            return
        self.qpicture.play(p)

    def boundingRect(self) -> QRectF:
        if self.qpicture is None:
            return QRectF(0.0, 0.0, 0.0, 0.0)
        return QRectF(self.qpicture.boundingRect())