首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在PyQt4中设置QDial的最小和最大浮点值?

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

Stack Overflow用户
提问于 2019-08-26 22:41:06
回答 1查看 634关注 0票数 0

在一个简单的图形用户界面项目中,我发现了如何设置QDial的最小值和最大值,即从0到99,但我希望设置范围从0.02到0.2。我该怎么做呢?

代码语言:javascript
复制
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'mainwindow.ui'
#
# Created by: PyQt4 UI code generator 4.11.4
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(459, 366)
        self.centralWidget = QtGui.QWidget(MainWindow)
        self.centralWidget.setObjectName(_fromUtf8("centralWidget"))
        self.dial = QtGui.QDial(self.centralWidget)
        self.dial.setGeometry(QtCore.QRect(60, 100, 111, 101))
        self.dial.setNotchesVisible(True)
        self.dial.setObjectName(_fromUtf8("dial"))
        self.lcdNumber = QtGui.QLCDNumber(self.centralWidget)
        self.lcdNumber.setGeometry(QtCore.QRect(200, 120, 101, 61))
        self.lcdNumber.setObjectName(_fromUtf8("lcdNumber"))
        MainWindow.setCentralWidget(self.centralWidget)
        self.menuBar = QtGui.QMenuBar(MainWindow)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 459, 25))
        self.menuBar.setObjectName(_fromUtf8("menuBar"))
        MainWindow.setMenuBar(self.menuBar)
        self.mainToolBar = QtGui.QToolBar(MainWindow)
        self.mainToolBar.setObjectName(_fromUtf8("mainToolBar"))
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar)
        self.statusBar = QtGui.QStatusBar(MainWindow)
        self.statusBar.setObjectName(_fromUtf8("statusBar"))
        MainWindow.setStatusBar(self.statusBar)

        self.retranslateUi(MainWindow)
        QtCore.QObject.connect(self.dial, QtCore.SIGNAL(_fromUtf8("valueChanged(int)")), self.lcdNumber.display)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))


if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    MainWindow = QtGui.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

输出必须类似,QDial值的范围应为0.02到0.2

EN

回答 1

Stack Overflow用户

发布于 2019-08-27 09:46:17

与任何其他QAbstractSlider子体一样,QDial仅支持整数值。

根据您的需要,至少有三种实现浮点数支持的解决方案。

其思想是使用基于整数的刻度盘的值作为索引,通过使用数学运算或基于索引的函数来获得实际的浮点值。

固定值列表

创建所有可能值的列表,这也可以使用它们之间不均匀的“间隙”(例如,0.02, 0.08, 0.16, 2.0),在len(valueList) - 1处设置最大值,然后使用valueChanged信号参数的值作为该列表的索引。

优点:实现“单向”拨号非常容易(拨号盘只能更新数值,反之亦然);

缺点:如果提供的值不在列表中,则问题设置表盘的“值”(参见下面的代码);“勾号”位置不考虑实际值,并且在使用“不均匀”步骤时可能在视觉上不一致:如果值为0.0, 0.01, 0.02, 0.1, 0.2,则中间位置将是0.02,而不是更直观的0.1

基于步长的值

您必须设置一个步长,该步长将用作最小值和最大值之间的范围(例如0.02),从而产生例如0.02, 0.04, 0.06, [...], 0.18, 0.2的值。

在底部的示例中,我假设该步骤允许刻度盘精确地达到最大值;如果没有达到最大值,则必须在末尾添加另一个步骤(通过将QDial.maximum()增加1)。

实际浮点值的计算方法是将当前整数值乘以步长,然后将结果与最小浮点值相加。

这种方法类似于this answer,它正确地实现了所有方法,但如果在QDials上使用,则有一个基于小数单位的固定步长的限制(这意味着您不能使用0.02, 0.04, 0.06,而只能使用0.02, 0.03, 0.04, 0.05, etc),这意味着它的实现应该相应地进行修复。

NB:虽然转换为字符串的值通常是四舍五入的,但请记住,浮点值的值是limitations,这意味着,在这个特定的示例中,0.2实际上是(大约) 0.20000000000000004。

优点:该步骤允许简化数据输入,缩小数据范围;

缺点:无论步长有多少位数,由于浮动的限制,实际值可能会“不精确”;范围(最大-最小)必须是步长的倍数;

基于步数(精度)

在本例中,您设置了在最小值和最大值之间需要多少个步长,从而允许您指定其值的精度。结果值取自当前拨号值和拨号盘最大值(步数)之间的比率,乘以最小值和最大值之间的范围,然后与前面的最小值相加。

优点:更高的精度;

结论:如果不是不可能的话,获取小数为零或很少的值是非常困难的;

SetValue()问题

如果你需要设置刻度盘的值,你总是会遇到一些问题,主要是由于前面提到的浮动限制,但不仅仅是这样。

此外,必须根据不同的实现应用不同的策略,最困难的是“固定值列表”的情况。

问题是,实际的QDial索引是基于整数的,这意味着如果您想将“值”设置为0.9,根据实现,它可以被解释为0或1。

在下面的示例中,我还使用了一个QDoubleSpinBox来显示当前值,当用户更改了旋转框的值时,它能够设置回拨号值;虽然基于步骤的拨盘几乎没有缺陷,但固定列表的情况清楚地显示了这一限制:如果您尝试在第一个旋转框上缓慢移动鼠标滚轮,您将看到拨盘并不总是正确更新,特别是当设置的值小于当前值时。

代码语言:javascript
复制
from bisect import bisect_left
from PyQt4 import QtCore, QtGui

class FixedValuesDial(QtGui.QDial):
    floatValueChanged = QtCore.pyqtSignal(float)
    def __init__(self, valueList):
        super(FixedValuesDial, self).__init__()
        self.valueList = valueList
        self.setMaximum(len(valueList) - 1)
        self.valueChanged.connect(self.computeFloat)
        self.currentFloatValue = self.value()

    def computeFloat(self, value):
        self.currentFloatValue = self.valueList[value]
        self.floatValueChanged.emit(self.currentFloatValue)

    def setFloatValue(self, value):
        try:
            # set the index, assuming the value is in the valueList
            self.setValue(self.valueList.index(value))
        except:
            # find the most closest index, based on the value
            index = bisect_left(self.valueList, value)
            if 0 < index < len(self.valueList):
                before = self.valueList[index - 1]
                after = self.valueList[index]
                # bisect_left returns the position where the value would be
                # added, assuming valueList is sorted; the resulting index is the
                # one preceding the one with a value greater or equal to the
                # provided value, so if the difference between the next value and
                # the current is greater than the difference between the previous
                # and the current, the index is closest to the previous
                if after - value > value - before:
                    index -= 1
            # note that the value -the new index- is actually updated only *if*
            # the new index is different from the current, otherwise there will
            # no valueChanged signal emission
            self.setValue(index)


class StepDial(QtGui.QDial):
    floatValueChanged = QtCore.pyqtSignal(float)
    def __init__(self, minimum, maximum, step):
        super(StepDial, self).__init__()
        self.scaleValues = []
        self.minimumFloat = minimum
        self.step = step
        self.setMaximum((maximum - minimum) // step)
        self.valueChanged.connect(self.computeFloat)

    def computeFloat(self, value):
        self.floatValueChanged.emit(value * self.step + self.minimumFloat)

    def setFloatValue(self, value):
        # compute the index by subtracting minimum from the value, divided by the
        # step value, then round provide a *rounded* value, otherwise values like
        # 0.9999[...] will be interpreted as 0
        index = (value - self.minimumFloat) / self.step
        self.setValue(int(round(index)))


class FloatDial(QtGui.QDial):
    floatValueChanged = QtCore.pyqtSignal(float)
    def __init__(self, minimum, maximum, stepCount=1001):
        super(FloatDial, self).__init__()
        self.minimumFloat = minimum
        self.maximumFloat = maximum
        self.floatRange = maximum - minimum
        # since QDial (as all QAbstractSlider subclasses), includes its maximum()
        # in its value range; to get the expected step count, we subtract 1 from
        # it: maximum = minimum + (stepCount - 1)
        # also, since the default minimum() == 0, the maximum is stepCount - 1.
        # Note that QDial is usually symmetrical, using 240 degrees
        # counterclockwise (in cartesian coordinates, as in 3-o'clock) for the
        # minimum, and -60 degrees for its maximum; with this implementation we
        # assume all of that, and get a correct "middle" value, but this requires
        # an *odd* stepCount number so that the odd "middle" index value is
        # actually what it should be.
        self.stepCount = stepCount
        self.setMaximum(stepCount - 1)
        self.valueChanged.connect(self.computeFloat)

    def computeFloat(self, value):
        ratio = float(value) / self.maximum()
        self.floatValueChanged.emit(self.floatRange * ratio + self.minimumFloat)

    def setFloatValue(self, value):
        # compute the "step", based on the stepCount then use the same concept
        # as in the StepDial.setFloatValue function
        step = (self.maximumFloat - self.minimumFloat) / self.stepCount
        index = (value - self.minimumFloat) // step
        self.setValue(int(round(index)))


class Window(QtGui.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        layout = QtGui.QGridLayout()
        self.setLayout(layout)

        layout.addWidget(QtGui.QLabel('List based'), 0, 0)
        self.listDial = FixedValuesDial([0.02, 0.03, 0.05, 0.08, 0.1, 0.15, 0.2])
        layout.addWidget(self.listDial, 1, 0)
        self.listDial.floatValueChanged.connect(self.updateListValue)
        self.listSpin = QtGui.QDoubleSpinBox(
            minimum=0.02, maximum=0.2, singleStep=0.01)
        layout.addWidget(self.listSpin, 2, 0)
        self.listSpin.valueChanged.connect(self.listDial.setFloatValue)

        layout.addWidget(QtGui.QLabel('Step precision (0.02)'), 0, 1)
        self.stepDial = StepDial(0.02, 0.2, 0.02)
        layout.addWidget(self.stepDial, 1, 1)
        self.stepDial.floatValueChanged.connect(self.updateStepDisplay)
        self.stepSpin = QtGui.QDoubleSpinBox(
            minimum=0.02, maximum=0.2, singleStep=0.02)
        layout.addWidget(self.stepSpin, 2, 1)
        self.stepSpin.valueChanged.connect(self.stepDial.setFloatValue)

        layout.addWidget(QtGui.QLabel('Step count (21 steps)'), 0, 2)
        # see the FloatDial implementation above to understand the reason of odd
        # numbered steps
        self.floatDial = FloatDial(0.02, 0.2, 21)
        layout.addWidget(self.floatDial, 1, 2)
        self.floatDial.floatValueChanged.connect(self.updateFloatValue)
        self.floatSpin = QtGui.QDoubleSpinBox(
            minimum=0.02, maximum=0.2, decimals=5, singleStep=0.001)
        layout.addWidget(self.floatSpin, 2, 2)
        self.floatSpin.valueChanged.connect(self.floatDial.setFloatValue)

    def updateStepDisplay(self, value):
        # prevent the spinbox sending valuechanged while updating its value,
        # otherwise you might face an infinite recursion caused by the spinbox
        # trying to update the dial, which will correct the value and possibly
        # send the floatValueChanged back again to it; obviously, this applies
        # to the following slots
        self.stepSpin.blockSignals(True)
        self.stepSpin.setValue(value)
        self.stepSpin.blockSignals(False)

    def updateFloatValue(self, value):
        self.floatSpin.blockSignals(True)
        self.floatSpin.setValue(value)
        self.floatSpin.blockSignals(False)

    def updateListValue(self, value):
        self.listSpin.blockSignals(True)
        self.listSpin.setValue(value)
        self.listSpin.blockSignals(False)

if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)
    w = Window()
    w.show()
    sys.exit(app.exec_())
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57660108

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档