们日常切换控件,例如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.


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

实现效果如下 

Qt控件焦点切换_双向链表_02 

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


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

主要使用

void setFocusPolicy(Qt::FocusPolicy policy)


  • 1

其中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);


  • 1

效果如下 

Qt控件焦点切换_输入框_03 

图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.


  • 1
  • 2
  • 3

可直接设置焦点

接下来可以看一下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);
}