c++ - QSpinBox dentro de QScrollArea: ¿Cómo evitar que Spin Box robe el foco cuando se desplaza?
qt (6)
Con la ayuda de este post, cocinamos una solución para Python / PySide . Si alguien se tropieza con esto. Como hicimos nosotros:]
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()
Tengo un control con varios objetos QSpinBox dentro de un QScrollArea. Todo funciona bien cuando se desplaza en el área de desplazamiento a menos que el mouse esté sobre uno de los QSpinBoxes. Luego, el QSpinBox roba el foco y los eventos de la rueda manipulan el valor del cuadro de giro en lugar de desplazarse por el área de desplazamiento.
No quiero deshabilitar completamente el uso de la rueda del ratón para manipular el QSpinBox, pero solo quiero que suceda si el usuario hace clic o pestañas en el QSpinBox explícitamente. ¿Hay alguna manera de evitar que QSpinBox robe el enfoque de QScrollArea?
Como se dijo en un comentario a una respuesta a continuación, la configuración de Qt :: StrongFocus evita que el enfoque correcto aparezca en el control, sin embargo, sigue robando la rueda del mouse y ajusta el valor en el cuadro de número y detiene el desplazamiento de QScrollArea. Lo mismo con Qt :: ClickFocus.
Mi intento de solución. Fácil de usar, no requiere subclasificación.
Primero, creé una nueva clase de ayuda:
#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);
}
Luego establezco la política de enfoque del widget problemático en StrongFocus
, ya sea en tiempo de ejecución o en Qt Designer. Y luego instalo mi filtro de eventos:
ui.comboBox->installEventFilter(new MouseWheelWidgetAdjustmentGuard(ui.comboBox));
Hecho. MouseWheelWidgetAdjustmentGuard
se eliminará automáticamente cuando se destruya el objeto principal, el cuadro combinado.
Para resolver esto, debemos preocuparnos por las dos cosas siguientes:
- El cuadro de giro no debe ganar el enfoque utilizando la rueda del ratón. Esto se puede hacer configurando la política de enfoque en
Qt::StrongFocus
. - El cuadro de giro solo debe aceptar eventos de rueda si ya tiene foco. Esto se puede hacer reimplementando
QWidget::wheelEvent
dentro de una subclase deQSpinBox
.
Código completo para una clase MySpinBox
que implementa esto:
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);
}
}
};
Eso es. Tenga en cuenta que si no desea crear una nueva subclase de QSpinBox
, también puede usar filtros de eventos para resolver esto.
Solo para ayudar a quien lo necesite, le falta un pequeño detalle:
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);
}
Solo para expandir puede hacer esto con el eventFilter en lugar de eliminar la necesidad de derivar una nueva clase de tipo QMySpinBox:
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);
}
Intente eliminar Qt::WheelFocus
de la casilla de selección '' QWidget::focusPolicy
:
spin->setFocusPolicy( Qt::StrongFocus );
Además, debes evitar que el evento de la rueda llegue a los spinbox. Puedes hacerlo con un filtro de eventos:
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 );
}
Edite de Grant Limberg para que esté completo, ya que esto me permitió llegar al 90%:
Además de lo que mmutz dijo anteriormente, necesitaba hacer algunas otras cosas. Tuve que crear una subclase de QSpinBox e implementar focusInEvent(QFocusEvent*)
y focusOutEvent(QFocusEvent*)
. Básicamente, en un focusInEvent
, cambio la Política de Focus a Qt::WheelFocus
y en el focusOutEvent
vuelvo a cambiar a Qt::StrongFocus
.
void MySpinBox::focusInEvent(QFocusEvent*)
{
setFocusPolicy(Qt::WheelFocus);
}
void MySpinBox::focusOutEvent(QFocusEvent*)
{
setFocusPolicy(Qt::StrongFocus);
}
Además, la implementación del método eventFilter en la clase de filtro de eventos cambia su comportamiento en función de la política de enfoque actual de la subclase spinbox:
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);
}