2019-11-18 17:48:50 +08:00
|
|
|
|
# QMainWindow 动态切换菜单栏
|
2019-11-18 16:15:40 +08:00
|
|
|
|
|
|
|
|
|
因为种种原因,需要根据情况动态切换菜单栏。可以手动编码,也可以使用 UI 类。使用 UI 类来实现的话更清晰些,每个菜单的功能代码写到各自的文件里,主界面只负责切换就行,非常简洁。
|
|
|
|
|
|
2019-11-18 17:48:50 +08:00
|
|
|
|
没时间,就只贴代码,不上图了,脑补一下吧。示例工程树如下:
|
2019-11-18 16:15:40 +08:00
|
|
|
|
|
|
|
|
|
* 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());
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 高级篇
|
|
|
|
|
|
2019-11-18 17:48:50 +08:00
|
|
|
|
上面每次切换菜单都会自动删除上次菜单栏对象,然后再重新分配一个。但是有的项目需要这些对象从一开始就分配出来,并且不再被释放。例如菜单有各自的状态,在不同状态下某些项目是禁能的,如果每次重新创建,就需要在其他地方记录状态,这使代码变得复杂。有没有办法不进行重新分配呢?这样每个菜单栏自己就可以记录状态。
|
2019-11-18 16:15:40 +08:00
|
|
|
|
|
|
|
|
|
通过分析 setMenuBar 方法,发现其会调用 oldMenuBar->deleteLater() 方法释放旧的菜单栏对象。而 deleteLater() 主要是给自己发送 DeferredDelete 事件,然后在事件循环中释放自己。因此我们可以重写 MenuA 和 MenuB 的 event() 方法,来拒绝删除操作,这样每次 setMenuBar() 的时候都不会删除旧的菜单栏,因此下次使用的时候也就不需要再重新分配了。参考代码如下:
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
/**
|
|
|
|
|
@file MenuA.h
|
|
|
|
|
*/
|
|
|
|
|
#ifndef MENUA_H
|
|
|
|
|
#define MENUA_H
|
|
|
|
|
|
|
|
|
|
#include <QMenuBar>
|
|
|
|
|
|
|
|
|
|
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 <QMainWindow>
|
|
|
|
|
|
|
|
|
|
#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()
|
|
|
|
|
{
|
2019-11-18 18:03:13 +08:00
|
|
|
|
delete MA;
|
|
|
|
|
delete MB;
|
2019-11-18 16:15:40 +08:00
|
|
|
|
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
|
|
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
<ui version="4.0">
|
|
|
|
|
<class>MenuA</class>
|
|
|
|
|
<widget class="QMenuBar" name="MenuA">
|
|
|
|
|
<property name="geometry">
|
|
|
|
|
<rect>
|
|
|
|
|
<x>0</x>
|
|
|
|
|
<y>0</y>
|
|
|
|
|
<width>400</width>
|
|
|
|
|
<height>300</height>
|
|
|
|
|
</rect>
|
|
|
|
|
</property>
|
|
|
|
|
<property name="windowTitle">
|
|
|
|
|
<string>Form</string>
|
|
|
|
|
</property>
|
|
|
|
|
<widget class="QMenu" name="menuA">
|
|
|
|
|
<property name="title">
|
|
|
|
|
<string>MenuA</string>
|
|
|
|
|
</property>
|
|
|
|
|
</widget>
|
|
|
|
|
<addaction name="menuA"/>
|
|
|
|
|
</widget>
|
|
|
|
|
<resources/>
|
|
|
|
|
<connections/>
|
|
|
|
|
</ui>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 附录B:MenuB.ui
|
|
|
|
|
|
|
|
|
|
MenuA.ui 实现了菜单 MenuB:
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
<ui version="4.0">
|
|
|
|
|
<class>MenuB</class>
|
|
|
|
|
<widget class="QMenuBar" name="MenuB">
|
|
|
|
|
<property name="geometry">
|
|
|
|
|
<rect>
|
|
|
|
|
<x>0</x>
|
|
|
|
|
<y>0</y>
|
|
|
|
|
<width>400</width>
|
|
|
|
|
<height>300</height>
|
|
|
|
|
</rect>
|
|
|
|
|
</property>
|
|
|
|
|
<property name="windowTitle">
|
|
|
|
|
<string>Form</string>
|
|
|
|
|
</property>
|
|
|
|
|
<widget class="QMenu" name="menuB">
|
|
|
|
|
<property name="title">
|
|
|
|
|
<string>MenuB</string>
|
|
|
|
|
</property>
|
|
|
|
|
</widget>
|
|
|
|
|
<addaction name="menuB"/>
|
|
|
|
|
</widget>
|
|
|
|
|
<resources/>
|
|
|
|
|
<connections/>
|
|
|
|
|
</ui>
|
|
|
|
|
```
|