Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QShortCut and QSpinBox conflict

Tags:

qt

qt4

I'm writing an application were i use my own shortcut. It looks like this:

myShortcut= new QShortcut(Qt::SHIFT + Qt::Key_B,this);
connect(myShortcut, SIGNAL(activated()), this, SLOT(setCameraBack()));

I defined it in the constructor of main widget and it works fine until i click one of the spinbox buttons which are also located on the main widget. After that my shortcut stop working and it doesn't work until i click push button or check box. When i do that everything is fine again. I'd like to add that after i click spinbox it seems to be "active" (because the cursor is still "blinking" on it) until i click one of the other buttons. Do you have any idea what is wrong? Is it some kind process or event problem? Thanks for all answers ~Marwroc

like image 327
Marwroc Avatar asked Apr 12 '11 18:04

Marwroc


3 Answers

A shortcut is "listened for" by Qt's event loop when the shortcut's parent widget is receiving events.

When the QSpinBox has keyboard focus, then the QShortcut object's parent is no longer receiving events. Therefore, the shortcut does not work until keyboard focus is removed form the QSpinBox. You can change this behavior by passing Qt::WidgetWithChildrenShortcut or Qt::ApplicationShortcut to the QShortcut::setContext method of your QShortcut.

like image 51
Judge Maygarden Avatar answered Nov 19 '22 16:11

Judge Maygarden


Before a shortcut is activated, the focus widget is given a ShortcutOverride event. If the event is accepted, the key event is passed along to the widget and the shortcut is not activated.

Source: https://wiki.qt.io/ShortcutOverride

Looking at Qt source

QAbstractSpinBox::event(QEvent *event)
{
    Q_D(QAbstractSpinBox);
    switch (event->type()) {
    ...
    case QEvent::ShortcutOverride:
        if (d->edit->event(event))
            return true;
        break;
    ...
    }
    return QWidget::event(event);
}

QAbstractSpinBox is allowing the internal edit to accept the event. QLineEdit defers to QLineControl. From qt/src/gui/widgets/qlinecontrol.cpp

    case QEvent::ShortcutOverride:{
        if (isReadOnly())
            return false;
        QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
        if (ke == QKeySequence::Copy
            || ke == QKeySequence::Paste
            || ke == QKeySequence::Cut
            || ke == QKeySequence::Redo
            || ke == QKeySequence::Undo
            || ke == QKeySequence::MoveToNextWord
            || ke == QKeySequence::MoveToPreviousWord
            || ke == QKeySequence::MoveToStartOfDocument
            || ke == QKeySequence::MoveToEndOfDocument
            || ke == QKeySequence::SelectNextWord
            || ke == QKeySequence::SelectPreviousWord
            || ke == QKeySequence::SelectStartOfLine
            || ke == QKeySequence::SelectEndOfLine
            || ke == QKeySequence::SelectStartOfBlock
            || ke == QKeySequence::SelectEndOfBlock
            || ke == QKeySequence::SelectStartOfDocument
            || ke == QKeySequence::SelectAll
            || ke == QKeySequence::SelectEndOfDocument) {
            ke->accept();
        } else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier
                   || ke->modifiers() == Qt::KeypadModifier) {
            if (ke->key() < Qt::Key_Escape) {
                ke->accept();
            } else {
                switch (ke->key()) {
                case Qt::Key_Delete:
                case Qt::Key_Home:
                case Qt::Key_End:
                case Qt::Key_Backspace:
                case Qt::Key_Left:
                case Qt::Key_Right:
                    ke->accept();
                default:
                    break;
                }
            }
        }
    }

This code accepts most keys if the control key is not also pressed.

So the easiest solution is to change the shortcut to include the control modifier.

Alternatively, you can subclass the spin box and override the event function

bool MySpinBox::event(QEvent *event)
{
    if( event->type() == QEvent::ShortcutOverride && !isReadOnly() )
    {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
        // Ignore 'B' shortcuts
        if( keyEvent->key() == Qt::Key_B )
        {
            Q_ASSERT( !event->isAccepted() );
            return true;
    }
    return QSpinBox::event(event);
}
like image 42
Jason Scott Avatar answered Nov 19 '22 15:11

Jason Scott


Have you tried MySpinBox -> setFocusPolicy (Qt::NoFocus) ?

like image 1
TonyK Avatar answered Nov 19 '22 14:11

TonyK