如何在 PyQt5 Python 中的 QDial 上显示标记

Posted

技术标签:

【中文标题】如何在 PyQt5 Python 中的 QDial 上显示标记【英文标题】:How to show markings on QDial in PyQt5 Python 【发布时间】:2020-09-02 04:28:27 【问题描述】:

我正在qt-desginer 中设计一个用户界面,我在其中添加了dial。如下所示:

它没有任何标记。是否可以添加一些标记以更好地理解它。如下所示:

【问题讨论】:

【参考方案1】:

不幸的是,QDial 是一个从未真正受到关注的小部件,主要是因为它很少使用,但也因为许多功能需求可能会以不一致的方式改变它的行为。无论如何,这在 Designer 中是不可能的。

这可以部分通过继承 QDial 并在设计器中提升它来完成,但您最终会在凹槽或表盘本身上绘制 数字,这将看起来很丑。

为了实现你想要的,你需要使用一个包含表盘的自定义小部件,并最终推广那个小部件,但你将无法在设计器中查看它或设置它的属性(除非您创建一个设计器插件,这不是很容易)。这是通过设置容器布局边距来实现的,以便始终有足够的空间将值显示为文本。

这是一个可能的实现。考虑一下,出于显而易见的原因,如果您的值范围较大且表盘较小,则数字将重叠。

class ValueDial(QtWidgets.QWidget):
    _dialProperties = ('minimum', 'maximum', 'value', 'singleStep', 'pageStep',
        'notchesVisible', 'tracking', 'wrapping', 
        'invertedAppearance', 'invertedControls', 'orientation')
    _inPadding = 3
    _outPadding = 2
    valueChanged = QtCore.pyqtSignal(int)

    def __init__(self, *args, **kwargs):
        # remove properties used as keyword arguments for the dial
        dialArgs = k:v for k, v in kwargs.items() if k in self._dialProperties
        for k in dialArgs.keys():
            kwargs.pop(k)
        super().__init__(*args, **kwargs)
        layout = QtWidgets.QVBoxLayout(self)
        self.dial = QtWidgets.QDial(self, **dialArgs)
        layout.addWidget(self.dial)
        self.dial.valueChanged.connect(self.valueChanged)
        # make the dial the focus proxy (so that it captures focus *and* key events)
        self.setFocusProxy(self.dial)

        # simple "monkey patching" to access dial functions
        self.value = self.dial.value
        self.setValue = self.dial.setValue
        self.minimum = self.dial.minimum
        self.maximum = self.dial.maximum
        self.wrapping = self.dial.wrapping
        self.notchesVisible = self.dial.notchesVisible
        self.setNotchesVisible = self.dial.setNotchesVisible
        self.setNotchTarget = self.dial.setNotchTarget
        self.notchSize = self.dial.notchSize
        self.invertedAppearance = self.dial.invertedAppearance
        self.setInvertedAppearance = self.dial.setInvertedAppearance

        self.updateSize()

    def inPadding(self):
        return self._inPadding

    def setInPadding(self, padding):
        self._inPadding = max(0, padding)
        self.updateSize()

    def outPadding(self):
        return self._outPadding

    def setOutPadding(self, padding):
        self._outPadding = max(0, padding)
        self.updateSize()

    # the following functions are required to correctly update the layout
    def setMinimum(self, minimum):
        self.dial.setMinimum(minimum)
        self.updateSize()

    def setMaximum(self, maximum):
        self.dial.setMaximum(maximum)
        self.updateSize()

    def setWrapping(self, wrapping):
        self.dial.setWrapping(wrapping)
        self.updateSize()

    def updateSize(self):
        # a function that sets the margins to ensure that the value strings always
        # have enough space
        fm = self.fontMetrics()
        minWidth = max(fm.width(str(v)) for v in range(self.minimum(), self.maximum() + 1))
        self.offset = max(minWidth, fm.height()) / 2
        margin = self.offset + self._inPadding + self._outPadding
        self.layout().setContentsMargins(margin, margin, margin, margin)

    def translateMouseEvent(self, event):
        # a helper function to translate mouse events to the dial
        return QtGui.QMouseEvent(event.type(), 
            self.dial.mapFrom(self, event.pos()), 
            event.button(), event.buttons(), event.modifiers())

    def changeEvent(self, event):
        if event.type() == QtCore.QEvent.FontChange:
            self.updateSize()

    def mousePressEvent(self, event):
        self.dial.mousePressEvent(self.translateMouseEvent(event))

    def mouseMoveEvent(self, event):
        self.dial.mouseMoveEvent(self.translateMouseEvent(event))

    def mouseReleaseEvent(self, event):
        self.dial.mouseReleaseEvent(self.translateMouseEvent(event))

    def paintEvent(self, event):
        radius = min(self.width(), self.height()) / 2
        radius -= (self.offset / 2 + self._outPadding)
        invert = -1 if self.invertedAppearance() else 1
        if self.wrapping():
            angleRange = 360
            startAngle = 270
            rangeOffset = 0
        else:
            angleRange = 300
            startAngle = 240 if invert > 0 else 300
            rangeOffset = 1
        fm = self.fontMetrics()

        # a reference line used for the target of the text rectangle
        reference = QtCore.QLineF.fromPolar(radius, 0).translated(self.rect().center())
        fullRange = self.maximum() - self.minimum()
        textRect = QtCore.QRect()

        qp = QtGui.QPainter(self)
        qp.setRenderHints(qp.Antialiasing)
        for p in range(0, fullRange + rangeOffset, self.notchSize()):
            value = self.minimum() + p
            if invert < 0:
                value -= 1
                if value < self.minimum():
                    continue
            angle = p / fullRange * angleRange * invert
            reference.setAngle(startAngle - angle)
            textRect.setSize(fm.size(QtCore.Qt.TextSingleLine, str(value)))
            textRect.moveCenter(reference.p2().toPoint())
            qp.drawText(textRect, QtCore.Qt.AlignCenter, str(value))


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    dial = ValueDial(minimum=1, maximum=11)
    dial.setNotchesVisible(True)
    dial.show()
    sys.exit(app.exec_())

【讨论】:

以上是关于如何在 PyQt5 Python 中的 QDial 上显示标记的主要内容,如果未能解决你的问题,请参考以下文章

如何将 QSizeGrip 放在 QDial 的右下角?

如何在 PyQt4 中为 QDial 设置最小和最大浮点值?

PyQt4/Qt:设置 QDial 的方向(最小值在顶部)

如何在pyqt5 Python中的行编辑中添加背景文本

PyQt5 Python:如何通过 MySQL 查询结果中的单个数据行

使用样式表自定义 QDial