`

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()

设置可通过方向键来控制焦点进行上下左右的移动。

  • 和QGraphicsView 相关的东西

参考


分享到:
评论

相关推荐

    qt键盘映射和焦点移动

    开发者可以通过重写QObject或QWidget的eventFilter()函数来捕获这些事件,以便根据需要自定义键盘行为。例如,你可以定义特定的按键组合来触发特定的功能,或者改变默认的键盘快捷键。 `QKeySequence`类是Qt中用于...

    自定义QGraphicsProxyWidget,嵌入自定义QWidget(可以拖拽,显示焦点边框)

    - 如果你的自定义QWidget需要响应键盘事件,记得设置`setAcceptDrops(true)`,以便接收键盘输入。 - 处理窗口最大化、最小化和关闭等操作,这可能需要与QMainWindow或其他父窗口类进行交互。 - 在复杂的场景中,可能...

    Qt例程源代码QWidget.7z

    3. **事件处理**:QWidget通过重写event()函数来处理各种事件,如鼠标点击、键盘输入等。每个事件都有对应的信号,可以通过connect()函数绑定槽函数来响应这些事件。 4. **子部件**:QWidget可以包含其他QWidget...

    Qt全局鼠标、键盘事件监听器库

    在Qt中,通常我们可以通过继承`QWidget`类并重写其`mouseMoveEvent`、`mousePressEvent`、`mouseReleaseEvent`、`keyPressEvent`和`keyReleaseEvent`等方法来处理特定窗口内的鼠标和键盘事件。然而,当需要监听全局...

    Qt 方向键控制焦点移动,改变选中的控件

    在Qt编程中,焦点管理是窗口应用程序中一个关键的部分,它决定了用户交互时键盘输入将被哪个控件接收。在创建具有多个交互元素(如LineEdit)的用户界面时,能够用方向键方便地在这些控件之间切换焦点,极大地提高了...

    QT数字软键盘,包括QLineEdit弹出

    开发者可以监听QLineEdit的焦点变化事件,当获得焦点时显示软键盘,失去焦点时隐藏。 数字软键盘的实现通常涉及以下几个步骤: 1. 创建一个包含数字键布局的QWidget子类,每个数字键都是一个QPushButton。 2. 实现...

    QT 软键盘 QT4.7

    3. **键盘显示与隐藏**:设计一个公共方法来控制软键盘的显示和隐藏,这可以通过调用`QWidget::show()`和`QWidget::hide()`实现。 4. **界面布局**:使用`QGridLayout`或`QBoxLayout`等布局管理器,合理排列数字键...

    pyqt实现虚拟小键盘

    1. **创建窗口**: 使用`QMainWindow`或`QWidget`作为主窗口,并设置合适的大小和布局。窗口将是虚拟键盘显示的地方。 2. **设计键盘布局**: 可以使用`QPushButton`创建各个按键,并按照标准键盘布局进行排列。每个...

    Qt实现软键盘

    最后,为了确保在多个文本框间切换时软键盘不关闭,我们需要在焦点改变时检查新焦点是否仍需软键盘: ```cpp QObject::connect(QApplication::instance(), &QApplication::focusChanged, this, &MainWindow::...

    qt4.8软键盘(通过点击输入框呼出).zip

    7. **编码实现**:实现这样的功能通常涉及创建一个自定义的键盘类,继承自QWidget或QDialog,并在其中定义按钮和布局。然后,在主窗口类中,需要添加对LineEdit焦点变化的响应,以及对软键盘按钮点击的处理。 8. **...

    QWidget是Qt框架中的一个核心类.docx

    QWidget支持事件处理机制,能够处理各种事件,包括鼠标事件、键盘事件、焦点事件等。 通过重写相应的事件处理函数(如mousePressEvent、keyPressEvent等),可以响应用户的交互动作。 布局管理: QWidget可以作为...

    QT简单软键盘设计文件

    - 键盘焦点管理:确保软键盘只在有输入需求的控件上显示,并正确处理焦点转移。 - 多语言支持:如果需要支持多种语言的输入,需要处理不同的字符集和编码。 总的来说,QT的QKeyEvent和UI框架为开发者提供了便利,使...

    QT-key_linuxQT虚拟小键盘_qt虚拟键盘_

    在开发虚拟键盘时,通常会创建一个自定义的QWidget或QDialog作为虚拟键盘的界面,然后在界面上放置一系列QPushButton或QLabel,每个按钮代表一个按键。按钮的点击事件处理器需要根据实际按键映射关系发送对应的...

    qt embedded 软键盘输入的实现

    2. **设计软键盘UI**:使用Qt的图形界面库(如QML或QWidget)设计软键盘的布局。这通常包括不同类型的按键(字母、数字、特殊字符等),以及确认、清除等操作按钮。 3. **实现输入逻辑**:在`MySoftKeyboard`类中,...

    qt软键盘应用实例

    要创建一个软键盘,我们可以自定义一个`QWidget`子类,包含一系列按钮来模拟键盘布局。每个按钮可以绑定一个字符,按下按钮时触发相应的事件。例如,我们可以通过继承`QPushButton`类并重写`clicked()`槽函数来实现...

Global site tag (gtag.js) - Google Analytics