QWidget 键盘事件 焦点(源代码级别研究)
- 在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进行操作。
QWidget 键盘事件 焦点(源代码级别研究)的更多相关文章
- QWidget 键盘事件 焦点(QApplication源码)
在Qt中,键盘事件和QWidget的focus密不可分:一般来说,一个拥有焦点(focus)的QWidget或者grabKeyboard()的QWidget才可以接受键盘事件. 键盘事件派发给谁? 如 ...
- Qt中的键盘事件,以及焦点的设置(比较详细)
Qt键盘事件属于Qt事件系统,所以事件系统中所有规则对按键事件都有效.下面关注点在按键特有的部分: focus 一个拥有焦点(focus)的QWidget才可以接受键盘事件.有输入焦点的窗口是活动窗口 ...
- js键盘事件和焦点事件
键盘事件onkeydown //当键盘按下的时候触发onkeyup //但键盘抬起的时候触发event.keyCode //数字类型 键盘按键的键值功能键 ctrlkey shiftkey altke ...
- 由chrome剪贴板问题研究到了js模拟鼠标键盘事件
写在前面 最近公司在搞浏览器兼容的事情,所有浏览器兼容的问题不得不一个人包了.下面来说一下今天遇到的一个问题吧 大家都知道IE下面如果要获得剪贴板里面的信息的话,代码应该如下所示 window.cli ...
- 深入了解jquery中的键盘事件
很多时候,我们需要获取用户的键盘事件,下面就一起来看看jquery是如何操作键盘事件的. 一.首先需要知道的是: 1.keydown() keydown事件会在键盘按下时触发. 2.keyup() k ...
- jquery键盘事件全记录
很多时候,我们需要获取用户的键盘事件,下面就一起来看看jquery是如何操作键盘事件的. 一.首先需要知道的是: 1.keydown() keydown事件会在键盘按下时触发. 2.keyup() k ...
- Qt事件系统之三:键盘事件
QKeyEvent类用来描述一个键盘事件.当键盘按键被按下或者被释放时,键盘事件便会被发送给拥有键盘输人焦点的部件. QKeyEvent的key()函数可以获取具体的按键,对于Qt中给定的所有按键,可 ...
- QT之键盘事件
Widget.h: #ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include<QKeyEvent> #incl ...
- 深入理解DOM事件类型系列第二篇——键盘事件
× 目录 [1]类型 [2]顺序 [3]按键信息[4]应用 前面的话 鼠标和键盘是电脑端主要的输入设备,上篇介绍了鼠标事件,本文将详细介绍键盘事件 类型 键盘事件用来描述键盘行为,主要有keydown ...
随机推荐
- Git工作中用法(Gitlab)
感觉又有了新的认识. 一共有3个仓库,本地自己的,远程自己的,远程主仓库. 为了方便能及时从主仓库获取更新的内容要将远程主仓库也clone下来 git clone upstream url / ...
- 移动端Web App自适应布局探索
1.困扰多时的问题 在这之前做Web App开发的的时候,在自适应方面一般都是宽度通过百分比,高度以iPhone6跟iPhone5之间的一个平衡值写死,我们的设计稿都是iPhone5的640 * 11 ...
- Keil C51.uew
/L15"Keil C51" Line Comment = // Block Comment On = /* Block Comment Off = */ Escape Char ...
- YY语音从4.0版本开始是基于Qt的开发过程,以及碰到的问题
作者:姚冬链接:http://www.zhihu.com/question/21359230/answer/20127715来源:知乎著作权归作者所有,转载请联系作者获得授权. YY语音从4.0版本开 ...
- Best Time to Buy and Sell Stock I II III
Best Time to Buy and Sell Stock Say you have an array for which the ith element is the price of a gi ...
- 【斗地主技巧】斗地主算法逻辑中的天之道<转>
******************************************************************** 作者比较喜欢玩斗地主,所以经常搜集一些网友斗地主的心得,下面这 ...
- PHP MySQL Delete From 之 Delete
删除数据库中的数据 DELETE FROM 语句用于从数据库表中删除记录. 语法 DELETE FROM table_name WHERE column_name = some_value 注释:SQ ...
- IOS ViewController 生命周期
加载过程 第一步 -(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 第二步 -(void) ...
- HDU 4978 A simple probability problem
A simple probability problem Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K ( ...
- UVa 836 - Largest Submatrix
题目:给你一个n*n的01矩阵,求里面最大的1组成的矩形的米娜及. 分析:dp.单调队列.UVa 1330同题,仅仅是输入格式变了. 我们将问题分解成最大矩形.即求解以k行为底边的图形中的最大矩形.然 ...