• 在Qt中,键盘事件和QWidget的focus密不可分:一般来说,一个拥有焦点(focus)的QWidget或者grabKeyboard()的QWidget才可以接受键盘事件。

键盘事件派发给谁?

如何确定谁来接收键盘事件,不妨看一点点QApplication的源码:

X11下

  1. QETWidget *keywidget=0;
  2. bool grabbed=false;
  3. if (event->type==XKeyPress || event->type==XKeyRelease) {
  4. keywidget = (QETWidget*)QWidget::keyboardGrabber();
  5. if (keywidget) {
  6. grabbed = true;
  7. } else if (!keywidget) {
  8. if (d->inPopupMode()) // no focus widget, see if we have a popup
  9. keywidget = (QETWidget*) (activePopupWidget()->focusWidget() ? activePopupWidget()->focusWidget() : activePopupWidget());
  10. else if (QApplicationPrivate::focus_widget)
  11. keywidget = (QETWidget*)QApplicationPrivate::focus_widget;
  12. else if (widget)
  13. keywidget = (QETWidget*)widget->window();
  14. }
  15. }

Windows下

  1. QWidget *g = QWidget::keyboardGrabber();
  2. if (g && qt_get_tablet_widget() && hwnd == qt_get_tablet_widget()->winId()) {
  3. // if we get an event for the internal tablet widget,
  4. // then don't send it to the keyboard grabber, but
  5. // send it to the widget itself (we don't use it right
  6. // now, just in case).
  7. g = 0;
  8. }
  9. if (g)
  10. widget = (QETWidget*)g;
  11. else if (QApplication::activePopupWidget())
  12. widget = (QETWidget*)QApplication::activePopupWidget()->focusWidget()
  13. ? (QETWidget*)QApplication::activePopupWidget()->focusWidget()
  14. : (QETWidget*)QApplication::activePopupWidget();
  15. else if (QApplication::focusWidget())
  16. widget = (QETWidget*)QApplication::focusWidget();
  17. else if (!widget || widget->internalWinId() == GetFocus()) // We faked the message to go to exactly that widget.
  18. widget = (QETWidget*)widget->window();

大致顺序:

  • QWidget::keyboardGrabber()
  • QApplication::activePopupWidget()
  • QApplication::focusWidget()
  • QWidget::window() [注:对于native的接收到键盘事件的widget,此时Qt将派发给其所属窗口]

在QWidget间切换焦点

Qt键盘事件一文中我们提到这个和focusPolicy相关。我们可以通过Tab键或者鼠标单击来使得某个QWidget获得焦点。

问题:当我们按下Tab键(或者上下左右箭头键)时,下一个或获取焦点的QWidget是如何被确定的?

我们重新贴出上文最后贴出过的QWidget::event()的源码:

  1. case QEvent::KeyPress: {
  2. QKeyEvent *k = (QKeyEvent *)event;
  3. bool res = false;
  4. if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
  5. if (k->key() == Qt::Key_Backtab
  6. || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier)))
  7. res = focusNextPrevChild(false);
  8. else if (k->key() == Qt::Key_Tab)
  9. res = focusNextPrevChild(true);
  10. if (res)
  11. break;
  12. }
  13. keyPressEvent(k);

老是觉得 QWidget::focusNextPrevChild() 这个函数有点名不符实(或者有点别扭),因为:

  1. bool QWidget::focusNextPrevChild(bool next)
  2. {
  3. Q_D(QWidget);
  4. QWidget* p = parentWidget();
  5. bool isSubWindow = (windowType() == Qt::SubWindow);
  6. if (!isWindow() && !isSubWindow && p)
  7. return p->focusNextPrevChild(next);
  8. ...
  9. }

当我们调用一个Widget该成员时,最终将递归调用到其所在窗口的focusNextPrevChild成员。(不过这是一个protected的虚函数,在派生类中可以覆盖它,从而控制派生类实例中的焦点移动。)

prev/next

QWidgetPrivate内有3个成员变量:

  1. class Q_GUI_EXPORT QWidgetPrivate : public QObjectPrivate
  2. {
  3. ...
  4. QWidget *focus_next;
  5. QWidget *focus_prev;
  6. QWidget *focus_child;

这3个变量可以分别用:

  • QWidget::nextInFocusChain()
  • QWidget::previousInFocusChain()
  • QWidget::focusWidget() [注意区分:QApplication::focusWidget()]

进行获取。

前两个可以用来构成一个focus链表。

  1. void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f)
  2. {
  3. Q_Q(QWidget);
  4. focus_next = focus_prev = q;
  5. ...
  6. void QWidgetPrivate::reparentFocusWidgets(QWidget * oldtlw)
  7. {
  8. ...

通过QWidget::setTabOrder()可以调整Widgets在focus链表中的顺序

Widget上放置大量按钮怎么样?

比如一个类似软键盘的东西,在一个QWidget上面放置了大量的QPushButton。此时,除了Tab/Shift+Tab外,上下左右箭头也都可以用来移动焦点。

这是因为:

  1. void QAbstractButton::keyPressEvent(QKeyEvent *e)
  2. {
  3. bool next = true;
  4. switch (e->key()) {
  5. case Qt::Key_Up:
  6. case Qt::Key_Left:
  7. next = false;
  8. // fall through
  9. case Qt::Key_Right:
  10. case Qt::Key_Down:
  11. ...
  12. focusNextPrevChild(next);
  13.  
  14. }

focus proxy

  • QWidget::setFocusProxy()
  • QWidget::focusProxy()

Manual中说的比较清楚:

  • If there is a focus proxy, setFocus() and hasFocus() operate on the focus proxy.

简单列出源码:

  1. void QWidget::setFocus(Qt::FocusReason reason)
  2. {
  3. QWidget *f = this;
  4. while (f->d_func()->extra && f->d_func()->extra->focus_proxy)
  5. f = f->d_func()->extra->focus_proxy;
  6.  
  7. bool QWidget::hasFocus() const
  8. {
  9. const QWidget* w = this;
  10. while (w->d_func()->extra && w->d_func()->extra->focus_proxy)
  11. w = w->d_func()->extra->focus_proxy;

其他

  • 对于 Qt for Embedded Linux、Symbian 和 Windows CE,还有一个
  1. QApplication::setNavigationMode()

设置可通过方向键来控制焦点进行上下左右的移动。

》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

setfocus() 是让某个窗体获得焦点

setfocusPolicy 是设置窗体怎么获得焦点

he focus policy is Qt::TabFocus if the widget accepts keyboard focus by tabbing, Qt::ClickFocus if the widget accepts focus by clicking, Qt::StrongFocus if it accepts both, and Qt::NoFocus (the default) if it does not accept focus at all.

void QWidget::setFocusProxy ( QWidget * w ) [virtual] 就是把窗体获得焦点时的处理,委托给窗体w处理

Sets this widget's focus proxy to w. If w is 0, this function resets this widget to not have any focus proxy.

Some widgets, such as QComboBox, can "have focus," but create a child widget to actually handle the focus. QComboBox, for example, creates a QLineEdit.

setFocusProxy() sets the widget which will actually get focus when "this widget" gets it. If there is a focus proxy, focusPolicy(), setFocusPolicy(), setFocus() and hasFocus() all operate on the focus proxy.

See also focusProxy().

将该widget的focus proxy设置给w。如果w为0,该函数将此widget设为没有任何focus proxy。

有些widget,比如QComboBox,可以“拥有focus”,但是它们会创建一个子的widget来实际地处理焦点。比如QComboBox创建的叫做QLineEdit。

setFocusProxy()用来指定当该widget获得焦点时实际上由谁来处理这个焦点。如果某个widget拥有focus proxy,focusPolicy(),setFocusPolicy(),setFocus()和hasFocus()都是对focus proxy进行操作。

 
http://blog.sina.com.cn/s/blog_a401a1ea0101ec9z.html

QWidget 键盘事件 焦点(源代码级别研究)的更多相关文章

  1. QWidget 键盘事件 焦点(QApplication源码)

    在Qt中,键盘事件和QWidget的focus密不可分:一般来说,一个拥有焦点(focus)的QWidget或者grabKeyboard()的QWidget才可以接受键盘事件. 键盘事件派发给谁? 如 ...

  2. Qt中的键盘事件,以及焦点的设置(比较详细)

    Qt键盘事件属于Qt事件系统,所以事件系统中所有规则对按键事件都有效.下面关注点在按键特有的部分: focus 一个拥有焦点(focus)的QWidget才可以接受键盘事件.有输入焦点的窗口是活动窗口 ...

  3. js键盘事件和焦点事件

    键盘事件onkeydown //当键盘按下的时候触发onkeyup //但键盘抬起的时候触发event.keyCode //数字类型 键盘按键的键值功能键 ctrlkey shiftkey altke ...

  4. 由chrome剪贴板问题研究到了js模拟鼠标键盘事件

    写在前面 最近公司在搞浏览器兼容的事情,所有浏览器兼容的问题不得不一个人包了.下面来说一下今天遇到的一个问题吧 大家都知道IE下面如果要获得剪贴板里面的信息的话,代码应该如下所示 window.cli ...

  5. 深入了解jquery中的键盘事件

    很多时候,我们需要获取用户的键盘事件,下面就一起来看看jquery是如何操作键盘事件的. 一.首先需要知道的是: 1.keydown() keydown事件会在键盘按下时触发. 2.keyup() k ...

  6. jquery键盘事件全记录

    很多时候,我们需要获取用户的键盘事件,下面就一起来看看jquery是如何操作键盘事件的. 一.首先需要知道的是: 1.keydown() keydown事件会在键盘按下时触发. 2.keyup() k ...

  7. Qt事件系统之三:键盘事件

    QKeyEvent类用来描述一个键盘事件.当键盘按键被按下或者被释放时,键盘事件便会被发送给拥有键盘输人焦点的部件. QKeyEvent的key()函数可以获取具体的按键,对于Qt中给定的所有按键,可 ...

  8. QT之键盘事件

    Widget.h: #ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include<QKeyEvent> #incl ...

  9. 深入理解DOM事件类型系列第二篇——键盘事件

    × 目录 [1]类型 [2]顺序 [3]按键信息[4]应用 前面的话 鼠标和键盘是电脑端主要的输入设备,上篇介绍了鼠标事件,本文将详细介绍键盘事件 类型 键盘事件用来描述键盘行为,主要有keydown ...

随机推荐

  1. Maven的使用--Eclipse在线安装Maven插件m2e

    我使用的Eclipse版本是3.7(Indigo) 通过Eclipse的help选项,点击“Install New Software...”弹出安装对话框, 点击add按钮,在Location里输入h ...

  2. JS中的的Url传递中文参数乱码,如何获取Url中参数问题

    一:Js的Url中传递中文参数乱码问题,重点:encodeURI编码,decodeURI解码: 1.传参页面Javascript代码:<script type=”text/javascript” ...

  3. Debain 7.2安装配置

    一 下载安装Debian 7.2 安装debian CD1,在最后一步,使用网络安装基本界面. 二 修改源 cd /etc/apt mv sources.list sources.list.bak g ...

  4. hash算法-time33算法

    http://my.oschina.net/freegeek/blog/325531 http://www.cnblogs.com/napoleon_liu/articles/1911571.html ...

  5. USB匹配电阻

    做过USB的人都或许有一个纠结,那就是D+和D-上到底要串多大的电阻,串在源端还是终端. 我想说:网络上的说法都不完全正确,首先USB有低速.全速和高速之分,在低速和全速模式下是电压驱动的,驱动电压为 ...

  6. DES、AES、TEA加密算法的比较

    1.     DES算法介绍: DES算法具有对称性, 既可以用于加密又可以用于解密.对称性带来的一个很大的好处在于硬件实现, DES 的加密和解密可以用完全相同的硬件来实现.DES 算法的明文分组是 ...

  7. Css 小技巧总结

    相对偏移 指定left top等属性就能够够完美控制一个元素的位置 如: position:relative; left:2px; 今天遇到一个很好玩的问题,当两个并排浮动框,当一个框的长度太大时就会 ...

  8. Linux系统目录/bin /sbin /usr/bin /usr/sbin和/lib /usrlib的一些分析

    其实就是相当于转载了. /bin,/sbin,/usr/sbin,/usr/bin 目录 这些目录都是存放命令的,首先区别下/sbin和/bin: 从命令功能来看,/sbin 下的命令属于基本的系统命 ...

  9. UESTC_秋实大哥与妹纸 2015 UESTC Training for Data Structures<Problem F>

    F - 秋实大哥与妹纸 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 1500/1500KB (Java/Others) Submit ...

  10. Impala 1、Impala理论

    1.Impala简介 • Cloudera公司推出,提供对HDFS.Hbase数据的高性能.低延迟的交互式SQL查询功能. • 基于Hive使用内存计算,兼顾数据仓库.具有实时.批处理.多并发等优点 ...