How to receive mouse events for QDoubleSpinBox
-
@summit said in How to receive mouse events for QDoubleSpinBox:
does not get invoked when i click on the spinbox.
It does... But only if you click the spinBox Widget, not the lineEdit or something else.
If you click theup
anddown
buttons, it should printMouse pressed
(your code worked for me, at least).Add
override
to your function definition and dont forget to pass the event to the base class after you did your stuff (unless you want to interrupt that)void SumDoubleBox::mousePressEvent(QMouseEvent* event) { qDebug() << "Mouse pressed"; QDoubleSpinBox::mousePressEvent(event); }
-
@summit
Hi
you can either set your own LineEdit with mouse press override to the
spinBox.
https://doc.qt.io/qt-5/qabstractspinbox.html#setLineEditor use event filter on the LineEdit to catch the click.
https://forum.qt.io/topic/67705/capture-mouse-click-on-qlineedit -
@summit said in How to receive mouse events for QDoubleSpinBox:
Can you please guide me towards some tutorial
Why do you need a tutorial for that?
Create a QLineEdit instance and pass its pointer to setLineEdit()... -
Hello!
Is there any way to get get the click events of the up and down buttons separately in the spin box? -
@shreya_agrawal - No direct way without overriding appropriate mouseEvent methods. As work-around you can check the change in value. You can decide up or down based on value increment/decrement.
-
You could use the
valueChanged
signal and check if the new value is higher or lower than the old one. Then you know what button what possibly clicked... (or what value was added via typing, if you can live with that) -
@dheerendra
Thank you for your reply !
Are you aware of any mouse events which can be used for this, so I don't have to use the valueChanged signal?
I did find a workaround by using rectangles inside the spin box to distinguish between up and down buttons, but I don't think this is the most accurate approach. -
@shreya_agrawal
Have you investigated something based on:for (QWidget *child: spinBox.findChildren<QWidget *>()) child->installEventFilter(this);
I am not sure whether the buttons you want are widgets or (as may well be the case) simply drawn as part of the style. The above will at least show where actual widgets are.
-
@shreya_agrawal
Yes, I think I read that. Sadly it means the arrows are just drawn as part of the style, they are not widgets you can connect to. Which should mean click is recognised by coordinates internally in spinbox code. Not what you wanted. You might examine Qt source code if you really need to know how it does it. It does sound as though working onvalueChanged()
, which the internal code must resolve via mouse position relative to arrow drawing, would be the simplest "high level" approach if you want to avoid duplicating internal coordinate calculation. -
override mousePressEvent and get the positions of the spinboxes through the style with subElementRect() - this is what QAbstractSpinBox is doing
-
@shreya_agrawal
I've had a look at the on-line source (I don't have source files myself). Although I haven't gone all the way the through, the gist seems to be that Qt notes whether you mouse down on what it maintains as an internal "hover control", which will be the up arrow, down arrow or nothing.The relevant functions are https://codebrowser.dev/qt6/qtbase/src/widgets/widgets/qabstractspinbox.cpp.html#_ZN16QAbstractSpinBox15mousePressEventEP11QMouseEvent
void QAbstractSpinBox::mousePressEvent(QMouseEvent *event) { Q_D(QAbstractSpinBox); d->keyboardModifiers = event->modifiers(); if (event->button() != Qt::LeftButton || d->buttonState != None) { return; } d->updateHoverControl(event->position().toPoint()); event->accept(); const StepEnabled se = (d->buttonSymbols == NoButtons) ? StepEnabled(StepNone) : stepEnabled(); if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp) { d->updateState(true); } else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown) { d->updateState(false); } else { event->ignore(); } }
and the call to
d->updateHoverControl(event->position().toPoint());
https://codebrowser.dev/qt6/qtbase/src/widgets/widgets/qabstractspinbox.cpp.html#_ZN23QAbstractSpinBoxPrivate18updateHoverControlERK6QPoint/*! \internal Updates the old and new hover control. Does nothing if the hover control has not changed. */ bool QAbstractSpinBoxPrivate::updateHoverControl(const QPoint &pos) { Q_Q(QAbstractSpinBox); QRect lastHoverRect = hoverRect; QStyle::SubControl lastHoverControl = hoverControl; bool doesHover = q->testAttribute(Qt::WA_Hover); if (lastHoverControl != newHoverControl(pos) && doesHover) { q->update(lastHoverRect); q->update(hoverRect); return true; } return !doesHover; } /*! \internal Returns the hover control at \a pos. This will update the hoverRect and hoverControl. */ QStyle::SubControl QAbstractSpinBoxPrivate::newHoverControl(const QPoint &pos) { Q_Q(QAbstractSpinBox); QStyleOptionSpinBox opt; q->initStyleOption(&opt); opt.subControls = QStyle::SC_All; hoverControl = q->style()->hitTestComplexControl(QStyle::CC_SpinBox, &opt, pos, q); hoverRect = q->style()->subControlRect(QStyle::CC_SpinBox, &opt, hoverControl, q); return hoverControl; }
So ultimately I think it relies on
hoverControl = q->style()->hitTestComplexControl(QStyle::CC_SpinBox, &opt, pos, q); hoverRect = q->style()->subControlRect(QStyle::CC_SpinBox, &opt, hoverControl, q);
to find the location of the arrow buttons. And https://codebrowser.dev/qt6/qtbase/src/widgets/widgets/qabstractspinbox.cpp.html#_ZNK16QAbstractSpinBox15initStyleOptionEP19QStyleOptionSpinBox includes
if (d->buttonSymbols != QAbstractSpinBox::NoButtons) { option->subControls |= QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown; if (d->buttonState & Up) { option->activeSubControls = QStyle::SC_SpinBoxUp; } else if (d->buttonState & Down) { option->activeSubControls = QStyle::SC_SpinBoxDown; }
so this is to do with recognising which button ultimately.
So like I said unless you want to replicate this (see
QStyle
for some of the calls) it would be easier for you to react to the generatedvalueChanged()
signal. -
@Christian-Ehrlicher said in How to receive mouse events for QDoubleSpinBox:
override mousePressEvent and get the positions of the spinboxes through the style with subElementRect() - this is what QAbstractSpinBox is doing
Ooohh, you posted this while I was doing all my source code searching! Not sure how your
subElementRect()
relates to thesubControlRect()
I have come across in the code above. -
@shreya_agrawal
Ah ha! I little Googling leads me to QSpinbox Check if up or down button is pressed which seems to give the relatively simple codeclass SpinBox(QSpinBox): upClicked = pyqtSignal() downClicked = pyqtSignal() def mousePressEvent(self, event): super().mousePressEvent(event) opt = QStyleOptionSpinBox() self.initStyleOption(opt) control = self.style().hitTestComplexControl( QStyle.CC_SpinBox, opt, event.pos(), self ) if control == QStyle.SC_SpinBoxUp: self.upClicked.emit() elif control == QStyle.SC_SpinBoxDown: self.downClicked.emit()
@eyllanesc's answers & code are usually good, so it may be this simple.
-
@JonB
Well thank you for sparing so much of your time!
Sadly, the Qt version I am using does not support the use of SC_SpinBoxUp and SC_SpinBoxDown subcontrols :(
I think I will have to rely on the valueChanged() signal for now. -
@shreya_agrawal said in How to receive mouse events for QDoubleSpinBox:
Sadly, the Qt version I am using does not support the use of SC_SpinBoxUp and SC_SpinBoxDown subcontrols :(
Pardon? State your exact Qt version. Since it is available in Qt4, 5, 6 I doubt your statement.
Show the code you have tried and the error message you get. Don't forget that Python'sQStyle.SC_SpinBoxUp
is C++'sQStyle::SC_SpinBoxUp
.... -
@JonB
Qt Version: Qt Creator 4.12.2 Based on Qt 5.14.2 (GCC 5.3.1 20160406 (Red Hat 5.3.1-6), 64 bit)Line of code: QRect downButtonRect = spinBox->style()->subElementRect(QStyle::SE_SpinBoxDown, spinBox);
Error Message: no member named 'SE_SpinBoxDown' in 'QStyle'