Example #1
0
    def paintEvent(self, e):
        """Paint star on frame."""
        qp = QPainter()
        qp.begin(self)

        self.drawStar(qp)
        qp.end()
Example #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)
Example #3
0
    def paintEvent(self, event):
        """Qt method override to paint a custom image on the Widget."""
        super(FigureCanvas, self).paintEvent(event)
        # Prepare the rect on which the image is going to be painted.
        fw = self.frameWidth()
        rect = QRect(0 + fw, 0 + fw,
                     self.size().width() - 2 * fw,
                     self.size().height() - 2 * fw)

        if self.fig is None or self._blink_flag:
            return

        # Prepare the scaled qpixmap to paint on the widget.
        if (self._qpix_scaled is None
                or self._qpix_scaled.size().width() != rect.width()):
            if self.fmt in ['image/png', 'image/jpeg']:
                self._qpix_scaled = self._qpix_orig.scaledToWidth(
                    rect.width(), mode=Qt.SmoothTransformation)
            elif self.fmt == 'image/svg+xml':
                self._qpix_scaled = QPixmap(svg_to_image(
                    self.fig, rect.size()))

        if self._qpix_scaled is not None:
            # Paint the image on the widget.
            qp = QPainter()
            qp.begin(self)
            qp.drawPixmap(rect, self._qpix_scaled)
            qp.end()
Example #4
0
    def paintEvent(self, event):
        """Qt method override to paint a custom image on the Widget."""
        super(FigureCanvas, self).paintEvent(event)
        # Prepare the rect on which the image is going to be painted :
        fw = self.frameWidth()
        rect = QRect(0 + fw, 0 + fw,
                     self.size().width() - 2 * fw,
                     self.size().height() - 2 * fw)

        if self.fig is None or self._blink_flag:
            return

        # Check/update the qpixmap buffer :
        qpix2paint = None
        for qpix in self._qpix_buffer:
            if qpix.size().width() == rect.width():
                qpix2paint = qpix
                break
        else:
            if self.fmt in ['image/png', 'image/jpeg']:
                qpix2paint = self._qpix_orig.scaledToWidth(
                    rect.width(), mode=Qt.SmoothTransformation)
            elif self.fmt == 'image/svg+xml':
                qpix2paint = QPixmap(svg_to_image(self.fig, rect.size()))
            self._qpix_buffer.append(qpix2paint)

        if qpix2paint is not None:
            # Paint the image on the widget :
            qp = QPainter()
            qp.begin(self)
            qp.drawPixmap(rect, qpix2paint)
            qp.end()
Example #5
0
    def paintEvent(self, event):
        """Qt method override to paint a custom image on the Widget."""
        super(FigureCanvas, self).paintEvent(event)
        # Prepare the rect on which the image is going to be painted :
        fw = self.frameWidth()
        rect = QRect(0 + fw, 0 + fw,
                     self.size().width() - 2 * fw,
                     self.size().height() - 2 * fw)

        if self.fig is None or self._blink_flag:
            return

        # Check/update the qpixmap buffer :
        qpix2paint = None
        for qpix in self._qpix_buffer:
            if qpix.size().width() == rect.width():
                qpix2paint = qpix
                break
        else:
            if self.fmt in ['image/png', 'image/jpeg']:
                qpix2paint = self._qpix_orig.scaledToWidth(
                    rect.width(), mode=Qt.SmoothTransformation)
            elif self.fmt == 'image/svg+xml':
                qpix2paint = QPixmap(svg_to_image(self.fig, rect.size()))
            self._qpix_buffer.append(qpix2paint)

        if qpix2paint is not None:
            # Paint the image on the widget :
            qp = QPainter()
            qp.begin(self)
            qp.drawPixmap(rect, qpix2paint)
            qp.end()
Example #6
0
File: byte.py Project: slaclab/pydm
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())
Example #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())
Example #8
0
    def paintEvent(self, evt):
        # get the widget dimensions
        orig_width = self.width()
        orig_height = self.height()

        # fill perc % of the widget
        perc = 1
        width = int(orig_width * perc)
        height = int(orig_height * perc)

        # get the starting origin
        x_orig = int((orig_width - width) / 2)
        # we want to start at the bottom and draw up.
        y_orig = orig_height - int((orig_height - height) / 2)

        # a running x-position
        running_pos = x_orig

        # calculate to number of bars
        nbars = len(self.counts)

        # calculate the bar widths, this compilcation is
        # necessary because integer trunction severly cripples
        # the layout.
        remainder = width % nbars
        bar_width = [int(width / nbars)] * nbars
        for i in range(remainder):
            bar_width[i] += 1

        paint = QPainter()
        paint.begin(self)

        # determine the scaling factor
        max_val = np.max(self.counts)
        scale = 1. * height / max_val

        # determine if we have a colormap and drop into the appopriate
        # loop.
        if hasattr(self.colormap[0], '__iter__'):
            # assume we have a colormap
            for i in range(len(self.counts)):
                bar_height = self.counts[i]
                r, g, b = self.colormap[i]
                paint.setPen(QColor(r, g, b))
                paint.setBrush(QColor(r, g, b))
                paint.drawRect(running_pos, y_orig, bar_width[i], -bar_height)
                running_pos += bar_width[i]

        else:
            # we have a tuple
            r, g, b = self.colormap
            paint.setPen(QColor(r, g, b))
            paint.setBrush(QColor(r, g, b))
            for i in range(len(self.counts)):
                bar_height = self.counts[i] * scale
                paint.drawRect(running_pos, y_orig, bar_width[i], -bar_height)
                running_pos += bar_width[i]

        paint.end()
Example #9
0
 def paintEvent(self, paintEvent):
     pen1 = QPen()
     pen1.setColor(self.color)
     painter = QPainter(self)
     painter.setPen(pen1)
     painter.begin(self)
     painter.drawRoundedRect(self.boundingRect(), 10, 10)  # 绘制函数
     painter.end()
Example #10
0
 def paintEvent(self, e):
     """Paint triangle on frame."""
     qp = QPainter()
     qp.begin(self)
     self.drawTriangle(qp)
     perc = (self._value - self._min_value) / (self._max_value -
                                               self._min_value)
     self.drawLine(qp, self.rect().width() * perc)
     qp.end()
Example #11
0
 def paintEvent(self, event):
     # This is necessary because by default QWidget ignores stylesheets
     # https://wiki.qt.io/How_to_Change_the_Background_Color_of_QWidget
     opt = QtWidgets.QStyleOption()
     opt.initFrom(self)
     painter = QPainter()
     painter.begin(self)
     self.style().drawPrimitive(QtWidgets.QStyle.PE_Widget, opt, painter,
                                self)
     super().paintEvent(event)
Example #12
0
def render_to_svggen(scene, svg_gen):
    svg_gen.setSize(QSize(scene.width(), scene.height()))
    svg_gen.setViewBox(QRect(0, 0, scene.width(), scene.height()))
    svg_gen.setTitle("Hierarchic Component Drawing")
    svg_gen.setDescription("A Hierarchic Component Drawing created by "
                           "Hildegard.")
    painter = QPainter()
    painter.begin(svg_gen)
    painter.setRenderHint(QPainter.Antialiasing)
    scene.render(painter)
    painter.end()
Example #13
0
 def get(self):
     """Override AbstractDataSetWidget method"""
     value = self.item.get()
     if value is not None:
         color = QColor(value)
         self.picture = QPicture()
         painter = QPainter()
         painter.begin(self.picture)
         painter.fillRect(QRect(0, 0, 60, 20), QBrush(color))
         painter.end()
         self.group.setPicture(self.picture)
Example #14
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()
Example #15
0
        def paintEvent(self, event):
            QPushButton.paintEvent(self, event)

            color = self.color()
            padding = self.padding()

            rect = event.rect()
            painter = QPainter()
            painter.begin(self)
            painter.setBrush(QBrush(color))
            painter.setPen(Qt.NoPen)
            rect.adjust(padding, padding, -1 - padding, -1 - padding)
            painter.drawRect(rect)
            painter.end()
Example #16
0
    def paintEvent(self, event):
        QPushButton.paintEvent(self, event)

        qp = QPainter()
        qp.begin(self)

        font = qp.font()
        font.setPixelSize(self.font().pixelSize() * 0.6)
        font.setWeight(QFont.Normal)
        qp.setFont(font)

        #qp.drawText(event.rect().translated(2, 2), str(self._atomic_number))

        qp.end()
Example #17
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()
Example #18
0
    def paintEvent(self, event):
        scaled_background = self.background_pixmap.scaled(self.rescale_w(self.background_pixmap.width()),
                                                          self.rescale_h(self.background_pixmap.height()),
                                                          Qt.KeepAspectRatio,
                                                          Qt.SmoothTransformation)

        scaled_mantid = self.mantid_pixmap.scaled(self.rescale_w(self.mantid_pixmap.width()),
                                                  self.rescale_h(self.mantid_pixmap.height()),
                                                  Qt.KeepAspectRatio,
                                                  Qt.SmoothTransformation)
        qp = QPainter()
        qp.begin(self)
        qp.drawPixmap(0, 0, scaled_background)
        qp.drawPixmap(self.width() - scaled_mantid.width(), self.height()-scaled_mantid.height(), scaled_mantid)
        qp.end()
Example #19
0
 def drawline(self, p1, p2, angle, tip=False):
     painter = QPainter()
     painter.begin(self)
     painter.setRenderHint(QPainter.Antialiasing)
     painter.setPen(Qt.blue)
     pen = painter.pen()
     pen.setWidth(2)
     painter.setPen(pen)
     painter.setBrush(Qt.blue)
     painter.translate(p1)
     painter.rotate(angle)
     pt = (p2 - p1)
     if tip:
         pt /= (pt.x()**2 + pt.y()**2)**0.5 / 10
     # painter.scale(scale, scale)
     # painter.drawLine(QPoint(0, 0), (p2-p1)*scale)
     painter.drawLine(QPoint(0, 0), pt)
     return painter
Example #20
0
def get_font_array(sz, chars=DEFAULT_CHARS):
    from qtpy.QtGui import QFont, QPainter, QColor

    font = QFont()
    font.setFixedPitch(True)
    font.setPixelSize(int(sz))
    font.setStyleStrategy(QFont.NoAntialias)
    dummy = QImage(10, 10, QImage.Format_ARGB32)
    pnt = QPainter(dummy)
    pnt.setFont(font)
    metric = pnt.fontMetrics()
    rct = metric.boundingRect(chars)
    pnt.end()
    h = rct.height()
    w = rct.width()
    img = QImage(w, h, QImage.Format_ARGB32)
    paint = QPainter()
    paint.begin(img)
    paint.setFont(font)
    paint.setBrush(QColor(255, 255, 255))
    paint.setPen(QColor(255, 255, 255))
    paint.drawRect(0, 0, w + 1, h + 1)
    paint.setPen(QColor(0, 0, 0))
    paint.setBrush(QColor(0, 0, 0))
    paint.drawText(0, paint.fontMetrics().ascent(), chars)
    paint.end()
    try:
        try:
            # PyQt4 (at least until Qt 4.8)
            data = img.bits().asstring(img.numBytes())
        except AttributeError:
            if PYSIDE2:
                # PySide2
                data = bytes(img.bits())
            else:
                # PyQt5
                data = img.bits().asstring(img.byteCount())
    except SystemError:
        # PyQt4 (4.11.3) and PyQt5 (5.3.2) on Python 3
        return
    npy = np.frombuffer(data, np.uint8)
    npy.shape = img.height(), int(img.bytesPerLine() / 4), 4
    return npy[:, :, 0]
Example #21
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()
Example #22
0
    def paintEvent(self, event):
        painter = QPainter()
        painter.begin(self.pixmap)
        painter.drawPixmap(0, 0, self.pixmap)

        pen1 = QPen(QColor(self.c1), 1)
        pen2 = QPen(QColor(self.c2), 2)

        painter.setPen(pen1)
        y = self.size - 1
        for x in range(self.size):
            painter.drawLine(0, x, y, x)
            y = y - 1

        painter.setPen(pen2)
        y = 1
        for x in range(self.size, 0, -1):
            painter.drawLine(y, x, self.size, x)
            y = y + 1

        painter.end()
Example #23
0
    def paintEvent(self, event):
        qp = QPainter()
        qp.begin(self)

        w = self.width()
        h = self.height()
        # paint background
        qp.setBrush(self.color_background)
        qp.drawRect(0, 0, w, h)

        # paint angle bar
        qp.setBrush(self.color_anglebar)
        angle_zero_pos = w // 2
        angle_bar_width = int(angle_zero_pos * self.rotate_angle / 360)
        if self.rotate_clockwise:
            qp.drawRect(angle_zero_pos, 0, angle_bar_width, h)
        else:
            qp.drawRect(angle_zero_pos - angle_bar_width, 0, angle_bar_width,
                        h)

        qp.end()
Example #24
0
    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.

        At PyDMSymbolEditor this method handles the image preview.

        Parameters
        ----------
        event : QPaintEvent
        """
        if not self.preview:
            return
        size = QSize(140, 140)
        _painter = QPainter()
        _painter.begin(self)
        opt = QStyleOption()
        opt.initFrom(self)
        self.style().drawPrimitive(QStyle.PE_Widget, opt, _painter, self)
        image_to_draw = self.preview_file
        if isinstance(image_to_draw, QPixmap):
            w = float(image_to_draw.width())
            h = float(image_to_draw.height())
            sf = min(size.width() / w, size.height() / h)
            scale = (sf, sf)
            _painter.scale(scale[0], scale[1])
            _painter.drawPixmap(335 / sf, 120 / sf, image_to_draw)
        elif isinstance(image_to_draw, QSvgRenderer):
            draw_size = QSizeF(image_to_draw.defaultSize())
            draw_size.scale(QSizeF(size), Qt.KeepAspectRatio)
            image_to_draw.render(
                _painter,
                QRectF(335, 120, draw_size.width(), draw_size.height()))
        _painter.end()
        self.preview = False
Example #25
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()
Example #26
0
class PyDMSymbol(QWidget, PyDMWidget):
    """
    PyDMSymbol will render an image (symbol) for each value of a channel.

    Parameters
    ----------
    parent : QWidget
        The parent widget for the Label
    init_channel : str, optional
        The channel to be used by the widget.
    """
    def __init__(self, parent=None, init_channel=None):
        QWidget.__init__(self, parent)
        PyDMWidget.__init__(self, init_channel=init_channel)
        if 'Index' not in PyDMSymbol.RULE_PROPERTIES:
            PyDMSymbol.RULE_PROPERTIES = PyDMWidget.RULE_PROPERTIES.copy()
            PyDMSymbol.RULE_PROPERTIES.update(
                {'Index': ['set_current_key', object]})
        self.app = QApplication.instance()
        self._current_key = 0
        self._state_images_string = ""
        self._state_images = {
        }  # Keyed on state values (ints), values are (filename, qpixmap or qsvgrenderer) tuples.
        self._aspect_ratio_mode = Qt.KeepAspectRatio
        self._sizeHint = self.minimumSizeHint()
        self._painter = QPainter()

    def init_for_designer(self):
        """
        Method called after the constructor to tweak configurations for
        when using the widget with the Qt Designer
        """
        self.value = 0
        self._current_key = 0

    def set_current_key(self, current_key):
        """
        Change the image being displayed for the one given by `current_key`.

        Parameters
        ----------
        current_key : object
            The current_key parameter can be of any type as long as it matches
            the type used as key for the imageFiles dictionary.

        """
        if self._current_key != current_key:
            self._current_key = current_key
            self.update()

    @Property(str)
    def imageFiles(self):
        """
        JSON-formatted dictionary keyed on states (integers), with filenames
        of the image file to display for the state.

        Returns
        -------
        str
        """
        if not self._state_images:
            return self._state_images_string
        return json.dumps({
            str(state): val[0]
            for (state, val) in self._state_images.items()
        })

    @imageFiles.setter
    def imageFiles(self, new_files):
        """
        JSON-formatted dictionary keyed on states (integers), with filenames
        of the image file to display for the state.

        Parameters
        ----------
        new_files : str
        """
        self._state_images_string = str(new_files)
        try:
            new_file_dict = json.loads(self._state_images_string)
        except Exception:
            self._state_images = {}
            return
        self._sizeHint = QSize(0, 0)
        parent_display = self.find_parent_display()
        base_path = os.path.dirname(parent_display.loaded_file())
        for (state, filename) in new_file_dict.items():
            file_path = find_file(filename, base_path=base_path)
            # First, lets try SVG.  We have to try SVG first, otherwise
            # QPixmap will happily load the SVG and turn it into a raster image.
            # Really annoying: We have to try to load the file as SVG,
            # and we expect it will fail often (because many images aren't SVG).
            # Qt prints a warning message to stdout any time SVG loading fails.
            # So we have to temporarily silence Qt warning messages here.
            qInstallMessageHandler(self.qt_message_handler)
            svg = QSvgRenderer()
            svg.repaintNeeded.connect(self.update)
            if svg.load(file_path):
                self._state_images[int(state)] = (filename, svg)
                self._sizeHint = self._sizeHint.expandedTo(svg.defaultSize())
                qInstallMessageHandler(None)
                continue
            qInstallMessageHandler(None)
            # SVG didn't work, lets try QPixmap
            image = QPixmap(file_path)
            if not image.isNull():
                self._state_images[int(state)] = (filename, image)
                self._sizeHint = self._sizeHint.expandedTo(image.size())
                continue
            # If we get this far, the file specified could not be loaded at all.
            logger.error("Could not load image: {}".format(filename))
            self._state_images[int(state)] = (filename, None)

    @Property(Qt.AspectRatioMode)
    def aspectRatioMode(self):
        """
        Which aspect ratio mode to use.

        Returns
        -------
        Qt.AspectRatioMode
        """
        return self._aspect_ratio_mode

    @aspectRatioMode.setter
    def aspectRatioMode(self, new_mode):
        """
        Which aspect ratio mode to use.

        Parameters
        -----------
        new_mode : Qt.AspectRatioMode
        """
        if new_mode != self._aspect_ratio_mode:
            self._aspect_ratio_mode = new_mode
            self.update()

    def connection_changed(self, connected):
        """
        Callback invoked when the connection state of the Channel is changed.
        This callback acts on the connection state to enable/disable the widget
        and also trigger the change on alarm severity to ALARM_DISCONNECTED.

        Parameters
        ----------
        connected : int
            When this value is 0 the channel is disconnected, 1 otherwise.
        """
        super(PyDMSymbol, self).connection_changed(connected)
        self.update()

    def value_changed(self, new_val):
        """
        Callback invoked when the Channel value is changed.

        Parameters
        ----------
        new_val : int
            The new value from the channel.
        """
        super(PyDMSymbol, self).value_changed(new_val)
        self._current_key = new_val
        self.update()

    def sizeHint(self):
        """
        This property holds the recommended size for the widget.

        Returns
        -------
        QSize
        """
        return self._sizeHint

    def minimumSizeHint(self):
        """
        This property holds the recommended minimum size for the widget.

        Returns
        -------
        QSize
        """
        return QSize(
            10, 10
        )  # This is totally arbitrary, I just want *some* visible nonzero size

    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.

        At PyDMSymbol this method handles the alarm painting with parameters
        from the stylesheet and draws the proper image.

        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)
        if self._current_key is None:
            self._painter.end()
            return
        image_to_draw = self._state_images.get(self._current_key,
                                               (None, None))[1]
        if image_to_draw is None:
            self._painter.end()
            return
        if isinstance(image_to_draw, QPixmap):
            w = float(image_to_draw.width())
            h = float(image_to_draw.height())
            if self._aspect_ratio_mode == Qt.IgnoreAspectRatio:
                scale = (event.rect().width() / w, event.rect().height() / h)
            elif self._aspect_ratio_mode == Qt.KeepAspectRatio:
                sf = min(event.rect().width() / w, event.rect().height() / h)
                scale = (sf, sf)
            elif self._aspect_ratio_mode == Qt.KeepAspectRatioByExpanding:
                sf = max(event.rect().width() / w, event.rect().height() / h)
                scale = (sf, sf)
            self._painter.scale(scale[0], scale[1])
            self._painter.drawPixmap(event.rect().x(),
                                     event.rect().y(), image_to_draw)
        elif isinstance(image_to_draw, QSvgRenderer):
            draw_size = QSizeF(image_to_draw.defaultSize())
            draw_size.scale(QSizeF(event.rect().size()),
                            self._aspect_ratio_mode)
            image_to_draw.render(
                self._painter,
                QRectF(0.0, 0.0, draw_size.width(), draw_size.height()))
        self._painter.end()

    def qt_message_handler(self, msg_type, *args):
        # Intentionally suppress all qt messages.  Make sure not to leave this handler installed.
        pass
Example #27
0
    def paintEvent(self, evt):
        # get the widget dimensions
        orig_width = self.width()
        orig_height = self.height()

        # fill perc % of the widget
        perc = 1
        width = int(orig_width * perc)
        height = int(orig_height * perc)

        # get the starting origin
        x_orig = int((orig_width - width) / 2)
        # we want to start at the bottom and draw up.
        y_orig = orig_height - int((orig_height - height) / 2)

        # a running x-position
        running_pos = x_orig

        # calculate to number of bars
        nbars = len(self.counts)

        # calculate the bar widths, this compilcation is
        # necessary because integer trunction severly cripples
        # the layout.
        remainder = width % nbars
        bar_width = [int(width / nbars)] * nbars
        for i in range(remainder):
            bar_width[i] += 1

        paint = QPainter()
        paint.begin(self)

        # determine the scaling factor
        max_val = np.max(self.counts)
        scale = 1. * height / max_val

        # determine if we have a colormap and drop into the appopriate
        # loop.
        if hasattr(self.colormap[0], '__iter__'):
            # assume we have a colormap
            for i in range(len(self.counts)):
                bar_height = self.counts[i]
                r, g, b = self.colormap[i]
                paint.setPen(QColor(r, g, b))
                paint.setBrush(QColor(r, g, b))
                paint.drawRect(running_pos, y_orig, bar_width[i],
                               -bar_height)
                running_pos += bar_width[i]

        else:
            # we have a tuple
            r, g, b = self.colormap
            paint.setPen(QColor(r, g, b))
            paint.setBrush(QColor(r, g, b))
            for i in range(len(self.counts)):
                bar_height = self.counts[i] * scale
                paint.drawRect(running_pos, y_orig, bar_width[i],
                               -bar_height)
                running_pos += bar_width[i]

        paint.end()
Example #28
0
class QScale(QFrame):
    """
    A bar-shaped indicator for scalar value.
    Configurable features include indicator type (bar/pointer), scale tick
    marks and orientation (horizontal/vertical).

    Parameters
    ----------
    parent : QWidget
        The parent widget for the Scale
    """
    def __init__(self, parent=None):
        super(QScale, self).__init__(parent)
        self._value = 1
        self._lower_limit = -5
        self._upper_limit = 5
        self.position = None  # unit: pixel

        self._bg_color = QColor('darkgray')
        self._bg_size_rate = 0.8  # from 0 to 1

        self._indicator_color = QColor('black')
        self._pointer_width_rate = 0.05
        self._barIndicator = False

        self._num_divisions = 10
        self._show_ticks = True
        self._tick_pen = QPen()
        self._tick_color = QColor('black')
        self._tick_width = 0
        self._tick_size_rate = 0.1  # from 0 to 1
        self._painter = QPainter()

        self._painter_rotation = None
        self._painter_translation_y = None
        self._painter_translation_x = None
        self._painter_scale_x = None
        self._flip_traslation_y = None
        self._flip_scale_y = None

        self._widget_width = self.width()
        self._widget_height = self.height()

        self._orientation = Qt.Horizontal
        self._inverted_appearance = False
        self._flip_scale = False
        self._scale_height = 35
        self._origin_at_zero = False
        self._origin_position = 0

        self.set_position()

    def adjust_transformation(self):
        """
        This method sets parameters for the widget transformations (needed to for
        orientation, flipping and appearance inversion).
        """
        self.setMaximumSize(QWIDGETSIZE_MAX,
                            QWIDGETSIZE_MAX)  # Unset fixed size
        if self._orientation == Qt.Horizontal:
            self._widget_width = self.width()
            self._widget_height = self.height()
            self._painter_translation_y = 0
            self._painter_rotation = 0
            self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
            self.setFixedHeight(self._scale_height)
        elif self._orientation == Qt.Vertical:
            # Invert dimensions for paintEvent()
            self._widget_width = self.height()
            self._widget_height = self.width()
            self._painter_translation_y = self._widget_width
            self._painter_rotation = -90
            self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
            self.setFixedWidth(self._scale_height)

        if self._inverted_appearance:
            self._painter_translation_x = self._widget_width
            self._painter_scale_x = -1
        else:
            self._painter_translation_x = 0
            self._painter_scale_x = 1

        if self._flip_scale:
            self._flip_traslation_y = self._widget_height
            self._flip_scale_y = -1
        else:
            self._flip_traslation_y = 0
            self._flip_scale_y = 1

    def set_tick_pen(self):
        """
        Define pen style for drawing scale tick marks.
        """
        self._tick_pen.setColor(self._tick_color)
        self._tick_pen.setWidth(self._tick_width)

    def draw_ticks(self):
        """
        Draw tick marks on the scale.
        """
        if not self._show_ticks:
            return
        self.set_tick_pen()
        self._painter.setPen(self._tick_pen)
        division_size = self._widget_width / self._num_divisions
        tick_y0 = self._widget_height
        tick_yf = (1 - self._tick_size_rate) * self._widget_height
        for i in range(self._num_divisions + 1):
            x = i * division_size
            self._painter.drawLine(x, tick_y0, x, tick_yf)  # x1, y1, x2, y2

    def draw_bar(self):
        """
        Draw a bar as indicator of current value.
        """
        self.set_origin()
        self.set_position()

        if self.position < 0 or self.position > self._widget_width:
            return
        self._painter.setPen(Qt.transparent)
        self._painter.setBrush(self._indicator_color)
        bar_width = self.position - self._origin_position
        bar_height = self._bg_size_rate * self._widget_height
        self._painter.drawRect(self._origin_position, 0, bar_width, bar_height)

    def draw_pointer(self):
        """
        Draw a pointer as indicator of current value.
        """
        self.set_position()
        if self.position < 0 or self.position > self._widget_width:
            return
        self._painter.setPen(Qt.transparent)
        self._painter.setBrush(self._indicator_color)
        pointer_width = self._pointer_width_rate * self._widget_width
        pointer_height = self._bg_size_rate * self._widget_height
        points = [
            QPoint(self.position, 0),
            QPoint(self.position + 0.5 * pointer_width, 0.5 * pointer_height),
            QPoint(self.position, pointer_height),
            QPoint(self.position - 0.5 * pointer_width, 0.5 * pointer_height)
        ]
        self._painter.drawPolygon(QPolygon(points))

    def draw_indicator(self):
        """
        Draw the selected indicator for current value.
        """
        if self._barIndicator:
            self.draw_bar()
        else:
            self.draw_pointer()

    def draw_background(self):
        """
        Draw the background of the scale.
        """
        self._painter.setPen(Qt.transparent)
        self._painter.setBrush(self._bg_color)
        bg_width = self._widget_width
        bg_height = self._bg_size_rate * self._widget_height
        self._painter.drawRect(0, 0, bg_width, bg_height)

    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.adjust_transformation()
        self._painter.begin(self)
        self._painter.translate(
            0, self._painter_translation_y)  # Draw vertically if needed
        self._painter.rotate(self._painter_rotation)
        self._painter.translate(self._painter_translation_x,
                                0)  # Invert appearance if needed
        self._painter.scale(self._painter_scale_x, 1)

        self._painter.translate(
            0, self._flip_traslation_y)  # Invert scale if needed
        self._painter.scale(1, self._flip_scale_y)

        self._painter.setRenderHint(QPainter.Antialiasing)

        self.draw_background()
        self.draw_ticks()
        self.draw_indicator()

        self._painter.end()

    def calculate_position_for_value(self, value):
        """
        Calculate the position (pixel) in which the pointer should be drawn for a given value.
        """
        if value is None or value < self._lower_limit or value > self._upper_limit or \
           self._upper_limit - self._lower_limit == 0:
            proportion = -1  # Invalid
        else:
            proportion = (value - self._lower_limit) / (self._upper_limit -
                                                        self._lower_limit)

        position = int(proportion * self._widget_width)
        return position

    def set_origin(self):
        """
        Set the position (pixel) in which the origin should be drawn.
        """
        if self._origin_at_zero:
            self._origin_position = self.calculate_position_for_value(0)
        else:
            self._origin_position = 0

    def set_position(self):
        """
        Set the position (pixel) in which the pointer should be drawn.
        """
        self.position = self.calculate_position_for_value(self._value)

    def update_indicator(self):
        """
        Update the position and the drawing of indicator.
        """
        self.set_position()
        self.repaint()

    def set_value(self, value):
        """
        Set a new current value for the indicator.
        """
        self._value = value
        self.update_indicator()

    def set_upper_limit(self, new_limit):
        """
        Set the scale upper limit.

        Parameters
        ----------
        new_limit : float
            The upper limit of the scale.
        """
        self._upper_limit = new_limit

    def set_lower_limit(self, new_limit):
        """
        Set the scale lower limit.

        Parameters
        ----------
        new_limit : float
            The lower limit of the scale.
        """
        self._lower_limit = new_limit

    def get_show_ticks(self):
        return self._show_ticks

    def set_show_ticks(self, checked):
        if self._show_ticks != bool(checked):
            self._show_ticks = checked
            self.repaint()

    def get_orientation(self):
        return self._orientation

    def set_orientation(self, orientation):
        self._orientation = orientation
        self.adjust_transformation()
        self.repaint()

    def get_flip_scale(self):
        return self._flip_scale

    def set_flip_scale(self, checked):
        self._flip_scale = bool(checked)
        self.adjust_transformation()
        self.repaint()

    def get_inverted_appearance(self):
        return self._inverted_appearance

    def set_inverted_appearance(self, inverted):
        self._inverted_appearance = inverted
        self.adjust_transformation()
        self.repaint()

    def get_bar_indicator(self):
        return self._barIndicator

    def set_bar_indicator(self, checked):
        if self._barIndicator != bool(checked):
            self._barIndicator = checked
            self.repaint()

    def get_background_color(self):
        return self._bg_color

    def set_background_color(self, color):
        self._bg_color = color
        self.repaint()

    def get_indicator_color(self):
        return self._indicator_color

    def set_indicator_color(self, color):
        self._indicator_color = color
        self.repaint()

    def get_tick_color(self):
        return self._tick_color

    def set_tick_color(self, color):
        self._tick_color = color
        self.repaint()

    def get_background_size_rate(self):
        return self._bg_size_rate

    def set_background_size_rate(self, rate):
        if rate >= 0 and rate <= 1 and self._bg_size_rate != rate:
            self._bg_size_rate = rate
            self.repaint()

    def get_tick_size_rate(self):
        return self._tick_size_rate

    def set_tick_size_rate(self, rate):
        if rate >= 0 and rate <= 1 and self._tick_size_rate != rate:
            self._tick_size_rate = rate
            self.repaint()

    def get_num_divisions(self):
        return self._num_divisions

    def set_num_divisions(self, divisions):
        if isinstance(
                divisions,
                int) and divisions > 0 and self._num_divisions != divisions:
            self._num_divisions = divisions
            self.repaint()

    def get_scale_height(self):
        return self._scale_height

    def set_scale_height(self, value):
        self._scale_height = int(value)
        self.adjust_transformation()
        self.repaint()

    def get_origin_at_zero(self):
        return self._origin_at_zero

    def set_origin_at_zero(self, checked):
        if self._origin_at_zero != bool(checked):
            self._origin_at_zero = checked
            self.repaint()
Example #29
0
class BarIndicatorBase(QWidget):
    """docstring for BarIndicator"""
    def __init__(self, parent=None):
        super(BarIndicatorBase, self).__init__(parent)

        self._value = 100
        self._minimum = 0.0
        self._maximum = 100.0
        self._value_at_100_percent = 100.0
        self._format = '$value %'

        self._text_color = QColor(0, 0, 0)
        self._border_color = Qt.gray
        self._border_radius = 2
        self._border_width = 1

        self._painter = QPainter()

        self._orientation = Qt.Horizontal
        self._bar_width = self.height()
        self._bar_length = self.width()

        self._painter_rotation = None
        self._painter_translation_y = None
        self._painter_translation_x = None
        self._painter_scale_x = None
        self._flip_translation_y = None
        self._flip_scale_y = None

        self._inverted_appearance = False
        self._flip_scale = False
        self._origin_at_zero = False
        self._origin_position = 0

        self.barGradient = [
            '0.0, 0, 255, 0',
            '0.8, 255, 255, 0',
            '1.0, 255, 0, 0',
        ]

    def adjustTransformation(self):
        """This method sets parameters for the widget transformations (needed
        for orientation, flipping and appearance inversion).
        """
        if self._orientation == Qt.Horizontal:
            self._bar_width = self.height()
            self._bar_length = self.width()
            self._painter_translation_y = 0
            self._painter_rotation = 0

        elif self._orientation == Qt.Vertical:
            # Invert dimensions for paintEvent()
            self._bar_width = self.width()
            self._bar_length = self.height()
            self._painter_translation_y = self._bar_length
            self._painter_rotation = -90

        if self._inverted_appearance:
            self._painter_translation_x = self._bar_width
            self._painter_scale_x = -1
        else:
            self._painter_translation_x = 0
            self._painter_scale_x = 1

        if self._flip_scale:
            self._flip_translation_y = self._bar_length
            self._flip_scale_y = -1
        else:
            self._flip_translation_y = 0
            self._flip_scale_y = 1

    def paintEvent(self, event):

        self.adjustTransformation()
        self._painter.begin(self)
        self._painter.translate(
            0, self._painter_translation_y)  # Draw vertically if needed
        self._painter.rotate(self._painter_rotation)
        self._painter.translate(self._painter_translation_x,
                                0)  # Invert appearance if needed
        self._painter.scale(self._painter_scale_x, 1)

        self._painter.translate(
            0, self._flip_translation_y)  # Invert scale if needed
        self._painter.scale(1, self._flip_scale_y)

        self._painter.setRenderHint(QPainter.Antialiasing)

        self.drawBackground()

        if self._border_width > 0:
            self.drawBorder()

        if self._format != '':
            self.drawText()

        self._painter.end()

    def drawBackground(self):

        bw = float(self._border_width)
        br = self._border_radius

        w = self.sliderPositionFromValue(self.minimum, self.maximum,
                                         self._value, self._bar_length)
        h = self._bar_width
        rect = QRectF(bw / 2, bw / 2, w - bw, h - bw)

        p = self._painter

        # draw the load meter value bar
        p.setPen(Qt.transparent)
        p.setBrush(self.gradient)

        p.drawRoundedRect(rect, br, br)

    def drawBorder(self):
        p = self._painter

        bw = float(self._border_width)
        br = self._border_radius

        p.setBrush(Qt.transparent)
        border_pen = QPen()
        border_pen.setWidth(int(bw))
        border_pen.setColor(self._border_color)
        p.setPen(border_pen)

        # deal with orientation
        if self._orientation == Qt.Horizontal:
            rect = QRectF(bw / 2, bw / 2,
                          self.width() - bw,
                          self.height() - bw)
        else:
            # must be Qt.Vertical
            rect = QRectF(bw / 2, bw / 2,
                          self.height() - bw,
                          self.width() - bw)

        p.drawRoundedRect(rect, br, br)

    def drawText(self):
        p = self._painter

        # draw the load percentage text
        p.setPen(self._text_color)
        if self.orientation == Qt.Vertical:
            p.drawText(0, 0, self.height(), self.width(), Qt.AlignCenter,
                       self.text())
        else:
            p.drawText(0, 0, self.width(), self.height(), Qt.AlignCenter,
                       self.text())

    def minimumSizeHint(self):
        return QSize(30, 30)

    def resizeEvent(self, event):
        if self._orientation == Qt.Horizontal:
            self.gradient.setStart(0, 0)
            self.gradient.setFinalStop(self.width(), 0)
        else:
            self.gradient.setStart(0, 0)
            self.gradient.setFinalStop(self.height(), 0)

    def sliderPositionFromValue(self, min, max, val, span, upsideDown=False):
        return span * (val / max - min)

    @Slot(int)
    @Slot(float)
    @Slot(object)
    def setValue(self, val):
        self.value = val

    @Slot(int)
    @Slot(float)
    def setMinimum(self, min):
        self.minimum = min

    @Slot(int)
    @Slot(float)
    def setMaximum(self, max):
        self.maximum = max

    @Property(float)
    def value(self):
        return self._value

    @value.setter
    def value(self, value):
        if value >= self.minimum and value <= self.maximum:
            self._value = value
            self.update()

    @Property(float)
    def minimum(self):
        return self._minimum

    @minimum.setter
    def minimum(self, min_val):
        self._minimum = min_val
        self.update()

    @Property(float)
    def maximum(self):
        return self._maximum

    @maximum.setter
    def maximum(self, max_val):
        self._maximum = max_val
        self.update()

    @Property(str)
    def format(self):
        return self._format

    @format.setter
    def format(self, fmt):
        self._format = fmt
        self.update()

    @Property(Qt.Orientation)
    def orientation(self):
        return self._orientation

    @orientation.setter
    def orientation(self, orient):
        if orient == self._orientation:
            return

        self._orientation = orient
        self.adjustTransformation()
        self.update()

    @Property(float)
    def valueAt100Percent(self):
        return self._value_at_100_percent

    @valueAt100Percent.setter
    def valueAt100Percent(self, value_at):
        self._value_at_100_percent = value_at
        self.update()

    def text(self):
        values = {
            'v': self._value,
            'p': int((self._value * 100 / self._value_at_100_percent) + .5)
        }
        try:
            return Template(self._format).substitute(value=values['p'])
        except:
            return self.format

    # ToDo: Make this a QLinearGradient
    @Property('QStringList')
    def barGradient(self):
        return self._gradient_def

    @barGradient.setter
    def barGradient(self, gradient):
        grad = QLinearGradient(0, 0, 0, 0)
        try:
            for stop in gradient:
                pos, r, g, b = stop.split(',')[:4]
                color = QColor(int(r), int(g), int(b))
                grad.setColorAt(float(pos), color)
        except:
            LOG.exception('Invalid gradient.')
            return

        self._gradient_def = gradient
        self.gradient = grad
        self.resizeEvent(None)
        self.update()

    # text color
    @Property(QColor)
    def textColor(self):
        return self._text_color

    @textColor.setter
    def textColor(self, text_color):
        self._text_color = text_color
        self.update()

    # border color
    @Property(QColor)
    def borderColor(self):
        return self._border_color

    @borderColor.setter
    def borderColor(self, border_color):
        self._border_color = border_color
        self.update()

    # border radius
    @Property(int)
    def borderRadius(self):
        return self._border_radius

    @borderRadius.setter
    def borderRadius(self, border_radius):
        self._border_radius = border_radius
        self.update()

    # border width
    @Property(int)
    def borderWidth(self):
        return self._border_width

    @borderWidth.setter
    def borderWidth(self, border_width):
        self._border_width = border_width
        self.update()
Example #30
0
 def paintEvent(self, e):
     qp = QPainter()
     qp.begin(self)
     self.drawWidget(qp)
     qp.end()
Example #31
0
class PyDMSymbol(QWidget, PyDMWidget):
    """
    PyDMSymbol will render an image (symbol) for each value of a channel.

    Parameters
    ----------
    parent : QWidget
        The parent widget for the Label
    init_channel : str, optional
        The channel to be used by the widget.
    """
    def __init__(self, parent=None, init_channel=None):
        QWidget.__init__(self, parent)
        PyDMWidget.__init__(self, init_channel=init_channel)
        if 'Index' not in PyDMSymbol.RULE_PROPERTIES:
            PyDMSymbol.RULE_PROPERTIES = PyDMWidget.RULE_PROPERTIES.copy()
            PyDMSymbol.RULE_PROPERTIES.update(
                {'Index': ['set_current_key', object]})
        self.app = QApplication.instance()
        self._current_key = 0
        self._state_images_string = ""
        self._state_images = {}  # Keyed on state values (ints), values are (filename, qpixmap or qsvgrenderer) tuples.
        self._aspect_ratio_mode = Qt.KeepAspectRatio
        self._sizeHint = self.minimumSizeHint()
        self._painter = QPainter()

    def init_for_designer(self):
        """
        Method called after the constructor to tweak configurations for
        when using the widget with the Qt Designer
        """
        self.value = 0
        self._current_key = 0

    def set_current_key(self, current_key):
        """
        Change the image being displayed for the one given by `current_key`.

        Parameters
        ----------
        current_key : object
            The current_key parameter can be of any type as long as it matches
            the type used as key for the imageFiles dictionary.

        """
        if self._current_key != current_key:
            self._current_key = current_key
            self.update()

    @Property(str)
    def imageFiles(self):
        """
        JSON-formatted dictionary keyed on states (integers), with filenames
        of the image file to display for the state.

        Returns
        -------
        str
        """
        if not self._state_images:
            return self._state_images_string
        return json.dumps({str(state): val[0] for (state, val) in self._state_images.items()})

    @imageFiles.setter
    def imageFiles(self, new_files):
        """
        JSON-formatted dictionary keyed on states (integers), with filenames
        of the image file to display for the state.

        Parameters
        ----------
        new_files : str
        """
        self._state_images_string = str(new_files)
        try:
            new_file_dict = json.loads(self._state_images_string)
        except Exception:
            self._state_images = {}
            return
        self._sizeHint = QSize(0, 0)
        for (state, filename) in new_file_dict.items():
            if is_pydm_app():
                try:
                    file_path = self.app.get_path(filename)
                except Exception as e:
                    logger.exception("Couldn't get file with path %s", filename)
                    file_path = filename
            else:
                file_path = filename
            # First, lets try SVG.  We have to try SVG first, otherwise
            # QPixmap will happily load the SVG and turn it into a raster image.
            # Really annoying: We have to try to load the file as SVG,
            # and we expect it will fail often (because many images aren't SVG).
            # Qt prints a warning message to stdout any time SVG loading fails.
            # So we have to temporarily silence Qt warning messages here.
            qInstallMessageHandler(self.qt_message_handler)
            svg = QSvgRenderer()
            svg.repaintNeeded.connect(self.update)
            if svg.load(file_path):
                self._state_images[int(state)] = (filename, svg)
                self._sizeHint = self._sizeHint.expandedTo(svg.defaultSize())
                qInstallMessageHandler(None)
                continue
            qInstallMessageHandler(None)
            # SVG didn't work, lets try QPixmap
            image = QPixmap(file_path)
            if not image.isNull():
                self._state_images[int(state)] = (filename, image)
                self._sizeHint = self._sizeHint.expandedTo(image.size())
                continue
            # If we get this far, the file specified could not be loaded at all.
            logger.error("Could not load image: {}".format(filename))
            self._state_images[int(state)] = (filename, None)

    @Property(Qt.AspectRatioMode)
    def aspectRatioMode(self):
        """
        Which aspect ratio mode to use.

        Returns
        -------
        Qt.AspectRatioMode
        """
        return self._aspect_ratio_mode

    @aspectRatioMode.setter
    def aspectRatioMode(self, new_mode):
        """
        Which aspect ratio mode to use.

        Parameters
        -----------
        new_mode : Qt.AspectRatioMode
        """
        if new_mode != self._aspect_ratio_mode:
            self._aspect_ratio_mode = new_mode
            self.update()

    def connection_changed(self, connected):
        """
        Callback invoked when the connection state of the Channel is changed.
        This callback acts on the connection state to enable/disable the widget
        and also trigger the change on alarm severity to ALARM_DISCONNECTED.

        Parameters
        ----------
        connected : int
            When this value is 0 the channel is disconnected, 1 otherwise.
        """
        super(PyDMSymbol, self).connection_changed(connected)
        self.update()

    def value_changed(self, new_val):
        """
        Callback invoked when the Channel value is changed.

        Parameters
        ----------
        new_val : int
            The new value from the channel.
        """
        super(PyDMSymbol, self).value_changed(new_val)
        self._current_key = new_val
        self.update()

    def sizeHint(self):
        """
        This property holds the recommended size for the widget.

        Returns
        -------
        QSize
        """
        return self._sizeHint

    def minimumSizeHint(self):
        """
        This property holds the recommended minimum size for the widget.

        Returns
        -------
        QSize
        """
        return QSize(10, 10)  # This is totally arbitrary, I just want *some* visible nonzero size

    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.

        At PyDMSymbol this method handles the alarm painting with parameters
        from the stylesheet and draws the proper image.

        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)
        if self._current_key is None:
            self._painter.end()
            return
        image_to_draw = self._state_images.get(self._current_key, (None, None))[1]
        if image_to_draw is None:
            self._painter.end()
            return
        if isinstance(image_to_draw, QPixmap):
            w = float(image_to_draw.width())
            h = float(image_to_draw.height())
            if self._aspect_ratio_mode == Qt.IgnoreAspectRatio:
                scale = (event.rect().width() / w, event.rect().height() / h)
            elif self._aspect_ratio_mode == Qt.KeepAspectRatio:
                sf = min(event.rect().width() / w, event.rect().height() / h)
                scale = (sf, sf)
            elif self._aspect_ratio_mode == Qt.KeepAspectRatioByExpanding:
                sf = max(event.rect().width() / w, event.rect().height() / h)
                scale = (sf, sf)
            self._painter.scale(scale[0], scale[1])
            self._painter.drawPixmap(event.rect().x(), event.rect().y(), image_to_draw)
        elif isinstance(image_to_draw, QSvgRenderer):
            draw_size = QSizeF(image_to_draw.defaultSize())
            draw_size.scale(QSizeF(event.rect().size()), self._aspect_ratio_mode)
            image_to_draw.render(self._painter, QRectF(0.0, 0.0, draw_size.width(), draw_size.height()))
        self._painter.end()

    def qt_message_handler(self, msg_type, *args):
        # Intentionally suppress all qt messages.  Make sure not to leave this handler installed.
        pass
Example #32
0
class QScale(QFrame):
    """
    A bar-shaped indicator for scalar value.
    Configurable features include indicator type (bar/pointer), scale tick
    marks and orientation (horizontal/vertical).

    Parameters
    ----------
    parent : QWidget
        The parent widget for the Scale
    """
    def __init__(self, parent=None):
        super(QScale, self).__init__(parent)
        self._value = 1
        self._lower_limit = -5
        self._upper_limit = 5
        self.position = None  # unit: pixel

        self._bg_color = QColor('darkgray')
        self._bg_size_rate = 0.8    # from 0 to 1

        self._indicator_color = QColor('black')
        self._pointer_width_rate = 0.05
        self._barIndicator = False

        self._num_divisions = 10
        self._show_ticks = True
        self._tick_pen = QPen()
        self._tick_color = QColor('black')
        self._tick_width = 0
        self._tick_size_rate = 0.1  # from 0 to 1
        self._painter = QPainter()

        self._painter_rotation = None
        self._painter_translation_y = None
        self._painter_translation_x = None
        self._painter_scale_x = None
        self._flip_traslation_y = None
        self._flip_scale_y = None

        self._widget_width = self.width()
        self._widget_height = self.height()

        self._orientation = Qt.Horizontal
        self._inverted_appearance = False
        self._flip_scale = False
        self._scale_height = 35
        self._origin_at_zero = False
        self._origin_position = 0

        self.set_position()

    def adjust_transformation(self):
        """
        This method sets parameters for the widget transformations (needed to for
        orientation, flipping and appearance inversion).
        """
        self.setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)  # Unset fixed size
        if self._orientation == Qt.Horizontal:
            self._widget_width = self.width()
            self._widget_height = self.height()
            self._painter_translation_y = 0
            self._painter_rotation = 0
            self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
            self.setFixedHeight(self._scale_height)
        elif self._orientation == Qt.Vertical:
            # Invert dimensions for paintEvent()
            self._widget_width = self.height()
            self._widget_height = self.width()
            self._painter_translation_y = self._widget_width
            self._painter_rotation = -90
            self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
            self.setFixedWidth(self._scale_height)

        if self._inverted_appearance:
            self._painter_translation_x = self._widget_width
            self._painter_scale_x = -1
        else:
            self._painter_translation_x = 0
            self._painter_scale_x = 1

        if self._flip_scale:
            self._flip_traslation_y = self._widget_height
            self._flip_scale_y = -1
        else:
            self._flip_traslation_y = 0
            self._flip_scale_y = 1

    def set_tick_pen(self):
        """
        Define pen style for drawing scale tick marks.
        """
        self._tick_pen.setColor(self._tick_color)
        self._tick_pen.setWidth(self._tick_width)

    def draw_ticks(self):
        """
        Draw tick marks on the scale.
        """
        if not self._show_ticks:
            return
        self.set_tick_pen()
        self._painter.setPen(self._tick_pen)
        division_size = self._widget_width / self._num_divisions
        tick_y0 = self._widget_height
        tick_yf = (1 - self._tick_size_rate)*self._widget_height
        for i in range(self._num_divisions+1):
            x = i*division_size
            self._painter.drawLine(x, tick_y0, x, tick_yf) # x1, y1, x2, y2

    def draw_bar(self):
        """
        Draw a bar as indicator of current value.
        """
        self.set_origin()
        self.set_position()

        if self.position < 0 or self.position > self._widget_width:
            return
        self._painter.setPen(Qt.transparent)
        self._painter.setBrush(self._indicator_color)
        bar_width = self.position - self._origin_position
        bar_height = self._bg_size_rate * self._widget_height
        self._painter.drawRect(self._origin_position, 0, bar_width, bar_height)

    def draw_pointer(self):
        """
        Draw a pointer as indicator of current value.
        """
        self.set_position()
        if self.position < 0 or self.position > self._widget_width:
            return
        self._painter.setPen(Qt.transparent)
        self._painter.setBrush(self._indicator_color)
        pointer_width = self._pointer_width_rate * self._widget_width
        pointer_height = self._bg_size_rate * self._widget_height
        points = [
            QPoint(self.position, 0),
            QPoint(self.position + 0.5*pointer_width, 0.5*pointer_height),
            QPoint(self.position, pointer_height),
            QPoint(self.position - 0.5*pointer_width, 0.5*pointer_height)
        ]
        self._painter.drawPolygon(QPolygon(points))

    def draw_indicator(self):
        """
        Draw the selected indicator for current value.
        """
        if self._barIndicator:
            self.draw_bar()
        else:
            self.draw_pointer()

    def draw_background(self):
        """
        Draw the background of the scale.
        """
        self._painter.setPen(Qt.transparent)
        self._painter.setBrush(self._bg_color)
        bg_width = self._widget_width
        bg_height = self._bg_size_rate * self._widget_height
        self._painter.drawRect(0, 0, bg_width, bg_height)

    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.adjust_transformation()
        self._painter.begin(self)
        self._painter.translate(0, self._painter_translation_y) # Draw vertically if needed
        self._painter.rotate(self._painter_rotation)
        self._painter.translate(self._painter_translation_x, 0) # Invert appearance if needed
        self._painter.scale(self._painter_scale_x, 1)

        self._painter.translate(0, self._flip_traslation_y)     # Invert scale if needed
        self._painter.scale(1, self._flip_scale_y)

        self._painter.setRenderHint(QPainter.Antialiasing)

        self.draw_background()
        self.draw_ticks()
        self.draw_indicator()

        self._painter.end()

    def calculate_position_for_value(self, value):
        """
        Calculate the position (pixel) in which the pointer should be drawn for a given value.
        """
        if value < self._lower_limit or value > self._upper_limit or \
           self._upper_limit - self._lower_limit == 0:
            proportion = -1 # Invalid
        else:
            proportion = (value - self._lower_limit) / (self._upper_limit - self._lower_limit)

        position = int(proportion * self._widget_width)
        return position

    def set_origin(self):
        """
        Set the position (pixel) in which the origin should be drawn.
        """
        if self._origin_at_zero:
            self._origin_position = self.calculate_position_for_value(0)
        else:
            self._origin_position = 0

    def set_position(self):
        """
        Set the position (pixel) in which the pointer should be drawn.
        """
        self.position = self.calculate_position_for_value(self._value)

    def update_indicator(self):
        """
        Update the position and the drawing of indicator.
        """
        self.set_position()
        self.repaint()

    def set_value(self, value):
        """
        Set a new current value for the indicator.
        """
        self._value = value
        self.update_indicator()

    def set_upper_limit(self, new_limit):
        """
        Set the scale upper limit.

        Parameters
        ----------
        new_limit : float
            The upper limit of the scale.
        """
        self._upper_limit = new_limit

    def set_lower_limit(self, new_limit):
        """
        Set the scale lower limit.

        Parameters
        ----------
        new_limit : float
            The lower limit of the scale.
        """
        self._lower_limit = new_limit

    def get_show_ticks(self):
        return self._show_ticks

    def set_show_ticks(self, checked):
        if self._show_ticks != bool(checked):
            self._show_ticks = checked
            self.repaint()

    def get_orientation(self):
        return self._orientation

    def set_orientation(self, orientation):
        self._orientation = orientation
        self.adjust_transformation()
        self.repaint()

    def get_flip_scale(self):
        return self._flip_scale

    def set_flip_scale(self, checked):
        self._flip_scale = bool(checked)
        self.adjust_transformation()
        self.repaint()

    def get_inverted_appearance(self):
        return self._inverted_appearance

    def set_inverted_appearance(self, inverted):
        self._inverted_appearance = inverted
        self.adjust_transformation()
        self.repaint()

    def get_bar_indicator(self):
        return self._barIndicator

    def set_bar_indicator(self, checked):
        if self._barIndicator != bool(checked):
            self._barIndicator = checked
            self.repaint()

    def get_background_color(self):
        return self._bg_color

    def set_background_color(self, color):
        self._bg_color = color
        self.repaint()

    def get_indicator_color(self):
        return self._indicator_color

    def set_indicator_color(self, color):
        self._indicator_color = color
        self.repaint()

    def get_tick_color(self):
        return self._tick_color

    def set_tick_color(self, color):
        self._tick_color = color
        self.repaint()

    def get_background_size_rate(self):
        return self._bg_size_rate

    def set_background_size_rate(self, rate):
        if rate >= 0 and rate <=1 and self._bg_size_rate != rate:
            self._bg_size_rate = rate
            self.repaint()

    def get_tick_size_rate(self):
        return self._tick_size_rate

    def set_tick_size_rate(self, rate):
        if rate >= 0 and rate <=1 and self._tick_size_rate != rate:
            self._tick_size_rate = rate
            self.repaint()

    def get_num_divisions(self):
        return self._num_divisions

    def set_num_divisions(self, divisions):
        if isinstance(divisions, int) and divisions > 0 and self._num_divisions != divisions:
            self._num_divisions = divisions
            self.repaint()

    def get_scale_height(self):
        return self._scale_height

    def set_scale_height(self, value):
        self._scale_height = int(value)
        self.adjust_transformation()
        self.repaint()

    def get_origin_at_zero(self):
        return self._origin_at_zero

    def set_origin_at_zero(self, checked):
        if self._origin_at_zero != bool(checked):
            self._origin_at_zero = checked
            self.repaint()
Example #33
0
 def paintEvent(self, event):
     qp = QPainter()
     qp.begin(self)
     qp.fillRect(self.rect(), self.bg_color)
     qp.end()