# QMainWindow 动态切换菜单栏 因为种种原因,需要根据情况动态切换菜单栏。可以手动编码,也可以使用 UI 类。使用 UI 类来实现的话更清晰些,每个菜单的功能代码写到各自的文件里,主界面只负责切换就行,非常简洁。 没时间,就只贴代码,不上图了,脑补一下吧。示例工程树如下: * Demo.pro * main.cpp * MainWindow.ui * MainWindow.h * MainWindow.cpp * MenuA.ui * MenuA.h * MenuA.cpp * MenuB.ui * MenuB.h * MenuB.cpp MainWindow 中设置了两个按钮 pushButtonA 和 pushButtonB,分别与两个槽关联用于展示切换菜单功能。 MenuA 和 MenuB 是直接继承于 QMenuBar 的界面类(别忘了修改 MenuA.ui 和 MenuB.ui)。在 MenuA 和 MenuB 中实现不同的菜单项和功能(可参考 附录A 和 附录B)。 ## 基础篇 MainWindow 中使用: ```cpp void QMainWindow::setMenuBar(QMenuBar *menuBar) ``` 方法可以设置主窗口菜单栏。当为主窗口设置新菜单栏时,会自动隐藏并删除旧的菜单栏。因此最基本的动态切换菜单的方法如下: ```cpp /** @file MainWindow.cpp */ #include "MainWindow.h" #include "ui_MainWindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_pushButtonA_clicked() { setMenuBar(new MenuA()); } void MainWindow::on_pushButtonB_clicked() { setMenuBar(new MenuB()); } ``` ## 高级篇 上面每次切换菜单都会自动删除上次菜单栏对象,然后再重新分配一个。但是有的项目需要这些对象从一开始就分配出来,并且不再被释放。例如菜单有各自的状态,在不同状态下某些项目是禁能的,如果每次重新创建,就需要在其他地方记录状态,这使代码变得复杂。有没有办法不进行重新分配呢?这样每个菜单栏自己就可以记录状态。 通过分析 setMenuBar 方法,发现其会调用 oldMenuBar->deleteLater() 方法释放旧的菜单栏对象。而 deleteLater() 主要是给自己发送 DeferredDelete 事件,然后在事件循环中释放自己。因此我们可以重写 MenuA 和 MenuB 的 event() 方法,来拒绝删除操作,这样每次 setMenuBar() 的时候都不会删除旧的菜单栏,因此下次使用的时候也就不需要再重新分配了。参考代码如下: ```cpp /** @file MenuA.h */ #ifndef MENUA_H #define MENUA_H #include namespace Ui { class MenuA; } class MenuA : public QMenuBar { Q_OBJECT public: explicit MenuA(QWidget *parent = 0); ~MenuA(); protected: bool event(QEvent *e) override; private: Ui::MenuA *ui; }; #endif // MENUA_H ``` 看看 MenuA.cpp ```cpp /** @file MenuA.cpp */ #include "MenuA.h" #include "ui_MenuA.h" MenuA::MenuA(QWidget *parent) : QMenuBar(parent), ui(new Ui::MenuA) { ui->setupUi(this); } MenuA::~MenuA() { delete ui; } bool MenuA::event(QEvent *e) { if(QEvent::DeferredDelete != e->type()) return QMenuBar::event(e); return true; } ``` MenuB 与 MenuA 这部分的处理一致,不再贴出了。MainWindow.h 中主要声明了 MenuA 和 MenuB 的指针,用于效果展示。 ```cpp /** @file MainWindow.h */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include "MenuA.h" #include "MenuB.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_pushButtonA_clicked(); void on_pushButtonB_clicked(); private: Ui::MainWindow *ui; MenuA* MA; MenuB* MB; }; #endif // MAINWINDOW_H ``` MainWindow.cpp 如下: ```cpp /** @file MainWindow.cpp */ #include "MainWindow.h" #include "ui_MainWindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); MA = new MenuA(); MB = new MenuB(); setMenuBar(MA); } MainWindow::~MainWindow() { delete MA; delete MB; delete ui; } void MainWindow::on_pushButtonA_clicked() { setMenuBar(MA); MA->show(); } void MainWindow::on_pushButtonB_clicked() { setMenuBar(MB); MB->show(); } ``` *注意:show() 方法一定要被调用,并且放在 setMenuBar() 方法之后。这是因为 setMenuBar() 中还调用了 oldMenuBar 的 hide() 方法,如果不调用 show() 方法,菜单栏就会“消失不见”。* ## 附录 ### 附录A:MenuA.ui MenuA.ui 实现了一个菜单 MenuA: ```xml MenuA 0 0 400 300 Form MenuA ``` ### 附录B:MenuB.ui MenuA.ui 实现了菜单 MenuB: ```xml MenuB 0 0 400 300 Form MenuB ```