diff --git a/Software/Development/Environment/Qt/Qt_Libs/QMenuBar/QMainWindow_动态切换菜单栏.md b/Software/Development/Environment/Qt/Qt_Libs/QMenuBar/QMainWindow_动态切换菜单栏.md new file mode 100644 index 0000000..254761c --- /dev/null +++ b/Software/Development/Environment/Qt/Qt_Libs/QMenuBar/QMainWindow_动态切换菜单栏.md @@ -0,0 +1,275 @@ +# 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 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 + + + + + + + +```