如何在Qt中打破小部件的标签顺序链

How to break the tab order chain of widgets in Qt?

本文关键字:标签 顺序 小部 Qt      更新时间:2023-10-16

在Qt中,您可以使用Qt设计器或使用c++来定义制表符顺序。小部件之间的关系是相对设置的,所以没有索引之类的东西。我现在想要的是"打破"小部件的循环链,这样我就得到了链的开始和结束。

圆形制表符顺序为:

A - B 
|   |
D - C

我想(注意A和D之间缺少链接):

A - B
    |
D - C

更像一条线而不是一个圆:

A - B - C - D

所以用户在一端"停止",必须从另一个方向返回。


更新:我现在有另一个想法。如果我重新实现:

bool QWidget::focusNextPrevChild(bool next)

根据文档,可以使用它来实现自定义焦点行为。

在我的动态场景中,GUI中的按钮在运行时进行调整,我将不得不重载函数并设置,例如,内部标志allowFocusNextallowFocusPrev,然后在必要时忽略焦点请求。我试过之后会回来报告的。同时,欢迎任何评论!?: -)

我找到了一个解决方案,但它有点粗糙。QWidget::setTabOrder不允许将小部件与自身链接,所以这种方法没有帮助(即使您使用焦点代理)

但是,你可以定义一个"焦点转发器":

class FocusForwarder : public QWidget
{
public:
    explicit FocusForwarder(QWidget *proxy) :
        QWidget((QWidget *) proxy->parent()),
        m_proxy(proxy)
    {
        setFocusPolicy(Qt::TabFocus);
    }
protected:
    void focusInEvent(QFocusEvent *) {
        m_proxy->setFocus();
    }
private:
    QWidget *m_proxy;
};

并将它们添加到链的开头和结尾:

FocusForwarder *w1 = new FocusForwarder(ui->bA);
FocusForwarder *w2 = new FocusForwarder(ui->bD);
QWidget::setTabOrder(w1, ui->bA);
QWidget::setTabOrder(ui->bA, ui->bB);
QWidget::setTabOrder(ui->bB, ui->bC);
QWidget::setTabOrder(ui->bC, ui->bD);
QWidget::setTabOrder(ui->bD, w2);
<标题> 详细信息

要使setTabOrder工作,小部件必须在同一个窗口中。为了确保这一点,转发器被放置在代理的父节点中(在初始化列表中)。

对于这种机制,焦点方向(Tab或Shit+Tab)无关紧要。focusforwarder一旦接收到焦点,就会将焦点"转发"给它的代理。方向是由Qt内部处理的。你只需在你的链条上添加"哨兵"。

在QtDesigner中使用

当您想在QtDesigner中使用它时,您将创建一个Widget并将其推广到转发器。由于不能直接设置代理,因此可以为代理的名称添加一个动态属性,如下所示:

class FocusForwarderDesigner : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(QString proxyName READ proxyName WRITE setProxyName)
public:
    QString proxyName() { 
        return (m_proxy) ? m_proxy->objectName() : QString::null; 
    }
    void setProxyName(QString name) {
       m_proxy = parent()->findChild<QWidget *>(name);
    }
    explicit FocusForwarderDesigner(QWidget *parent = NULL) :
        QWidget(parent) {}
protected:
    void focusInEvent(QFocusEvent *) {
        if (m_proxy) m_proxy->setFocus();
    }
private:
    QWidget *m_proxy;
}

在设计器中,您将添加名称为proxyName的字符串属性,并将其设置为代理的名称。不要忘记在设计器中将focus policy设置为Tab Focus

经过一些额外的思考,我发布了一个回答我自己的问题,因为它是一个工作的解决方案,但它不是理想的。因此,我还在寻找一个更好的!值得一提的是,我的应用程序主要依靠鼠标滚轮交互来改变小部件的焦点。

在我的问题中我提到了重写:

bool focusNextPrevChild(bool next)

可能导致一个工作系统。如果焦点被标记为"最后一项"或"第一项","接收"小部件将通过返回"true"来忽略焦点,而"next"参数将导致循环行为。虽然这适用于tab和空格+tab键组合,但在某些情况下,focusNextPrevChild没有被显式调用。在我的例子中,它不调用与鼠标滚轮事件相关的焦点更改。

我所做的是重写:

void wheelEvent(QWheelEvent* event)

这使我能够直接控制与鼠标滚轮相关的所有焦点事件。我的重写函数看起来像这样:

void SelectionIconButton::wheelEvent(QWheelEvent* event)
{
    bool next = event->delta() > 0;
    if (m_IsLastInFocusChain && next) {
        event->accept();
        return;
    }
    if (m_IsFirstInFocusChain && !next) {
        event->accept();
        return;
    }
    QPushButton::wheelEvent(event);
}

所以这个系统的要求是:

  • 每个小部件必须以某种方式实现两个bool并处理它们状态。
  • 这些小部件必须在启动时配置或者在应用程序使用
  • 期间的动态屏幕中
  • 只听wheelEvent不允许我处理tab键和空格+tab键

您可以看到此解决方案是有效的,但是要将其应用到大型应用程序中需要付出一些努力。我在想一个更一般的解决方案。也许是一个全局列表,当屏幕发生变化时更新。然后,这个全局列表将以某种方式决定是否允许焦点更改。不幸的是,这对于鼠标滚轮事件来说也是有问题的,因为一些小部件是"活动的",而滚轮事件甚至不想改变焦点,而是改变输入字段中的值,例如,

编辑:

我可能不得不补充说,QWidget::wheelEvent()QPushButton::wheelEvent()以及更多Qt-Widgets的默认实现只是通过设置event->ignore()来忽略事件。在我的应用程序中,所有被忽略的事件都被一个高级小部件捕获,然后该小部件解释QWheelEvent并使用其增量来调用focusPreNextChild()适当的时间。