QWidget 键盘事件 焦点(QApplication源码)
- 在Qt中,键盘事件和QWidget的focus密不可分:一般来说,一个拥有焦点(focus)的QWidget或者grabKeyboard()的QWidget才可以接受键盘事件。
键盘事件派发给谁?
如何确定谁来接收键盘事件,不妨看一点点QApplication的源码:
X11下
QETWidget *keywidget=0;
bool grabbed=false;
if (event->type==XKeyPress || event->type==XKeyRelease) {
keywidget = (QETWidget*)QWidget::keyboardGrabber();
if (keywidget) {
grabbed = true;
} else if (!keywidget) {
if (d->inPopupMode()) // no focus widget, see if we have a popup
keywidget = (QETWidget*) (activePopupWidget()->focusWidget() ? activePopupWidget()->focusWidget() : activePopupWidget());
else if (QApplicationPrivate::focus_widget)
keywidget = (QETWidget*)QApplicationPrivate::focus_widget;
else if (widget)
keywidget = (QETWidget*)widget->window();
}
}
Windows下
QWidget *g = QWidget::keyboardGrabber();
if (g && qt_get_tablet_widget() && hwnd == qt_get_tablet_widget()->winId()) {
// if we get an event for the internal tablet widget,
// then don't send it to the keyboard grabber, but
// send it to the widget itself (we don't use it right
// now, just in case).
g = 0;
}
if (g)
widget = (QETWidget*)g;
else if (QApplication::activePopupWidget())
widget = (QETWidget*)QApplication::activePopupWidget()->focusWidget()
? (QETWidget*)QApplication::activePopupWidget()->focusWidget()
: (QETWidget*)QApplication::activePopupWidget();
else if (QApplication::focusWidget())
widget = (QETWidget*)QApplication::focusWidget();
else if (!widget || widget->internalWinId() == GetFocus()) // We faked the message to go to exactly that widget.
widget = (QETWidget*)widget->window();
大致顺序:
- QWidget::keyboardGrabber()
- QApplication::activePopupWidget()
- QApplication::focusWidget()
- QWidget::window() [注:对于native的接收到键盘事件的widget,此时Qt将派发给其所属窗口]
在QWidget间切换焦点
在Qt键盘事件一文中我们提到这个和focusPolicy相关。我们可以通过Tab键或者鼠标单击来使得某个QWidget获得焦点。
问题:当我们按下Tab键(或者上下左右箭头键)时,下一个或获取焦点的QWidget是如何被确定的?
我们重新贴出上文最后贴出过的QWidget::event()的源码:
case QEvent::KeyPress: {
QKeyEvent *k = (QKeyEvent *)event;
bool res = false;
if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
if (k->key() == Qt::Key_Backtab
|| (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier)))
res = focusNextPrevChild(false);
else if (k->key() == Qt::Key_Tab)
res = focusNextPrevChild(true);
if (res)
break;
}
keyPressEvent(k);
老是觉得 QWidget::focusNextPrevChild() 这个函数有点名不符实(或者有点别扭),因为:
bool QWidget::focusNextPrevChild(bool next)
{
Q_D(QWidget);
QWidget* p = parentWidget();
bool isSubWindow = (windowType() == Qt::SubWindow);
if (!isWindow() && !isSubWindow && p)
return p->focusNextPrevChild(next);
...
}
当我们调用一个Widget该成员时,最终将递归调用到其所在窗口的focusNextPrevChild成员。(不过这是一个protected的虚函数,在派生类中可以覆盖它,从而控制派生类实例中的焦点移动。)
prev/next
QWidgetPrivate内有3个成员变量:
class Q_GUI_EXPORT QWidgetPrivate : public QObjectPrivate
{
...
QWidget *focus_next;
QWidget *focus_prev;
QWidget *focus_child;
这3个变量可以分别用:
- QWidget::nextInFocusChain()
- QWidget::previousInFocusChain()
- QWidget::focusWidget() [注意区分:QApplication::focusWidget()]
进行获取。
前两个可以用来构成一个focus链表。
void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f)
{
Q_Q(QWidget);
focus_next = focus_prev = q;
...
void QWidgetPrivate::reparentFocusWidgets(QWidget * oldtlw)
{
...
通过QWidget::setTabOrder()可以调整Widgets在focus链表中的顺序
Widget上放置大量按钮怎么样?
比如一个类似软键盘的东西,在一个QWidget上面放置了大量的QPushButton。此时,除了Tab/Shift+Tab外,上下左右箭头也都可以用来移动焦点。
这是因为:
void QAbstractButton::keyPressEvent(QKeyEvent *e)
{
bool next = true;
switch (e->key()) {
case Qt::Key_Up:
case Qt::Key_Left:
next = false;
// fall through
case Qt::Key_Right:
case Qt::Key_Down:
...
focusNextPrevChild(next); }
focus proxy
- QWidget::setFocusProxy()
- QWidget::focusProxy()
Manual中说的比较清楚:
- If there is a focus proxy, setFocus() and hasFocus() operate on the focus proxy.
简单列出源码:
void QWidget::setFocus(Qt::FocusReason reason)
{
QWidget *f = this;
while (f->d_func()->extra && f->d_func()->extra->focus_proxy)
f = f->d_func()->extra->focus_proxy; bool QWidget::hasFocus() const
{
const QWidget* w = this;
while (w->d_func()->extra && w->d_func()->extra->focus_proxy)
w = w->d_func()->extra->focus_proxy;
其他
- 对于 Qt for Embedded Linux、Symbian 和 Windows CE,还有一个
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 键盘事件 焦点(QApplication源码)的更多相关文章
- QWidget 键盘事件 焦点(源代码级别研究)
在Qt中,键盘事件和QWidget的focus密不可分:一般来说,一个拥有焦点(focus)的QWidget或者grabKeyboard()的QWidget才可以接受键盘事件. 键盘事件派发给谁? 如 ...
- Cocos2d-X3.0 刨根问底(七)----- 事件机制Event源码分析
这一章,我们来分析Cocos2d-x 事件机制相关的源码, 根据Cocos2d-x的工程目录,我们可以找到所有关于事件的源码都存在放在下图所示的目录中. 从这个event_dispatcher目录中的 ...
- Android View 事件分发机制 源码解析 (上)
一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个My ...
- Android查缺补漏(View篇)--事件分发机制源码分析
在上一篇博文中分析了事件分发的流程及规则,本篇会从源码的角度更进一步理解事件分发机制的原理,如果对事件分发规则还不太清楚的童鞋,建议先看一下上一篇博文 <Android查缺补漏(View篇)-- ...
- React事件杂记及源码分析
前提 最近通过阅读React官方文档的事件模块,发现了其主要提到了以下三个点 调用方法时需要手动绑定this React事件是一种合成事件SyntheticEvent,什么是合成事件? 事件属性 ...
- Android View事件分发-从源码分析
View事件分发-从源码分析 学习自 <Android开发艺术探索> https://blog.csdn.net/qian520ao/article/details/78555397?lo ...
- Android事件分发机制源码分析
Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...
- Qt事件分发机制源码分析之QApplication对象构建过程
我们在新建一个Qt GUI项目时,main函数里会生成类似下面的代码: int main(int argc, char *argv[]) { QApplication application(argc ...
- Nodejs事件引擎libuv源码剖析之:句柄(handle)结构的设计剖析
声明:本文为原创博文,转载请注明出处. 句柄(handle)代表一种对持有资源的索引,句柄的叫法在window上较多,在unix/linux等系统上大多称之为描述符,为了抽象不同平台的差异,libuv ...
随机推荐
- Rain on your Parade
Rain on your Parade Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 655350/165535 K (Java/Ot ...
- BZOJ3735 : [Pa2013]Konduktorzy
二分一个最大的位置$x$,计算$t=\sum_{i=1}^k\lfloor\frac{x}{a_i}\rfloor$. 如果$t\leq n$,那么说明就算全部检票员都走到了这里,也不够$n$个指令, ...
- HDU 4533 威威猫系列故事——晒被子
题目链接 扫描线可做,然后当时比赛后问虎哥,他说可以标记,然后拖了很久,今天从早上折腾到晚上,终于把两种情况写出来,分析太弱.改天扫描线,再来一次. 被子如果被y = x 穿过,可以分成两部分,上和下 ...
- 简单了解Flux,注意这是一个设计思想,是一个架构!!!!!
在RN开发中,我们总是需要去更改一个组件个数据(也就是所谓的状态),我们一般是采用是在初始化的函数constror()(好像拼错了) 在这个函数里面申明我们的初始化数据(状态)eg:this.stat ...
- Resources
McGuire Computer Graphics Data http://mesh.brown.edu/calibration/software.html Pixar Online Library ...
- Medical Image Processing Conference and Journal 医学图像处理会议与期刊
会议: Information Processing in Medical Imaging,IPMI IPMI2013 International Conference on Medical Imag ...
- <html:option获取文本值
<p class="w120">变更后IP:</p> <div class="comBobox w200 f_l"> < ...
- java 遍历文件夹里的文件
Java遍历文件夹的2种方法: A.不使用递归: import java.io.File; import java.util.LinkedList; public class FileSystem { ...
- IOS第五天(1:取消按钮的监听和设置代理textField字数限制)
***********取消按钮的监听和设置代理textField字数限制 UITextFieldDelegate #import "HMViewController.h" @int ...
- could not build module 'uikit'
今天 新建一个SingleView-APP 无法运行程序,在 AppDelegate.h中 第一行代码处报错. #import <UIKit/UIKit.h> /Users/wjw/Des ...