Why is my child widget enabled inside an unchecked QGroupBox? - c++

When I enable a child of my QGroupBox after unchecking the group box, that child is enabled. But if I do the same to a grandchild widget, that widget remains disabled. I would expect all children of an unchecked group box to act the same as all children of a disabled parent widget (with respect to being enabled).
Gist of the code used to create the image above.
What should I do to ensure my child automatically remains disabled, even if I call child.setEnabled(true) after the group box is unchecked?
I am using Qt 5.9.1.

This appears to be a known bug.
At the moment, you need to know if the parent group box is checked to call setEnabled on a child widget:
child.setEnabled(groupBox.isChecked() && otherCondition);

That happens because a QGroupBox itself is not disabled when the checkbox is off, and thus normal widget enablement propagation doesn't apply to this behavior. This workaround is IMHO sensible. The only possible workaround via public APIs would be to add a viewport child widget in the groupbox, and make the everything a child of that viewport:
class GroupBoxViewport : public QWidget {
Q_OBJECT
void updateGeometry() {
if (parent())
setGeometry(parentWidget()->contentsRect());
}
void newParent() {
if (parent()) {
parent()->installEventFilter(this);
updateGeometry();
}
}
protected:
bool eventFilter(QObject *obj, QEvent *ev) override {
if (obj == parent() && ev->type() == QEvent::Resize)
updateGeometry();
return QWidget::eventFilter(obj, ev);
}
bool event(QEvent *ev) override {
if (ev->type() == QEvent::ParentAboutToChange) {
if (parent())
parent()->uninstallEventFilter(this);
} else if (ev->type() == QEvent::ParentChange)
newParent();
return QWidget::event(ev);
}
public:
QWidget(QWidget *parent = {}) : QWidget(parent) {
newParent();
}
};
Then, set the layout on and add all the children to the viewport:
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QGroupBox box("The Group");
GroupBoxViewPort viewport(&box);
QVBoxLayout layout(&viewport);
QLabel label("A, uh, member of the group");
layout.addwidget(&label);
box.show();
return app.exec();
}

Related

How do I capture the key press event for an edit cell of a QTableWidget?

Here are 2 answers for capturing the key press event for QTableWidget.
How to create a SIGNAL for QTableWidget from keyboard?
Follow the way above, I can "hook" key press event, When I press space, the background color becomes red.
However, it only works for a selected cell, but not for a in-editing cell.
When it's in editing state, the 2 ways both fail. I can type space freely.
When it's in "editing state" there is editor widget atop QTableWidget (item delegate) which receives key events, and since it's on top you can't see cell content and cell background behind it. But you can access and "hook" this events by setting QAbstractItemDelegate to QTableWidget.
// itemdelegate.h
class ItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ItemDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent)
{
}
bool eventFilter(QObject *object, QEvent *event) override
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Space) {
qDebug() << "space pressed";
}
}
return false;
}
};
// main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTableWidget* widget = new QTableWidget();
widget->setColumnCount(2);
widget->setRowCount(2);
widget->setItemDelegate(new ItemDelegate(widget));
widget->show();
return a.exec();
}

My Qt eventFilter() doesn't stop events as it should

Something is fundamentally wrong with my eventFilter, as it lets every single event through, while I want to stop everything. I've read lots of documentation on QEvent, eventFilter() and so on, but obviously I'm missing something big. Essentially, I'm trying to create my own modal-functionality for my popup-window class based on QDialog. I want to implement my own since the built-in setModal(true) includes a lot of features, e.g. playing QApplication::Beep(), that I want to exclude. Basically, I want to discard all events going to the QWidget (window) that created my popup. What I have so far is,
// popupdialog.h
#ifndef POPUPDIALOG_H
#define POPUPDIALOG_H
#include <QDialog>
#include <QString>
namespace Ui {class PopupDialog;}
class PopupDialog : public QDialog
{
Q_OBJECT
public:
explicit PopupDialog(QWidget *window=0, QString messageText="");
~PopupDialog();
private:
Ui::PopupDialog *ui;
QString messageText;
QWidget window; // the window that caused/created the popup
void mouseReleaseEvent(QMouseEvent*); // popup closes when clicked on
bool eventFilter(QObject *, QEvent*);
};
...
// popupdialog.cpp
#include "popupdialog.h"
#include "ui_popupdialog.h"
PopupDialog::PopupDialog(QWidget *window, QString messageText) :
QDialog(NULL), // parentless
ui(new Ui::PopupDialog),
messageText(messageText),
window(window)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true); // Prevents memory leak
setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
ui->message_text_display->setText(messageText);
window->installEventFilter(this);
//this->installEventFilter(window); // tried this also, just to be sure ..
}
PopupDialog::~PopupDialog()
{
window->removeEventFilter(this);
delete ui;
}
// popup closes when clicked on
void PopupDialog::mouseReleaseEvent(QMouseEvent *e)
{
close();
}
Here's the problem, the filter doesn't work. Note that if I write a std::cout
inside the if(...), I see that it does trigger whenever events are sent to window, it just doesn't stop them.
bool PopupDialog::eventFilter(QObject *obj, QEvent *e)
{
if( obj == window )
return true; //should discard the signal (?)
else
return false; // I tried setting this to 'true' also without success
}
When the user interacts with the main program, a PopupDialog can be created like this:
PopupDialog *popup_msg = new PopupDialog(ptr_to_source_window, "some text message");
popup_msg->show();
// I understand that naming the source 'window' might be a little confusing.
// I apologise for that. The source can in fact be any 'QWidget'.
Everything else works as expected. Only the event filter fails. I want the filter to remove events sent to the window that created the popup; like mouse clicking and key pressing, until the popup is closed. I'm expecting to be extremely embarrassed when someone points out a trivial fix in my code.
You have to ignore all events that arrive in the widget tree of the window. Therefore, you need to install the eventFilter application-wide and check, if the object you are filtering on is a descendant of window. In other words: Replace
window->installEventFilter(this);
by
QCoreApplication::instance()->installEventFilter(this);
and implement the event filter function this way:
bool PopupDialog::eventFilter(QObject *obj, QEvent *e)
{
if ( !dynamic_cast<QInputEvent*>( event ) )
return false;
while ( obj != NULL )
{
if( obj == window )
return true;
obj = obj->parent();
}
return false;
}
I tried it, tested it and it worked for me.
Note: Using event filters in Qt is a bit messy in my experience, since it is not quite transparent what is happening. Expect bugs to pop up from time to time. You may consider disabling the main window instead, if you and your clients don't have a problem with the grayed-out main window as a consequence.
After the massive amount of responses, feedback, suggestions and time ivested in extensive research I've finally found what I believe to be the optimal, and safest solution. I wish to express my sincere gratidtude to everyone for their aid to what Kuba Ober describes as "(...) not as simple of a problem as you think".
We want to filter out all certain events from a widget, including its children. This is difficult, because events may be caught in the childrens default eventfilters and responded to, before they are caught and filtered by the the parent's custom filter for which the programmer implements. The following code solves this problem by installing the filter on all children upon their creation. This example assumes the use of Qt Creator UI-forms and is based on the following blog post: How to install eventfilters for all children.
// The widget class (based on QMainWindow, but could be anything) for
// which you want to install the event filter on, includings its children
class WidgetClassToBeFiltered : public QMainWindow
{
Q_OBJECT
public:
explicit WidgetClassToBeFiltered(QWidget *parent = 0);
~WidgetClassToBeFiltered();
private:
bool eventFilter(QObject*, QEvent*);
Ui::WidgetClassToBeFiltered *ui;
};
...
WidgetClassToBeFiltered::WidgetClassToBeFiltered(QWidget *parent) :
QMainWindow(parent), // Base Class constructor
ui(new Ui::WidgetClassToBeFiltered)
{
installEventFilter(this); // install filter BEFORE setupUI.
ui->setupUi(this);
}
...
bool WidgetClassToBeFiltered::eventFilter(QObject *obj, QEvent* e)
{
if( e->type() == QEvent::ChildAdded ) // install eventfilter on children
{
QChildEvent *ce = static_cast<QChildEvent*>(e);
ce->child()->installEventFilter(this);
}
else if( e->type() == QEvent::ChildRemoved ) // remove eventfilter from children
{
QChildEvent *ce = static_cast<QChildEvent*>(e);
ce->child()->removeEventFilter(this);
}
else if( (e->type() == QEvent::MouseButtonRelease) ) // e.g. filter out Mouse Buttons Relases
{
// do whatever ..
return true; // filter these events out
}
return QWidget::eventFilter( obj, e ); // apply default filter
}
Note that this works, because the eventfilter installs itself on added children! Hence, it should also work without the use of UI-forms.
Refer this code to filter out specific event:-
class MainWindow : public QMainWindow
{
public:
MainWindow();
protected:
bool eventFilter(QObject *obj, QEvent *ev);
private:
QTextEdit *textEdit;
};
MainWindow::MainWindow()
{
textEdit = new QTextEdit;
setCentralWidget(textEdit);
textEdit->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == textEdit) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
qDebug() << "Ate key press" << keyEvent->key();
return true;
} else {
return false;
}
} else {
// pass the event on to the parent class
return QMainWindow::eventFilter(obj, event);
}
}
If you want to set more specific event filter on multiple widgets you can refer following code:
class KeyPressEater : public QObject
{
Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event);
};
bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
qDebug("Ate key press %d", keyEvent->key());
return true;
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}
}
KeyPressEater *keyPressEater = new KeyPressEater(this);
QPushButton *pushButton = new QPushButton(this);
pushButton->installEventFilter(keyPressEater);

Qt event filter hides the widget

I have a parent widget inside which I have to place a custom widget (say a QFrame). Inside that custom widget, I have to place a number of child widgets (derived from QPushButton). I want the child widgets to have a certain background under normal circumstances, and another one when hovered upon. This is my code:
//parent widget code, where the QFrame derived widget is initialized
QFrameDerivedWidget *qFrameWidget = new QFrameDerivedWidget(this, someString);
This is the QFrameDerivedWidget header file:
//QFrameDerivedWidget header file
class QFrameDerivedWidget: public QFrame
{
Q_OBJECT
public:
QFrameDerivedWidget(QWidget *aParent,
std::string someValue);
bool eventFilter(QObject *obj, QEvent *event);
}
This is the QFrameDerivedWidget implementation file, the ChildWidget class is defined and declared inline:
class ChildWidget: public QPushButton
{
Q_Object
public:
ChildWidget(std::string aText, QWidget *aParent);
};
ChildWidget::ChildWidget(std::string aText, QWidget *aParent):
QPushButton(QString::fromStdString(aText),aParent)
{
this->setFixedHeight(30);
this->setMouseTracking(true);
this->setCursor(Qt::PointingHandCursor);
/* ---other custom styling--- */
}
bool QFrameDerivedWidget::eventFilter(QObject *obj, QEvent *event)
{
// this never prints out anything, even though it should for any mouseenter, mouseleave, click, etc event on it
qDebug() << obj->metaObject()->className() << endl;
if (obj->metaObject()->className() == "ChildWidget")
{
//including this line throws a 'missing QObject missing macro error' as well
ChildWidget *option = qobject_cast<ChildWidget* >(obj);
if (event->type() == QEvent::Enter)
{
option->setStyleSheet("---");
}
if (event->type() == QEvent::Leave)
{
option->setStyleSheet("---");
}
return QWidget::eventFilter(obj, event);
}
else
{
// pass the event on to the parent class
return QWidget::eventFilter(obj, event);
}
}
QFrameDerivedWidget::QFrameDerivedWidget(QWidget *aParent,
std::string someVal): fParent(aParent)
{
initUI();
}
QFrameDerivedWidget::initUI()
{
this->setParent(fParent);
this->setAttribute(Qt::WA_Hover);
this->setMouseTracking(true);
QWidget *dd = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout();
dd->setLayout(layout);
for (int i = 0; i < fValues.size(); i++)
{
ChildWidget *button = new ChildWidget(fValues[i],dd);
button->addEventFilter(this);
layout->addWidget(button);
}
}
The idea is, whenever I hover over the QFrameDerivedWidget and enter any ChildWidget, its background color should change. Additionally, I have set a qDebug() statement inside the eventFilter. It is currently not working, the ChildWidget buttons are not visible, but they are there, for the cursor turns pointer when I hover over where they are supposed to be.
What am I doing wrong, and how do I make it work?
You forget to add Q_OBJECT macro in ChildWidget declaration
You need to track mouse (setMouseTracking(true))
You need to set setAttribute(Qt::WA_Hover) to your widget
Be sure that you really need to return true; in your event filter, instead of returning QWidget::eventFilter(obj, event);. You don't need to filter out hover events.

Dark transparent layer over a QMainWindow in Qt

I need to implement a "Loading..." window in my application but I do prefer to cover the whole QMainWindow with a dark transparent layer with a text above. Does anybody know how to do that? I am not sure how to overlap widgets/layouts in Qt. Any help will be appreciated.
This answer is in a series of my overlay-related answers: first, second, third.
The most trivial solution is to simply add a child transparent widget to QMainWindow. That widget must merely track the size of its parent window. It is important to properly handle changes of widget parentage, and the z-order with siblings. Below is a correct example of how to do it.
If you want to stack overlays, subsequent overlays should be the children of OverlayWidget, in the z-order. If they were to be siblings of the OverlayWidget, their stacking order is undefined.
This solution has the benefit of providing minimal coupling to other code. It doesn't require any knowledge from the widget you apply the overlay to. You can apply the overlay to a QMainWindow or any other widget, the widget can also be in a layout.
Reimplementing QMainWindow's paint event would not be considered the best design. It makes it tied to a particular class. If you really think that a QWidget instance is too much overhead, you better had measurements to show that being the case.
It is possible, of course, to make the overlay merely a QObject and to put the painting code into an event filter. That'd be an alternative solution. It's harder to do since you have to also properly deal with the parent widget's Qt::WA_StaticContents attribute, and with the widget potentially calling its scroll() method. Dealing with a separate widget is the simplest.
// https://github.com/KubaO/stackoverflown/tree/master/questions/overlay-widget-19362455
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
class OverlayWidget : public QWidget
{
void newParent() {
if (!parent()) return;
parent()->installEventFilter(this);
raise();
}
public:
explicit OverlayWidget(QWidget * parent = {}) : QWidget{parent} {
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TransparentForMouseEvents);
newParent();
}
protected:
//! Catches resize and child events from the parent widget
bool eventFilter(QObject * obj, QEvent * ev) override {
if (obj == parent()) {
if (ev->type() == QEvent::Resize)
resize(static_cast<QResizeEvent*>(ev)->size());
else if (ev->type() == QEvent::ChildAdded)
raise();
}
return QWidget::eventFilter(obj, ev);
}
//! Tracks parent widget changes
bool event(QEvent* ev) override {
if (ev->type() == QEvent::ParentAboutToChange) {
if (parent()) parent()->removeEventFilter(this);
}
else if (ev->type() == QEvent::ParentChange)
newParent();
return QWidget::event(ev);
}
};
class LoadingOverlay : public OverlayWidget
{
public:
LoadingOverlay(QWidget * parent = {}) : OverlayWidget{parent} {
setAttribute(Qt::WA_TranslucentBackground);
}
protected:
void paintEvent(QPaintEvent *) override {
QPainter p{this};
p.fillRect(rect(), {100, 100, 100, 128});
p.setPen({200, 200, 255});
p.setFont({"arial,helvetica", 48});
p.drawText(rect(), "Loading...", Qt::AlignHCenter | Qt::AlignVCenter);
}
};
int main(int argc, char * argv[])
{
QApplication a{argc, argv};
QMainWindow window;
QLabel central{"Hello"};
central.setAlignment(Qt::AlignHCenter | Qt::AlignTop);
central.setMinimumSize(400, 300);
LoadingOverlay overlay{&central};
QTimer::singleShot(5000, &overlay, SLOT(hide()));
window.setCentralWidget(&central);
window.show();
return a.exec();
}
I would suggest execute a modal, frameless dialog on top and add a graphics effect on the background widget.
This is IMHO a very flexible and short solution without touching the event system directly.
The performance might be bad - one could improve that by calling drawSource(), but I haven't a reliable solution here yet.
class DarkenEffect : public QGraphicsEffect
{
public:
void draw( QPainter* painter ) override
{
QPixmap pixmap;
QPoint offset;
if( sourceIsPixmap() ) // No point in drawing in device coordinates (pixmap will be scaled anyways)
pixmap = sourcePixmap( Qt::LogicalCoordinates, &offset );
else // Draw pixmap in device coordinates to avoid pixmap scaling;
{
pixmap = sourcePixmap( Qt::DeviceCoordinates, &offset );
painter->setWorldTransform( QTransform() );
}
painter->setBrush( QColor( 0, 0, 0, 255 ) ); // black bg
painter->drawRect( pixmap.rect() );
painter->setOpacity( 0.5 );
painter->drawPixmap( offset, pixmap );
}
};
// prepare overlay widget
overlayWidget->setWindowFlags( Qt::FramelessWindowHint | Qt::Dialog | Qt::WindowStaysOnTopHint );
// usage
parentWidget->setGraphicsEffect( new DarkenEffect );
overlayWidget->exec();
parentWidget->setGraphicsEffect( nullptr );
If you are talking about using a separate layout/widget over your 'Main Window', you could just make the "Loading..." window modal either through the UI editor or in the constructor for your UI.

QSpinBox inside a QScrollArea: How to prevent Spin Box from stealing focus when scrolling?

I have a control with several QSpinBox objects inside a QScrollArea. All works fine when scrolling in the scroll area unless the mouse happens to be over one of the QSpinBoxes. Then the QSpinBox steals focus and the wheel events manipulate the spin box value rather than scrolling the scroll area.
I don't want to completely disable using the mouse wheel to manipulate the QSpinBox, but I only want it to happen if the user explicitly clicks or tabs into the QSpinBox. Is there a way to prevent the QSpinBox from stealing the focus from the QScrollArea?
As said in a comment to an answer below, setting Qt::StrongFocus does prevent the focus rect from appearing on the control, however it still steals the mouse wheel and adjusts the value in the spin box and stops the QScrollArea from scrolling. Same with Qt::ClickFocus.
In order to solve this, we need to care about the two following things:
The spin box mustn't gain focus by using the mouse wheel. This can be done by setting the focus policy to Qt::StrongFocus.
The spin box must only accept wheel events if it already has focus. This can be done by reimplementing QWidget::wheelEvent within a QSpinBox subclass.
Complete code for a MySpinBox class which implements this:
class MySpinBox : public QSpinBox {
Q_OBJECT
public:
MySpinBox(QWidget *parent = 0) : QSpinBox(parent) {
setFocusPolicy(Qt::StrongFocus);
}
protected:
virtual void wheelEvent(QWheelEvent *event) {
if (!hasFocus()) {
event->ignore();
} else {
QSpinBox::wheelEvent(event);
}
}
};
That's it. Note that if you don't want to create a new QSpinBox subclass, then you can also use event filters instead to solve this.
Try removing Qt::WheelFocus from the spinbox' QWidget::focusPolicy:
spin->setFocusPolicy( Qt::StrongFocus );
In addition, you need to prevent the wheel event from reaching the spinboxes. You can do that with an event filter:
explicit Widget( QWidget * parent=0 )
: QWidget( parent )
{
// setup ...
Q_FOREACH( QSpinBox * sp, findChildren<QSpinBox*>() ) {
sp->installEventFilter( this );
sp->setFocusPolicy( Qt::StrongFocus );
}
}
/* reimp */ bool eventFilter( QObject * o, QEvent * e ) {
if ( e->type() == QEvent::Wheel &&
qobject_cast<QAbstractSpinBox*>( o ) )
{
e->ignore();
return true;
}
return QWidget::eventFilter( o, e );
}
edit from Grant Limberg for completeness as this got me 90% of the way there:
In addition to what mmutz said above, I needed to do a few other things. I had to create a subclass of QSpinBox and implement focusInEvent(QFocusEvent*) and focusOutEvent(QFocusEvent*). Basically, on a focusInEvent, I change the Focus Policy to Qt::WheelFocus and on the focusOutEvent I change it back to Qt::StrongFocus.
void MySpinBox::focusInEvent(QFocusEvent*)
{
setFocusPolicy(Qt::WheelFocus);
}
void MySpinBox::focusOutEvent(QFocusEvent*)
{
setFocusPolicy(Qt::StrongFocus);
}
Additionally, the eventFilter method implementation in the event filter class changes its behavior based on the current focus policy of the spinbox subclass:
bool eventFilter(QObject *o, QEvent *e)
{
if(e->type() == QEvent::Wheel &&
qobject_cast<QAbstractSpinBox*>(o))
{
if(qobject_cast<QAbstractSpinBox*>(o)->focusPolicy() == Qt::WheelFocus)
{
e->accept();
return false;
}
else
{
e->ignore();
return true;
}
}
return QWidget::eventFilter(o, e);
}
My attempt at a solution. Easy to use, no subclassing required.
First, I created a new helper class:
#include <QObject>
class MouseWheelWidgetAdjustmentGuard : public QObject
{
public:
explicit MouseWheelWidgetAdjustmentGuard(QObject *parent);
protected:
bool eventFilter(QObject* o, QEvent* e) override;
};
#include <QEvent>
#include <QWidget>
MouseWheelWidgetAdjustmentGuard::MouseWheelWidgetAdjustmentGuard(QObject *parent) : QObject(parent)
{
}
bool MouseWheelWidgetAdjustmentGuard::eventFilter(QObject *o, QEvent *e)
{
const QWidget* widget = static_cast<QWidget*>(o);
if (e->type() == QEvent::Wheel && widget && !widget->hasFocus())
{
e->ignore();
return true;
}
return QObject::eventFilter(o, e);
}
Then I set the focus policy of the problematic widget to StrongFocus, either at runtime or in Qt Designer.
And then I install my event filter:
ui.comboBox->installEventFilter(new MouseWheelWidgetAdjustmentGuard(ui.comboBox));
Done. The MouseWheelWidgetAdjustmentGuard will be deleted automatically when the parent object - the combobox - is destroyed.
Just to expand you can do this with the eventFilter instead to remove the need to derive a new QMySpinBox type class:
bool eventFilter(QObject *obj, QEvent *event)
{
QAbstractSpinBox* spinBox = qobject_cast<QAbstractSpinBox*>(obj);
if(spinBox)
{
if(event->type() == QEvent::Wheel)
{
if(spinBox->focusPolicy() == Qt::WheelFocus)
{
event->accept();
return false;
}
else
{
event->ignore();
return true;
}
}
else if(event->type() == QEvent::FocusIn)
{
spinBox->setFocusPolicy(Qt::WheelFocus);
}
else if(event->type() == QEvent::FocusOut)
{
spinBox->setFocusPolicy(Qt::StrongFocus);
}
}
return QObject::eventFilter(obj, event);
}
With help from this post we cooked a solution for Python/PySide. If someone stumbles across this. Like we did :]
class HumbleSpinBox(QtWidgets.QDoubleSpinBox):
def __init__(self, *args):
super(HumbleSpinBox, self).__init__(*args)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
def focusInEvent(self, event):
self.setFocusPolicy(QtCore.Qt.WheelFocus)
super(HumbleSpinBox, self).focusInEvent(event)
def focusOutEvent(self, event):
self.setFocusPolicy(QtCore.Qt.StrongFocus)
super(HumbleSpinBox, self).focusOutEvent(event)
def wheelEvent(self, event):
if self.hasFocus():
return super(HumbleSpinBox, self).wheelEvent(event)
else:
event.ignore()
This is my Python PyQt5 port of Violet Giraffe answer:
def preventAnnoyingSpinboxScrollBehaviour(self, control: QAbstractSpinBox) -> None:
control.setFocusPolicy(Qt.StrongFocus)
control.installEventFilter(self.MouseWheelWidgetAdjustmentGuard(control))
class MouseWheelWidgetAdjustmentGuard(QObject):
def __init__(self, parent: QObject):
super().__init__(parent)
def eventFilter(self, o: QObject, e: QEvent) -> bool:
widget: QWidget = o
if e.type() == QEvent.Wheel and not widget.hasFocus():
e.ignore()
return True
return super().eventFilter(o, e)
Just to help anyone's in need, it lacks a small detail:
call focusInEvent and focusOutEvent from QSpinBox :
void MySpinBox::focusInEvent(QFocusEvent* pEvent)
{
setFocusPolicy(Qt::WheelFocus);
QSpinBox::focusInEvent(pEvent);
}
void MySpinBox::focusOutEvent(QFocusEvent*)
{
setFocusPolicy(Qt::StrongFocus);
QSpinBox::focusOutEvent(pEvent);
}