[Qt] 窗口 | 菜单栏MenuBar
目录
QMainWindow 概述
一、菜单栏
1、创建菜单栏
2、在菜单栏中添加菜单
3、创建菜单项
4、在菜单项之间添加分割线
5、添加快捷键
6、添加子菜单
7、添加图标
综合示例
QMainWindow 概述
Qt 窗口是通过 QMainWindow
类来实现的。
- QMainWindow 是一个为用户 提供主窗口程序的类。
- 该类继承自
QWidget
,并提供了一个预定义的布局 - QMainWindow 包含一个菜单栏(Menu Bar)、多个工具栏(Tool Bars)、多个浮动窗口(铆接部件)(Dock Widgets)、⼀个状态栏(Status Bar)和一个中心部件(Central Widget),它是许多应用程序的基础,如文本编辑器,图片编辑器等。
如下图为 QMainwindow 中各组件所处的位置
一、菜单栏
Qt 中的菜单栏是通过 QMenuBar 这个类来实现的。一个主窗口最多只有一个菜单栏,位于主窗口顶部、主窗口标题栏下面。
菜单栏中包含菜单,菜单中包含菜单项。
工具栏本质上就是把菜单中一些比较常用的选项直接放到工具栏里,直接点工具栏中的按钮就能快速生效。
1、创建菜单栏
(1)方式一
菜单栏的创建可以借助于 QMainWindow 类提供的 menuBar() 函数来实现。menubar() 函数原型如下:
如果我们自己创建的项目没有勾选自动生成 ui 文件,那么上述代码是没有问题的。但如果勾选了自动生成 ui 文件(Qt 已经给我们生成了一个 QMenuBar),那么上述代码就会引起内存泄漏。
程序自己已经创建好了一个 QMenuBar,当设置新的 QMenuBar 进来时,就会导致旧的 QMenuBar 脱离了 Qt 的对象树,意味着后续就无法对这个对象进行释放了。
- 上述程序如果窗口关闭,对象树释放,此时进程就结束了,自然所有的内存都回收给系统,上述内存泄漏也就不会造成影响。
- 但是如果上述代码是出现在一个多窗口的程序中,如果涉及到窗口的频繁跳转切换(窗口的频繁创建销毁),上述内存泄漏就会更严重。但是实际上由于现在的计算机内存比较充裕,上述内存泄漏都还好,但还是要求代码写得更规范一些,所以选中采用下面这种写法。
(2)方式二(更合理的写法)
在堆上动态创建。
如果是获取到已经存在的 QMenuBar,那么这里的设置就是自己替换自己,仍然在对象树上。
使用 setMenuBar 把菜单栏放到窗口中。
2、在菜单栏中添加菜单
创建菜单,并通过 QMenu 提供的 addMenu() 函数 来添加菜单。
// 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、创建菜单项
在 Qt 中,并没有专门的菜单项类(QMenuBarItem),可以通过 QAction 类,抽象出公共的动作,如在菜单中添加菜单项。
// 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);
QAction 可以给菜单栏使用,也可以给工具栏使用。
4、在菜单项之间添加分割线
在菜单项之间可以添加分割线。分割线如下图所示,添加分割线是通过 QMenu 类提供的 addSeparator() 函数来实现:
示例:
5、添加快捷键
在设置菜单的 title 时,在字母前加 & 符号。
示例:
QMenuBar* menuBar = new QMenuBar();
this->setMenuBar(menuBar);
QMenu* menu1 = new QMenu("文件(&F)");
QMenu* menu2 = new QMenu("视图(&V)");
menuBar->addMenu(menu1);
menuBar->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);
!不绑定槽函数, 通过快捷键选中也没啥反应
效果展示:
先按下 Alt 键,再按 F,其显示的 W 下面会有下划线,表示是有快捷键的,快捷键为 W。
6、添加子菜单
- 菜单栏 -> 菜单 -> 菜单栏
- 菜单栏 -> 菜单 -> 子菜单 -> 子菜单 -> 菜单栏
QMenu 也提供了 addMenu,通过这个操作可以给某个菜单项添加子菜单。
示例:
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);
7、添加图标
按照之前设置图标的方法:
QMenuBar* menuBar = new QMenuBar();
this->setMenuBar(menuBar);
QMenu* menu = new QMenu("菜单");
menu->setIcon(QIcon(":/open.png"));
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);
如果给 QMenu 设置图标。因为当前 QMenu 是长在 QMenuBar 上的,此时文本就不显示,图标会覆盖文本。
而 QMenu 是子菜单,图标和文本都是可以显示的。
综合示例
在窗口上创建一个菜单栏,在菜单栏中添加一些菜单,在某一个菜单中添加一些菜单项。
(1)新建 Qt 项目
注意:此时新建项目时选择的基类 QMainwindow
(2)在 "mainwindow.cpp" 文件中创建菜单和中央控件
头文件中声明:
- 创建一个菜单栏,一个菜单
- 两个菜单项:保存,加载
- 创建一个 QTextEdit 作为窗口的中央控件
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 设置标题
this->setWindowTitle("我的记事本");
// 创建菜单栏
QMenuBar* menuBar = new QMenuBar(this);
this->setMenuBar(menuBar);
// 创建菜单
QMenu* menu = new QMenu("文件");
menuBar->addMenu(menu);
// 创建菜单项
QAction* action1 = new QAction("保存");
QAction* action2 = new QAction("加载");
menu->addAction(action1);
menu->addAction(action2);
// 创建中央控件
edit = new QTextEdit(this);
this->setCentralWidget(edit);
edit->setPlaceholderText("此处编写⽂本内容...");
}
(3)给 action 添加一些动作
// 连接信号槽,点击 action 时触发一定的效果
connect(action1, &QAction::triggered, this, &MainWindow::save);
connect(action2, &QAction::triggered, this, &MainWindow::load);
实现这两个槽函数:
使用 QFileDialog 来实现选择文件的效果
- getSaveFileName 用于保存文件的场景,此时的对话框可以输入文件名
- getOpenFileName 用于打开文件的场景,此时的对话框可以获取到鼠标选择的文件名
搭配 C++ 标准库的⽂件操作实现文件读写
void MainWindow::save()
{
// 弹出对话框,选择写入文件的路径
QFileDialog* dialog = new QFileDialog(this);
QString fileName = dialog->getSaveFileName(this, "保存⽂件", "C:/Users/1/");
qDebug() << "fileName: " << fileName;
// 写⼊⽂件
std::ofstream file(fileName.toStdString().c_str());
if (!file.is_open()) {
qDebug() << "文件保存失败!";
return;
}
const QString& text = edit->toPlainText();
file << text.toStdString();
file.close();
}
void MainWindow::load()
{
// 弹出对话框,选择打开的文件
QFileDialog* dialog = new QFileDialog(this);
QString fileName = dialog->getOpenFileName(this, "加载文件", "C:/Users/1/");
qDebug() << "fileName: " << fileName;
// 读取文件
std::ifstream file(fileName.toStdString().c_str());
if (!file.is_open()) {
qDebug() << "文件加载失败!";
return;
}
std::string content;
std::string line;
while (std::getline(file, line)) {
content += line;
content += "\n";
}
file.close();
// 显示到界面上
QString text = QString::fromStdString(content);
edit->setPlainText(text);
}
执行程序,可以看到此时就可以通过程序来保存 / 加载文件了,并且对文件进行编辑。