第二章 GUI应用程序设计基础

GUI应用程序设计基础

一、UI文件设计与运行机制

1、UI界面设计

Label: QLabel表示QLabel类组件,objectName是label。

QLabel组件的继承关系:QObject→QWidget→QFrame→QLabel

(1) 在第一章的基础上,在hello_world.ui中添加新的组件按钮(Push Button)在左侧组件面板Buttons分组中,将Push Button组件拖曳到设计窗体上面。

(2) 双击刚刚放置的Label组件,可以编辑其文字内容,将文字内容修改为“Close”,点大小默认为12。

(3) 为QPushButton的objectName是pushButton,为pushButton按钮增加功能,单击此按钮,关闭窗口,退出程序。

(4) 使用Signals和Slots编辑器完成这个功能。

1) 在信号与槽编辑器的工具栏单击按钮,出现如下界面

2) 双击<发送者>,在出现的条目中,选择pushButton

3) 双击<信号>,出现的条目中,选择clicked()

4) 双击<接收者>,再出现的条目,选择hello_world(对应QMainWindow最高级别;若最高级别是QWidget,选择QWidget:objectName)

5) 双击<槽>,在出现的条目中选择close()

6)这样设置当按钮pushButton被单击时,就执行hello_world的close()函数,实现关闭窗口的功能。

7)点击编译程序后,点击运行,显示如下界面

点击Close按钮,关闭窗口。

2、主函数文件

main()函数是应用程序的入口程序。他的主要作用是创建应用程序(QApplication a(argc,argv)),创建窗口(hello_world w)、显示窗口并运行程序(w.show()),开始应用程序的消息循环和事件处理(a.exec)。

3、窗体相关文件

(1)hello_world.h文件(根据实际命名为准,hello_world)

窗体相关文件(hello_world.h、hello_world.cpp、hello_world.ui、ui_hello_world.h)
hello_world.h:定义窗体类的头文件,定义了类MainWindow:

namespace Ui这是声明一个名称为Uid的命名空间,包含一个类MainWindow,但是这个类并不是文件中定义的MainWindow,而是ui_MainWindow.h文件中定义的类,用于描述界面的组件的,这个声明相当于外部类型声明。
MainWindowt类定义,继承与QMainWindow基类,是本实验的窗体类。
Ui:: MainWindow *ui是命名空间中MainWindow类定义的指针,该指针指向可视化设计界面,可以使用这个指针访问界面上的组件。

(2)hello_world.cpp文件

hello_world.h 文件自动加入的

析构函数简单的删除用new创建的指针ui

#include "hello_world.h"Qt编译生成的UI文件hello_world.ui对应的类定义文件。
hello_world: hello_world (QWidget *parent) :QMainWindow(parent), ui(new Ui:: hello_world):执行父类QMainWindow的构造函数,创建一个Ui:: MainWindow类的对象ui。设个ui就是MainWindow的私有部分定义的指针ui。
ui->setupUi(this):调用Ui:: MainWindow类的setupUi()实现窗口的生成和各种属性的设置,信号和槽的关联等等。

hello_world.ui窗体界面文件,存储窗体上的各个组件的属性设置和布局;


ui_ hello_world.h:编译后窗体上的组件及其属性、信号和槽的关联等自动组成的一个类的定义文件,类的名称Ui_Widget。

二、可视化UI设计

以下以一个实例介绍如何通过可视化的方法进行UI设计。

1、实例程序功能

程序界面如下

实现功能:

程序的主要功能是对中间一个文本框的文字字体样式和颜色进行设置。

2、界面组件布局

Qt 的界面设计使用了布局(Layout) 功能。所谓布局,就是界面上组件的排列方式,使用布局可以使组件有规则地分布,并且随着窗体大小变化自动地调整大小和相对位置。以下将会对实现过程逐步进行讲解。

1、界面组件的层次关系

  1. 新建Qtsample_2_1项目,基类为QDialog,objectName:QWDialog

  

(2)为了将界面上的各个组件的分布设计得更加美观,经常使用一些容器类,如 QgoupBox、QtabWidget、QFrame 等。

1) 将3 个 CheckBox 组件放置在一个 GroupBox 组件里

2)在 groupBox2 里放置3个RadioButton 组件

3) 添加文本编辑器Text Edit

4) 添加3个按钮Push Button

5)按照上图要求修改对应的名称

2、布局管理

Qt 为界面设计提供了丰富的布局管理功能,在 UI 设计器中,组件面板里有 Layouts 和 Spacers两个组件面板,在窗体上方的工具栏里有布局管理的按钮。

(1)使用组件面板里的布局组件设计布局时,先放一个 Horizontal Layout 到窗体上,布局组件会以红色边框显示。再往布局组件里拖放 3个 Push Button 和 2个 Horizontal Spacer,就可以得到下图 中3 个按钮的水平布局效果。

(2)在设计窗体的上方有一个工具栏,用于调整设计器进入不同的状态,以及进行布局设计,工具栏上各按钮的功能见下图。

使用工具栏上的布局控制按钮时,只需在窗体上选中需要设计布局的组件,然后点击某个布局按钮即可。在窗体上选择组件时同时按住 Ctrl 键,可以实现组件多选,选择某个容器类组件,相当于选择了其内部的所有组件。

  1. 在上面的界面中,选中 groupBox,然后单击“Lay OutHorizontally”工具栏按钮,就可以对 groupBoxl内的3个 CheckBox 水平布局。

  1. 在界面上,使 groupBox1里的 3个 CheckBox 水平布局,groupBox2 里的 3个RadioButton 水平布局,下方3 个按钮水平布局。在窗体上又放置了一个 PlainTextEdit 组件。

现在改变 groupBoxl、groupBox2 或按钮的水平布局的大小,其内部组件都会自动改变大小。但是当改变窗体大小时,界面上的各组件却并不会自动改变大小。

  1. 随后还需为窗体指定一个总的布局。选中窗体(即不要选择任何组件),单击工具栏上的“Layout Vertically”按钮,使4 个组件垂直分布。这样布局后,当窗体大小改变时,各个组件都会自动改变大小。最后的布局效果如下图所示:

3、伙伴关系与 Tab 顺序

伙伴关系是为了在程序运行时,在窗体上用快捷键快速将输入焦点切换到某个组件上;Tab 顺序是指在程序运行时,按下键盘上的 Tab 键时输入焦点的移动顺序。

3、信号与槽简要介绍

1、信号槽的基础概念

信号(Signal)就是在特定情况下被发射的事件,例如 PushButton 最常见的信号就是鼠标单击时发射的 clicked()信号,一个 ComboBox 最常见的信号是选择的列表项变化时发射的CurrentIndexChanged()信号。GUI程序设计的主要内容就是对界面上各组件的信号的响应,只需要知道什么情况下发射哪些信号,合理地去响应和处理这些信号就可以了。

槽 (Slot)就是对信号响应的函数。槽就是一个函数,与一般的 C++函数是一样的,可以定义在类的任何部分 (public、private 或 protected),可以具有任何参数,也可以被直接调用。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。

2、信号槽的基本格式

信号与槽关联是用QObject::connect()函数实现的,其基本格式是:

QObject::connect(sender,SIGNAL(signal()),receiver,SLOT(slot()));

connect()是 OObiect 类的一个静态函数,而OObiect 是所有 Ot类的基类,在实际调用时可以忽略前面的限定符,所以可以直接写为:

connect(sender,SIGNAL(signal()), receiver, SLOT(slot()));

    sender 是发射信号的对象的名称,signal()是信号名称。信号可以看做是特殊的函数需要带括号,有参数时还需要指明参数。receiver 是接收信号的对象名称,slot()是槽函数的名称需要带括号,有参数时还需要指明参数。

    SIGNAL 和 SLOT是 Qt 的宏,用于指明信号和槽,并将它们的参数转换为相应的字符串。例如,上篇的ui widget.h 文件中,在 setupUi()函数中有如下的语句:

    QObject;:connect(btnClose,SIGNAL(clicked()),Widget,SLOT(close()));

    其作用就是将 btnClose 按钮的 clicked()信号与窗体(Widget)的槽函数 close()相关联,这样当单击 btnClose 按钮(就是界面上的“Close”按钮)时,就会执行 Widget 的 close(槽函数)。

3、信号槽的使用的注意事项

    一个信号可以连接多个槽;

    多个信号可以连接同一个槽;

    一个信号可以连接另外一个信号;

    严格的情况下,信号与槽的参数个数和类型需要一致,至少信号的参数不能少于槽的参数。如果不匹配,会出现编译错误或运行错误。;

    在使用信号与槽的类中,必须在类的定义中加入宏Q OBJECT;

    当一个信号被发射时,与其关联的槽函数通常被立即执行,就像正常调用一个函数一样。只有当信号关联的所有槽函数执行完毕后,才会执行发射信号处后面的代码。

4、可视化生成槽函数原型和框架

1、功能需求

    单击 UnderLine、Italic、Bold 3 个 CheckBox 时,根据其状态,设置 PlainTextEdit 里的文字的字体样式

    Black、Red、Blue 3 个 RadioButton 是互斥选择的,单击某个 RadioButton 时,设置文字的颜色

    单击“确定”“取消”或“退出”按钮时,关闭窗口,退出程序

2、字体样式设置

(1)功能实现

选中 checkBoxUnder 组件,单击右键调出其快捷菜单。在快捷菜单中单击菜单项“转到槽”,有下面的界面出现:

信号 clicked(bool)会将 CheckBox 组件当前的选择状态作为一个参数传递,在响应代码里可以直接利用这个传递的参数。而如果用信号clicked0,则需要在代码里读取 CheckBox 组件的选中状态。为了简化代码,选择 clicked(bool)信号。点击OK后。

会在qwdialog.h中添加槽函数声明

在.cpp中自动添加void Dialog::on_checkBoxUnder_clicked(bool checked)框架,在其中添加对应的代码即可。

以同样的方法为 Italic 和 Bold 两个CheckBox 设计槽函数,编译后运行,可以看到已经实现想要的功能

以后需要编写connect()语句

编译运行后

(2)可视化UI设计下的信号槽连接的原理

上面的结果表明信号与槽实现了关联,但是在构造函数中只有一下一句:

查看编译生成的 ui_qwdialog.h 文件。构造函数里调用的 setupUi()是在 ui_qwdialog.h 文件里实现的, setupUi()中也是没有信号槽关联实现的操作,但是有以下语句:

QMetaObject::connectSlotsByName(Dialog);

秘密就在于这句,QMetaObject::connectSlotsByName(Dialog);将会搜索Dialog界面上所有组件,将信号与槽函数匹配的信号和槽关联起来,假设槽函数的名称是:

void on_<object name>_<signal name>(<signal parameters>);

    通过UI设计器的操作,为checkBoxUnder自动生成的槽函数是:on_checkBoxUnder_clicked(bool checked)

正好是 checkBoxUnder 的信号 clicked(bool)的槽函数。那么,connectSlotsByName0)就会将此信号和槽函数关联起来,如同执行了下面的这样一条语句:

connect(checkBoxUnder,SIGNAL(clicked(bool)),this,SLOT(on_checkBoxUnder_clicked(bool)));

这就是用 UI设计器可视化设计某个组件的信号响应槽函数,而不用手工去将其关联起来的原因,都是在界面类的构造函数里调用 setupUi()自动完成了关联。

3、字体颜色设置

(1) Black、Red、Blue 3 个 RadioButton 是互斥选择的,单击某个 RadioButton 时,设置文字的颜色,虽然可以采用可视化设计的方式设计其clicked()信号的槽函数,但这样就要生成三个槽函数。此处使用3个RadioButton 的 clicked()信号关联到这一个槽函数方式简化设计。

.h中添加: 

 

(2)可以通过鼠标停留在槽函数上,“Alt+Enter“的方式在.cpp中添加对应的实现及代码。

(3)由于这个槽函数是自定义的,所以不会自动与 RadioButton 的 clicked()事件关联,此时编译后运行程序不会实现改变字体颜色的功能。需要在构造函数中手工进行关联,代码如下:

4、三个按钮的功能设计

(1)功能实现

界面上还有“确定”“取消”“退出”3 个按钮,这是在对话框中常见的按钮。“确定”表示确认选择并关闭对话框,“取消”表示取消选择并关闭对话框,“退出”则直接关闭对话框。

Dialog 是从 QDialog 继承而来的,QDialog 提供了 accept0、reject0)、close0等槽函数来表示这三种状态,只需将按钮的 clicked0信号与相应槽函数关联即可。

下面采用可视化的方式,将按钮的 clicked0)信号与这些槽函数关联起来。在 UI 设计器里,单击上方工具栏里的“Edit Signals/Slots”按钮,窗体进入信号与槽函数编辑状态。将鼠标移动到“确定”按钮上方,再按下鼠标左键,移动到窗体的空白区域释放左键,这时出现如下图 所示的关联设置对话框,并进行“确定”按钮的关联。

注意:右侧列表框中没有 close()槽函数,需要勾选下方的“Show signals and slots inherited from QWidget”才会出现 close()函数。

同样的方法可以将 btnCancel 的 clicked()信号与 QWDialog 的 reject()槽函数关联,将 btnClose的 clicked()信号与 QWDialog 的 close()槽函数关联。

效果如下图所示:

  1. 原理介绍

设置完 3 个按钮的信号与槽关联之后,在窗体下方的 Sigmals 和 Slots 编辑器里也显示了这3个关联。实际上,可以直接在 Signals 和 Slots 编辑器进行关联设置。现在编译并运行程序,单击这3个按钮都会关闭程序。

那么,这 3个按钮的信号与槽函数的关联是在哪里实现的呢?答案在 setupUi0函数里,在setupUi()函数里自动增加了以下3行代码:

QObject::connect(rBtnOK, SIGNAL(clicked()), QWDialog, SLOT(accept()));

QObject::connect(rBtnClose, SIGNAL(clicked()), QWDialog, SLOT(reject()));

QObject::connect(rBtnCancel, SIGNAL(clicked()), QWDialog, SLOT(close()));

三、代码化UI设计

由于界面设计的底层其实都是由 C++语言实现的,底层实现的功能比可视化设计更加强大和灵活。某些界面效果是可视化设计无法完成的,或者某些人习惯了用纯代码的方式来设计界面,就可以采用纯代码的方式设计界面,如 Qt 自带的实例基本都是用纯代码方式实现用户界面的。

实现效果

框架主要由构造函数中的以下程序实现:

    iniUI(); //界面创建与布局

    iniSignalSlots(); //信号与槽的关联

    setWindowTitle("Form created manually");//设置窗体标题

  1. 界面创建

  1. 信号与槽

  1. 编译问题

出现如下错误

解决方案:QT使用MSVC编译时,出现常量中有换行符 语法错误:缺少 等问题_qt5.15.2 msvc2019编译报错: 常量中有换行符_PuddingHouChou的博客-CSDN博客

原来编译器为MSVC2015 64bit

更换编译器MinGW 64-bit

编译成功

四、混合方式UI设计

1、设计目的

采用纯代码方式进行 UI 设计虽然无所不能,但是设计效率太低,过程非常繁琐,而可视化UI设计简单高效。所以,能用可视化设计的就尽可能用可视化设计解决,无法解决的再用纯代码方式,将两种方法结合,才是高效设计 UI的方法。

本节用一个实例讲解如何用混合方式创建 UI,即部分界面设计用 UI 设计器可视化实现,部分无法在 UI 设计器里实现的界面设计用代码实现。同时用这个实例讲解如何使用资源文件、如何使用Actions,如何设计主窗口里的菜单、工具栏和状态栏,这些是一般应用程序主窗口都有的功能。

本项目的窗口类是从QMainWindow 继承而来的,具有主菜单、工具栏和状态栏。这个例子实现了主菜单、主工具栏和状态栏,中间工作区里是一个 TextEdit 组件,用于编辑文本。下图是在 UI设计器里设计完成的窗口。

本项目的窗口类是从QMainWindow 继承而来的,具有主菜单、工具栏和状态栏。这个例子实现了主菜单、主工具栏和状态栏,中间工作区里是一个 TextEdit 组件,用于编辑文本。下图是在 UI设计器里设计完成的窗口。

我们希望在工具栏上添加一个 SpinBox 组件,用于设置字体大小,还想在工具栏上添加一个FontComboBox组件,用于选择字体名称。但是在 UI设计器里,将这些组件拖放到工具栏上时,会显示不能添加到工具栏上。同样,在窗体下方的状态栏上也不能直接添加 Label 和ProgressBar 组件。这就是 UI可视化设计的局限,无法实现某些界面效果。

通过编写代码解决了上面的问题,并且在状态栏添加了显示功能,实现期望的界面效果,下图就是最终界面效果

2、创建项目并添加资源文件

(1)创建项目

创建一个 Widget Application 项目,在向导的创建窗口类时,选择基类QMainWindow,新建类的名称设置为QWMainWind,并选择生成窗体。

(2)添加资源文件

项目中需要使用到图标,Qt中是将图标存储在资源文件中,添加资源文件的过程如下面这些图所示:

单击选择

单击下一步

单击完成

添加前缀,前缀类似于资源的分组

单击添加文件

进入images,并ctrl+A 全选

单击打开

然后保存编译后就可以得到下图框架

此时源代码文件夹内如下图

缺少图标文件?

3、设计Action

QAction 是一个非常有用的类,在界面设计时创建 Action,并编写其 trigger()信号的槽函数。使用设计的 Action 可以创建菜单项、工具栏按钮,还可以设置为 QToolButton 按钮的关联 Action。点击这些由 Action 创建的菜单项、按钮就是执行 Action 的槽函数

按照如下步骤,在.ui下的Action编辑器中创建Action列表

输入文本和对象名称,快捷方式方框执行ctrl+c

选择图标中…键,点击image/120.bmp作为复制图标

单击确定—确定,显示复制复制

依次添加剪切,粘贴,粗体,斜体,下划线,退出,打开,清空,字体,新建,显示工具栏文字标签

4、设计菜单和工具栏

建立 Action 之后,就可以在主窗体上设计菜单和工具栏了。本项目的窗体类 QWMainWind是从QMainWindow 继承的,具有菜单栏、工具栏和状态栏。

双击项目文件目录树里的 qwmainwind.ui,在 UI 设计器里打开此窗口。在窗口最上方显示“Type Here”的地方是菜单栏,菜单栏下方是工具栏,窗口最下方是状态栏。

  1. 创建菜单栏

在菜单栏显示“Type Here”的地方双击,出现一个编辑框,在编辑框里输入所要设计菜单的分组名称,如“文件”,然后回车,这样就创建了一个“文件”菜单分组,同样可以创建“编辑”“格式”分组。

创建主菜单的分组后,从Action 编辑器的列表里将一个 Action 拖放到菜单某个分组下,就可以创建一个菜单项,如同在界面上放置一个组件一样。如果需要在菜单里增加一个分隔条,双击“Add Seperator”就可以创建一个分隔条,然后拖动到需要的位置即可。如果需要删除某个菜单项或分隔条,单击右键,选择“Remove”菜单项。菜单设计的结果如下图

(2)创建工具栏

设计工具栏的方式也很相似。将一个 Action 拖放到窗口的工具栏上,就会新建一个工具栏按钮。同样可以在工具栏上添加分隔条,可以移除工具栏按钮。主窗口上初始的只有一个工具栏,如果需要设计多个工具栏,在主窗口上单击右键,单击“Add Tool Bar”即可新建一个工具栏。

工具栏上按钮的显示方式有多种,只需设置工具栏的 toolButtonStvle 属性,具体详见《Qt5.9 c++开发指南》,得到的界面如下图所示

(3)放置QTextEdit

在窗体上放置一个QTextEdit 组件,其 objectname 设置为 txtEdit。如此就完成了菜单栏、工具栏的可视化设计。如下图

(4)可视化界面的底层实现

可视化设计的界面的底层实现是由ui qwmainwind.h 文件实现的。打开ui gwmainwind.h文件,可以看到ui_qwmainwind.h中定义了类 Ui_QWMainWind。在 public 部分定义了界面所有 Action 和各种组件的指针变量,在 setupUi()函数里依次创建所有的 Action 和界面组件,然后将 Action 添加到主菜单和工具栏上。可以想象如果都采用代码的方式去编写,会花费很长的时间。

5、代码创建其他界面组件

在工具栏上增加一个 SpinBox 用于设置字体大小,增加一个 FontComboBox 来选择字体。当从组件面板里拖放一个 SpiBox 到工具栏上时,却发现工具栏“拒收”,同样,在状态栏上放一个Label 和一个 ProgreeBar 也是被“拒收”的。这是 U设计器的局限性,某些界面效果无法用可视化设计方式实现。

iniUI()函数一定要在 ui->setupUi(this)之后再调用,两行语句的先后顺序不能调换。因为 ui->setupUi(this)实现了可视化设计的界面的创建,iniUI()是在可视化创建的界面基础之上添加其他组件,所以必须在其后面调用。

6、Action的功能实现

Action 是一种不可见的界面元素,主要用于菜单项、工具栏按钮的设计。Action 的主要信号是 trigger(),为一个Action 的 trigger()信号编写槽函数之后,菜单和工具栏上由此 Action 创建的菜单项和工具栏按钮就都关联此槽函数。

(1)编辑功能Action的实现

用于编辑的 Action 有剪切、复制、粘贴和清除的功能,以便对 txtEdit 组件进行相应的操作。而QTextEdit 类就有相应的槽函数,无需再编写实现代码,只需将 Action 的 trigger()信号与 txtEdit 的相应槽函数进行关联即可。在 Signals 和 Slots 编辑器里设置信号与槽的关联,设计好后的几个 Action 的关联如下图 所示。

  1. 其他 Action 的功能实现

Action 的主要信号是 trigger()和 trigger(bool),单击菜单项或工具栏按钮时发射。

用于设置粗体、斜体和下划线的 3 个 Action 具有 Checkable 属性,选择 trigger(bool)信号设计槽函数更合适,该信号会将 Action 的复选状态作为一个参数传递,在响应代码里可以直接使用。其他Action 可选择 trigger()信号生成槽函数。在 Action 的“Go to slot”对话框,选择信号为Action创建槽函数。

因为不小心删除信号与槽的操作界面,右键单击,显示如下

将Signals and Slots Editor勾选上

7、代码创建的组件的信号与槽

在界面上还有两个用代码创建的组件,用于设置字体大小的 spinFontSize 和用于设置字体的comboFont,这两个组件的信号与槽的功能实现显然不能通过前面所述的可视化的方法来实现。对于代码创建的组件需要利用代码编写槽函数,并将信号与槽关联起来。

为此需要在QWMainWind类中定义两个槽函数,以及用于进行信号与槽关联的函数 iniSignalSlots0,该函数在构造函数里调用。

最终代码见下。

8、最终的功能实现代码

9、为应用程序设置图标

用Qt Creator 创建的项目编译后的可执行文件具有默认的图标,如果需要为应用设置一个自己的图标,其操作很简单,只需两步。

将一个图标文件(必须是“ico”后缀的图标文件) 复制到项目源程序目录下

在项目配置文件即.pro文件中里用 RC_ICONS 设置图标文件名,添加下面一行代码。

其中,“AppIcon.ico”就是复制到项目源程序目录下的图标文件名称。这样设置后,编译后生成的可执行文件,以及主窗口的图标就换成设置的图标了。

:-1: error: [Makefile.Debug:72: debug/QtIcon_resource_res.o] Error 1 原因与彻底解决方案 | 码农家园

缺少.ico文件,添加AppIcon.ico图标,可以复制别的图标,名称保持一致即可。

五. 源码下载

上述提到的实例的源码,有需要的可以https://download.csdn.net/download/xia19920812/88346883进行下载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值