gpt4 book ai didi

python - 使用 PyQt 自定义 QDial notch ticks

转载 作者:太空宇宙 更新时间:2023-11-04 09:27:17 25 4
gpt4 key购买 nike

目前我有这个自定义旋转的 QDial() 小部件,其拨盘 handle 指向上方的 0 位置而不是默认的 180 值位置。

要更改刻度间距,setNotchTarget() 用于间隔凹口,但这会创建均匀分布的刻度(左)。我想创建一个只有三个 可调刻度的自定义表盘(右)。

enter image description here enter image description here

中心刻度永远不会移动,并且始终位于 0 的北位置。但其他两个刻度可以调整,并且应该均匀分布。因此,例如,如果刻度设置为 70,它会将左/右刻度放置在距中心 35 个单位的位置。类似地,如果刻度更改为 120,它会将刻度间隔 60

enter image description here enter image description here

我该怎么做?如果使用 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_())

最佳答案

Image showing test code results

首先。 Qt 的表盘一团糟。它们是不错的小部件,但它们主要是为简单的用例开发的。

如果您需要“特殊”行为,则需要覆盖一些重要的方法。这个例子显然需要覆盖paintEvent,但最重要的部分是鼠标事件和滚轮事件。跟踪设置单个和页面步进值范围所需的键盘事件,并“覆盖”原始 valueChanged 信号以确保发出的值范围始终在 -1 和 1 之间。您显然可以更改通过添加专用函数来获取这些值。
理论上,QDial 小部件应该始终使用 240|-60 度角,但将来可能会改变,因此我决定启用环绕以将度数保留为“内部”值。请记住,您可能还需要提供自己的 value() 属性实现。

from PyQt4 import QtCore, QtGui
from math import sin, cos, atan2, degrees, radians
import sys

class Dial(QtGui.QDial):
MinValue, MidValue, MaxValue = -1, 0, 1
__valueChanged = QtCore.pyqtSignal(int)

def __init__(self, valueRange=120):
QtGui.QDial.__init__(self)
self.setWrapping(True)
self.setRange(0, 359)
self.valueChanged.connect(self.emitSanitizedValue)
self.valueChanged = self.__valueChanged
self.valueRange = valueRange
self.__midValue = valueRange / 2
self.setPageStep(valueRange)
self.setSingleStep(valueRange)
QtGui.QDial.setValue(self, 180)
self.oldValue = None
# uncomment this if you want to emit the changed value only when releasing the slider
# self.setTracking(False)
self.notchSize = 5
self.notchPen = QtGui.QPen(QtCore.Qt.black, 2)
self.actionTriggered.connect(self.checkAction)

def emitSanitizedValue(self, value):
if value < 180:
self.valueChanged.emit(self.MinValue)
elif value > 180:
self.valueChanged.emit(self.MaxValue)
else:
self.valueChanged.emit(self.MidValue)

def checkAction(self, action):
value = self.sliderPosition()
if action in (self.SliderSingleStepAdd, self.SliderPageStepAdd) and value < 180:
value = 180 + self.valueRange
elif action in (self.SliderSingleStepSub, self.SliderPageStepSub) and value > 180:
value = 180 - self.valueRange
elif value < 180:
value = 180 - self.valueRange
elif value > 180:
value = 180 + self.valueRange
else:
value = 180
self.setSliderPosition(value)

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)
QtGui.QDial.setValue(self, final)
self.blockSignals(False)
return value

def value(self):
rawValue = QtGui.QDial.value(self)
if rawValue < 180:
return self.MinValue
elif rawValue > 180:
return self.MaxValue
return self.MidValue

def setValue(self, value):
if value < 0:
QtGui.QDial.setValue(self, 180 - self.valueRange)
elif value > 0:
QtGui.QDial.setValue(self, 180 + self.valueRange)
else:
QtGui.QDial.setValue(self, 180)

def mousePressEvent(self, event):
self.oldValue = self.value()
value = self.valueFromPosition(event.pos())
if self.hasTracking() and self.oldValue != value:
self.oldValue = value
self.valueChanged.emit(value)

def mouseMoveEvent(self, event):
value = self.valueFromPosition(event.pos())
if self.hasTracking() and self.oldValue != value:
self.oldValue = value
self.valueChanged.emit(value)

def mouseReleaseEvent(self, event):
value = self.valueFromPosition(event.pos())
if self.oldValue != value:
self.valueChanged.emit(value)

def wheelEvent(self, event):
delta = event.delta()
oldValue = QtGui.QDial.value(self)
if oldValue < 180:
if delta < 0:
outValue = self.MinValue
value = 180 - self.valueRange
else:
outValue = self.MidValue
value = 180
elif oldValue == 180:
if delta < 0:
outValue = self.MinValue
value = 180 - self.valueRange
else:
outValue = self.MaxValue
value = 180 + self.valueRange
else:
if delta < 0:
outValue = self.MidValue
value = 180
else:
outValue = self.MaxValue
value = 180 + self.valueRange
self.blockSignals(True)
QtGui.QDial.setValue(self, value)
self.blockSignals(False)
if oldValue != value:
self.valueChanged.emit(outValue)

def paintEvent(self, event):
QtGui.QDial.paintEvent(self, event)
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
qp.translate(.5, .5)
rad = radians(self.valueRange)
qp.setPen(self.notchPen)
c = -cos(rad)
s = sin(rad)
# use minimal size to ensure that the circle used for notches
# is always adapted to the actual dial size if the widget has
# width/height ratio very different from 1.0
maxSize = min(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(QtGui.QWidget):
def __init__(self, *sizes):
QtGui.QWidget.__init__(self)
layout = QtGui.QGridLayout()
self.setLayout(layout)
if not sizes:
sizes = 70, 90, 120
self.dials = []
for col, size in enumerate(sizes):
label = QtGui.QLabel(str(size))
label.setAlignment(QtCore.Qt.AlignCenter)
dial = Dial(size)
self.dials.append(dial)
dial.valueChanged.connect(lambda value, dial=col: self.dialChanged(dial, value))
layout.addWidget(label, 0, col)
layout.addWidget(dial, 1, col)

def dialChanged(self, dial, value):
print('dial {} changed to {}'.format(dial, value))

def setDialValue(self, dial, value):
self.dials[dial].setValue(value)

if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dialexample = Test(70, 90, 120)
# Change values here
dialexample.setDialValue(1, 1)
dialexample.show()
sys.exit(app.exec_())

编辑:我更新了代码以实现键盘导航并避免不必要的多重信号发射。

关于python - 使用 PyQt 自定义 QDial notch ticks,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57119714/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com