navigate between widgets using arrows in QT - 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

Related

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();
}

Mapping a position within a QHeaderView to the context of the scrolled Viewport

I have a QHeaderView that does a few things when clicking either to the left or right of the center of a section header.
For this, I need to know the position of the click in relation to the (possibly scrolled) content of the QHeaderView's viewport.
The actual click position, however, refers only to the QHeaderView (which is always fixed).
I tried variants of mapTo/From, but can't find the correct way to do it.
Here's simplified code:
void MyTableHeader::headerSectionClicked(int section_index, int click_pos)
{
int section_size = sectionSize(0); // All sections in a header are equally sized
int section_center = (section_size * (section_index+ 1)) - (section_size / 2); // Center of the clicked section
if (section_index>= 0)// if mouse is over an item
{
if (orientation() == Qt::Horizontal)
{
QPoint x_pos = QPoint(click_pos, 0);
int mapped_offset = viewport()->mapFrom(this, x_pos).x();
if (mapped_offset != -1)
{
// If the click was to the right of the center, iterate on the index
if (mapped_offset >= section_center)
{
section_index++;
}
}
}
else
{
// Same thing for the Y-dimension
}
}
// Neat stuff after this
}
The part where the problem occurs, is where I want to find out on which side of the section the click happened.
// If the click was to the right of the center, iterate on the index
if (mapped_offset >= section_center)
{
in_index++;
}
This mapped_offset does not properly refer to the same context as the section center.
The following solution might give you an idea on what to do.
MyHeaderView.h
#pragma once
#include <QHeaderView>
class MyHeaderView : public QHeaderView
{
Q_OBJECT
public:
MyHeaderView(Qt::Orientation orientation, QWidget* parent = nullptr);
};
MyHeaderView.cpp
#include "MyHeaderView.h"
#include <QDebug>
MyHeaderView::MyHeaderView(Qt::Orientation orientation, QWidget* parent) : QHeaderView(orientation, parent)
{
setSectionsClickable(true);
connect(this, &MyHeaderView::sectionClicked, [this](int section)
{
QRect currentSectionRect;
currentSectionRect.setRect(sectionViewportPosition(section), 0, sectionSize(section), viewport()->height());
auto pos = QCursor::pos();
auto localPos = mapFromGlobal(pos);
qDebug() << currentSectionRect << localPos;
qDebug() << currentSectionRect.contains(localPos); //"Always true!"
});
}
main.cpp
#include "MyHeaderView.h"
#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>
int main(int argc, char **args)
{
QApplication app(argc, args);
auto model = new QStandardItemModel;
auto view = new QTableView;
view->setModel(model);
model->setRowCount(4);
model->setColumnCount(4);
view->setHorizontalHeader(new MyHeaderView(Qt::Horizontal));
view->show();
app.exec();
}

Programmatic scrolling with QGraphicsView and QGraphicsItem?

I would like to programmatically scroll a scene to the left / right, but I am not sure how to do that properly. Note that I do not want to have (visible) scroll bars.
I use a standard QGraphicsView + QGraphicsScene + QGraphicsItem setup. I have downsized it to the minimum, with one single QGraphicsItem (a QGraphicsRectItem) in the scene.
I have managed to achieve programmatic scrolling by setting my view like this:
// view setup
view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
and then, in another part of the code:
// programmatic scrolling
QScrollBar* const sb = view->horizontalScrollBar();
sb->setRange(0, 1000); // some values for experimenting
sb->setValue(sb->value() + 100 or -100); // some increment for experimenting
This works, but... scrolling through invisible scrollbars doesn't feel right.
I tried this more straightforward approach:
// programmatic scrolling - doesn't quite work
view->viewport()->scroll(100 or -100, 0); // some increment for experimenting
This code does scroll, but when the rectangle goes off the left edge of the view, and I reverse the scrolling direction (increment changed from 100 to -100 in the call to scroll()), the uncovered part of the rectangle is not repainted. The reason is that QGraphicsRectItem::paint() is not called in that case (it is called when using the scrollbar method).
So, is there a way to get viewport()->scroll() work? Or some other simple way to achieve programmatic scrolling? Or is the artificial scrollbar method just the way to go?
Moving the view assumes that it's smaller than its scene. If they're the same size, it won't move.
QGraphicsView can be set to centerOn any position in scene coordinates. Use a timer to call centerOn to move the view one frame at a time.
Here's a working example: -
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QTimer>
class MyView : public QGraphicsView
{
private:
public:
MyView(QGraphicsScene* pScene)
: QGraphicsView(pScene, NULL)
{}
void AnimateBy(int x)
{
float updateFrequency = (1000/30.0); // ~30 frames per second
QPointF currScenePos = sceneRect().center();
int curX = currScenePos.x();
int endPos = curX + x;
int distanceToAnimate = (endPos - curX);
// speed = dist / time
float updatePosInterval = (float)distanceToAnimate / updateFrequency;
printf("updatePosInterval: %f \n", updatePosInterval);
static float newXPos = sceneRect().center().x();
QTimer* pTimer = new QTimer;
QObject::connect(pTimer, &QTimer::timeout, [=](){
newXPos += updatePosInterval;
centerOn(newXPos, sceneRect().center().y());
// check for end position or time, then....
if(newXPos >= endPos)
{
pTimer->stop();
pTimer->deleteLater();
}
});
pTimer->start(updateFrequency);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene scene(0, 0, 10000, 20000);
MyView* view = new MyView(&scene);
QGraphicsRectItem* pRect = new QGraphicsRectItem(0, 0, 100, 100);
pRect->setPos(scene.width()/2, scene.height()/2);
scene.addItem(pRect);
// timer to wait for the window to appear, before starting to move
QTimer* pTimer = new QTimer;
pTimer->setSingleShot(true);
QObject::connect(pTimer, &QTimer::timeout,[=](){
view->centerOn(pRect); // centre on the rectangle
view->AnimateBy(100);
pTimer->deleteLater();
});
pTimer->start(1000);
view->show();
return a.exec();
}
So, we create the animation by moving the view frame-by-frame using the call to centerOn.
For simplicity, the code just deals with moving in one axis. To move in 2 axis, use 2D vector maths to calculate the interval position.
Try to change the view transformation with the QGraphicsView::translate() or QGraphicsView::setTransform().
But keep in mind that you can't move the viewport "outside" the scene, so make sure that your scene rectangle is large enough.
If I got your question correctly, there is a dojo classes library with such class as PanWebView that allow QWebView to scroll smoothly with mouse without any scrollbars. Take a look at sources. It supports panning and can be suitable for mobile apps, but maybe it'll help you too.
PanWebView class looks like this
#include <QWebView>
#include <QWebFrame>
#include <QMouseEvent>
#include <QApplication>
class PanWebView : public QWebView
{
Q_OBJECT
private:
bool pressed;
bool scrolling;
QPoint position;
QPoint offset;
QList<QEvent*> ignored;
public:
PanWebView(QWidget *parent = 0): QWebView(parent), pressed(false), scrolling(false) {
QWebFrame *frame = page()->mainFrame();
frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
}
protected:
void mousePressEvent(QMouseEvent *mouseEvent) {
if (ignored.removeAll(mouseEvent))
return QWebView::mousePressEvent(mouseEvent);
if (!pressed && !scrolling && mouseEvent->modifiers() == Qt::NoModifier)
if (mouseEvent->buttons() == Qt::LeftButton) {
pressed = true;
scrolling = false;
position = mouseEvent->pos();
QWebFrame *frame = page()->mainFrame();
int x = frame->evaluateJavaScript("window.scrollX").toInt();
int y = frame->evaluateJavaScript("window.scrollY").toInt();
offset = QPoint(x, y);
QApplication::setOverrideCursor(Qt::OpenHandCursor);
return;
}
return QWebView::mousePressEvent(mouseEvent);
}
void mouseReleaseEvent(QMouseEvent *mouseEvent) {
if (ignored.removeAll(mouseEvent))
return QWebView::mouseReleaseEvent(mouseEvent);
if (scrolling) {
pressed = false;
scrolling = false;
QApplication::restoreOverrideCursor();
return;
}
if (pressed) {
pressed = false;
scrolling = false;
QMouseEvent *event1 = new QMouseEvent(QEvent::MouseButtonPress,
position, Qt::LeftButton,
Qt::LeftButton, Qt::NoModifier);
QMouseEvent *event2 = new QMouseEvent(*mouseEvent);
ignored << event1;
ignored << event2;
QApplication::postEvent(this, event1);
QApplication::postEvent(this, event2);
QApplication::restoreOverrideCursor();
return;
}
return QWebView::mouseReleaseEvent(mouseEvent);
}
void mouseMoveEvent(QMouseEvent *mouseEvent) {
if (scrolling) {
QPoint delta = mouseEvent->pos() - position;
QPoint p = offset - delta;
QWebFrame *frame = page()->mainFrame();
frame- >evaluateJavaScript(QString("window.scrollTo(%1,%2);").arg(p.x()).arg(p.y()));
return;
}
if (pressed) {
pressed = false;
scrolling = true;
return;
}
return QWebView::mouseMoveEvent(mouseEvent);
}
};
And usage:
PanWebView web;
web.setUrl(QUrl("http://news.google.com"));
web.setWindowTitle("Web View - use mouse to drag and pan around");
web.show();
Also did you check this and this topics? I think it can be usefull.

How display multiple values in QLabel in Qcreator

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.

Qt - Declaring a QLabel array and adding it to a QTabWidget's tab

I have declared a QLabel array (which i will be using as picture boxes) and the function call which is going to fill the array in the header file of the project as:
void fillArray();
QLabel *label_array[7];
Then i called the declared function in the main(). That function is going to fill the array and add the labels to the QTabWidget tab's body. In the source file i wrote the following:
void MainWindow::fillArray()
{
for (int i = 0; i < sizeof(MainWindow::label_array) - 1; i++)
{
MainWindow::label_array[i] = new QLabel("Label " + i + 1);
ui->tabWgt->widget(0)->layout()->addWidget(MainWindow::label_array[i])
}
}
But the problem is it returns an error at the moment i build the program. I tried creating a layout and adding it to the layout and setting the tab's layout to that layout, it works, but it gets really messy and it is not understandable.
void MainWindow::fillArray()
{
QHBoxLayout *layout = new QHBoxLayout;
for (int i = 0; i < sizeof(MainWindow::label_array) - 1; i++)
{
MainWindow::label_array[i] = new QLabel("Label " + i + 1);
MainWindow::label_array[i]->move(10, 5*i); // this doesn't work...
layout->addWidget(MainWindow::label_array[i]);
}
ui->tabWgt->widget(0)->setLayout(layout);
}
Is it possible to add the labels directly to the tab's body without having to declare a layout (since i'd need to do it to every created array i declare along my program, and i have no idea how they are being located)?
How to position the label correctly?
Is it possible to add the labels directly to the tab's body without having to declare a layout (...) ?
No, because by default widget don't have a layout and you have to create it. But you can do it more clear:
void MainWindow::fillArray()
{
/* set layout for tab */
ui->tabWgt->widget(0)->setLayout(new QVBoxLayout);
/* calculate size of array */
const uint array_size = sizeof(MainWindow::label_array)/sizeof(MainWindow::label_array[0]);
/* fill tab widget */
for (uint i = 0; i < array_size; i++)
{
MainWindow::label_array[i] = new QLabel(QString("Label %1").arg(i+1));
ui->tabWgt->widget(0)->layout()->addWidget(MainWindow::label_array[i]);
}
}
How to position the label correctly?
Here is an article which describe how to use different layouts for Qt 4.8 or here for Qt5.3.
In no case will you need to allocate the labels explicitly on the heap. You should store them directly as a member array.
With C++11, you should use range-based for to enumerate the safe, zero-overhead std::array.
Otherwise, use the C-style array and the sizeof(array)/sizeof(array[0]) idiom to determine its size from within the setup method.
Having written a whole bunch of QWidget based code, I now generally consider child widgets explicitly allocated on the heap in the widget's constructor to be premature pessimizations. There's rarely any reason to write the code that way. The widgets already allocate their implementation data on the heap (the pimpl idiom). When you allocate this skinny, pointer-sized QWidget subclass on the heap, you're simply doubling the number of heap allocations for no good reason at all. Don't.
#include <QApplication>
#include <QLabel>
#include <QVBoxLayout>
#include <array>
class WidgetCpp11 : public QWidget {
QVBoxLayout m_layout;
std::array<QLabel, 3> m_labels; // No overhead compared to `QLabel m_labels[3]`
void setupLabels() {
int n = 1;
for (QLabel & label: m_labels) {
label.setText(QString("Label %1").arg(n++));
m_layout.addWidget(&label);
}
}
public:
WidgetCpp11(QWidget * parent = 0) : QWidget(parent), m_layout(this) {
setupLabels();
}
};
class WidgetCpp98 : public QWidget {
QVBoxLayout m_layout;
QLabel m_labels[3];
void setupLabels() {
for (uint i = 0; i < sizeof(m_labels)/sizeof(m_labels[0]); ++i) {
m_labels[i].setText(QString("Label %1").arg(i+1));
m_layout.addWidget(m_labels+i);
}
}
public:
WidgetCpp98(QWidget * parent = 0) : QWidget(parent), m_layout(this) {
setupLabels();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
WidgetCpp98 w;
w.show();
return a.exec();
}

Resources