qteventsqt5qmainwindoweventfilter

QMainWindow not receiving keyPressEvent, even with event filter


I'm trying to design an image viewer application where I use a QGraphicsView to display my images. I want to enable the user to use the arrow keys to open the next/previous image, but my QGraphicsView is always consuming my keyPressEvent. I would like these events to be handled by my QMainWindow class instead. I realize this is a common issue, and apparently I can solve it by installing an event filter and/or ensuring that my QMainWindow can have focus. I have done both, but so far the only thing I have done is to not let the QGraphicsView get the event, but it still does not propagate to the QMainWindow. So far I've implemented the eventFilter method in my QMainWindow class and installed it on my QGraphicsView object.

QMainWindow class

IVMainWindow::IVMainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setWindowTitle("ImageViewer++");
    setFocusPolicy(Qt::StrongFocus);  // Enabled the QMainWindow to get focus

    m_image_widget = new IVImageWidget();
    m_image_widget->installEventFilter(this);  // Install event filter on QGraphicsView
    setCentralWidget(m_image_widget);

    resize(QGuiApplication::primaryScreen()->availableSize() * 3 / 5);

    // For DEBUG purpose
    loadImage("res/image.png");

    createMenuBar();
    createToolBar();
}

/**
 * Filters out arrow key presses.
 */
bool IVMainWindow::eventFilter(QObject *obj, QEvent *event) {
    if (event->type() == QEvent::KeyPress) {
        auto *keyEvent = static_cast<QKeyEvent *>(event);
        int key = keyEvent->key();
        // Return true to reject the key-presses
        return (key == Qt::Key_Left || key == Qt::Key_Right || key == Qt::Key_Up || key == Qt::Key_Down);
    } else {
        // standard event processing
        return QMainWindow::eventFilter(obj, event);
    }
}

//Never gets to this point, unless I explicitly give it focus by clicking on some other widget than the QGraphicsView...
void IVMainWindow::keyPressEvent(QKeyEvent *event) {
    if (event->key() == Qt::RightArrow) {
        m_logger.Debug("Right arrow pressed.");
    } else if (event->key() == Qt::LeftArrow) {
        m_logger.Debug("Left arrow pressed.");
    }
}

Solution

  • You need to process the event in the event filter, it is ok to call QMainWindow::eventFilter for sanity but it do not process the event as an incoming event, so the event handlers will not be called.

    bool IVMainWindow::eventFilter(QObject *obj, QEvent *event) {
        if (event->type() == QEvent::KeyPress) {
            auto *keyEvent = static_cast<QKeyEvent *>(event);
            int key = keyEvent->key();
            // Return true to reject the key-presses
            if  (key == Qt::Key_Left || key == Qt::Key_Right || key == Qt::Key_Up || key == Qt::Key_Down)
            {
                //process event here somehow, or instruct your class to do it later
                return true; //filter the event
            }
        } else {
            // standard event processing
            return QMainWindow::eventFilter(obj, event);
        }
    }
    
    //This will only be called for the 'real' events, the ones that eventually are received by the main window
    void IVMainWindow::keyPressEvent(QKeyEvent *event) {
        if (event->key() == Qt::RightArrow) {
            m_logger.Debug("Right arrow pressed.");
        } else if (event->key() == Qt::LeftArrow) {
            m_logger.Debug("Left arrow pressed.");
        }
    }