- 在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()
设置可通过方向键来控制焦点进行上下左右的移动。
等
参考
分享到:
相关推荐
开发者可以通过重写QObject或QWidget的eventFilter()函数来捕获这些事件,以便根据需要自定义键盘行为。例如,你可以定义特定的按键组合来触发特定的功能,或者改变默认的键盘快捷键。 `QKeySequence`类是Qt中用于...
- 如果你的自定义QWidget需要响应键盘事件,记得设置`setAcceptDrops(true)`,以便接收键盘输入。 - 处理窗口最大化、最小化和关闭等操作,这可能需要与QMainWindow或其他父窗口类进行交互。 - 在复杂的场景中,可能...
3. **事件处理**:QWidget通过重写event()函数来处理各种事件,如鼠标点击、键盘输入等。每个事件都有对应的信号,可以通过connect()函数绑定槽函数来响应这些事件。 4. **子部件**:QWidget可以包含其他QWidget...
在Qt中,通常我们可以通过继承`QWidget`类并重写其`mouseMoveEvent`、`mousePressEvent`、`mouseReleaseEvent`、`keyPressEvent`和`keyReleaseEvent`等方法来处理特定窗口内的鼠标和键盘事件。然而,当需要监听全局...
在Qt编程中,焦点管理是窗口应用程序中一个关键的部分,它决定了用户交互时键盘输入将被哪个控件接收。在创建具有多个交互元素(如LineEdit)的用户界面时,能够用方向键方便地在这些控件之间切换焦点,极大地提高了...
开发者可以监听QLineEdit的焦点变化事件,当获得焦点时显示软键盘,失去焦点时隐藏。 数字软键盘的实现通常涉及以下几个步骤: 1. 创建一个包含数字键布局的QWidget子类,每个数字键都是一个QPushButton。 2. 实现...
3. **键盘显示与隐藏**:设计一个公共方法来控制软键盘的显示和隐藏,这可以通过调用`QWidget::show()`和`QWidget::hide()`实现。 4. **界面布局**:使用`QGridLayout`或`QBoxLayout`等布局管理器,合理排列数字键...
1. **创建窗口**: 使用`QMainWindow`或`QWidget`作为主窗口,并设置合适的大小和布局。窗口将是虚拟键盘显示的地方。 2. **设计键盘布局**: 可以使用`QPushButton`创建各个按键,并按照标准键盘布局进行排列。每个...
最后,为了确保在多个文本框间切换时软键盘不关闭,我们需要在焦点改变时检查新焦点是否仍需软键盘: ```cpp QObject::connect(QApplication::instance(), &QApplication::focusChanged, this, &MainWindow::...
7. **编码实现**:实现这样的功能通常涉及创建一个自定义的键盘类,继承自QWidget或QDialog,并在其中定义按钮和布局。然后,在主窗口类中,需要添加对LineEdit焦点变化的响应,以及对软键盘按钮点击的处理。 8. **...
QWidget支持事件处理机制,能够处理各种事件,包括鼠标事件、键盘事件、焦点事件等。 通过重写相应的事件处理函数(如mousePressEvent、keyPressEvent等),可以响应用户的交互动作。 布局管理: QWidget可以作为...
- 键盘焦点管理:确保软键盘只在有输入需求的控件上显示,并正确处理焦点转移。 - 多语言支持:如果需要支持多种语言的输入,需要处理不同的字符集和编码。 总的来说,QT的QKeyEvent和UI框架为开发者提供了便利,使...
在开发虚拟键盘时,通常会创建一个自定义的QWidget或QDialog作为虚拟键盘的界面,然后在界面上放置一系列QPushButton或QLabel,每个按钮代表一个按键。按钮的点击事件处理器需要根据实际按键映射关系发送对应的...
2. **设计软键盘UI**:使用Qt的图形界面库(如QML或QWidget)设计软键盘的布局。这通常包括不同类型的按键(字母、数字、特殊字符等),以及确认、清除等操作按钮。 3. **实现输入逻辑**:在`MySoftKeyboard`类中,...
要创建一个软键盘,我们可以自定义一个`QWidget`子类,包含一系列按钮来模拟键盘布局。每个按钮可以绑定一个字符,按下按钮时触发相应的事件。例如,我们可以通过继承`QPushButton`类并重写`clicked()`槽函数来实现...