欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Qt控件焦点切换

程序员文章站 2022-05-29 11:01:54
...

人们日常切换控件,例如QQ登陆的账号和密码输入框就可以通过Tab键切换焦点。
Qt控件焦点切换
图1 qq切换焦点
Qt中QWidget提供了一个静态方式实现该效果
其中也包含介绍使用

[static] void QWidget::setTabOrder(QWidget *first, QWidget *second)
Puts the second widget after the first widget in the focus order.
Note that since the tab order of the second widget is changed, you should order a chain like this:

  setTabOrder(a, b); // a to b
  setTabOrder(b, c); // a to b to c
  setTabOrder(c, d); // a to b to c to d

not like this:

  // WRONG
  setTabOrder(c, d); // c to d
  setTabOrder(a, b); // a to b AND c to d
  setTabOrder(b, c); // a to b to c, but not c to d

If first or second has a focus proxy, setTabOrder() correctly substitutes the proxy.

实现效果如下
Qt控件焦点切换
图2 Tab切换焦点
主要部分源码

    QPushButton *p = new QPushButton("按钮");
    QLineEdit *l = new QLineEdit("输入框");
    QCheckBox *c =  new QCheckBox("复选框");
    QComboBox *b = new QComboBox;
    setTabOrder(p, l);
    setTabOrder(l, c);
    setTabOrder(c, b);

当然也可以让控件屏蔽焦点的使用,或者接受响应其他类型的焦点方式
主要使用

void setFocusPolicy(Qt::FocusPolicy policy)

其中Qt::FocusPolicy 这个枚举类型定义了一个控件可以用来获取键盘焦点的各种策略。

Constant Value Description
Qt::TabFocus 0x1 the widget accepts focus by tabbing.
Qt::ClickFocus 0x2 the widget accepts focus by clicking.
Qt::StrongFocus TabFocus | ClickFocus | 0x8 he widget accepts focus by both tabbing and clicking. On macOS this will also be indicate that the widget accepts tab focus when in ‘Text/List focus mode’.
Qt::WheelFocus StrongFocus | 0x4 like Qt::StrongFocus plus the widget accepts focus by using the mouse wheel.
Qt::NoFocus 0 the widget does not accept focus.

例如设置
QCheckBox为NoFouse

c->setFocusPolicy(Qt::NoFocus);

效果如下
Qt控件焦点切换
图3 复选框设置NoFocuse
函数

[slot] void QWidget::setFocus() //可通过信号槽方式设置
This is an overloaded function. 
Gives the keyboard input focus to this widget (or its focus proxy) if this widget or one of its parents is the active window.

可直接设置焦点

接下来可以看一下Qt是怎么实现焦点切换的,查看qwidget.cpp源码

void QWidget::setTabOrder(QWidget* first, QWidget *second)
{
    //如果这两个控件都设置为没有焦点则不进行焦点设置,如图3中所示
    if (!first || !second || first->focusPolicy() == Qt::NoFocus || second->focusPolicy() == Qt::NoFocus)
        return;

    /******************************************************************
            QWidget *QWidget::window() const
        {
            QWidget *w = const_cast<QWidget *>(this);
            QWidget *p = w->parentWidget();
            while (!w->isWindow() && p) {
                w = p;
                p = p->parentWidget();
            }
            return w;
        }
    ******************************************************************/
    //查看当前控件“祖先”(ancestor widget)窗口,见上面代码
    if (Q_UNLIKELY(first->window() != second->window())) {
        qWarning("QWidget::setTabOrder: 'first' and 'second' must be in the same window");
        return;
    }

    //找到first或其子类中焦点的控件(不是很懂)
    QWidget *fp = first->focusProxy();
    if (fp) {
        QList<QWidget *> l = first->findChildren<QWidget *>();
        for (int i = l.size()-1; i >= 0; --i) {
            QWidget * next = l.at(i);
            if (next->window() == fp->window()) {
                fp = next;
                if (fp->focusPolicy() != Qt::NoFocus)
                    break;
            }
        }
        first = fp;
    }

    if (fp == second)
        return;

    if (QWidget *sp = second->focusProxy())
        second = sp;

    //双向链表存储焦点触发控件顺序
//    QWidget *fp = first->d_func()->focus_prev;
    QWidget *fn = first->d_func()->focus_next;

    if (fn == second || first == second)
        return;

    QWidget *sp = second->d_func()->focus_prev;
    QWidget *sn = second->d_func()->focus_next;

    fn->d_func()->focus_prev = second;
    first->d_func()->focus_next = second;

    second->d_func()->focus_next = fn;
    second->d_func()->focus_prev = first;

    sp->d_func()->focus_next = sn;
    sn->d_func()->focus_prev = sp;

    //查错
    Q_ASSERT(first->d_func()->focus_next->d_func()->focus_prev == first);
    Q_ASSERT(first->d_func()->focus_prev->d_func()->focus_next == first);

    Q_ASSERT(second->d_func()->focus_next->d_func()->focus_prev == second);
    Q_ASSERT(second->d_func()->focus_prev->d_func()->focus_next == second);
}
相关标签: qt 控件 焦点