简体   繁体   English

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

[英]How to set min and max float values for QDial in PyQt4?

working on an simple GUI project,which has QDial and LCD Widget.I found out how to set minimum and maximum value for QDial ie from 0 to 99 ,but i want to set the range from 0.02 to 0.2. 我在一个简单的GUI项目上工作,该项目具有QDial和LCD Widget。我发现了如何设置QDial的最小值和最大值,即从0到99,但是我想将范围设置为0.02到0.2。 How am i supposed to do that? 我应该怎么做?

# -*- 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_())

The output must be like, the QDial value should range from 0.02 to 0.2 输出必须类似,QDial值的范围应从0.02到0.2

QDial, like any other QAbstractSlider descendant, only supports integer values. 与其他QAbstractSlider子代一样,QDial仅支持整数值。
There are at least three solutions to achieve floating numbers support, depending on what you need. 至少有三种解决方案可以实现浮点数支持,具体取决于您的需求。
The idea is that you use the integer based value of the dial as an index to get the actual floating value, by using mathematical operations or index based functions. 想法是通过使用数学运算或基于索引的函数,将基于整数的刻度盘值用作索引来获取实际的浮动值。

Fixed value list 固定值清单

Create a list of all possible values, which can also use uneven "gaps" between them (eg. 0.02, 0.08, 0.16, 2.0 ), set the maximum value at len(valueList) - 1 , then use the value of the valueChanged signal argument as an index for that list. 创建的所有可能值的列表,这也可以使用不均匀的“间隙”在它们之间(例如, 0.02, 0.08, 0.16, 2.0 ,设定为最大的值) len(valueList) - 1 ,则使用的值valueChanged信号参数作为该列表的索引。

Pros : very easy implementation for "one-way" dials (the dial can only update the value, not the other way around); 优点 :“单向”拨盘的实现非常简单(拨盘只能更新值,而不能相反);
Cons : issues setting the "value" of the dial if the provided value is not in the list (see code below); 缺点 :如果提供的值不在列表中,则设置表盘的“值”时会出现问题(请参见下面的代码); "tick" positions don't take into account the actual values and can be visually inconsistent whenever "uneven" steps are used: if values are 0.0, 0.01, 0.02, 0.1, 0.2 , the middle position will be 0.02 , not the more intuitive 0.1 ; “刻度”位置未考虑实际值,并且在使用“不均匀”步骤时可能在视觉上不一致:如果值分别0.0, 0.01, 0.02, 0.1, 0.2 ,则中间位置将为0.02 ,而不是更直观 0.1 ;

Step value based 基于步长值

You have to set a step that will be used as a range between the minimum and the maximum (such as 0.02 ), resulting in values being, for example, 0.02, 0.04, 0.06, [...], 0.18, 0.2 . 你必须设置将用于作为最小值和最大值(如之间的范围内的步骤 0.02 ),导致值是,例如, 0.02, 0.04, 0.06, [...], 0.18, 0.2
In the example at the bottom I assume that the step allows the dial to exactly reach the maximum; 在底部的示例中,我假设该步骤允许刻度盘精确地达到最大值; if it doesn't, you'll have to add another step at the end (by increasing the QDial.maximum() by one). 如果没有,则必须在最后添加另一步(通过将QDial.maximum()增加一个)。
The actual floating value is computed by multiplying the current integer value by the step, and adding the result to the minimum float value. 通过将当前整数值乘以步长,然后将结果与最小浮点值相加,可以计算出实际的浮点值。
This approach is similar to this answer , which correctly implements all methods, but has the limit of a fixed step based on decimal units if used on QDials (meaning you can't have 0.02, 0.04, 0.06 , but only 0.02, 0.03, 0.04, 0.05, etc ), meaning that its implementation should be fixed accordingly. 这种方法类似于这样的回答 ,其正确实施的所有方法,但基于十进制单位的固定步骤的限制,如果在QDials(这意味着你使用不能有0.02, 0.04, 0.06 ,但只有0.02, 0.03, 0.04, 0.05, etc ),表示应相应地固定其实现。
NB : while values converted to strings are usually rounded, remember that floating point values have limitations , meaning that, in this specific example, 0.2 is actually ( about ) 0.20000000000000004. 注意 :虽然通常将转换为字符串的值四舍五入,但请记住,浮点值具有局限性 ,这意味着,在此特定示例中,0.2实际上是( 大约 )0.20000000000000004。

pros : the step allows simplified data entries, reducing the data range; 优点 :该步骤允许简化数据输入,减小数据范围;
cons : no matter how many digits the step has, the actual value will probably be "imprecise" due to floating limitations; 缺点 :无论步长为几位数,由于浮动限制,实际值可能会“不精确”; the range (maximum - minimum) has to be a multiple of the step value; 范围(最大值-最小值) 必须是步长值的倍数;

Step count (precision) based 基于步数(精度)

In this case you set how many steps you'll need between minumum and maximum values, allowing you to specify the precision of its values. 在这种情况下,您可以设置最小值和最大值之间需要多少步,从而可以指定其值的精度。 The resulting value is taken from the ratio between the current dial value and the dial maximum (the number of steps), multiplied by the range between the minimum and the maximum, and added to the minimum like before. 结果值取自当前刻度盘值和最大刻度盘之间的比率(步数),乘以最小值和最大值之间的范围,然后像以前一样添加到最小值。

pros : higher precision; 优点 :精度更高;
conts : getting values with zero or very few decimals is very hard, if not impossible; 续数 :很难,即使不是不可能,也很难获得零或十进制的值;

SetValue() issues SetValue()问题

If you need to set the value of the dial, you'll always encounter some kind of problem, mostly due to the aforementioned floating limitations, but not only. 如果您需要设置刻度盘的值,则总是会遇到某种问题,这主要是由于上述浮动限制所致,但不仅如此。
Also, different strategies have to be applied according to the different implementations, with the most hard being the "fixed value list" case. 另外,必须根据不同的实现方式应用不同的策略,其中最困难的是“固定值列表”情况。

The problem is that the actual QDial index is integer based, meaning that if you want to set a "value" of 0.9, it could be interpreted as 0 or 1, according to the implementation. 问题是实际的QDial索引是基于整数的,这意味着如果您想将“值”设置为0.9,则根据实现可以将其解释为0或1。

In the following example I'm also using a QDoubleSpinBox to show the current value, and that is able to set back the dial value when the spinbox value is changed by the user; 在下面的示例中,我还使用QDoubleSpinBox来显示当前值,并且当用户更改了spinbox值时,它可以重新设置拨号值。 while the step based dials work almost without flaws, the fixed list case clearly shows that limitation: if you try to slowly move the mouse wheel on the first spin box, you'll see that the dial is not always updated properly, expecially when setting a value that is less than the current one. 尽管基于步进的转盘几乎没有缺陷,但固定列表的情况清楚地表明了这一局限性:如果尝试在第一个旋转框上缓慢移动鼠标滚轮,则会发现转盘并非总是正确更新,尤其是在设置时一个小于当前值的值。

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_())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM