如何确定谁来接收键盘事件,不妨看一点点QApplication的源码:
X11下
QETWidget*keywidget=0;boolgrabbed=false;if(event->type==XKeyPress||event->type==XKeyRelease){keywidget=(QETWidget*)QWidget::keyboardGrabber();if(keywidget){grabbed=true;}elseif(!keywidget){if(d->inPopupMode())//nofocuswidget,seeifwehaveapopupkeywidget=(QETWidget*)(activePopupWidget()->focusWidget()activePopupWidget()->focusWidget():activePopupWidget());elseif(QApplicationPrivate::focus_widget)keywidget=(QETWidget*)QApplicationPrivate::focus_widget;elseif(widget)keywidget=(QETWidget*)widget->window();}}Windows下
QWidget*g=QWidget::keyboardGrabber();if(g&&qt_get_tablet_widget()&&hwnd==qt_get_tablet_widget()->winId()){//ifwegetaneventfortheinternaltabletwidget,//thendon'tsendittothekeyboardgrabber,but//sendittothewidgetitself(wedon'tuseitright//now,justincase).g=0;}if(g)widget=(QETWidget*)g;elseif(QApplication::activePopupWidget())widget=(QETWidget*)QApplication::activePopupWidget()->focusWidget()(QETWidget*)QApplication::activePopupWidget()->focusWidget():(QETWidget*)QApplication::activePopupWidget();elseif(QApplication::focusWidget())widget=(QETWidget*)QApplication::focusWidget();elseif(!widget||widget->internalWinId()==GetFocus())//Wefakedthemessagetogotoexactlythatwidget.widget=(QETWidget*)widget->window();大致顺序:
问题:当我们按下Tab键(或者上下左右箭头键)时,下一个或获取焦点的QWidget是如何被确定的?
我们重新贴出上文最后贴出过的QWidget::event()的源码:
caseQEvent::KeyPress:{QKeyEvent*k=(QKeyEvent*)event;boolres=false;if(!(k->modifiers()&(Qt::ControlModifier|Qt::AltModifier))){//###AddMetaModifierif(k->key()==Qt::Key_Backtab||(k->key()==Qt::Key_Tab&&(k->modifiers()&Qt::ShiftModifier)))res=focusNextPrevChild(false);elseif(k->key()==Qt::Key_Tab)res=focusNextPrevChild(true);if(res)break;}keyPressEvent(k);老是觉得QWidget::focusNextPrevChild()这个函数有点名不符实(或者有点别扭),因为:
boolQWidget::focusNextPrevChild(boolnext){Q_D(QWidget);QWidget*p=parentWidget();boolisSubWindow=(windowType()==Qt::SubWindow);if(!isWindow()&&!isSubWindow&&p)returnp->focusNextPrevChild(next);...}当我们调用一个Widget该成员时,最终将递归调用到其所在窗口的focusNextPrevChild成员。(不过这是一个protected的虚函数,在派生类中可以覆盖它,从而控制派生类实例中的焦点移动。)
QWidgetPrivate内有3个成员变量:
classQ_GUI_EXPORTQWidgetPrivate:publicQObjectPrivate{...QWidget*focus_next;QWidget*focus_prev;QWidget*focus_child;这3个变量可以分别用:
进行获取。
前两个可以用来构成一个focus链表。
voidQWidgetPrivate::init(QWidget*parentWidget,Qt::WindowFlagsf){Q_Q(QWidget);focus_next=focus_prev=q;...voidQWidgetPrivate::reparentFocusWidgets(QWidget*oldtlw){...通过QWidget::setTabOrder()可以调整Widgets在focus链表中的顺序
比如一个类似软键盘的东西,在一个QWidget上面放置了大量的QPushButton。此时,除了Tab/Shift+Tab外,上下左右箭头也都可以用来移动焦点。
这是因为:
voidQAbstractButton::keyPressEvent(QKeyEvent*e){boolnext=true;switch(e->key()){caseQt::Key_Up:caseQt::Key_Left:next=false;//fallthroughcaseQt::Key_Right:caseQt::Key_Down:...focusNextPrevChild(next);}focusproxyManual中说的比较清楚:
简单列出源码:
voidQWidget::setFocus(Qt::FocusReasonreason){QWidget*f=this;while(f->d_func()->extra&&f->d_func()->extra->focus_proxy)f=f->d_func()->extra->focus_proxy;boolQWidget::hasFocus()const{constQWidget*w=this;while(w->d_func()->extra&&w->d_func()->extra->focus_proxy)w=w->d_func()->extra->focus_proxy;其他QApplication::setNavigationMode()设置可通过方向键来控制焦点进行上下左右的移动。
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
setfocus()是让某个窗体获得焦点
setfocusPolicy是设置窗体怎么获得焦点
Setsthiswidget'sfocusproxytow.Ifwis0,thisfunctionresetsthiswidgettonothaveanyfocusproxy.
将该widget的focusproxy设置给w。如果w为0,该函数将此widget设为没有任何focusproxy。
有些widget,比如QComboBox,可以“拥有focus”,但是它们会创建一个子的widget来实际地处理焦点。比如QComboBox创建的叫做QLineEdit。
setFocusProxy()用来指定当该widget获得焦点时实际上由谁来处理这个焦点。如果某个widget拥有focusproxy,focusPolicy(),setFocusPolicy(),setFocus()和hasFocus()都是对focusproxy进行操作。