Question

I'm working on UI using QT. The ui is simple, it's app based like IPhone or Android. Say there are 9 items (3 rows x 3 cols).

What I want to do is to navigate between widgets using arrow keys. If the focus is in [row 1,col 1] and I press down arrow, I want it to go to [row 2, col 1] another example. If the focus is in [row 2,col 3] and I press up arrow, I want it to go to [row 1, col 3]

But the current behavior is up and right always go to next widget and down and left always go to previous widget.

Is there any way to do this in qt? or I need to create some algorithm to do this?

Thanks

Was it helpful?

Solution

UPDATE: See amazing example at the end.

Basic Widget focus navigation starts out with this:

http://qt-project.org/doc/qt-4.8/focus.html

Arrow navigation is available easily with a QTableView:

http://qt-project.org/doc/qt-4.8/qtableview.html#navigation

If you can get your widgets to work inside the structure of a QTableView, then you don't need to implement it, it comes as a part of the wrapper/view widget.

http://qt-project.org/doc/qt-4.8/qtablewidget.html#details

http://qt-project.org/doc/qt-4.8/model-view-programming.html

Model View programming does have a learning curve, but it is worth while to learn and use.

But this is by no means the only way to accomplish this.

There are event filters, key events, focus events that can be leveraged to accomplish this feat without using a QTableView or QTableWidget. But figuring out the best way to do it without making it look messy may take some time.

http://qt-project.org/doc/qt-4.8/qcoreapplication.html#notify

http://doc.qt.digia.com/qq/qq11-events.html

http://qt-project.org/doc/qt-4.8/eventsandfilters.html

http://qt-project.org/doc/qt-4.8/qkeyevent.html#details

http://qt-project.org/doc/qt-4.8/qfocusevent.html

Key events are set to the item with the focus, and if they ignore the event it propagates up to its parent. So as long as your items in your table/grid ignore the key events having to do with the arrow keys, then you could have your parent widget listen for the key events and handle them appropriately.

http://qt-project.org/doc/qt-4.8/qt.html#Key-enum

http://qt-project.org/doc/qt-4.8/qt.html#FocusReason-enum

http://qt-project.org/doc/qt-4.8/qwidget.html#setFocus

http://qt-project.org/doc/qt-4.8/qapplication.html#focusWidget

Hope that helps.

EDIT: Fully working example in QGraphicsView of what you want to do:

Qt Creator > Welcome tab > Examples > Pad Navigator Example

http://qt-project.org/doc/qt-4.8/graphicsview-padnavigator.html

Here is the relevant code from the example:

// Enable key navigation using state transitions
for (int y = 0; y < rows; ++y) {
    for (int x = 0; x < columns; ++x) {
        QState *state = stateGrid[y][x];
        QKeyEventTransition *rightTransition = new QKeyEventTransition(this, QEvent::KeyPress,
                                                                       Qt::Key_Right, state);
        QKeyEventTransition *leftTransition = new QKeyEventTransition(this, QEvent::KeyPress,
                                                                      Qt::Key_Left, state);
        QKeyEventTransition *downTransition = new QKeyEventTransition(this, QEvent::KeyPress,
                                                                      Qt::Key_Down, state);
        QKeyEventTransition *upTransition = new QKeyEventTransition(this, QEvent::KeyPress,
                                                                    Qt::Key_Up, state);
        rightTransition->setTargetState(stateGrid[y][(x + 1) % columns]);
        leftTransition->setTargetState(stateGrid[y][((x - 1) + columns) % columns]);
        downTransition->setTargetState(stateGrid[(y + 1) % rows][x]);
        upTransition->setTargetState(stateGrid[((y - 1) + rows) % rows][x]);

EDIT: Amazing example using QShortcuts and a QGridLayout and a bunch of QPushButtons:

widget.cpp

#include "widget.h"
#include <QPushButton>
#include <QApplication>
#include <QShortcut>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_grid = new QGridLayout;
    for(int r = 0; r < 10; r++)
    {
        for(int c = 0; c < 10; c++)
        {
            m_grid->addWidget(new QPushButton("Row " + QString::number(r)
                                              + ", Col " + QString::number(c)),
                              r, c);
        }
    }
    this->setLayout(m_grid);

    m_grid->itemAtPosition(1, 1)->widget()->setFocus();

    this->setStyleSheet("QPushButton::focus{ background: black; color: white;}");

    // only works for in Qt for Embedded Linux, Symbian and Windows CE only.
    //    QApplication::setNavigationMode(Qt::NavigationModeKeypadDirectional);

    QShortcut * shortcut;
    shortcut = new QShortcut(QKeySequence(Qt::Key_Up),this,
                             SLOT(on_up()));
    shortcut = new QShortcut(QKeySequence(Qt::Key_Down),this,
                             SLOT(on_down()));
    shortcut = new QShortcut(QKeySequence(Qt::Key_Left),this,
                             SLOT(on_left()));
    shortcut = new QShortcut(QKeySequence(Qt::Key_Right),this,
                             SLOT(on_right()));
}

void Widget::on_up()
{
    moveFocus(0, -1);
}

void Widget::on_down()
{
    moveFocus(0, 1);
}

void Widget::on_left()
{
    moveFocus(-1, 0);
}

void Widget::on_right()
{
    moveFocus(1, 0);
}

void Widget::moveFocus(int dx, int dy)
{
    if(qApp->focusWidget() == 0)
        return;
    int idx = m_grid->indexOf(qApp->focusWidget());
    if(idx == -1)
        return;
    int r, c, rowSpan, colSpan;
    m_grid->getItemPosition(idx, &r, &c, &rowSpan, &colSpan);
    QLayoutItem* layoutItem = m_grid->itemAtPosition(r + dy, c + dx);
    if(layoutItem == 0)
        return;
    layoutItem->widget()->setFocus();
}

Widget::~Widget()
{

}

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QGridLayout>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
    QGridLayout * m_grid;
public slots:
    void on_up();
    void on_down();
    void on_left();
    void on_right();
    void moveFocus(int dx, int dy);
};

#endif // WIDGET_H

main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

OTHER TIPS

To enable keypad navigation, build Qt with QT_KEYPAD_NAVIGATION defined. https://het.as.utexas.edu/HET/Software/html/qapplication.html#keypadNavigationEnabled

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top