Increases / decreases QSpinBox value when dragging with the mouse - python / pyside

How can I do this, when the user clicks the up or down arrow of the QSpinBox, the value will increase as the cursor moves, and the value will decrease when dragging down. I like this feature, very useful for users to be able to just click and drag the cursor than constantly click on errors. Here is the source code for a counter created in C # that works the way I would like in 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()
+4
source share
4

, , ! , Max pyside, , , ( "" Max ).

, . 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 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()
+3

, - Google.

, . , "" QSpinBox QDoubleSpinBox . :

////////////////////////////////////////////////////////////////////////////////
// Label for a QSpinBox or QDoubleSpinBox (or derivatives) that scrubs the spinbox value on click-drag
//
// Notes:
//  - Cursor is hidden and cursor position remains fixed during the drag
//  - Holding 'Ctrl' reduces the speed of the scrub
//  - Scrub multipliers are currently hardcoded - may want to make that a parameter in the future
template <typename SpinBoxT, typename ValueT>
class SpinBoxLabel : public QLabel
{
public:
    SpinBoxLabel(const QString& labelText, SpinBoxT& buddy)
        : QLabel(labelText)
        , Buddy(&buddy)
    {
        setBuddy(&buddy);
    }

protected:
    virtual void mouseMoveEvent(QMouseEvent* event) override {
        if (!(event->buttons() & Qt::LeftButton))
            return QLabel::mouseMoveEvent(event);

        if (!IsDragging) {
            StartDragPos = QCursor::pos();
            Value = double(Buddy->value());
            IsDragging = true;
            QApplication::setOverrideCursor(Qt::BlankCursor);
        }
        else {
            int dragDist = QCursor::pos().x() - StartDragPos.x();
            if (dragDist == 0)
                return;

            double dragMultiplier = .25 * Buddy->singleStep();
            if (!(event->modifiers() & Qt::ControlModifier))
                dragMultiplier *= 10.0;

            Value += dragMultiplier * dragDist;

            Buddy->setValue(ValueT(Value));

            QCursor::setPos(StartDragPos);
        }
    }

    virtual void mouseReleaseEvent(QMouseEvent* event) override {
        if (!IsDragging || event->button() != Qt::LeftButton)
            return QLabel::mouseReleaseEvent(event);

        IsDragging = false;
        QApplication::restoreOverrideCursor();
    }

private:
    SpinBoxT* Buddy;
    bool IsDragging = false;
    QPoint StartDragPos;
    double Value = 0.0;
};

typedef SpinBoxLabel<QDoubleSpinBox, double> DoubleSpinBoxLabel;
typedef SpinBoxLabel<QSpinBox, int> IntSpinBoxLabel;
+1

, , , , , , . , .

QLineEdit . , , , .

, . , Maya Max, , . , :

from PySide2 import QtCore
from PySide2 import QtGui
from PySide2 import QtWidgets


class CustomSpinBox(QtWidgets.QLineEdit):

    """
    Tries to mimic behavior from Maya internal slider that 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, , . , , , :).

:

  • , ,
  • Ctrl, , Shift,
  • , ( Ctrl )

Example

0

QAbstractSpinBox.setAccelerated:

    self.spinFrameCountA.setAccelerated(True)

, .

-1

All Articles