目前,我已将此自定义旋转的QDial()
小部件的拨号手柄向上指向0
位置,而不是默认的180
值位置。
要更改刻度线间距,可以使用setNotchTarget()
来分隔凹口,但这会创建均匀的刻度线分布(左)。我想创建一个只有三个可调刻度的自定义刻度盘(右)。
中心刻度永远不会移动,并且始终位于0
的北位置。但是其他两个刻度是可以调整的,并且应该均匀分布。因此,例如,如果将刻度线设置为70
,则会将左/右刻度线35
放置在距中心的位置。同样,如果将价格变动更改为120
,则会将价格变动隔开60
。
我该怎么做?如果使用QDial()
无法做到这一点,那么还有哪些其他小部件可以做到这一点?我正在使用PyQt4和Python 3.7
import sys
from PyQt4 import QtGui, QtCore
class Dial(QtGui.QWidget):
def __init__(self, rotation=0, parent=None):
QtGui.QWidget.__init__(self, parent)
self.dial = QtGui.QDial()
self.dial.setMinimumHeight(160)
self.dial.setNotchesVisible(True)
# self.dial.setNotchTarget(90)
self.dial.setMaximum(360)
self.dial.setWrapping(True)
self.label = QtGui.QLabel('0')
self.dial.valueChanged.connect(self.label.setNum)
self.view = QtGui.QGraphicsView(self)
self.scene = QtGui.QGraphicsScene(self)
self.view.setScene(self.scene)
self.graphics_item = self.scene.addWidget(self.dial)
self.graphics_item.rotate(rotation)
# Make the QGraphicsView invisible
self.view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.view.setFixedHeight(self.dial.height())
self.view.setFixedWidth(self.dial.width())
self.view.setStyleSheet("border: 0px")
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.view)
self.layout.addWidget(self.label)
self.setLayout(self.layout)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dialexample = Dial(rotation=180)
dialexample.show()
sys.exit(app.exec_())
答案 0 :(得分:1)
首先。 Qt的拨盘很乱。它们是不错的小部件,但主要是为简单用例开发的。
如果您需要“特殊”行为,则需要覆盖一些重要的方法。该示例显然需要paintEvent
覆盖,但是最重要的部分是鼠标事件和滚轮事件。您可能也可能需要实现keyPressEvent
,但是我认为逻辑也很清楚。
从理论上讲,QDial窗口小部件始终应该使用240 | -60度的角度,但是将来可能会改变,因此我决定启用包装以将度数保持为“内部”值。请记住,您可能还需要提供自己的value()
属性实现。
from PyQt5 import QtCore, QtGui, QtWidgets
from math import sin, cos, atan2, degrees, radians
import sys
class Dial(QtWidgets.QDial):
MinValue, MidValue, MaxValue = -1, 0, 1
def __init__(self, valueRange=120):
QtWidgets.QDial.__init__(self)
self.setWrapping(True)
self.setRange(0, 359)
self.valueRange = valueRange
self.__midValue = valueRange / 2
self.setValue(180)
self.oldValue = None
# uncomment this if you need to always keep track of value change
# self.setTracking(False)
self.notchSize = 5
self.notchPen = QtGui.QPen(QtCore.Qt.black, 2)
def valueFromPosition(self, pos):
y = self.height() / 2. - pos.y()
x = pos.x() - self.width() / 2.
angle = degrees(atan2(y, x))
if angle > 90 + self.__midValue or angle < -90:
value = self.MinValue
final = 180 - self.valueRange
elif angle >= 90 - self.__midValue:
value = self.MidValue
final = 180
else:
value = self.MaxValue
final = 180 + self.valueRange
self.blockSignals(True)
self.setValue(final)
self.blockSignals(False)
return value
def mousePressEvent(self, event):
self.oldValue = self.value()
if self.hasTracking():
self.valueChanged.emit(self.valueFromPosition(event.pos()))
def mouseMoveEvent(self, event):
value = self.valueFromPosition(event.pos())
if self.hasTracking():
self.valueChanged.emit(value)
def mouseReleaseEvent(self, event):
self.valueChanged.emit(self.valueFromPosition(event.pos()))
def wheelEvent(self, event):
delta = event.delta()
if self.value() < 180:
if delta < 0:
outValue = -1
value = 180 - self.valueRange
else:
outValue = 0
value = 180
elif self.value() == 180:
if delta < 0:
outValue = -1
value = 180 - self.valueRange
else:
outValue = 1
value = 180 + self.valueRange
else:
if delta < 0:
outValue = 0
value = 180
else:
outValue = 1
value = 180 + self.valueRange
self.blockSignals(True)
self.setValue(value)
self.blockSignals(False)
self.valueChanged.emit(outValue)
def paintEvent(self, event):
QtWidgets.QDial.paintEvent(self, event)
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
rad = radians(self.valueRange)
qp.setPen(self.notchPen)
c = -cos(rad)
s = sin(rad)
maxSize = max(self.width() / 2, self.height() / 2)
minSize = maxSize - self.notchSize
center = self.rect().center()
qp.drawLine(center.x(), center.y() -minSize, center.x(), center.y() - maxSize)
qp.drawLine(center.x() + s * minSize, center.y() + c * minSize, center.x() + s * maxSize, center.y() + c * maxSize)
qp.drawLine(center.x() - s * minSize, center.y() + c * minSize, center.x() - s * maxSize, center.y() + c * maxSize)
class Test(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
sizes = 70, 90, 120
for col, size in enumerate(sizes):
label = QtWidgets.QLabel(str(size))
label.setAlignment(QtCore.Qt.AlignCenter)
self.dial = Dial(size)
self.dial.valueChanged.connect(lambda value, dial=col: self.dialChanged(self.dial, value))
layout.addWidget(label, 0, col)
layout.addWidget(self.dial, 1, col)
def dialChanged(self, dial, value):
print('dial {} changed to {}'.format(dial, value))
def setValue(self, value):
self.dial.setValue(value)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
dialexample = Test()
# Change value here
dialexample.setValue(180)
dialexample.show()
sys.exit(app.exec_())