【Qt】深入探索Qt主窗口与菜单栏:构建高效用户界面的实践指南

前言

在现代软件开发中,用户界面的设计对于提升用户体验至关重要。Qt框架作为功能强大的跨平台开发工具,提供了丰富的组件和工具来帮助开发者构建复杂的应用程序。本文将深入探讨Qt中的主窗口(QMainWindow)及其菜单栏(QMenuBar)的创建和使用,包括菜单栏的创建、菜单项的添加、快捷键的设置、子菜单的嵌套、分割线的使用以及图标的添加。此外,文章还将讨论内存泄漏问题,以及如何在Qt中避免这类问题,确保应用程序的稳定性和性能。
前面学习的所有代码,都是基于 QWidget(控件),QWidget 更多的是作为别的窗口的一个部分。

1. 什么是Main Window?

QMainWindow 是⼀个为用户提供主窗口程序的类,继承自 QWidget 类,并且提供了⼀个预定义的布局。QMainWindow 包含 ⼀个菜单栏(menu bar)、多个工具栏(tool bars)、多个浮动窗口(铆接部件)(dock widgets)、⼀个状态栏(status bar) 和⼀个中心部件(central widget),它是许多应用程序的基础,如文本编辑器,图片编辑器等。如下图为 QMainwindow 中 各组件所处的位置:
在这里插入图片描述

  • 菜单栏(Menu Bar):
    Qt 中的菜单栏是通过 QMenuBar 这个类来实现的。⼀个主窗口最多只有⼀个菜单栏。位于主窗口顶部、主窗口标题栏下面。
    在这里插入图片描述

  • 工具栏(Tool Bar Area):
    工具具栏是应用程序中集成各种功能实现快捷键使用的⼀个区域。可以有多个,也可以没有,它并不是应用程序中必须存在的组件。它是⼀个可移动的组件,它的元素可以是各种窗口组件,它的元素通常以图标按钮的方式存在。如下图为工具栏的示意图:
    在这里插入图片描述

  • 铆接部件/子窗口(Dock Widget Area)
    一个主窗口往往可以是由多个子窗口所构成的。
    在这里插入图片描述

  • 中央控件(Central Widget)
    窗口最核心的部分
    在这里插入图片描述

  • 状态栏(Status Bar)
    用于显示一些信息供用户去参考。
    在这里插入图片描述

2. 详细了解一下其中的 菜单栏:

Qt 中的菜单栏是通过 QMenuBar 这个类来实现的。⼀个主窗口最多只有⼀个菜单栏。位于主窗口顶部、主窗口标题栏下面。
菜单栏中包含菜单. 菜单中包含菜单项。
在这里插入图片描述

一个主窗口最多只有一个菜单栏,工具栏,本质上就是菜单中的一些选项的“快捷方式”
QAction 动作

2.1. 创建菜单栏

“在这里输入” 可以在这里添加菜单了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

菜单栏(QMenuBar) -> 菜单(QMenu) -> 菜单项(QAction

在这里插入图片描述
由于当前每个菜单都是空着的,点上去没反应,添加后有反应。
在这里插入图片描述
使用代码去创建菜单结构:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 1.先创建一个菜单栏
    QMenuBar* menuBar = new QMenuBar();
    this->setMenuBar(menuBar);

    // 2. 创建菜单
    QMenu* menu1 = new QMenu("文件");
    QMenu* menu2 = new QMenu("编辑");
    QMenu* menu3 = new QMenu("视图");

    menuBar->addMenu(menu1);
    menuBar->addMenu(menu2);
    menuBar->addMenu(menu3);

    // 3. 给菜单添加菜单项
    QAction* action1 = new QAction("新建");
    QAction* action2 = new QAction("打开");
    QAction* action3 = new QAction("保存");
    QAction* action4 = new QAction("另存为");
    QAction* action5 = new QAction("退出");
    menu1->addAction(action1);
    menu1->addAction(action2);
    menu1->addAction(action3);
    menu1->addAction(action4);
    menu1->addAction(action5);

}

MainWindow::~MainWindow()
{
    delete ui;
}

在这里插入图片描述
怎样使它点击的时候有反应呢?

QAction* action1 = new QAction("新建");
QAction* action2 = new QAction("打开");
QAction* action3 = new QAction("保存");
QAction* action4 = new QAction("另存为");
QAction* action5 = new QAction("退出");

被点击的时候会触发一个信号!triggered 触发

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 1.先创建一个菜单栏
    QMenuBar* menuBar = new QMenuBar();
    this->setMenuBar(menuBar);

    // 2. 创建菜单
    QMenu* menu1 = new QMenu("文件");
    QMenu* menu2 = new QMenu("编辑");
    QMenu* menu3 = new QMenu("视图");

    menuBar->addMenu(menu1);
    menuBar->addMenu(menu2);
    menuBar->addMenu(menu3);

    // 3. 给菜单添加菜单项
    QAction* action1 = new QAction("新建");
    QAction* action2 = new QAction("打开");
    QAction* action3 = new QAction("保存");
    QAction* action4 = new QAction("另存为");
    QAction* action5 = new QAction("退出");
    menu1->addAction(action1);
    menu1->addAction(action2);
    menu1->addAction(action3);
    menu1->addAction(action4);
    menu1->addAction(action5);

    // 4. 给 action 添加信号槽
    connect(action1, &QAction::triggered, this, &MainWindow::handle);
    connect(action5, &QAction::triggered, this, &MainWindow::close); // close 是Qt自带关闭窗口的槽函数

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::handle()
{
    qDebug() << "触发新建操作!";
}

在这里插入图片描述

2.2. 添加快捷键

给菜单和菜单项设置快捷键
设置好的快捷键就可以搭配 alt 来进行使用了

QMenu* menu1 = new QMenu("文件(&F)");

通过给文本添加 &F 这样的操作,就是添加了快捷键 alt+F
与,QLabel,中设置伙伴的方式比较相似,当然使用 QShortcut 也可以实现同样的效果但是太麻烦了。
在这里插入图片描述
给菜单项同样可以添加快捷键:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QMenuBar* menBar = new QMenuBar();
    this->setMenuBar(menBar);

    QMenu* menu1 = new QMenu("文件(&F)");
    QMenu* menu2 = new QMenu("视图(&V)");
    menBar->addMenu(menu1);
    menBar->addMenu(menu2);

    // 创建四个菜单项
    QAction* action1 = new QAction("action1(&Q)");
    QAction* action2 = new QAction("action2(&W)");
    QAction* action3 = new QAction("action3(&E)");
    QAction* action4 = new QAction("action4(&R)");

    menu1->addAction(action1);
    menu1->addAction(action2);
    menu2->addAction(action3);
    menu2->addAction(action4);

    // 如果不绑定槽函数,通过快捷键选中也没啥反应
    connect(action1, &QAction::triggered, this, &MainWindow::handle1);
    connect(action2, &QAction::triggered, this, &MainWindow::handle2);
    connect(action3, &QAction::triggered, this, &MainWindow::handle3);
    connect(action4, &QAction::triggered, this, &MainWindow::handle4);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::handle1()
{
    qDebug() << "handle1" ;
}

void MainWindow::handle2()
{
    qDebug() << "handle2" ;
}

void MainWindow::handle3()
{
    qDebug() << "handle3" ;
}

void MainWindow::handle4()
{
    qDebug() << "handle4" ;
}

2.3. 添加子菜单

菜单栏 -> 菜单 -> 菜单项
菜单栏->菜单->子菜单->菜单项

QMenuBar 可以通过 addMenu 添加菜单的,QMenu 也提供了 addMenu

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QMenuBar* menuBar = new QMenuBar();
    this->setMenuBar(menuBar);

    QMenu* menuParent = new QMenu("父菜单");
    QMenu* menuChild = new QMenu("子菜单");

    menuBar->addMenu(menuParent);
    menuParent->addMenu(menuChild);

    QAction* action1 = new QAction("菜单项1");
    QAction* action2 = new QAction("菜单项2");
    menuChild->addAction(action1);
    menuChild->addAction(action2);

    QMenu* menuChild2 = new QMenu("子菜单2");
    menuChild->addMenu(menuChild2);

}

MainWindow::~MainWindow()
{
    delete ui;
}

在这里插入图片描述

2.4. 添加分割线

菜单里菜单特别多,就可以通过分割线进行分组

QMenu 中提供了 addSeparator 这样的函数

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QMenuBar* menuBar = new QMenuBar();
    this->setMenuBar(menuBar);

    QMenu* menu = new QMenu("菜单");
    menuBar->addMenu(menu);

    QAction* action1 = new QAction("菜单项1");
    QAction* action2 = new QAction("菜单项2");

    menu->addAction(action1);
    menu->addSeparator();
    menu->addAction(action2);
}

MainWindow::~MainWindow()
{
    delete ui;
}

在这里插入图片描述

2.5. 添加图标

Qlcon 类, qrc

在这里插入图片描述

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QMenuBar* menuBar = new QMenuBar();
    this->setMenuBar(menuBar);

    QMenu* menu = new QMenu("菜单");
    menuBar->addMenu(menu);

    QAction* action1 = new QAction("菜单项1");
    action1->setIcon(QIcon(":/open.png"));
    QAction* action2 = new QAction("菜单项2");
    action2->setIcon(QIcon(":/save.png"));
    menu->addAction(action1);
    menu->addAction(action2);
}

MainWindow::~MainWindow()
{
    delete ui;
}

在这里插入图片描述
如果给 QMenu 设置图标,当前 QMenu 是长在 QMenuBar 上的,此时文本就不显示,图标就覆盖了文本。

QMenu 是子菜单,图标和文本都是能显示的。

3. 内存泄漏问题:

QMenuBar* menuBar = new QMenuBar();
this->setMenuBar(menuBar);

如果咱们创建项目,没有勾选生成ui,文件,此时上述代码是可以的,如果勾选了自动生成 ui 文件,上述代码则会引起内存泄漏。
自动生成 ui 文件:Qt已经给你生成了一个 QMenuBar 了。

之前程序已经创建好了一个 QMenuBar , 当设置新的 QMenuBar 进来的时候, 就会导致旧的 QMenuBar 脱离了 Qt 的对象树了,意味着后续无法对这个对象树进行释放了

上诉程序如果窗口关闭,对象树释放,此时进程也就介绍了。进程结束,自然所有内存都回收给系统。上述内存泄漏也不会造成影响,但是如果这样的如果出现在一个多窗口的程序中。如果涉及到窗口的频繁跳转切换(窗口的频繁创建销毁),上述代码泄漏会更严重一些。但是实际上由于现在的计算机内存都比较充裕上述内存泄漏都还好。

服务器程序相比于客户端程序相比更害怕内存泄漏。

  1. 服务器要处理很多请求,每个请求泄漏一点,请求累积下来就会泄漏很多
  2. 服务器要 7 * 24 小时运行。

当然,即使如此,还是期望代码写的更规范一些。

QMenuBar* menuBar = this->menuBar();
  1. 如果 QMenuBar 已经存在,直接获取并返回
  2. 如果 QMenuBar 不存在,就先创建一个新的,再返回。
this->setMenuBar(menuBar);

如果是活的已经存在的 QMenuBar, 这里的设置就是自己替换自己,仍然对象树上

如在嵌入式,设备上内存泄漏的问题就比较重要了。

总结

本文详细介绍了Qt中主窗口(QMainWindow)的构成和功能,特别是菜单栏(QMenuBar)的创建和使用。通过一系列具体的代码示例,我们学习了如何创建菜单栏、添加菜单和菜单项、设置快捷键、嵌套子菜单、使用分割线以及添加图标,这些都是构建用户友好界面的关键步骤。同时,文章也指出了在Qt开发中可能遇到的内存泄漏问题,并提供了解决方案,强调了在开发过程中遵循良好编程实践的重要性。通过这些知识点,开发者可以更有效地利用Qt框架,创建出既美观又实用的应用程序界面。

  • 30
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
当将窗口设置为`Qt::WindowStaysOnTopHint`(始终置顶)时,可能会导致子窗口(例如QComboBox)无法正常弹出。这是因为`Qt::WindowStaysOnTopHint`属性使得窗口始终位于其他窗口之上,包括子窗口。 解决这个问题的一种方法是,在需要使用`Qt::WindowStaysOnTopHint`属性的窗口中,临时禁用该属性,以允许子窗口正常弹出。当子窗口关闭后,再恢复窗口的`Qt::WindowStaysOnTopHint`属性。 以下是一个示例代码: ```cpp // 创建窗口 QMainWindow* mainWindow = new QMainWindow(parent); mainWindow->setWindowFlags(Qt::WindowStaysOnTopHint); // 创建子窗口,例如QComboBox QComboBox* comboBox = new QComboBox(mainWindow); // 当需要弹出子窗口时,禁用窗口Qt::WindowStaysOnTopHint属性 QObject::connect(comboBox, QOverload<const QString&>::of(&QComboBox::activated), [=](const QString& text) { mainWindow->setWindowFlags(mainWindow->windowFlags() & ~Qt::WindowStaysOnTopHint); mainWindow->show(); // 延迟一段时间后恢复窗口Qt::WindowStaysOnTopHint属性 QTimer::singleShot(100, [=]() { mainWindow->setWindowFlags(mainWindow->windowFlags() | Qt::WindowStaysOnTopHint); mainWindow->show(); }); }); // 后续代码... ``` 在上述示例中,我们通过连接QComboBox的`activated`信号,当需要弹出子窗口时,临时禁用窗口的`Qt::WindowStaysOnTopHint`属性,并在一定延迟后恢复该属性。这样,在用户触发子窗口弹出操作时,窗口将不会遮挡子窗口。 请注意,这只是一种解决方案,并不是通用的解决方法。具体实现可能需要根据您的应用程序需求进行调整和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Q_hd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值