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 ...
随机推荐
- 不用写软件,纯JS 实现QQ空间自动点赞
这里分享一个自己写的点赞JS,已实现了好友动态.右侧栏猜你喜欢 点赞,有兴趣的朋友可以加上去玩玩.打开浏览器的开发者模式运行就可以看到效果了 var count = 0; var total = 0; ...
- Codeforces Round #295 (Div. 2)
水 A. Pangram /* 水题 */ #include <cstdio> #include <iostream> #include <algorithm> # ...
- LIS HDOJ 1257 最少拦截系统
题目传送门 题意:中文题面 分析:LIS模板题:n - 最长下降子序列 -> 最长上升子序列 贪心做法以后再补:) 代码: #include <cstdio> #include &l ...
- Unity Built-in Shader详解二
这次主要讨论Unity自带的Shader中Transparent Shader Family 这个家族的Shader一共7种,原理和类型与Normal中的上差不多,只不过这些Shader是用在半透明或 ...
- 【转】CSS清除浮动_清除float浮动
CSS清除浮动方法集合 一.浮动产生原因 一般浮动是什么情况呢?一般是一个盒子里使用了CSS float浮动属性,导致父级对象盒子不能被撑开,这样CSS float浮动就产生了. 浮动产生样式效果截图 ...
- HDU 2579 (记忆化BFS搜索)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2579 题目大意:走迷宫.对于障碍点,只有当前(dep+1)%k才能走,问最少时间. 解题思路: 只有 ...
- Nginx 伪静态教程
1.将多个域名指向同一web目录: server_name www.php100.com php100.com; rewrite ^/$ / redirect; 2.将不带www的域名301转向到带w ...
- IOS第七天(1:UiTableView 的基本用法)
***表格控件 #import "HMViewController.h" @interface HMViewController () <UITableViewDataSou ...
- 点击li标记中的<a>标记改变li背景图片怎样实现
<div class="nav"><ul><li id="li1" class="dianji" onclic ...
- Web 在线文件管理器学习笔记与总结(9)下载文件
① 普通形式的文件可以使用超链接形式下载 <a href = '下载文件名'>点击下载</a> ② 如果下载图片.html 等类型的文件,使用header() 函数发送网页头信 ...