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

S A*_*rew 1 python qt-designer pyqt5 qdial

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

在此输入图像描述

它没有任何标记。是否可以添加一些标记以便更好地理解它。像下面这样:

在此输入图像描述

mus*_*nte 6

不幸的是,QDial 是一个从未真正受到关注的小部件,主要是因为它很少被使用,而且还因为许多功能需求可能会以不一致的方式改变其行为。无论如何,这对于设计师来说是不可能的。

这可以通过子类化 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_())
Run Code Online (Sandbox Code Playgroud)

表盘截图