我正在做一个小部件。它有一个表盘和一个滑块,都修改相同的数字,虽然我希望滑块有一个更高的变化率比表盘。把它想象成微调的刻度盘。例如,滑块的单位为0.1,但刻度盘的单位为0.01。此外,我希望拨号盘增加或减少数字的值,只要我把它在一个方向。因此,如果我转5圈,数字将增加50个单位(考虑到表盘“轴”上有100个步骤)。
我成功地实现了其中的大部分,但有一个问题,我不知道如何解决它。
表盘按我想的那样工作。它无止境地旋转。它的变化速度也比滑块慢。问题是,这个速率只在我顺时针旋转(增加数字)时才起作用。
刻度盘增加0.01,滑块增加0.1。我可以从1到1. 1(1. 01,1. 02...)通过操作拨号和滑块只会移动一次,但是如果我从1. 1返回拨号,它将直接改变为1,然后0.9(跳过之间的数字)。换句话说,只要我顺时针旋转,表盘的速率就会不同。
这是密码
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QDial, QSlider
from PyQt5.QtCore import Qt
class DialWidget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self._label_number = 0.000
self.label = QLabel(str(self._label_number), self)
self.label.setAlignment(Qt.AlignCenter)
self.dial = QDial(self)
self.dial.setNotchesVisible(True)
self.dial.setWrapping(True)
self.dial.setMinimum(0)
self.dial.setMaximum(100) # 3600 for full revolutions (0-3600 = 0-360 degrees)
self.dial.valueChanged.connect(self.dial_value_changed)
self.slider = QSlider(Qt.Horizontal, self)
self.slider.setMinimum(0)
self.slider.setMaximum(80)
self.slider.valueChanged.connect(self.slider_value_changed)
self._slider_value = self.slider.value()
layout = QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.dial)
layout.addWidget(self.slider)
self.dial_value = self.dial.value()
self.setLayout(layout)
@property
def label_number(self):
return self._label_number
@label_number.setter
def label_number(self, number):
print(f' Inside label_number.setter')
if number < 0 or number > 8:
pass
else:
print(f' Changing label_number {self._label_number} --> {number}')
self._label_number = number
#print(f' Changing label text {self.label.text()} --> {str(round(number, 4))}')
self.label.setText(str(round(number, 4)))
print(f' Changing slider value: {self.slider.value()} --> {number * 10.000} ')
print(f'self.slider.setValue({number * 10.000})')
self.slider_number = number * 10.000
self.slider.setValue(self.slider_number)
print(f' Exiting label_number.setter')
def dial_value_changed(self):
print(f'Inside dial_value_changed')
# Get the current value of the dial
dial_delta = self.dial.value() - self.dial_value
print(f'Delta number of the dial: {dial_delta}')
if dial_delta == 1:
print(f'dn = {1/100}')
new_number = self.label_number + 1/100
self.label_number = new_number
elif dial_delta == -1:
print(f'dn = {1/100}')
new_number = self.label_number - 1/100
self.label_number = new_number
elif dial_delta == -100:
print(f'dn = {1/100}')
new_number = self.label_number + 1/100
self.label_number = new_number
elif dial_delta == 99:
print(f'dn = {1/100}')
new_number = self.label_number - 1/100
self.label_number = new_number
#print(f'Setting self.dial_value to {self.dial.value()}')
self.dial_value = self.dial.value()
print(f'Exiting dial_value_changed')
print()
def slider_value_changed(self, value):
print(f' Inside slider_value_changed')
print(f' {self.slider.value()}')
print(f' Value sent {value}')
#print(f' Changing self.label_number {self.label_number} --> {value / 10.0}')
self.label_number = value / 10.000
print(f' Exiting slider_value_changed')
if __name__ == '__main__':
app = QApplication(sys.argv)
window = DialWidget()
window.setWindowTitle('Dial Widget')
window.show()
sys.exit(app.exec_())
字符串
我添加了指纹以便更好地遵循终端中的代码。我发现问题源于self.slider.valueChanged.connect(self.slider_value_changed)
行。信号只发送整数(因为滑块的位置以整数为单位)。
然后,我尝试通过添加一个额外的属性来解决这个问题,该属性包含滑块的位置或编号,但作为一个浮点数。当我尝试这样做时,表盘在两个方向上都工作正常,但是如果我按下滑块,滑块不会移动。
class DialWidget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self._label_number = 0.000
self.label = QLabel(str(self._label_number), self)
self.label.setAlignment(Qt.AlignCenter)
self.dial = QDial(self)
self.dial.setNotchesVisible(True)
self.dial.setWrapping(True)
self.dial.setMinimum(0)
self.dial.setMaximum(100) # 3600 for full revolutions (0-3600 = 0-360 degrees)
self.dial.valueChanged.connect(self.dial_value_changed)
self.slider = QSlider(Qt.Horizontal, self)
self.slider.setMinimum(0)
self.slider.setMaximum(80)
self.slider.valueChanged.connect(self.slider_value_changed)
self._slider_number = self.slider.value()
layout = QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.dial)
layout.addWidget(self.slider)
self.dial_value = self.dial.value()
self.setLayout(layout)
@property
def label_number(self):
return self._label_number
@label_number.setter
def label_number(self, number):
print(f' Inside label_number.setter')
if number < 0 or number > 8:
pass
else:
print(f' Changing label_number {self._label_number} --> {number}')
self._label_number = number
#print(f' Changing label text {self.label.text()} --> {str(round(number, 4))}')
self.label.setText(str(round(number, 4)))
print(f' Changing slider value: {self.slider.value()} --> {number * 10.000} ')
print(f'self.slider.setValue({number * 10.000})')
self.slider_number = number * 10.000
self.slider.setValue(self.slider_number)
#self.slider.setValue(number * 10.000)
print(f' Exiting label_number.setter')
@property
def slider_number(self):
return self._slider_number
@slider_number.setter
def slider_number(self, number):
self._slider_number = number
def dial_value_changed(self):
print(f'Inside dial_value_changed')
# Get the current value of the dial
dial_delta = self.dial.value() - self.dial_value
print(f'Delta number of the dial: {dial_delta}')
if dial_delta == 1:
print(f'dn = {1/100}')
new_number = self.label_number + 1/100
self.label_number = new_number
elif dial_delta == -1:
print(f'dn = {1/100}')
new_number = self.label_number - 1/100
self.label_number = new_number
elif dial_delta == -100:
print(f'dn = {1/100}')
new_number = self.label_number + 1/100
self.label_number = new_number
elif dial_delta == 99:
print(f'dn = {1/100}')
new_number = self.label_number - 1/100
self.label_number = new_number
#print(f'Setting self.dial_value to {self.dial.value()}')
self.dial_value = self.dial.value()
print(f'Exiting dial_value_changed')
print()
def slider_value_changed(self, value):
print(f' Inside slider_value_changed')
print(f' {self.slider.value()}')
print(f' Value sent {value}')
#print(f' Changing self.label_number {self.label_number} --> {value / 10.0}')
value = self.slider_number
self.label_number = value / 10.000
#self.slider.setValue(self.slider_value)
print(f' Exiting slider_value_changed')
型
这就是我所尝试的。我添加了slider_number作为属性。我一整天都在试图解决这个问题。我会非常感激另一个大脑来帮助我。
编辑(可能的解决方案):
我设法解决了问题,我有断开slider.valueChanged
从slider_value_changed
时,拨号是行动(dial_value_changed
),并连接它再次一旦我退出了功能。这是因为当slider.valueChanged
被调用两次时会出现问题,其中一次是当移动拨号盘时数字以0.1个单位变化时,调用set_number()
,然后再次调用slider.valueChanged
。
def dial_value_changed(self):
dial_delta = self.dial.value() - self.dial_value
self.slider.valueChanged.disconnect()
if dial_delta == 1:
new_number = self.number + 1/10000
self.set_number(new_number)
elif dial_delta == -1:
new_number = self.number - 1/10000
self.set_number(new_number)
elif dial_delta == -100:
new_number = self.number + 1/10000
self.set_number(new_number)
elif dial_delta == 99:
new_number = self.number - 1/10000
self.set_number(new_number)
self.dial_value = self.dial.value()
self.slider.valueChanged.connect(self.slider_value_changed)
型
1条答案
按热度按时间ki1q1bka1#
QDial在用户体验方面存在重要问题,最重要的是,当使用鼠标时,它会将二维向量Map到一维(线性)空间。
当使用 wrapping 时,这个问题甚至更加明显(并且是有问题的),因为没有直接的方法来知道鼠标“移动”是打算顺时针旋转还是逆时针旋转。请记住,鼠标移动事件不是连续的:它们只是一个 * 表面上的 * 运动,但实际上鼠标“跳跃”到点,给出了 * 运动的 * 错觉 *。
这必然意味着没有直接的方式来知道滑块值是否应该保持相同,或者当旋转通过刻度盘的最小值和最大值之间的边缘时,滑块值是否应该最终加/减。
我们不能仅仅依赖于前一个值和当前值之间的差异;较小的新值可能并不总是意味着刻度盘已逆时针移动:如果值是例如50和40,则该假设是有效的,但是如果您只是将刻度盘从99移动到2呢?
一个可能的解决方案是使用自定义信号子类化QDial,并基于先前的值 * 和 * 触发的动作发出它。
由于
actionTriggered
信号,这是可能的:当发出信号时,sliderPosition已经根据动作进行了调整,但是值还没有被传播(意味着valueChanged()信号还没有发出),并且视觉显示还没有被更新。
诀窍是将该信号连接到一个跟踪当前滑块位置(在值实际更改之前)和触发它的操作的函数,然后连接到标准
valueChanged
信号并使用可以根据以前的值和相关操作更改的值发出自定义信号。每当边缘“交叉”时,新值将增加或减去整个刻度盘范围。
一些动作让我们知道方向,所以很容易知道结果是否:如果前一个值是100,新的是0,动作是
SliderSingleStepAdd
,那么我们知道我们应该将滑块增加1,等等。鼠标移动需要通过前一个值来获得方向,因此我们必须假设通过获得值之间的差异,但使用全范围的模。那么我们必须做一个假设:如果该差值小于该范围的一半,则意味着该Angular 是顺时针的。
字符串
以上将给予以下结果:
oldValue = 90
和newValue = 10
,diff
将是20
,因此旋转是顺时针的,并且由于newValue
小于oldValue
,我们将其递增100,因此滑块将增加1;10
和90
,diff
将80
,因此旋转是逆时针的,并且由于newValue
大于oldValue
,我们将其递减100,因此滑块将减少1;请注意,由于开头提到的问题,当“旋转”没有“穿过”边缘时,这仍然可能产生意外或不可靠的结果:例如,如果当前刻度盘值是25,并且在75之后单击,则实际上可能导致逆时针旋转。
然后,在主小部件中,我们连接自定义信号:如果值小于0,那么我们知道我们必须从滑块值中减去1,如果它大于或等于100,那么加上1。
请注意,为了避免递归并提供适当的范围边界,我们还应该防止滑块的
valueChanged
信号传播,以便我们最终可以在最终值达到最小值或最大值时将刻度盘调整为0
。最后,由于换行可能导致最大值和最小值之间的鼠标/键盘范围非常窄(并且不可靠),因此最好防止表盘实际达到最大值:每当位置等于100时,我们根据先前的位置或动作(如上所述)来调整它,因此如果它大于50,则它将变为0,否则它将变为99。这在连接到
actionTriggered
的函数中已经完成,因为此时值尚未调整。型