Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fix tab order after inserting widgets in a layout in QT

I have a custom list implementation (a subclass of QWidget) in QT 5.5. The elements of the list are organized using a QVBoxLayout. At runtime, elements (which are also QWidgets) can be dynamically added to and removed from the list at any position in the layout. This is working fine, except for one detail: the tab order of inserted focusable elements is wrong. The last element inserted will always be the last in the tab order, even if inserted in between two other elements.

How can I fix the tab order to represent the layout order? I already tried iterating over the list elements and using setTabOrder() on each adjacent pair, without success.

A few more details on the implementation:

  • Widgets are not added directly to the list. Each time a widget should be added, a proxy widget is created and added instead, the 'real' widget will be reparented to the proxy (The proxy is doing some graphic stuff).
  • QVBoxLayout::insertWidget() is used to insert proxy widgets, followed by a call to QWidget::show()
  • when removing elements, the element will be hidden, removed from the proxy, the proxy is removed from the list layout and deallocated
  • focusable widgets can be anywhere in the object tree of elements which are added to the list, they are not necessarily the elements themselves

Update: Added a MCVE!

The following minified example demonstrates the problem. For completeness, I also included the headers, main function, and .pro file. You can safely skip those files if you don't want to reproduce the issue, TabOrderTestWindow.cpp is the important one.

TabOrderTestWindow.cpp:

#include "TabOrderTestWindow.h"

#include <QVBoxLayout>
#include <QPushButton>

// create a button inside a proxy widget
QWidget* createButtonProxy(const QString& caption, QWidget* parent) {
    QWidget* proxy = new QWidget(parent);
    QPushButton* button = new QPushButton(caption, proxy);
    proxy->setFocusProxy(button);
    return proxy;
}

TabOrderTestWindow::TabOrderTestWindow()
    : QWidget()
{
    setMinimumHeight(200);
    setMinimumWidth(350);

    QVBoxLayout* layout = new QVBoxLayout(this);

    // create and add 3 buttons in order
    QWidget* button1 = createButtonProxy("button 1", this);
    QWidget* button2 = createButtonProxy("button 2", this);
    QWidget* button3 = createButtonProxy("button 3", this);
    layout->addWidget(button1);
    layout->addWidget(button2);
    layout->addWidget(button3);

    // now insert a fourth button in between the others - incorrect tab order!
    QWidget* buttonInbetween = createButtonProxy("button in between", this);
    layout->insertWidget(1, buttonInbetween);

    // attempt to correct tab order - not working, even with focus proxy set...
    setTabOrder(button1, buttonInbetween);
    setTabOrder(buttonInbetween, button2);
}

TabOrderTestWindow.h:

#ifndef TABORDERTESTWINDOW_H
#define TABORDERTESTWINDOW_H

#include <QMainWindow>

class TabOrderTestWindow : public QWidget
{
    Q_OBJECT

public:
    TabOrderTestWindow();
};

#endif // TABORDERTESTWINDOW_H

main.cpp:

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

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

    return a.exec();
}

TabOrderTest.pro:

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = TabOrderTest
TEMPLATE = app


SOURCES += main.cpp\
        TabOrderTestWindow.cpp

HEADERS  += TabOrderTestWindow.h
like image 657
Cybran Avatar asked Nov 17 '15 15:11

Cybran


People also ask

How do I change the order of tabs in Qt?

To enter tab order editing mode, open the Edit menu and select Edit Tab Order. In this mode, each input widget in the form is shown with a number indicating its position in the tab order.

How do I align widgets in Qt?

Right-click the relevant widget and select Layout alignment > Top.

How do I use tab widget in Qt?

The normal way to use QTabWidget is to do the following: Create a QTabWidget. Create a QWidget for each of the pages in the tab dialog, but do not specify parent widgets for them. Insert child widgets into the page widget, using layouts to position them as normal.

Should I use Qt Quick or Qt widgets?

Qt Widgets provide means for customization via style sheets, but Qt Quick is a better performing choice for user interfaces that do not aim to look native. Qt Widgets do not scale well for animations. Qt Quick offers a convenient and natural way to implement animations in a declarative manner.


1 Answers

Here really seems to be a bug as the Doc state that focus proxies would be cared of.

But we can care of them ourself by using:

setTabOrder(button1->focusProxy(), buttonInbetween->focusProxy());
setTabOrder(buttonInbetween->focusProxy(), button2->focusProxy());

So it seems you need to do what Qt should have done for you.

like image 141
Aaron Avatar answered Sep 28 '22 20:09

Aaron