当前位置: 首页 > news >正文

Qt 窗口 - 1

Qt 窗口是通过 QMainWindow 类来实现的。 QMainWindow 是一个为用户提供主窗口程序的类,继承自 QWidget 类,并且提供了一个预定义的布局。QMainWindow 包含一个菜单栏(menu bar)、多个工具栏(tool bars)、多个浮动窗口(停靠部件)(dock widgets)、一个状态栏(status bar) 和一个中心部件(central widget),它是许多应用程序的基础,如文本编辑器等。如下图为 QMainWindow 中各组件所处的位置:

工具栏本质就是把菜单中一些比较常用的选项,直接放在工具栏里了,直接点击工具栏中的按钮就能快速生效!

1. 菜单栏

Qt 中的菜单栏是通过 QMenuBar 这个类来实现的。一个主窗口最多只有一个菜单栏。位于主窗口顶部、主窗口标题栏下面。 菜单栏中包含菜单,菜单中包含菜单项。

工具栏本质上就是菜单中的一些选项的“快捷方式”!

1.1 创建菜单栏

方式一:菜单栏的创建可以借助于 QMainWindow 类提供的 menuBar() 函数来实现。

menubar()函数原型如下:

QMenuBar* menuBar() const
创建菜单栏 QMenuBar *menubar = menuBar();将菜单栏放入窗口中 this->setMenuBar(menubar);

方式二:在堆上动态创建:

QMenuBar *menuBar = new QMenuBar(this);将菜单栏放入窗口中 this->setMenuBar(menuBar);

使用 setMenuBar 把菜单栏放在窗口中!

1.2 在菜单栏中添加菜单

我们可以直接在 ui 界面上进行编辑:

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

下面,我们使用代码来进行操作:

创建菜单,并通过 QMenu 提供的 addMenu() 函数来添加菜单。

1.3 创建菜单项

在 Qt 中,并没有专门的菜单项类,可以通过 QAction 类,抽象出公共的动作。如在菜单中添加菜单项。

💡 QAction 可以给菜单栏使用,也可以给工具栏使用。

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(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//......// 4. 给 action 添加信号槽connect(action1, &QAction::triggered, this, &MainWindow::handle);connect(action5, &QAction::triggered, this, &MainWindow::close);
}void MainWindow::handle()
{qDebug()<<"触发新建操作";
}

1.4 给菜单设置快捷键

我们打开我们常用的记事本:

都有对应的菜单/菜单项的快捷键,所以,我们可以对其设置快捷键:

通过给文本中添加 &F 这样的操作,就是添加了一个快捷键 --- (Alt + F)

我们在 QLabel 设置伙伴控件的时候也设置过了!

QShortCut 也可以实现类似的效果,但是太麻烦了

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QMenuBar* menuBar = new QMenuBar();this->setMenuBar(menuBar);QMenu* menu1 = new QMenu("文件 (&F)");QMenu* menu2 = new QMenu("编辑 (&E)");QMenu* menu3 = new QMenu("视图 (&V)");QMenu* menu4 = new QMenu("关于 (&A)");menuBar->addMenu(menu1);menuBar->addMenu(menu2);menuBar->addMenu(menu3);menuBar->addMenu(menu4);QAction* action1 = new QAction("菜单项1");QAction* action2 = new QAction("菜单项2");QAction* action3 = new QAction("菜单项3");QAction* action4 = new QAction("菜单项4");menu1->addAction(action1);menu2->addAction(action2);menu3->addAction(action3);menu4->addAction(action4);
}

我们当让也是可以给 QAction 添加快捷键:

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);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("action1 (&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);
}void MainWindow::handle1()
{qDebug()<<"handle1";
}void MainWindow::handle2()
{qDebug()<<"handle2";
}void MainWindow::handle3()
{qDebug()<<"handle3";
}void MainWindow::handle4()
{qDebug()<<"handle4";
}

补充:

在Qt中,当你为菜单项(QAction)设置了快捷键,并且这个菜单项被添加到了菜单栏(QMenu)中,Qt会自动处理这些菜单项的快捷键事件。这意味着,即使没有显式地使用connect函数将动作(QAction)与槽函数(slot)连接起来,按下快捷键时,菜单项对应的槽函数也会被调用。

这是因为Qt的事件系统会自动将菜单项的触发事件(如点击菜单项或按下快捷键)与相应的槽函数关联起来。这是Qt框架内部实现的一部分,旨在简化开发者的工作流程。

具体来说,当你创建一个QAction并将其添加到QMenu中时,Qt会自动将这个动作的触发事件(包括快捷键触发)与一个默认的槽函数关联起来。这个默认的槽函数会调用QActiontrigger()方法,而这个方法又会调用与该动作关联的任何槽函数。

如果你希望自定义这些动作的行为,可以通过connect函数将这些动作与你自己定义的槽函数连接起来 --- 比如我们简单的进行 deBug 打印!

1.5 添加子菜单

菜单栏 - 菜单 - 子菜单 - 子菜单 - ... - 菜单项

QMenu 也提供了 addMenu 的方法(QMenuBar 可以通过 addMenu 添加菜单的,QMenu 也可以add来添加菜单,也就是子菜单!)

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);
}

1.6 在菜单项之间添加分割线

在菜单项之间可以添加分割线,分割线如下图所示,添加分割线是通过 QMenu 类提供的addSeparator() 函数来实现;

// 创建菜单项
QAction *act1 = new QAction("open");
QAction *act2 = new QAction("close");
QAction *act3 = new QAction("create");// 将菜单项添加到菜单上
menu1->addAction(act1);
menu1->addSeparator(); //在"open"和"close"中间添加分割线
menu1->addAction(act2);
menu1->addAction(act3);

1.7 给菜单/菜单项添加图标

我们依旧使用 QIcon 这个类,和我们之前的操作是一样的 --- 通过 qrc 进行管理!

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QMenuBar* menuBar = new QMenuBar();this->setMenuBar(menuBar);QMenu* menu = new QMenu("文件");menu->setIcon(QIcon(":/open.png"));menuBar->addMenu(menu);QAction* action1 = new QAction("打开");action1->setIcon(QIcon(":/open.png"));QAction* action2 = new QAction("保存");action2->setIcon(QIcon(":/save.png"));menu->addAction(action1);menu->addSeparator();menu->addAction(action2);
}

注意:如果给 Menu 设置图标,当前的 QMenu 是长在 QMenuBar 上的,此时文本就不显示了,图标覆盖了文本!

如果当前 QMenu 是子菜单的话,图标和文本是可以显示的!

1.8 综合示例

在窗口上创建一个菜单栏,在菜单栏中添加一些菜单,在某一个菜单中添加一些菜单项。

1. 新建 Qt 项目

注意:此时新建项目时选择的基类 QMainWindow,如下图示:

2. 在 "mainwindow.cpp" 文件中创建菜单和中央控件

  • 创建一个菜单栏,一个菜单
  • 两个菜单项:保存,加载
  • 创建一个 QTextEdit 作为窗口中央控件
#include <QMainWindow>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QTextEdit>QMainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{// 设置标题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);// 创建中央控件QTextEdit* edit = new QTextEdit(this);this->setCentralWidget(edit);this->setPlaceholderText("此处编写文本内容...");
}

3. 给 action 添加一些动作

// 连接信号槽,点击 action1 时触发一定的效果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);
}

执行程序,可以看到此时就可以通过程序来保存/加载文件了,并且对文件进行编辑。

注意事项

对于这一段代码:

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

如果我们创建项目的时候,没有勾选自动生成 ui 文件,此时上述代码是 OK 的,如果勾选了自动生成 ui 文件,上述代码回引起内存泄漏!

因为 Qt 已经帮我们自动生成了一个 QMenuBar 的!导致原有的,旧的 QMenuBar 脱离了 Qt 的对象树,就意味着后续无法对这个对象进行释放了!这就导致了内存泄漏!

我们之前说过,不主动 delete,是因为这个对象挂在对象树上!

上述程序如果窗口关闭,对象树释放了,此时进程就结束了,进程结束,自然所有内存都回收系统了,上述的内存泄漏也不会造成影响,但是如果这个代码出现在多窗口程序中呢?

如果涉及到窗口的频繁跳转切换,也就是窗口的频繁创建销毁,上述内存泄漏就会更严重一些!

我们就换种方式创建 MenuBar:

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

如果 QMenuBar 已经存在,直接获取并返回;

如果 QMenuBar 不存在,就先创建一个新的,再返回!

http://www.dtcms.com/a/351530.html

相关文章:

  • [Maven 基础课程]IDEA 配置 Maven
  • 基于 W55MH32Q-EVB 实现 FatFs 文件系统+FTP 服务器
  • C++ SNIFE
  • 数据结构——链表(2)——双向链表
  • 【图像处理 - 基础知识】ISP(Image Signal Processor)处理
  • 深度学习之卷积神经网络
  • 组件版本升级 - MySQL_8.4对比MySQL_8.0
  • 用 mem0 构建一个简单的 AI Healthcare 助手
  • 基于Vue通用组件定制化的场景解决
  • UNet改进(35):基于WGAM模块的PyTorch实战
  • Qt在Linux下编译发布 -- linuxdeployqt的使用
  • 第十九节:阴影进阶 - 软阴影与性能平衡技术
  • FileMenu Tools for Win:突破 Windows 文件管理困境的利器
  • Git:基本使用
  • 数字化转型三阶段:从信息化、数字化到数智化的战略进化
  • Leetcode+Java+动态规划II
  • 知行——同为科技24周年庆典
  • Thingsboard 租户管理员权限,增加租户普通用户权限
  • Go errgroup:高效并发控制与错误处理
  • WPF基于LiveCharts2图形库,实现:折线图,柱状图,饼状图
  • 03. 协程入门_Android异步处理机制
  • 系统架构设计师备考第7天——网络协议中间件软件构件
  • WebSocket简单了解
  • 线性代数之深入理解旋转矩阵
  • lesson46-2:Linux 高级指令全解析:从文件操作到系统管理
  • mybatisplus 配置二级缓存
  • 【系统编程】线程简介
  • 【人工智能】2025年AI代理开源革命:社区驱动的智能体生态重塑未来
  • Linux--seLinux的概述
  • FRET、PLA、Co-IP和GST pull-down有何区别? 应该如何选择?