在漫谈QWidget及其派生类(三)中,我们已经知道:QMenuBar和QStatusBar都是QWidget的派生类,所以我们可以像添加一个QPushButton一样将它们任意放置到另一个QWidget中。
继续看个别的例子:
#include <QtGui> int main(int argc, char *argv[]) { QApplication app(argc, argv); QMenuBar menubar; menubar.addAction("Action1"); menubar.addAction("Action1"); menubar.show(); return app.exec(); }
我们知道,QWidget本来就有一个成员函数:
void QWidget::addAction ( QAction * action )
而此处只不过是QMenuBar提供的重载版本:
QAction *QMenuBar::addAction(const QString &text) { QAction *ret = new QAction(text, this); addAction(ret); return ret; }
可是同样的东西,到了QMenuBar中,就显示出一种菜单栏的效果了呢?
不同的Widget,为什么不一样?我们都会自定义控件,所以都熟悉:
void QMenuBar::paintEvent(QPaintEvent *e) { ... for (int i = 0; i < d->actions.count(); ++i) { QAction *action = d->actions.at(i); QRect adjustedActionRect = d->actionRect(action); ...
便利它自身的actions列表,然后依次将它们paint出来。
然后当触发快捷键,或者在某个区域点击鼠标时:
void QMenuBar::mousePressEvent(QMouseEvent *e) { ... QAction *action = d->actionAt(e->pos()); .... d->setCurrentAction(action, true); ...
在继续之前,我们需要简单了解一下addAction和addMenu
QMenu *QMenuBar::addMenu(const QString &title) { QMenu *menu = new QMenu(title, this); addAction(menu->menuAction()); return menu; }
看,无非是创建一个QMenu,然后将该菜单对应的QAction添加进来了。而这个QMenu,稍候可以通过对应的QAction的成员函数
QMenu * QAction::menu () const
来获得
先看看QMenu,稍候再回来
QMenu 也是一个QWidget的派生类
但是,为什么QMenu长成这个样子呢,一条一条的?
其实看了前面的QMenuBar,我们对QMenu为什么长这个样子,应该不会感到任何惊奇了。
先添加一些QAction,然后在paintEvent中遍历Action列表,并画出一个一个矩形条。
void QMenu::paintEvent(QPaintEvent *e) { //draw the items that need updating.. for (int i = 0; i < d->actions.count(); ++i) { QAction *action = d->actions.at(i); ...
当然,为了有点立体效果,或者说为了让QMenu和其他东西分开,还需要画边框。
我们在一个Widget的某个位置上点击右键,或者通过键盘上某个键。会弹出上下文菜单。
这个菜单和我们在菜单栏看到的菜单其实没有区别,要说区别的话,就是需要我们自己:
考虑:一般我们是如何使用上下文菜单的?
void Widget::contextMenuEvent(QContextMenuEvent *event) { QMenu menu(this); menu.addAction("A1"); menu.addAction("A2"); filemenu->exec(this->mapToGlobal(event.pos()));
还是要从QMenu是QWidget的派生类说起。在前面我们提到了这样的内容:
QMenu 是 Window,所以位置坐标是全局的。而event坐标是当前窗口部件的。
其内部肯定调用了show(),而且还使用QEventLoop开启了局部的事件循环。
既然直接调用show()就可以让菜单显示出来,这个popup()又有何用?
void QMenu::popup(const QPoint &p, QAction *atAction) { ... }
它做的工作:
而我们刚提到的exec()就是在popup的基础上加了个事件循环
QAction *QMenu::exec(const QPoint &p, QAction *action) { QEventLoop eventLoop; d->eventLoop = &eventLoop; popup(p, action); ...
了解了上下文菜单,我们可以回头看看菜单栏是如何控制菜单显示的。
一开始说了,
void QMenuBarPrivate::popupAction(QAction *action, bool activateFirst) { if(!action || !action->menu() || losePopupMode) return; activeMenu = action->menu(); .... activeMenu->popup(pos);
源码中,最长的一段就是在计算和调整菜单的位置(以确保菜单不会跑到屏幕外),即那个pos