How display multiple values in QLabel in Qcreator - qt

I am developing calculator using Qt SDK I want to get values from the calculator buttons in to one Qlabel. I have designed GUI using design view I tried to get values using following code.
void MainWindow::on_three_button_clicked(){
ui->label->setText("3");
}
but then Only I can get one button value at a time
please help me on this...

You can have one QString variable to store current pressed value. Here I am considering your class variable defined as QString myEnteredNum. Then you can use it as below code:
void MainWindow::on_three_button_clicked(){
myEnteredNum.append("3"); // This method you can add in each number slot
ui->label->setText(myEnteredNum);
}
I hope this helps you.

There are two general approaches to this. You can append the digits directly to a number, and bind the label to the number. The m_first label below is updated that way. You can also append the digits directly to the label. The m_second label is updated so.
#include <QtWidgets>
class Window : public QWidget {
Q_OBJECT
Q_PROPERTY(double number MEMBER m_number WRITE setNumber NOTIFY numberChanged)
double m_number;
QGridLayout m_grid;
QLabel m_first, m_second;
QPushButton m_digits[10], m_clear;
public:
Window(QWidget * parent = 0) : QWidget(parent),
m_grid(this), m_clear("C") {
m_grid.addWidget(&m_first, 0, 0, 1, 3);
m_grid.addWidget(&m_second, 1, 0, 1, 3);
m_grid.addWidget(&m_clear, 5, 2);
for (int i = 0; i < 9; ++i) {
m_digits[i].setText(QString::number(i));
if (i > 0) m_grid.addWidget(m_digits + i, 2 + (i-1)/3, (i-1)%3);
else m_grid.addWidget(m_digits + i, 5, 1);
QObject::connect(m_digits + i, &QPushButton::clicked, [i, this]{
// Add the digit to the number
setNumber(m_number * 10.0 + i);
// Add the digit to the label
m_second.setText(m_second.text().append(QString::number(i)));
});
}
QObject::connect(&m_clear, &QPushButton::clicked, [this]{
setNumber(0);
m_second.setText("0");
});
QObject::connect(this, &Window::numberChanged,
&m_second, &QLabel::setNum);
emit m_clear.clicked(); // initialize everything
}
Q_SIGNAL void numberChanged(double);
void setNumber(double n) { m_number = n; emit numberChanged(n); }
};
int main(int argc, char ** argv) {
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}
#include "main.moc"
Finally, a calculator is stateful and, to avoid common mistakes, you could use a QStateMachine to make this fact explicit, and to adjust the behavior of the calculator depending on which state it's in.

Related

Qt5 : Displaying a Qpainter with layouts

I started using Qt5 a little while ago and I don't know how to set the position of my drawing in my window.
I have a Drawing class which is a QWidget and which contains my paintEvent() function and other functions; and a MainWindow class which contains some widgets. So in order to display my Qpainter, I have to include it in my layout, which is the main window layout, but the window doesn't adapt to the Qpainter at all; the dimensions of the buttons, sliders .. adapt so as to organize themselves and occupy all the space of my window, but they totally ignore my Qpainter and it partly disappears if I put at least 2 widgets.
Do you have any solutions to better manage the position of these elements?
main.cpp :
#include <QtGui>
#include <QApplication>
#include "mywidget.h"
int main( int argc, char **argv )
{
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
mywidget.h :
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QtGui>
#include <QWidget>
#include <QSlider>
#include <QScrollBar>
#include <QApplication>
#include <QGridLayout>
#include <QObject>
#include <QPoint>
#include <QLabel>
#include <QPolygon>
class Drawing : public QWidget
{
Q_OBJECT
public:
Drawing();
void paintEvent(QPaintEvent* e);
public slots:
void slide(int abscisse);
void rotate();
private:
QPoint o;
QPoint a;
QPoint b;
QPoint c;
QPoint d;
};
//--------------------------------------
class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow();
private:
QSlider* m_slider1;
QSlider* m_slider2;
QGridLayout* m_layout;
Drawing* m_dessin;
};
#endif // MYWIDGET_H
mywidget.cpp :
#include "mywidget.h"
#include <iostream> //POUR LES TESTS
MainWindow::MainWindow() : QWidget()
{
setGeometry(330, 140, 840, 620);
m_slider1 = new QSlider(Qt::Horizontal, this);
m_slider1->setRange(150, 650);
m_slider1->setSliderPosition(400);
m_slider2 = new QSlider(Qt::Horizontal, this);
m_slider2->setSliderPosition(50);
m_layout = new QGridLayout;
m_layout->addWidget(new QLabel("Translation Horizontale"), 1, 0);
m_layout->addWidget(m_slider1, 2, 0);
m_layout->addWidget(new QLabel("Rotation"), 0, 1);
m_layout->addWidget(m_slider2, 1, 1);
m_dessin = new Drawing;
m_layout->addWidget(m_dessin, 0, 0);
setLayout(m_layout);
QObject::connect(m_slider1, SIGNAL(valueChanged(int)), m_dessin, SLOT(slide(int)));
QObject::connect(m_slider2, SIGNAL(valueChanged(int)), m_dessin, SLOT(rotate()));
}
//--------------------------------------------------------
Drawing::Drawing() : QWidget(), o(400, 150), a(o.x()-50 , o.y()-50), b(o.x()+50 , o.y()-50), c(o.x()+50 , o.y()+50), d(o.x()-50 , o.y()+50) {}
void Drawing::paintEvent(QPaintEvent *e) {
QPolygon poly;
poly << a << b << c << d;
QWidget::paintEvent(e); // effectue le comportement standard
QPainter painter(this); // construire
painter.setPen( QPen(Qt::white, 2) ); // personnaliser
painter.drawPolygon(poly); // dessiner
}
void Drawing::slide(int abscisse) {
if (a == QPoint(o.x()-50 , o.y()-50)) {
o.setX(abscisse);
a.setX(o.x()-50);
b.setX(o.x()+50);
c.setX(o.x()+50);
d.setX(o.x()-50);
}
else {
o.setX(abscisse);
a.setX(o.x());
b.setX(o.x()+75);
c.setX(o.x());
d.setX(o.x()-75);
}
update();
}
void Drawing::rotate() {
if (a == QPoint(o.x()-50 , o.y()-50)) {
a = QPoint(o.x() , o.y()+75);
b = QPoint(o.x()+75 , o.y());
c = QPoint(o.x() , o.y()-75);
d = QPoint(o.x()-75 , o.y());
}
else {
a = QPoint(o.x()-50 , o.y()-50);
b = QPoint(o.x()+50 , o.y()-50);
c = QPoint(o.x()+50 , o.y()+50);
d = QPoint(o.x()-50 , o.y()+50);
}
update();
}
Snapshots:
After having seen the snapshots of OP, I thought about what might been happen.
The layout of OP doesn't look that wrong.
I still believe that the layout plays only a minor role in OPs issue.
I tried to reproduce OPs issue with an even smaller MCVE of mine.
My testQGridLayout:
#include <QtWidgets>
class Drawing: public QFrame {
public:
Drawing(QWidget *pQParent = nullptr);
virtual ~Drawing() = default;
Drawing(const Drawing&) = delete;
Drawing& operator=(const Drawing&) = delete;
protected:
virtual void paintEvent(QPaintEvent *pQEvent) override;
};
Drawing::Drawing(QWidget* pQParent):
QFrame(pQParent)
{
setFrameStyle(Box | Plain);
}
void Drawing::paintEvent(QPaintEvent* pQEvent)
{
{ QPainter qPainter(this);
qPainter.drawText(QPoint(40, 40),
QString("Size: %1 x %2").arg(width()).arg(height()));
qPainter.setPen(Qt::red);
qPainter.drawRect(300, 100, 200, 200);
}
// call base class paint event to keep it working
QFrame::paintEvent(pQEvent);
}
class MainWindow: public QWidget {
public:
MainWindow(QWidget *pQParent = nullptr);
virtual ~MainWindow() = default;
MainWindow(const MainWindow&) = delete;
MainWindow& operator=(const MainWindow&) = delete;
private:
QGridLayout _qGrid;
Drawing _qDrawing;
QSlider _qSliderT;
QSlider _qSliderR;
};
MainWindow::MainWindow(QWidget *pQParent):
QWidget(pQParent),
_qSliderT(Qt::Horizontal),
_qSliderR(Qt::Horizontal)
{
resize(840, 620);
_qGrid.addWidget(&_qDrawing, 0, 0);
_qGrid.addWidget(new QLabel("Translation Horizontal"), 1, 0);
_qSliderT.setRange(150, 650);
_qSliderT.setSliderPosition(400);
_qGrid.addWidget(&_qSliderT, 2, 0);
_qGrid.addWidget(new QLabel("Rotation"), 1, 1);
_qSliderR.setSliderPosition(50);
_qGrid.addWidget(&_qSliderR, 2, 1);
setLayout(&_qGrid);
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
MainWindow qWinMain;
qWinMain.setWindowTitle("Test QGridLayout");
qWinMain.show();
// runtime loop
return app.exec();
}
Output:
Qt Version: 5.15.1
I made some changes to exclude what is a possible issue and what not.
I derived my Drawing from QFrame. Thus, it was easy to give it a visible border. As expected, my Drawing _qDrawing occupies only the space above the first slider (QSlider _qSliderT; in my case).
I added output of widget size to the Drawing::paintEvent() to see its size. Then I added the painting of a red rectangle. For that, I cared to cover a space which is partly inside the widget and partly below and right of it.
This is what I conclude:
As exposed in the OPs code, the layout should be the same.
OPs rectangle is always drawn at the same coordinates. Hence, it doesn't get visible until the Drawing grows large enough (with the main window).
The origin of the QPainter (i.e. QPoint(0, 0)) is the upper left corner of the widget. This can be changed by applying transformations but I couldn't see this in OPs code. (The effect of the sliders, I neglect for now.)
Though, there are still some things which are not clear to me:
The Drawing should clip the painting. Hence, I wonder, how OPs rectangle can appear over the rotate slider. Either, the OP used a span for the Drawing m_dessin, or the widget doesn't clip painting on the paint engine the OP uses. (The look is quite different than mine. Thus, it might be a different platform.)
The layout which can be seen in OPs snapshots doesn't match the exposed code. In OPs snapshot, the Drawing occupies all extra space resulting from growing the main window. This is only possible when QGridLayout::setRowStretch()/GridLayout::setColumnStretch() had been used (as recommended in my first comment). However, the exposed code doesn't contain them.
To check this out, I changed the layout in MainWindow::MainWindow():
MainWindow::MainWindow(QWidget *pQParent):
QWidget(pQParent),
_qSliderT(Qt::Horizontal),
_qSliderR(Qt::Horizontal)
{
resize(840, 620);
_qGrid.setRowStretch(0, 1);
_qGrid.setColumnStretch(0, 1);
_qGrid.addWidget(&_qDrawing, 0, 0, 1, 2);
_qGrid.addWidget(new QLabel("Translation Horizontal"), 1, 0);
_qSliderT.setRange(150, 650);
_qSliderT.setSliderPosition(400);
_qGrid.addWidget(&_qSliderT, 2, 0);
_qGrid.addWidget(new QLabel("Rotation"), 1, 1);
_qSliderR.setSliderPosition(50);
_qGrid.addWidget(&_qSliderR, 2, 1);
setLayout(&_qGrid);
}
Output:
Now, the layout seems to match the one of OPs snapshots.
Trying resize:
This looks exactly as it should:
the Drawing _qDrawing shrinks and grows with the main window size
the painting is clipped if the size of Drawing _qDrawing becomes too small to cover it.
Final Conclusion:
There is nothing wrong in OPs layout.
IMHO, OP is not yet fully clear about how coordinate systems apply in QPainter.
For this, I can warmly recommend an extra page of the Qt online doc., precisely dedicated to this topic:
Qt Doc.: Coordinate System
Continuation:
How to add a vertical slider:
class MainWindow: public QWidget {
public:
MainWindow(QWidget *pQParent = nullptr);
virtual ~MainWindow() = default;
MainWindow(const MainWindow&) = delete;
MainWindow& operator=(const MainWindow&) = delete;
private:
QGridLayout _qGrid;
Drawing _qDrawing;
QSlider _qSliderV;
QSlider _qSliderT;
QSlider _qSliderR;
};
MainWindow::MainWindow(QWidget *pQParent):
QWidget(pQParent),
_qSliderV(Qt::Vertical),
_qSliderT(Qt::Horizontal),
_qSliderR(Qt::Horizontal)
{
resize(840, 620);
_qGrid.setRowStretch(0, 1);
_qGrid.setColumnStretch(0, 1);
_qGrid.addWidget(&_qDrawing, 0, 0, 1, 2);
_qGrid.addWidget(&_qSliderV, 0, 2);
_qGrid.addWidget(new QLabel("Translation Horizontal"), 1, 0);
_qSliderT.setRange(150, 650);
_qSliderT.setSliderPosition(400);
_qGrid.addWidget(&_qSliderT, 2, 0);
_qGrid.addWidget(new QLabel("Rotation"), 1, 1, 1, 2);
_qSliderR.setSliderPosition(50);
_qGrid.addWidget(&_qSliderR, 2, 1, 1, 2);
setLayout(&_qGrid);
}
Output:
To achieve this specific layout, I placed the _qSliderV into column 2 and gave _qSliderR (and its label) a column span of 2 as well.
To illustrate this, I added a sketch of the resulting grid to the above snapshot:
Which behavior would you like for your Drawing widget?
By default it will be resized freely by the layout, you can change this by using QWidget::sizeHint() and QWidget::sizePolicy(). You'll find detailed information in Qt documentation about custom widgets and layouts.

QListView max number of items in view

I need to calculate max number of items in current view of QListView.
I wrote code like this:
void MyListView::resizeEvent(QResizeEvent *event)
{
QListView::resizeEvent(event);
QFontMetrics fm (this->font());
int fontHeight = fm.lineSpacing();
QRect cr = contentsRect();
int windowHeight = cr.bottom() - cr.top();
int maxItemsCount = windowHeight / fontHeight;
qDebug()<<"max items in view: "<< maxItemsCount;
}
but calculated max number of items is is incorrect.
E.g. in case of my window height and font height I get 32 max items in view when in fact current view has 28 items. Perhaps someone can suggest something, how to calculate it properly?
My idea is to use QListView::indexAt() (inherited from QAbstractView) to obtain the row index for
the top-left corner
the bottom-left corner
of the list view viewport and determining the number of visible items by difference of them.
To check this out, I made an MCVE testQListViewNumVisibleItems.cc:
// Qt header:
#include <QtWidgets>
class ListWidget: public QListWidget {
public:
ListWidget(QWidget *pQParent = nullptr): QListWidget(pQParent) { }
virtual ~ListWidget() = default;
ListWidget(const ListWidget&) = delete;
ListWidget& operator=(const ListWidget&) = delete;
int getNumVisibleItems() const
{
const QSize size = viewport()->size();
const QModelIndex qMIdx0 = indexAt(QPoint(0, 0));
const QModelIndex qMIdx1 = indexAt(QPoint(0, size.height() - 1));
//qDebug() << "qMIdx0:" << qMIdx0 << "qMIdx1:" << qMIdx1;
return qMIdx0.isValid()
? (qMIdx1.isValid() ? qMIdx1.row() : count()) - qMIdx0.row()
: 0;
}
};
const int MaxItems = 20;
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
ListWidget qLst;
qLst.resize(200, 200);
qLst.show();
// timer to populate list view
using namespace std::chrono_literals;
QTimer qTimer;
qTimer.setInterval(1000ms);
// install signal handlers
int n = 0;
QObject::connect(&qTimer, &QTimer::timeout,
[&]() {
qLst.addItem(QString("item %0").arg(++n));
qDebug() << "Visible items:" << qLst.getNumVisibleItems();
if (n >= MaxItems) qTimer.stop();
});
// runtime loop
qTimer.start();
return app.exec();
}
and a CMakeLists.txt:
project(QListViewNumVisibleItems)
cmake_minimum_required(VERSION 3.10.0)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(Qt5Widgets CONFIG REQUIRED)
include_directories("${CMAKE_SOURCE_DIR}")
add_executable(testQListViewNumVisibleItems testQListViewNumVisibleItems.cc)
target_link_libraries(testQListViewNumVisibleItems Qt5::Widgets)
built and tested in VS2017 on Windows 10:
After having implemented what came in my mind, I googled a bit to possibly see other approaches. (I admit I should've done before.)
Thereby I found the following possible duplicate:
SO: Simple way to get all visible items in the QListView
The accepted answer doesn't contain much more than the hint for indexAt and a link to a Qt-FAQ article:
How can i get hold of all of the visible items in my QListView?
In order to get hold of the visible items in a QListView http://doc.qt.io/qt-5/qlistview.html, then you can iterate over them using indexAt() http://doc.qt.io/qt-5/qlistview.html#indexAt. You can get hold of the first visible item using indexAt(QPoint(0, 0)), then in order to get the index at the next position then use visualRect() http://doc.qt.io/qt-5/qlistview.html#visualRect to find out what your next call to itemAt() should be. This position would be:
visualRect.y() + visualRect.height() + 1 effectively.
See the following example for an illustration:
#include <QtGui>
QList <QModelIndex>myList;
class ListView : public QListView
{
Q_OBJECT
public:
ListView()
{
QStringListModel *myModel = new QStringListModel(this);
QStringList list;
list << "a" << "b" <<"c" <<"d" <<"e" <<"f" <<"g" <<"h" <<"i" <<"j" <<"k";
myModel->setStringList(list);
setModel(myModel);
QTimer::singleShot(3000, this, SLOT(test1()));
}
public slots:
void test1()
{
QModelIndex firstIndex = indexAt(QPoint(0, 0));
if (firstIndex.isValid()) {
myList << firstIndex;
} while (viewport()->rect().contains(QPoint(0, visualRect(firstIndex).y() + visualRect(firstIndex).height() + 1 ))) {
firstIndex = indexAt(QPoint(0, visualRect(firstIndex).y() + visualRect(firstIndex).height() + 1 ));
myList << firstIndex;
}
qDebug() << myList.count() << "are visible";
}
};
#include "main.moc"
int main(int argc, char** argv)
{
QApplication app(argc, argv);
ListView window;
window.resize(100, 50);
window.show();
return app.exec();
}

Qt - Overriding ResizeEvent give multiple calls when showed

I get this strange behavior with a simple reimplementation of ResizeEvent from a standard Widget. After calling show, the resize event is called 2 times. I trace the behavior with breakpoint (see where below "<- - - -") and I got this :
first call :
event->oldSize = (-1, -1)
event->size = (5, 13)
second call :
event->oldSize = (640, 480)
event->size = (640, 480)
The first call seem coherent. But, is anyone can explain to me why I got a second call with inconsistant values and how to get rid of this second resize event?
class MyClass : public QLabel
{
Q_OBJECT
public:
MyClass(QWidget *parent = 0, Qt::WindowFlags f = 0);
~MyClass();
protected:
void resizeEvent(QResizeEvent *event);
};
MyClass::MyClass(QWidget *parent, Qt::WindowFlags f)
: QLabel(parent, f)
{
}
MyClass::~MyClass()
{
}
void MyClass::resizeEvent(QResizeEvent *event)
{
int a = 0; // <- - - - use break point here
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyClass w;
w.show();
return a.exec();
}
Your last question make me do some tests and I found a solution.
I just add in the constructor :
this->setGeometry(...)
With that, I got only one resizeEvent call (the first one).
I do not know why Qt make this second call but I can guess is to set an initial geometry.
However, thank you for your time. It help me a lot! :-)

How to get the row/column location of a widget in a QGridLayout?

I made an own GridPushButton class to store the buttons position in gridlayout. The parent is QPushButton. I have a problem with asking it's x and y coordinates in window (like x:654, y:768). I thought it will be inherited from base class, but it doesn't. Now i have two options:
Use the original QPushButton class and somehow get its position in gridlayout (like x:0, y:1 if it's in the first row and second column) or
Use my GridPushButton and somehow get the x and y coordinate in window.
class GridPushButton : public QPushButton
{
Q_OBJECT
public:
GridPushButton(int coordX, int coordY, QWidget *parent = 0);
int coordinateX() { return _coordX; }
int coordinateY() { return _coordY; }
protected:
int _coordX;
int _coordY;
};
This is my class. I tried to make a new private variable and give it the QPushButton::x(), but doesn't work. Any idea to get the x and y coordinate from parent?
Or any idea to get the QPushButtons position in GridLayout?
There are two notions of coordinates that you're mixing up. There is the position within the parent widget. That's available via QWidget::x(), QWidget::y() and QWidget::pos() methods. You don't need to implement anything here: it already works.
Then there's the notion of the row and column within the grid layout. This can be obtained without a need for any subclassing. The grid layout knows where its widgets are, you can simply ask it for the row/column location of any widget.
#include <QtWidgets>
struct Pos { int row = -1, col = -1; };
Pos gridPosition(QWidget * widget) {
if (! widget->parentWidget()) return {};
auto layout = qobject_cast<QGridLayout*>(widget->parentWidget()->layout());
if (! layout) return {};
int index = layout->indexOf(widget);
Q_ASSERT(index >= 0);
int _;
Pos pos;
layout->getItemPosition(index, &pos.row, &pos.col, &_, &_);
return pos;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QGridLayout l(&w);
QLabel gridPos;
l.addWidget(&gridPos, 0, 0, 1, 4);
for (int i = 1; i < 4; ++ i)
for (int j = 0; j < 3; ++ j) {
auto b = new QPushButton(QString("%1,%2").arg(i).arg(j));
l.addWidget(b, i, j);
QObject::connect(b, &QPushButton::clicked, [&gridPos, b]{
auto p = gridPosition(b);
gridPos.setText(QString("Grid Pos: %1,%2")
.arg(p.row).arg(p.col));
});
}
w.show();
return a.exec();
}

navigate between widgets using arrows in QT

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
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();
}
To enable keypad navigation, build Qt with QT_KEYPAD_NAVIGATION defined.
https://het.as.utexas.edu/HET/Software/html/qapplication.html#keypadNavigationEnabled

Resources