zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    格式化 | python笔记(1)
    在docker容器中部署python-selenium+chrome-headless自动化脚本(续)
    Jenkins+Postman+Newma+Xmysql之API全自动化测试
    1分钟搭建极简mock server
    绘图神器-matplotlib入门
    请务必每天早上8点将前十条科技要闻发给三爷
    请以excel管理你的接口测试用例
    如何优雅地使用httprunner进行接口测试
    一键压测工具改造(locust)
    shell中if的可判断的类型
  • 原文地址:https://www.cnblogs.com/findumars/p/6218907.html
Copyright © 2011-2022 走看看