QMenuBar焦点问题
来源:互联网 发布:java中的random类 编辑:程序博客网 时间:2024/05/05 10:57
QTBUG18896问题
http://bugreports.qt.nokia.com/browse/QTBUG-18896
Sometimes it occurs that keypresses act like the Alt-key has been pressed also, which means that the key event is only sent to the menubar and not to the application. I encountered this in our own application, but I could reproduce this with Qt Designer. Please follow the next recipe exactly and come back to me if you can't reproduce.
简单地说:菜单栏本来需要ALT+'M'(或其他字符)来激活并弹出某个菜单,现在直接按'M'就可以激活了。
初次看到感觉很有意思,后来发现问题还算简单。我们可以用下面的程序来重现这个问题:
#include <QApplication>#include <QMainWindow>#include <QMenuBar>class MainWindow : public QMainWindow{public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(){}};MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent){ QMenu * menu = menuBar()->addMenu("&Menu"); menu->addAction("&Item"); menu->addAction("I&tem");}int main(int argc, char *argv[]){ QApplication a(argc, argv); MainWindow w; w.show(); return a.exec();}
- 一旦菜单栏获得焦点,我们就可以通过"m"键来弹出菜单(而不需要"Alt+M")。这是正常行为
- 在这个例子中,一旦工具栏获得焦点,它不会自动失去焦点(没有其他控件接受焦点)。这是原因
根源
- 工具栏开始工作时,
- setKeyboardMode(true)
- 会先设置自己获得焦点(如果此次其他widget拥有焦点,则先保存下来该指针(使用QPointer,作用你应该懂的))。
- 工作完毕
- setKeyboardMode(false)
- 尝试恢复原来的焦点
- 如果一开始没有其他widget拥有焦点,或者拥有焦点的widget在这期间被销毁了怎么办??
- Qt 目前的做法是:让工具栏继续持有焦点
- 我们期待的应:让工具栏失去焦点
源码
- QToolBar 初始化会给它的parent和它所在的顶级窗口安装事件过滤器:
void QMenuBarPrivate::handleReparent(){ Q_Q(QMenuBar); QWidget *newParent = q->parentWidget(); //Note: if parent is reparented, then window may change even if parent doesn't // we need to install an event filter on parent, and remove the old one if (oldParent != newParent) { if (oldParent) oldParent->removeEventFilter(q); if (newParent) newParent->installEventFilter(q); } //we also need event filter on top-level (for shortcuts) QWidget *newWindow = newParent ? newParent->window() : 0; if (oldWindow != newWindow) { if (oldParent && oldParent != oldWindow) oldWindow->removeEventFilter(q); if (newParent && newParent != newWindow) newWindow->installEventFilter(q); } oldParent = newParent; oldWindow = newWindow;
- 在显示之前,它还会将菜单栏各菜单的加速键注册成快捷键:
- 通过QKeySequence的mnemonic()成员
void QMenuBarPrivate::updateGeometries(){... for(int i = 0; i < actions.count(); i++) shortcutIndexMap.append(q->grabShortcut(QKeySequence::mnemonic(actions.at(i)->text())));
- 事件过滤器所做工作:
- 主要为了处理Alt键?(菜单导航)
注意:一旦遇到包含Alt键值的QEvent::ShortcutOverride事件,它会将自己安装成QApplication的事件过滤器
bool QMenuBar::eventFilter(QObject *object, QEvent *event){ if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, this)) { if (d->altPressed) { switch (event->type()) { case QEvent::KeyPress: case QEvent::KeyRelease: { QKeyEvent *kev = static_cast<QKeyEvent*>(event); if (kev->key() == Qt::Key_Alt || kev->key() == Qt::Key_Meta) { if (event->type() == QEvent::KeyPress) // Alt-press does not interest us, we have the shortcut-override event break; d->setKeyboardMode(!d->keyboardState); } } // fall through case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseMove: case QEvent::FocusIn: case QEvent::FocusOut: case QEvent::ActivationChange: d->altPressed = false; qApp->removeEventFilter(this); break; default: break; } } else if (isVisible()) { if (event->type() == QEvent::ShortcutOverride) { QKeyEvent *kev = static_cast<QKeyEvent*>(event); if ((kev->key() == Qt::Key_Alt || kev->key() == Qt::Key_Meta) && kev->modifiers() == Qt::AltModifier) { d->altPressed = true; qApp->installEventFilter(this); } } } } return false;
- QShortcutEvent 事件的接收
- 其中 _q_internalShortcutActivated 用来激活(弹出)相应的菜单。
bool QMenuBar::event(QEvent *e){ Q_D(QMenuBar); switch (e->type()) { case QEvent::Shortcut: { QShortcutEvent *se = static_cast<QShortcutEvent *>(e); int shortcutId = se->shortcutId(); for(int j = 0; j < d->shortcutIndexMap.size(); ++j) { if (shortcutId == d->shortcutIndexMap.value(j)) d->_q_internalShortcutActivated(j); } } break;
- keypress的处理
- 菜单的导航控制
- 菜单的弹出控制
- 对加速键字符的处理
void QMenuBar::keyPressEvent(QKeyEvent *e){ Q_D(QMenuBar); d->updateGeometries(); int key = e->key(); if(isRightToLeft()) { // in reverse mode open/close key for submenues are reversed if(key == Qt::Key_Left) key = Qt::Key_Right; else if(key == Qt::Key_Right) key = Qt::Key_Left; } if(key == Qt::Key_Tab) //means right key = Qt::Key_Right; else if(key == Qt::Key_Backtab) //means left key = Qt::Key_Left; bool key_consumed = false; switch(key) { case Qt::Key_Up: case Qt::Key_Down: case Qt::Key_Enter: case Qt::Key_Space: case Qt::Key_Return: { if(!style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, this) || !d->currentAction) break; if(d->currentAction->menu()) { d->popupAction(d->currentAction, true); } else if(key == Qt::Key_Enter || key == Qt::Key_Return || key == Qt::Key_Space) { d->activateAction(d->currentAction, QAction::Trigger); d->setCurrentAction(d->currentAction, false); d->setKeyboardMode(false); } key_consumed = true; break; } case Qt::Key_Right: case Qt::Key_Left: { if(d->currentAction) { int index = d->actions.indexOf(d->currentAction); if (QAction *nextAction = d->getNextAction(index, key == Qt::Key_Left ? -1 : +1)) { d->setCurrentAction(nextAction, d->popupState, true); key_consumed = true; } } break; } case Qt::Key_Escape: d->setCurrentAction(0); d->setKeyboardMode(false); key_consumed = true; break; default: key_consumed = false; } if(!key_consumed && (!e->modifiers() || (e->modifiers()&(Qt::MetaModifier|Qt::AltModifier))) && e->text().length()==1 && !d->popupState) { int clashCount = 0; QAction *first = 0, *currentSelected = 0, *firstAfterCurrent = 0; { QChar c = e->text()[0].toUpper(); for(int i = 0; i < d->actions.size(); ++i) { if (d->actionRects.at(i).isNull()) continue; QAction *act = d->actions.at(i); QString s = act->text(); if(!s.isEmpty()) { int ampersand = s.indexOf(QLatin1Char('&')); if(ampersand >= 0) { if(s[ampersand+1].toUpper() == c) { clashCount++; if(!first) first = act; if(act == d->currentAction) currentSelected = act; else if (!firstAfterCurrent && currentSelected) firstAfterCurrent = act; } } } } } QAction *next_action = 0; if(clashCount >= 1) { if(clashCount == 1 || !d->currentAction || (currentSelected && !firstAfterCurrent)) next_action = first; else next_action = firstAfterCurrent; } if(next_action) { key_consumed = true; d->setCurrentAction(next_action, true, true); } } if(key_consumed) e->accept(); else e->ignore();}
- 这儿还有一个setKeyboardMode,做什么工作呢?
- 控制焦点转移的
- true:设置自己为焦点控件,将先保存原来拥有焦点的控件
- false:恢复原来拥有焦点的控件
- 控制焦点转移的
void QMenuBarPrivate::setKeyboardMode(bool b){ Q_Q(QMenuBar); if (b && !q->style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, q)) { setCurrentAction(0); return; } keyboardState = b; if(b) { QWidget *fw = QApplication::focusWidget(); if (fw != q) keyboardFocusWidget = fw; focusFirstAction(); q->setFocus(Qt::MenuBarFocusReason); } else { if(!popupState) setCurrentAction(0); if(keyboardFocusWidget) { if (QApplication::focusWidget() == q) keyboardFocusWidget->setFocus(Qt::MenuBarFocusReason); keyboardFocusWidget = 0; } } q->update();}
- QMenuBar焦点问题 (QTBUG18896)
- QMenuBar焦点问题
- QMenuBar
- 焦点问题
- 焦点问题
- qmenubar qtoolbar qaction记录
- 回车切换焦点问题
- textbox 获得焦点问题
- TOleContainer 焦点问题
- 文本框焦点问题
- TOleContainer 焦点问题
- OLE 焦点问题
- 窗口焦点问题
- treeview 保留焦点问题
- Kuix 焦点跳转问题
- updatepanel焦点问题
- flex canvas焦点问题
- android 焦点问题
- error: request for member '*' in '*', which is of non-class type '*'
- 一个apk调用另一个apk的某个activity (根据帖子整理)
- windows server2008 配置RRAS服务器
- android drawable 应用
- 【IOS】IOS上实现的自定义仪表盘(可以用在流量监控之类的应用上)
- QMenuBar焦点问题
- red hat 上的东西不要乱删
- 【GPU编程】体绘制传输函数-分类(Volume Rendering Transfer function:Pre- VS Post-Classification)
- 表变量与临时表的优缺点
- 在订单支付设计中,重点关注请求和订单的排重设计
- Qt国际化(源码含中文时)的点滴分析
- Bumblebee 3.0在Ubuntu 11.10上NVidia Optimus安装教程,效果惊人!
- SIP应答消息状态码与功能
- c# 下载文件xmlhttp