当用户点击QSpinBox的向上或向下箭头时,我怎么能这样做,当光标向上拖动时,该值会增加,如果向下拖动则值会减小。我喜欢这个功能非常有用,用户只需单击并拖动光标,就可以不断点击错误。这是C#中使用的spinner的参考源代码,它在python中以我希望的方式工作。 http://www.paulneale.com/tutorials/dotNet/numericUpDown/numericUpDown.htm
import sys
from PySide import QtGui, QtCore
class Wrap_Spinner( QtGui.QSpinBox ):
def __init__( self, minVal=0, maxVal=100, default=0):
super( Wrap_Spinner, self ).__init__()
self.drag_origin = None
self.setRange( minVal, maxVal )
self.setValue( default)
def get_is_dragging( self ):
# are we the widget that is also the active mouseGrabber?
return self.mouseGrabber( ) == self
### Dragging Handling Methods ################################################
def do_drag_start( self ):
# Record position
# Grab mouse
self.drag_origin = QtGui.QCursor( ).pos( )
self.grabMouse( )
def do_drag_update( self ):
# Transpose the motion into values as a delta off of the recorded click position
curPos = QtGui.QCursor( ).pos( )
offsetVal = self.drag_origin.y( ) - curPos.y( )
self.setValue( offsetVal )
print offsetVal
def do_drag_end( self ):
self.releaseMouse( )
# Restore position
# Reset drag origin value
self.drag_origin = None
### Mouse Override Methods ################################################
def mousePressEvent( self, event ):
if QtCore.Qt.LeftButton:
print 'start drag'
self.do_drag_start( )
elif self.get_is_dragging( ) and QtCore.Qt.RightButton:
# Cancel the drag
self.do_drag_end( )
else:
super( Wrap_Spinner, self ).mouseReleaseEvent( event )
def mouseMoveEvent( self, event ):
if self.get_is_dragging( ):
self.do_drag_update( )
else:
super( Wrap_Spinner, self ).mouseReleaseEvent( event )
def mouseReleaseEvent( self, event ):
if self.get_is_dragging( ) and QtCore.Qt.LeftButton:
print 'finish drag'
self.do_drag_end( )
else:
super( Wrap_Spinner, self ).mouseReleaseEvent( event )
class Example(QtGui.QWidget ):
def __init__( self):
super( Example, self ).__init__( )
self.initUI( )
def initUI( self ):
self.spinFrameCountA = Wrap_Spinner( 2, 50, 40)
self.spinB = Wrap_Spinner( 0, 100, 10)
self.positionLabel = QtGui.QLabel( 'POS:' )
grid = QtGui.QGridLayout( )
grid.setSpacing( 0 )
grid.addWidget( self.spinFrameCountA, 0, 0, 1, 1 )
grid.addWidget( self.spinB, 1, 0, 1, 1 )
grid.addWidget( self.positionLabel, 2, 0, 1, 1 )
self.setLayout( grid )
self.setGeometry( 800, 400, 200, 150 )
self.setWindowTitle( 'Max Style Spinner' )
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint)
self.show( )
def main( ):
app = QtGui.QApplication( sys.argv )
ex = Example( )
sys.exit( app.exec_( ) )
if __name__ == '__main__':
main()
答案 0 :(得分:3)
我是你插件的忠实粉丝,所以我很高兴能为你解答这个问题!我假设你正在编写一个pyside中的Max插件,因为当我遇到同样的问题时,这正是我正在做的事情(我也喜欢Max默认的“scrubby”微调器)。
解决方案实际上非常简单,您只需手动完成即可。我将QSpinBox子类化并捕获鼠标事件,使用它来计算相对于第一次开始单击窗口小部件时的y位置。这是代码,这是pyside2,因为从3DS Max和Maya 2018开始,这就是Autodesk所使用的:
from PySide2 import QtWidgets, QtGui, QtCore
import MaxPlus
class SampleUI(QtWidgets.QDialog):
def __init__(self, parent=MaxPlus.GetQMaxMainWindow()):
super(SampleUI, self).__init__(parent)
self.setWindowTitle("Max-style spinner")
self.initUI()
MaxPlus.CUI.DisableAccelerators()
def initUI(self):
mainLayout = QtWidgets.QHBoxLayout()
lbl1 = QtWidgets.QLabel("Test Spinner:")
self.spinner = SuperSpinner(self)
#self.spinner = QtWidgets.QSpinBox() -- here's the old version
self.spinner.setMaximum(99999)
mainLayout.addWidget(lbl1)
mainLayout.addWidget(self.spinner)
self.setLayout(mainLayout)
def closeEvent(self, e):
MaxPlus.CUI.EnableAccelerators()
class SuperSpinner(QtWidgets.QSpinBox):
def __init__(self, parent):
super(SuperSpinner, self).__init__(parent)
self.mouseStartPosY = 0
self.startValue = 0
def mousePressEvent(self, e):
super(SuperSpinner, self).mousePressEvent(e)
self.mouseStartPosY = e.pos().y()
self.startValue = self.value()
def mouseMoveEvent(self, e):
self.setCursor(QtCore.Qt.SizeVerCursor)
multiplier = .5
valueOffset = int((self.mouseStartPosY - e.pos().y()) * multiplier)
print valueOffset
self.setValue(self.startValue + valueOffset)
def mouseReleaseEvent(self, e):
super(SuperSpinner, self).mouseReleaseEvent(e)
self.unsetCursor()
if __name__ == "__main__":
try:
ui.close()
except:
pass
ui = SampleUI()
ui.show()
答案 1 :(得分:1)
我遇到了同样的问题,不幸的是,我发现的解决方案仅在您单击并从箭头或旋转框的边界拖动时才起作用。但是大多数用户都希望从实际的文本字段中进行拖动,因此这样做并不直观。
相反,您可以继承QLineEdit
以获得正确的行为。当您单击它时,它将保存其当前值,以便在用户拖动它时获得鼠标位置的增量并将其重新应用到旋转框上。
这是我使用自己的一个完整示例。但是很抱歉,它采用Maya的属性样式,而不是Max的属性样式,因此您单击并拖动鼠标中键来设置值。通过一些调整,您可以轻松地使其完全像Max一样工作:
from PySide2 import QtCore
from PySide2 import QtGui
from PySide2 import QtWidgets
class CustomSpinBox(QtWidgets.QLineEdit):
"""
Tries to mimic behavior from Maya's internal slider that's found in the channel box.
"""
IntSpinBox = 0
DoubleSpinBox = 1
def __init__(self, spinbox_type, value=0, parent=None):
super(CustomSpinBox, self).__init__(parent)
self.setToolTip(
"Hold and drag middle mouse button to adjust the value\n"
"(Hold CTRL or SHIFT change rate)")
if spinbox_type == CustomSpinBox.IntSpinBox:
self.setValidator(QtGui.QIntValidator(parent=self))
else:
self.setValidator(QtGui.QDoubleValidator(parent=self))
self.spinbox_type = spinbox_type
self.min = None
self.max = None
self.steps = 1
self.value_at_press = None
self.pos_at_press = None
self.setValue(value)
def wheelEvent(self, event):
super(CustomSpinBox, self).wheelEvent(event)
steps_mult = self.getStepsMultiplier(event)
if event.delta() > 0:
self.setValue(self.value() + self.steps * steps_mult)
else:
self.setValue(self.value() - self.steps * steps_mult)
def mousePressEvent(self, event):
if event.buttons() == QtCore.Qt.MiddleButton:
self.value_at_press = self.value()
self.pos_at_press = event.pos()
self.setCursor(QtGui.QCursor(QtCore.Qt.SizeHorCursor))
else:
super(CustomSpinBox, self).mousePressEvent(event)
self.selectAll()
def mouseReleaseEvent(self, event):
if event.button() == QtCore.Qt.MiddleButton:
self.value_at_press = None
self.pos_at_press = None
self.setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor))
return
super(CustomSpinBox, self).mouseReleaseEvent(event)
def mouseMoveEvent(self, event):
if event.buttons() != QtCore.Qt.MiddleButton:
return
if self.pos_at_press is None:
return
steps_mult = self.getStepsMultiplier(event)
delta = event.pos().x() - self.pos_at_press.x()
delta /= 6 # Make movement less sensitive.
delta *= self.steps * steps_mult
value = self.value_at_press + delta
self.setValue(value)
super(CustomSpinBox, self).mouseMoveEvent(event)
def getStepsMultiplier(self, event):
steps_mult = 1
if event.modifiers() == QtCore.Qt.CTRL:
steps_mult = 10
elif event.modifiers() == QtCore.Qt.SHIFT:
steps_mult = 0.1
return steps_mult
def setMinimum(self, value):
self.min = value
def setMaximum(self, value):
self.max = value
def setSteps(self, steps):
if self.spinbox_type == CustomSpinBox.IntSpinBox:
self.steps = max(steps, 1)
else:
self.steps = steps
def value(self):
if self.spinbox_type == CustomSpinBox.IntSpinBox:
return int(self.text())
else:
return float(self.text())
def setValue(self, value):
if self.min is not None:
value = max(value, self.min)
if self.max is not None:
value = min(value, self.max)
if self.spinbox_type == CustomSpinBox.IntSpinBox:
self.setText(str(int(value)))
else:
self.setText(str(float(value)))
class MyTool(QtWidgets.QWidget):
"""
Example of how to use the spinbox.
"""
def __init__(self, parent=None):
super(MyTool, self).__init__(parent)
self.setWindowTitle("Custom spinboxes")
self.resize(300, 150)
self.int_spinbox = CustomSpinBox(CustomSpinBox.IntSpinBox, parent=self)
self.int_spinbox.setMinimum(-50)
self.int_spinbox.setMaximum(100)
self.float_spinbox = CustomSpinBox(CustomSpinBox.DoubleSpinBox, parent=self)
self.float_spinbox.setSteps(0.1)
self.main_layout = QtWidgets.QVBoxLayout()
self.main_layout.addWidget(self.int_spinbox)
self.main_layout.addWidget(self.float_spinbox)
self.setLayout(self.main_layout)
# Run the tool.
global tool_instance
tool_instance = MyTool()
tool_instance.show()
我试图使函数与Qt的本机spinBox
相匹配。在我的情况下,我不需要它,但是当释放值改变时,添加信号很容易。像Houdini的滑块一样,将其提升到一个新的水平也很容易,因此步速可以根据鼠标的垂直位置而变化。 ah,也许是下雨天:)。
这是现在的功能:
答案 2 :(得分:-1)
可以使用QAbstractSpinBox.setAccelerated:
更改旋转框增量的速度 self.spinFrameCountA.setAccelerated(True)
启用此功能后,按住鼠标按钮的时间越长,旋转框值的变化就会越快。