一旦 Tab 键导航到达最后一项,就会从 QTableWidget 中清除焦点

问题描述 投票:0回答:1

我自定义了一个对话框,当按下Tab键时,焦点从按钮切换到表格小部件。我希望在遍历表格小部件中的所有项目后,焦点返回到初始按钮。这是一个最小的可重现示例:

#include <QApplication>
#include <QPushButton>
#include <QTableWidget>
#include <QVBoxLayout>

class Widget : public QWidget
{
public:
    explicit Widget()
    {
        setLayout(&vl);

        tw.setRowCount(1);
        tw.setColumnCount(3);

        vl.addWidget(&btn);
        vl.addWidget(&tw);
    }

    bool focusNextPrevChild(bool next)
    {
        QWidget* focusWidget = this->focusWidget();

        if (focusWidget == &tw)
        {
            int currentRow = tw.currentRow();
            int currentCol = tw.currentColumn();
            int lastRow = tw.rowCount() - 1;
            int lastCol = tw.columnCount() - 1;

            if (next && currentRow == lastRow && currentCol == lastCol)
            {
                tw.clearFocus();
                return true;
            }
        }

        return QWidget::focusNextPrevChild(next);
    }
    
private:
    QPushButton btn;
    QTableWidget tw;
    QVBoxLayout vl;
};

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);

    Widget w;
    w.show();

    return a.exec();
}

enter image description here

我尝试覆盖

focusNextPrevChild
函数,但它不起作用。一旦焦点进入表格小部件,就无法离开它。

c++ qt qtablewidget tab-ordering
1个回答
0
投票

musicamante

您正在覆盖父级的

focusNextPrevChild
,这是完全没有意义的,因为只要表格小部件管理它,它就永远不会被触发。 [...]

您可以通过添加

qDebug
来检查
focusWidget
中的
focusNextPrevChild 
来进行检查。
if (focusWidget == &tw)
检查永远不会是
true

[...]

moveCursor()
实施。

以下是如何使用

moveCursor()
来实现你想要的:

#include <QApplication>
#include <QPushButton>
#include <QTableWidget>
#include <QVBoxLayout>
#include <QKeyEvent>

class TableWidget : public QTableWidget
{
    Q_OBJECT

signals:
    void firstItemBackTab();
    void lastItemTab();

protected:
    QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override
    {
        QModelIndex current = currentIndex();
        int row = current.row();
        int col = current.column();
        int lastRow = rowCount() - 1;
        int lastCol = columnCount() - 1;

        //tab press on the last cell
        if(cursorAction == MoveNext && row == lastRow && col == lastCol)
            emit lastItemTab();

        //tab+shift (backtab) press on the first cell
        if(cursorAction == MovePrevious && row == 0 && col == 0)
            emit firstItemBackTab();

        return QTableWidget::moveCursor(cursorAction, modifiers);
    }

    void focusInEvent(QFocusEvent *event) override
    {
        QTableWidget::focusInEvent(event);

        if(hasFocus())
        {
            //if the tab navigation is going forward,
            //as a user, I'd expect the first cell to be active
            if(event->reason() == Qt::TabFocusReason)
            {
                setCurrentCell(0, 0);
            }
            //if the tab navigation is going backwards,
            //as a user, I'd expect the last cell to be active
            else if(event->reason() == Qt::BacktabFocusReason)
            {
                setCurrentCell(rowCount() - 1, columnCount() - 1);
            }
        }
    }
};

class Widget : public QWidget
{
public:
    explicit Widget()
    {
        setLayout(&vl);

        tw.setRowCount(3);
        tw.setColumnCount(3);

        vl.addWidget(&btn);
        vl.addWidget(&tw);
        vl.addWidget(&btn1);

        //the weakest point of this approach is here (as far as my "expertise" go)
        //it's possible the next/previous child is not what you'd expect.
        //Tab order and all. Perhaps QWidget::setTabOrder could help with that.
        connect(&tw, &TableWidget::firstItemBackTab, [=]()
        {
            focusPreviousChild();
        });
        connect(&tw, &TableWidget::lastItemTab, [=]()
        {
            focusNextChild();
        });
    }

private:
    QPushButton btn{"Button 1"};
    TableWidget tw;
    QPushButton btn1{"Button 2"};
    QVBoxLayout vl;
};

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);

    Widget w;
    w.show();

    return a.exec();
}

#include "main.moc"

moveCursor
此处替换了
focusNextPrevChild
中的逻辑,并使用已经可用的
QAbstractItemView::CursorAction
替换了对下一个/上一个的检查。

此方法适用于基本的小部件单元。所以,我认为没有理由重新实现

focusNextPrevChild
。当您的单元格小部件很复杂并且您想要自定义其中的选项卡导航顺序时,您可能会希望这样做。但根据您在
focusNextPrevChild
中实现的逻辑,基本方法应该足够了。


演示:

Tab key navigation inside a widget that contains a table widget between two buttons in a vertical layout. The navigation is circular, and includes all the items inside the table as well.

© www.soinside.com 2019 - 2024. All rights reserved.