在一个简单的图形用户界面项目中,我发现了如何设置QDial的最小值和最大值,即从0到99,但我希望设置范围从0.02到0.2。我该怎么做呢?
# -*- 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
发布于 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来显示当前值,当用户更改了旋转框的值时,它能够设置回拨号值;虽然基于步骤的拨盘几乎没有缺陷,但固定列表的情况清楚地显示了这一限制:如果您尝试在第一个旋转框上缓慢移动鼠标滚轮,您将看到拨盘并不总是正确更新,特别是当设置的值小于当前值时。
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_())https://stackoverflow.com/questions/57660108
复制相似问题