在 QT 的布局中插入小部件后修复 Tab 键顺序

2022-01-18 00:00:00 qt widget layout c++ tab-ordering

我在 QT 5.5 中有一个自定义列表实现(QWidget 的子类).列表的元素使用 QVBoxLayout 进行组织.在运行时,元素(也是 QWidgets)可以在布局中的任何位置动态添加到列表中或从列表中删除.这工作正常,除了一个细节:插入的可聚焦元素的制表符顺序错误.插入的最后一个元素始终是 Tab 键顺序中的最后一个元素,即使插入在其他两个元素之间也是如此.

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.

如何固定制表符顺序以表示布局顺序?我已经尝试遍历列表元素并在每个相邻对上使用 setTabOrder() ,但没有成功.

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.

关于实现的更多细节:

  • 小部件不会直接添加到列表中.每次应添加小部件时,都会创建并添加一个代理小部件,真实"小部件将重新设置为代理的父级(代理正在执行一些图形操作).
  • QVBoxLayout::insertWidget() 用于插入代理小部件,然后调用 QWidget::show()
  • 移除元素时,元素会被隐藏,从代理中移除,代理从列表布局中移除并释放
  • 可聚焦小部件可以位于添加到列表的元素对象树中的任何位置,它们不一定是元素本身
  • 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

更新:添加了 MCVE!

以下缩小的示例演示了该问题.为了完整起见,我还包括了标题、主要功能和 .pro 文件.如果您不想重现问题,可以安全地跳过这些文件,TabOrderTestWindow.cpp 是重要的.

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

推荐答案

这似乎确实是一个错误,因为焦点代理将被处理的 Doc 状态.

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

所以看来你需要做 Qt 应该为你做的事情.

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

相关文章