本地合肥网站建设火蝠电商代运营公司
窗口类
- 一. 控件窗口:QWidget
- 二. 主窗口:QMainWindow
- 菜单栏:QMenuBar
- QMenuBar 内存泄漏问题
- 为什么创建菜单栏不需要传入 this 指针
- 工具栏:QToolBar
- 状态栏:QStatusBar
- 浮动窗口:QDockWidget
- 三. 对话框窗口:QDialog
- QDialog 内存泄漏问题
- 自定义对话框
- 模态对话框、非模态对话框
- Qt 内置对话框
- 消息对话框:QMessageBox
- 颜色对话框:QColorDialog
- 文件对话框:QFileDialog
- 字体对话框:QFontDialog
- 输入对话框:QInputDialog
一. 控件窗口:QWidget
之前的博客创建项目的时候,使用的就是 QWidget,里面存放的就是一些控件,这里就不再介绍了!
二. 主窗口:QMainWindow
- Qt 窗口是通过 QMainWindow 类来实现的,QMainWindow 是一个为用户提供主窗口程序的类,继承 QWidget 类,并且提供了一个预定义的布局。
- QMainWindow 包含一个菜单栏 (menu bar)、多个工具栏 (tool bars)、多个浮动窗口(dock widgets)、一个状态栏 (status bar) 和一个中心部件 (central widget),它是许多应用程序的基础,如文本编辑器,图片编辑器等。
如下图为 QMainwindow 中各组件所处的位置:
菜单栏:QMenuBar
Qt 中的菜单栏是通过 QMenuBar 这个类来实现的,一个主窗口最多只有一个菜单栏,位于主窗口顶部、主窗口标题栏下面。
菜单栏中包含菜单,菜单中包含菜单项,如下:
图形化界面:创建菜单栏
之前在创建项目的时候,选中的是 QWidget,但是这里要选中 QMainWindow,如下:
ui 文件如下:
菜单栏 (QMenuBar)、菜单 (QMenu)、菜单项 (QAction),如下:
添加菜单和菜单项,如下:
- 这里 Qt Designed 存在一个 bug,就是无法直接输入菜单项,而是需要复制粘贴。
程序运行后的效果,如下:
代码:创建菜单栏
修改 mainwindow.cpp 文件,运行程序后,结果如下:
- 创建菜单栏,并通过 QMainWindow 提供的 setMenuBar() 函数来添加到窗口中。
- 创建菜单,并通过 QMenu 提供的 addMenu() 函数来添加到菜单栏中。
- 创建菜单项,并通过 QAction 提供的 addAction() 函数来添加到菜单中。
- 在 Qt 中,并没有专门的菜单项类,可以通过 QAction 类,抽象出公共的动作,如在菜单中添加菜单项、QAction 可以给菜单栏使用,也可以给工具栏使用。
修改 mainwindow.cpp 文件,使用信号和槽,添加点击菜单项 (triggered:触发) 对应的槽函数,如下:
给菜单和菜单项,设置快捷键
通过给文本中添加 (&F)
这样的操作,就是添加了快捷键 Alt+F,在 QLabel 设置伙伴空间的时候讲过。
- 在菜单/菜单项后面添加 (&F),例如:
文件(&F)
,这个 & 符号并不会在界面上显示。 - 上古时期,还没有图形化界面的时候,文本编辑器都是靠类似风格的快捷键来操作的。
- 也可以使用 QShortCut 达到类似的效果。
运行程序,通过 Alt+F 选中菜单中的文件选项,再通过 Alt+N 选中菜单项新建选项,触发信号,执行对应的槽函数,如下:
添加子菜单
- 菜单栏 QMenuBar 通过 addMenu 函数,添加菜单。
- 菜单 QMenu 也提供了 addMenu 函数,添加子菜单。
添加分割线
菜单中菜单项特别多时,就可以通过分割线,进行分组,如下:
- 菜单 QMenu 中提供了 addSeparator 函数类添加分割线。
添加图标
使用 阿里巴巴矢量图标库,下载以下图片:
创建 qrc 文件,如下:
将图片存放在 / 目录下,如下:
编写 mianwindow.cpp 文件,添加使用 QAction 中的 setIcon 函数添加菜单项的图标,如下:
- 如果给 菜单 QMenu 设置图标,当前 菜单 QMenu 是长在 菜单栏 QMenuBar 上的,此时文本就不会显示了。
- 如果给 子菜单 QMenu 设置图标,图标和文本都会显示,就像上图的 菜单项 QAction 一样。
QMenuBar 内存泄漏问题
之前程序中,是如上创建 菜单栏 QMenuBar 的,但是存在问题。
- 如果我们创建的项目,没有勾选自动生成 ui 文件,此时上述的代码是 ok 的。
- 如果勾选了自动生成 ui 文件,上述代码存在内存泄漏问题。
- Qt 已经给你生成了一个 QMenuBar 了,如下在 Qt Designed 中查看,如下:
- 如果新创建的 QMenuBar 时,就会导致旧的 QMenuBar 脱离了 Qt 的对象树了,意味着后续就无法对这个对象进行释放了。
- 上述程序如果窗口关闭,对象树释放,此时进程也就结束了,进程结束,自然所有内存都回收给系统了,上述内存泄漏也不会造成影响。
- 但是如果这样的代码是出现在一个多窗口的程序中,如果涉及到窗口的频繁跳转切换 (窗口的频繁创建销毁),上述内存泄漏就会更严重一些。
- 但是实际上由于现在的计算机都比较充裕,上述的内存泄漏都还好。
- 但是工作中可能会参与 “嵌入式” 开发,嵌入式设备具有的硬件资源就比较紧张了。
- 服务器程序相比于客户端程序更害怕内存泄漏:服务器要处理很多请求,每一个请求泄漏一点,请求累积下来就会泄漏更多,且服务器要时刻运行。
- 当然,即使如此,还是期望把代码写得更规范一些,如下:
- Qt 已经给你生成了一个 QMenuBar 了,如下在 Qt Designed 中查看,如下:
如果 QMenuBar:
- 存在:this->menuBar() 就直接获取并返回。
- 不存在:this->menuBar() 就先创建一个新的,再返回。
如果 this->setMenuBar(menuBar) 中的 menuBar:
- 存在:这里的设置就是自己替换自己,还是在对象树上。
- 不存在:就将新的 menuBar 挂到对象树上。
为什么创建菜单栏不需要传入 this 指针
- 使用 QMainWindow 的 setMenuBar() 方法设置菜单栏时,不需要在创建 QMenuBar 时传入 this 指针,因为 setMenuBar() 方法会自动将 QMenuBar 的父对象设置为 QMainWindow 对象。
- 同理:使用 QMenuBar 的 addMenu() 方法设置菜单时,使用 QMenu 的 addAction() 方法设置菜单项时,都不需要传入 this 指针。
- 同理:下面的工具栏也是如此。
工具栏:QToolBar
工具栏是应用程序中集成各种功能实现快捷键使用的一个区域。可以有多个,也可以没有,它并不是应用程序中必须存在的组件。它是一个可手动移动的组件,它的元素可以是各种窗口组件,它的元素通常以图标按钮的方式存在。
如下图为工具栏的示意图:
代码:创建工具栏
修改 mainwindow.cpp 文件,运行程序后,结果如下:
- 使用 QToolBar 表示工具栏,一个窗口可以有多个工具栏,也可以没有,工具栏往往也可以手动移动位置。
- 添加菜单栏,使用的是 setMenuBar,菜单栏只能有一个,重复设置会导致新的替换旧的,其中的 set 就包含了 “替换” 的语义。
- 添加工具栏,使用的是 addToolBar,工具栏可以有多个,重复设置,就会出现多个工具栏,不包含 “替换” 的语义。
- 典型的工具栏一般是图标,而不是文本,QAction 显示图标使用内置的 setIcon 行数,如果 QAction 出现在工具栏上,也会产生图标覆盖文本这样的情况。
- 当鼠标悬停在图标上,就会显示一段提示信息,可以通过 QAction 内置的 setToolTip 来进行手动设置。
代码:工具栏搭配菜单栏
工具栏往往也是和菜单栏搭配使用的,工具栏中的 QAction 也可以出现在菜单栏中,代码如下:
- 注意:如果一个 QAction 既是 QMenu 的子元素,又是 QToolBar 的子元素,对象树释放的时候只会释放一次。
运行效果如下:
创建多个工具栏
如果把工具栏拖出来放到窗口的任意位置,称之为 “浮动” 状态。
- 可以设置工具栏出现的初始位置 (上下左右…)
- 可以设置工具栏允许停放到哪些边缘。
- 可以设置工具栏是否允许浮动。
- 可以设置工具栏是否允许移动。
代码:设置工具栏出现的位置
- 通过 MainWindow 提供的 addToolBar() 函数,新增添加工具项的同时,设置工具栏出现的位置。
- 通过 QToolBar 提供的 setAllowedAreas() 函数,设置工具栏可以停靠位置。
- 说明:在创建工具栏的同时指定其停靠的位置,指的是程序运行时工具栏默认所在的位置;而使用 setAllowedAreas() 函数,设置停靠位置,指的是工具栏允许其所能停靠的位置。
- 工具栏的浮动属性可以通过 QToolBar类,提供的 setFloatable() 函数来设置。
- 工具栏的移动属性可以通过 QToolBar类,提供的 setMovable() 函数来设置。
- 说明:若设置工具栏为不移动状态,则设置其停靠位置的操作就不会生效,所以设置工具栏的移动属性类似于总开关的效果。
具体工具栏的位置的枚举类型如下:
代码以及运行效果如下:
状态栏:QStatusBar
状态栏是应用程序中输出简要信息的区域,一般位于主窗口的最底部,一个窗口中最多只能有一个状态栏。在 Qt 中,状态栏是通过 QStatusBar 类来实现的。
在状态栏中可以显示的消息类型有:
- 实时消息:如当前程序状态。
- 永久消息:如程序版本号,机构名称。
- 进度消息:如进度条提示,百分百提示。
代码:创建状态栏
创建状态栏和创建菜单栏的方法类似,如下:
- 在状态栏中显示 “临时消息”,是通过 QStatusBar 类,提供的 showMessage() 函数来实现的。
- 在状态栏中显示 “永久消息”,是通过 QStatusBar 类,提供的 addWidget() 和 addPermanentWidget() 函数来实现的。
创建临时消息:状态信息 3000 ms 后消失,代码如下:
创建永久消息:两个按比例拉伸的 QLabel 标签控件,代码如下:
创建永久消息:左侧进度条,右侧按钮,代码如下:
浮动窗口:QDockWidget
在 Qt 中,浮动窗口也称之为铆接部件 (子窗口),浮动窗口是通过 QDockWidget 类来实现浮动的功能。浮动窗口一般是位于核心部件的周围,可以有多个。
代码:创建浮动窗口
- 浮动窗口是位于中心部件的周围,可以通过 QDockWidget 类,提供 setAllowedAreas() 函数,设置其允许停靠的位置。
其中可以设置允许停靠的位置有:
代码:给浮动窗口添加子控件。
- 不能直接给这个浮动窗口添加子控件,而是需要创建一个单独的 QWidget,把要添加的控件加入到 QWidget 中,然后再把这个 QWidget 设置到 QDockWidget 中。
- 由于 QDockWidget 中只能包含一个 QWidget,要想添加更多的控件,就只能往 QWidget 中进行添加了。
三. 对话框窗口:QDialog
- 对话框窗口是 GUI 程序中不可或缺的组成部分,一些不适合在主窗口实现的功能组件可以设置在对话框中,对话框通常是一个顶层窗口,出现在程序最上层,用于实现短期任务或者简洁的用户交互。
- Qt 常用的内置对话框有:QFiledialog (文件对话框)、QColorDialog (颜色对话框)、QFontDialog (字体对话框)、QInputDialog (输入对话框) 和 QMessageBox (消息框)
关闭一个画图板时,弹出以下对话框:
创建对话框项目
- 总体来说,基于 QDialog 作为父元素创建出来的程序窗口,和之前通过 QWidget 创建出来的程序窗口是非常相似的。
- 实际开发中,更多的情况,往往不是直接在项目中创建 QDialog,而是在代码中创建额外的类,让额外的类继承 QDialog
QDialog 内存泄漏问题
代码:通过点击按钮,弹出对话框
- QDialog 其实也是 QWidget 的子类,QWidget 的各种属性方法,QDialog 也能使用。
- 注意:不同于界面上的控件,此处的 QDialog 每次按下按钮,都会创建一个新的 QDialog 对象,并进行显示,每次点击都会创建新的对话框对象。
- 一个程序运行的过程中,可以无数点击这个按钮,进一步的就产生出无数个这样的 QDialog 对象,存在内存泄漏问题。
QDialog* dialog = new QDialog(this)
:确实是把父元素设置为 this,QMainWindow 销毁的时候,此时 QDialog 是会随之销毁的,但是架不住 QDialog 会在 QMainWindow 销毁之前就存在很多个。- 就怕你这个 QDialog 对象占用内存很大,而你的机器本身剩余的内存很少 (嵌入式系统)
当点击按钮时,对话框弹出之后马上就消失了,用户体验不好。
代码:delete 和 关闭对话框 关联起来
- 用户点击关闭的时候,触发销毁对话框:一般是点击关闭对话框 (也就是 X 号) 信号,关联一个销毁对话框的槽函数,但是这样太麻烦了。
- Qt 为了让我们写的更方便,直接给 QDialog 设置了一个属性,可以通过设置属性,完成上述效果。
自定义对话框
纯代码:自定义 QDialog 界面
先创建一个 Dialog 类,并且继承 QDialog,如下:
修改 mainwindow.cpp 文件,做到点击按钮,弹出一个对话框,如下:
修改 dialog.cpp 文件,做到点击关闭按钮,直接关闭对话框,如下:
- 注意:这里控件的释放由布局管理器进行管理,而布局管理器的释放又由 Dialog 进行管理,所以可以不用传入 this 指针。
图形化界面:自定义 QDialog 界面
在 mainwindow.ui 文件中添加按钮,它的作用是弹出对话框,如下:
如何通过图形化的界面弹出对话框?关键的操作,就是创建出一个新的 ui 文件出来,如下:
修改 对话框的 ui 文件 (dialog.ui),如下:
修改 mainwindow.cpp 文件,做到点击按钮,弹出一个对话框,如下:
修改 dialog.cpp 文件,做到点击按钮,关闭对话框,如下:
实际开发中,更推荐图形化界面的方式自定义对话框。
模态对话框、非模态对话框
对话框分为 “模态对话框” 和 “非模态对话框”。
- 模态对话框:弹出对话框的时候,此时用户无法操作父窗口,必须得完成对话框内部的操作,关闭对话框之后,才能操作父窗口,是一种阻塞式的对话框。
- 适用于必须依赖用户选择的场合,比如:消息显示,文件选择,打印设置等。
- 使用 QDialog::exec() 函数,进行调用。
- 非模态对话框:弹出对话框的时候,用户可以操作父窗口,是一种非阻塞式的对话框。
- 一般在堆上创建,这是因为如果创建在栈上时,弹出的非模态对话框就会一闪而过。
- 同时还需要设置 Qt:WA_DeleteOnClose 属性,目的是:当创建多个非模态对话框时 (如果打开了多个非模态窗口),为了避免内存泄漏要设置此属性。
- 适用于特殊功能设置的场合,比如查找操作,属性设置等。
- 使用 QDialog::show() 函数,进行调用。
上面写的代码 dialog->show() 函数,创建的是 “非模态对话框”,如果换成 dialog->exec() 函数,创建的是 “模态对话框”,代码如下:
此时点击 MainWindow 窗口,无法操作!但是使用 “模态对话框” 会影响用户的体验。
Qt 内置对话框
Qt 提供了多种可复用的对话框类型,即 Qt 标准对话框,全部继承 QDialog 类,常用标准对话框如下:
消息对话框:QMessageBox
消息对话框是应用程序中最常用的界面元素,主要用于为用户提示重要信息,强制用户进行选择操作,如下:
代码:显示消息对话框,让用户进行选择
消息对话框的图标,对应的宏如下:
消息对话框的按钮,对应的宏如下:
代码:自定义消息对话框的按钮
QMessageBox 类,提供的 addButton 函数的第二个参数 (按钮作用),宏如下:
代码:按下消息对话框内置的按钮,窗口关闭,如何得知按下的是什么
可以通过 exec 的返回值,判断按下的是什么按钮,进而执行后续的操作!
代码:更简洁的创建消息对话框
QMessageBox 类中定义了静态成员函数,可以直接调用创建不同风格的消息对话框,其中包括:
颜色对话框:QColorDialog
功能是允许用户选择颜色,如下:
代码:显示颜色对话框
QColorDialog::getColor() 静态函数可以弹出一个模态对话框,用户选择颜色之后,点击确定,对话框关闭,此时返回的值就是用户选择的颜色值,getColor() 是静态函数,不必创建对话框对象,就可以直接使用,更推荐,代码如下:
文件对话框:QFileDialog
用于应用程序中需要打开一个外部文件或需要将当前内容存储到指定的外部文件。
代码:文件对话框
QFileDialog 常用的静态方法:
- 打开一个文件:getOpenFileName()
- 打开多个文件:getOpenFileNames()
- 保存文件:getSaveFileName()
此处的打开和保存这里的功能都是需要额外去实现的,并不是说直接一按保存就真的保存了,之后会讲解 Qt 针对文件的操作。
字体对话框:QFontDialog
Qt 中提供了预定义的字体对话框类 QFontDialog,用于提供选择字体的对话框部件。
代码:创建字体对话框
QFontDialog 静态方法 getFont(bool* ok, …) 其中 ok 是输出型参数,表示点击的是字体对话框中的 OK 按钮、还是 Cancle 按钮,返回值是选择的字体,代码如下:
输入对话框:QInputDialog
Qt 中提供了预定义的输入对话框类 QInputDialog,用于进行临时数据输入的场合。
代码:创建输入对话框
QInputDialog 的常用静态方法:
- 双精度浮点型输入数据对话框:getDouble()
- 整型输入数据对话框:getInt()
- 选择条目型输入数据框:getItem()