You can install an event filter for every button and catch key presses there.
This is an example of how you can implement such navigation:
Widget::Widget(QWidget *parent) :
QWidget(parent)
{
grid = new QGridLayout;
for (int row = 0; row < 4; ++row)
{
for (int col = 0; col < 4; ++col)
{
QString text = QString("btn %1/%2").arg(row).arg(col);
QPushButton *btn = new QPushButton(text);
btn->installEventFilter(this);
grid->addWidget(btn, row, col);
}
}
setLayout(grid);
}
bool Widget::eventFilter(QObject *obj, QEvent *event)
{
QPushButton *btn = dynamic_cast<QPushButton*>(obj);
if (btn != NULL && event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
int index = grid->indexOf(btn);
int row, col, rowSpan, colSpan;
grid->getItemPosition(index, &row, &col, &rowSpan, &colSpan);
int nextRow = row;
int nextCol = col;
bool keyArrow = true;
int key = keyEvent->key();
switch (key)
{
case Qt::Key_Up:
{
nextRow--;
if (nextRow < 0)
{
nextRow = grid->rowCount() - 1;
}
break;
}
case Qt::Key_Down:
{
nextRow++;
if (nextRow >= grid->rowCount())
{
nextRow = 0;
}
break;
}
case Qt::Key_Right:
{
nextCol++;
if (nextCol >= grid->columnCount())
{
nextCol = 0;
}
break;
}
case Qt::Key_Left:
{
nextCol--;
if (nextCol < 0)
{
nextCol = grid->columnCount() - 1;
}
break;
}
default:
{
keyArrow = false;
}
}
if (keyArrow)
{
grid->itemAtPosition(nextRow, nextCol)->widget()->setFocus();
}
return true;
}
return QWidget::eventFilter(obj, event);
}