如何在Qt中打破小部件的Tab键顺序链?

时间:2015-10-09 12:18:19

标签: c++ qt

在Qt中,您可以使用 Qt Designer 或使用C ++来定义Tab键顺序。小部件之间的关系彼此相对设置,因此没有索引或类似的东西。我现在想要的是“打破”小部件的循环链,以便我得到链的起点和终点。

圆形标签顺序为:

A - B 
|   |
D - C

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

A - B
    |
D - C

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

A - B - C - D

因此用户在一端“停止”并且必须使用另一个方向返回。

更新:我现在有了另一个想法。如果我重新实现该怎么办:

bool QWidget::focusNextPrevChild(bool next)

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

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

2 个答案:

答案 0 :(得分:2)

我找到了一个解决方案,但它有点hacky。 QWidget::setTabOrder将不允许将小部件链接到自身,因此这种方法无济于事(即使您使用焦点代理)

但是,您可以定义“Focus Forwarder”:

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)无关紧要。只要FocusFowarder获得焦点,它就会将其“转发”到其代理。 方向由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

答案 1 :(得分:0)

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

在我的问题中,我提到了压倒一切:

bool focusNextPrevChild(bool next)

可能导致一个工作系统。如果“接收”小部件被标记为“最后一个项目”或“第一个项目”并且“下一个”参数将导致循环行为,则通过返回“真”来简单地忽略焦点。虽然这适用于制表符和空格+制表符组合键,但有些情况下不会显式调用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()适当的时间。