NotePublic/Software/Development/Environment/Qt/Libs/QMenuBar/QMainWindow_动态切换菜单栏.md

5.7 KiB
Raw Blame History

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 中使用:

void QMainWindow::setMenuBar(QMenuBar *menuBar)

方法可以设置主窗口菜单栏。当为主窗口设置新菜单栏时,会自动隐藏并删除旧的菜单栏。因此最基本的动态切换菜单的方法如下:

/**
 @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() 的时候都不会删除旧的菜单栏,因此下次使用的时候也就不需要再重新分配了。参考代码如下:

/**
 @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

/**
 @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 的指针,用于效果展示。

/**
 @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 如下:

/**
 @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() 方法,菜单栏就会“消失不见”。

附录

附录AMenuA.ui

MenuA.ui 实现了一个菜单 MenuA

<?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>

附录BMenuB.ui

MenuA.ui 实现了菜单 MenuB

<?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>