✥2-GUI应用程序设计基础
Posted itzyjr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了✥2-GUI应用程序设计基础相关的知识,希望对你有一定的参考价值。
Application->Qt Widget Application
- 项目组织文件MyWidgets.pro,存储项目设置的文件。
- 主程序入口文件main.cpp,实现main()函数的程序文件。
- 窗体界面文件widget.ui,一个XML格式存储的窗体上的元件及其布局的文件。
- widget.h是所设计的窗体类的头文件,widget.cpp是widget.h里定义类的实现文件。在C++里,任何窗体或界面组件都是用类封装的,一个类一般有一个头文件和一个源程序文件。
Hello.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \\
main.cpp \\
widget.cpp
HEADERS += \\
widget.h
FORMS += \\
widget.ui
# Default rules for deployment.
qnx: target.path = /tmp/$$TARGET/bin
else: unix:!android: target.path = /opt/$$TARGET/bin
!isEmpty(target.path): INSTALLS += target
“QT += core gui” 表示项目中加入core gui模块。core gui是Qt用于GUI设计的类库模块,如果创建的控制台应用程序,就不需要添加core gui。
如果项目中使用到涉及数据库操作的类,就需要用到sql模块,在.pro文件中增加如下一行:
QT += sql
“greaterThan(QT_MAJOR_VERSION, 4): QT += widgets” 表示当Qt主版本大于4时,才加入widgets模块。
双击widget.ui,打开Qt Designer:
- 组件面板:分为多个组,如Layouts、Buttons、Display Widgets等,界面设计的常见组件都可以在组件面板里找到。
- Signals和Slots编辑器:可视化地进行信号与插槽的关联
- Action编辑器:可视化地设计Action
- 对象浏览器:用树状视图显示窗体上各组件之间的布局包含关系
- 属性编辑器:显示某个选中的组件或窗体的各种属性及其取值
选中拖到待设计的窗体的Label组件:
可以看出QLabel的继承关系是QObject->QWidget->QFrame->QLabel。
objectName表示组件的对象名称,界面上的每个组件都需要一个唯一的对象名称,以便被引用。
如果一个类继承自QLabel,并增加了几个自定义属性,那么在属性编辑器里也会多出几个增加的属性供修改。
标准C++语言里并没有property关键字,property是Qt对标准C++的扩展,使得在Qt Designer里就可以可视化设置类的数据。
为拖入的PushButton(objectName设置为“btnClose”)增加一个功能,就是单击此按钮,关闭窗口(objectName设置为“MyWidget”)。
步骤:Add,Sender选择closeBtn,Signal选择clicked(),Receiver选择窗体MyWidget,Slot选择close()。
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
QApplication a(argc, argv);// 定义并创建应用程序
Widget w;// 定义并创建窗口
w.show();// 显示窗口
return a.exec();// 应用程序运行
QApplication是Qt的标准应用程序类。本例中QApplication类的实例a,就是应用程序对象。
Widget类继承自QWidget,它是本实例设计的窗口的类名,定义此窗口后再用w.show()显示此窗口。
a.exec()启动应用程序的执行,开始应用程序的消息循环和事件处理。
编译后在项目目录下会自动生成一个文件“ui_widget.h”。
- widget.h:定义窗体类的头文件,定义了类Widget
- ui_widget.h:编译后,根据窗体上的组件及其属性、信号与插槽的关联等自动生成的一个类的定义文件,类名称是Ui_MyWidget
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui
class MyWidget;
QT_END_NAMESPACE
class Widget: public QWidget
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::MyWidget *ui;
;
#endif
(1) namespace声明
这是声明了一个名称为Ui的命名空间,包含一个类MyWidget;这个类是ui_widget.h文件里定义的类,用于描述界面组件的。这个声明相当于一个外部类型声明。
(2) Widget类的定义
widget.h文件的主体部分是一个继承于QWidget的类Widget的定义,也是本实例的窗体类。
在Widget类中使用宏Q_OBJECT,这是使用Qt的信号与槽(signal and slot)机制的类都必须加入的一个宏。
在public部分定义了Widget类的构造函数和析构函数;在private部分又定义了一个指针Ui::MyWidget *ui;
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::MyWidget)
ui->setupUi(this);
Widget::~Widget()
delete ui;
注意到,这个文件的包含文件部分自动加入了#include “ui_widget.h”,这就是Qt编译生成的与UI文件widget.ui对应的类定义文件。
其中,构造函数的意义是:执行父类QWidget的构造函数,创建一个Ui::MyWidget类的对象ui。
构造函数里只有一行语句,它是执行Ui::MyWidget类的setupUi()函数,这个函数实现窗口的生成与各种属性的设置、信号与槽的关联。
析构函数只是简单地删除用new创建的指针ui。
ui_widget.h
/********************************************************************************
** Form generated from reading UI file 'widget.ui'
**
** Created by: Qt User Interface Compiler version 6.2.4
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_WIDGET_H
#define UI_WIDGET_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_MyWidget
public:
QLabel *label;
QPushButton *btnClose;
void setupUi(QWidget *qWidget)
if (qWidget->objectName().isEmpty())
qWidget->setObjectName(QString::fromUtf8("MyWidget"));
qWidget->resize(353, 158);
label = new QLabel(qWidget);
label->setObjectName(QString::fromUtf8("label"));
label->setGeometry(QRect(130, 20, 101, 16));
QFont font;
font.setPointSize(12);
font.setBold(true);
label->setFont(font);
btnClose = new QPushButton(qWidget);
btnClose->setObjectName(QString::fromUtf8("btnClose"));
btnClose->setGeometry(QRect(260, 130, 80, 18));
retranslateUi(qWidget);
QObject::connect(btnClose, &QPushButton::clicked, qWidget, qOverload<>(&QWidget::close));
QMetaObject::connectSlotsByName(qWidget);
void retranslateUi(QWidget *qWidget)
qWidget->setWindowTitle(QCoreApplication::translate("MyWidget", "Widget", nullptr));
label->setText(QCoreApplication::translate("MyWidget", "Hello World", nullptr));
btnClose->setText(QCoreApplication::translate("MyWidget", "Close", nullptr));
;
namespace Ui
class MyWidget: public Ui_MyWidget ;
QT_END_NAMESPACE
#endif
(1) 定义了一个类Ui_Widget,用于封装可视化设计的界面。
(2) 自动生成了界面各个组件的类成员变量定义。在public部分为界面上每个组件定义了一个指针变量,变量的名称就是设置的objectName。
(3) 定义了setupUi()函数,这个函数用于创建各个界面组件,并设置其位置、大小、文字内容、字体等属性,设置信号与槽的关联。
setupUi()调用了函数retranslateUi(qWidget),用来设置界面组件的文字内容属性,如标签的文字、按键的文字、窗体的标题等。将界面上的文字设置的内容独立出来作为一个函数,在设计多语言界面时会用到这个函数。
(4) QObject::connect(btnClose, &QPushButton::clicked, qWidget, qOverload<>(&QWidget::close));调用connect()函数,将在UI设计器里设置的信号与槽的关联转换为语句。这里将btnClose按键的clicked()信号与窗体qWidget的close()槽函数关联起来。这样,当单击btnClose按钮时,就会执行qWidget的close()槽函数,而close()槽函数的功能是关闭窗口。
QMetaObject::connectSlotsByName(qWidget);这句是设置槽函数的关联方式,用于将UI设计器自动生成的组件信号的槽函数与组件信号相关联。
(5) 定义namespace UI,并定义一个从Ui_MyWidget继承的类MyWidget。
Application->Qt Widget Application
通过UI Designer来完成下表给出的组件及属性:
类层次关系:
需要注意以下几点:
(1) objectName是窗体上创建的组件的【实例名称】,界面上的每个组件需要有一个唯一的objectName,程序里访问界面组件时是通过其objectName进行访问,自动生成的槽函数名称里也有objectName。所以,组件的objectName需要在设计程序之前设置好,设置好之后一般不要再改动。若设计程序之后再改动objectName,涉及的代码需要相应的改动。
(2) 窗体的objectName就是窗体的类名称,在UI设计器里不要修改窗体的objectName,窗体的实例名称需要在使用窗体的代码里去定义。
组件面板上用于而已的组件:
UI Designer工具栏各按钮的功能:
当组件都拖动到窗体上后,改变窗体大小,界面上的各组件却并不会自动改变大小。随后还需为窗体指定一个总布局;选中窗体(即不要选择任何组件),单击工具栏上的“Lay Out Vertically”按钮,使4组组件垂直分布;这样布局后,当窗体大小改变时,各组件都会自动改变大小。
在UI设计器里可视化设计布局时,要善于利用水平和垂直空格组件,善于设置组件的最大、最小宽度和高度来实现某些需要的布局效果。
伙伴关系(Buddy)是指界面上一个Label和一个组件相关联。在伙伴关系编辑状态下,单击一个Label,按住鼠标左键,然后拖向一个组件,就建立了Label和组件之间的伙伴关系。
伙伴关系是为了在程序运行时,在窗体上用快捷键快速将输入焦点切换到某个组件上。如,设定“姓名”标签的Text属性为“姓名(&N)”,其中符号“&”用来指定快捷字符,界面上并不显示“&”,这里指定快捷键字母N。那么在程序运行时,用户按下Alt+N,输入焦点就会快速切换到“姓名”关联的输入框内。
信号与槽
信号(Signal)就是在特定情况下被发射的事件;槽(Slot)就是对信号响应的函数。
槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。
信号与槽关联是用QObject::connect()函数实现的,其基本格式是:
QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
connect()是QObject类的一个静态函数,而QObject是所有Qt类的基类,所以在实际调用时可以省略掉前面的限定符“QObject::”。
sender - 发射信号的对象名称;signal() - 信号名称。
receiver - 接收信号的对象名称;slot() - 槽函数名称。
SIGNAL和SLOT是Qt的宏,用于指明信号和槽,并将它们的参数转换为相应的字符串。
在示例中的ui_widget.h文件中,在setupUi()函数中有如下语句:
QObject::connect(btnClose, SIGNAL(clicked()), qWidget, SLOT(close()));
其作用就是将btnClose按钮的clicked()信号与窗体(Widget)的槽函数close()相关联,这样,当单击btnClose按钮时,就会执行qWidget的close()槽函数。
关于信号与槽的使用,有以下一些规则需要注意:
(1) 一个信号可以连接多个槽
connect(spinNum, SIGNAL(valueChanged(int)), this, SLOT(addFun(int));
connect(spinNum, SIGNAL(valueChanged(int)), this, SLOT(aupdateStatus(int));
当一个对象spinNum的数值发生变化时,所在窗体的两个槽进行响应,一个addFun()用于计算,一个updateStatus()用于更新状态。
(2) 多个信号可以连接同一个槽
connect(ui->rBtnBlue, SIGNAL(clicked()), this, SLOT(setTextFontColor()));
connect(ui->rBtnRed, SIGNAL(clicked()), this, SLOT(setTextFontColor()));
connect(ui->rBtnBlack, SIGNAL(clicked()), this, SLOT(setTextFontColor()));
当任何一个RadioButton被单击时,都会执行setTextFontColor()函数。
(3) 一个信号可以连接另外一个信号
connect(spinNum, SIGNAL(valueChanged(int)), this, SIGNAL(refreshInfo(int));
当一个信号发射时,也会发射另外一个信号,实现某些特殊的功能。
(4) 严格的情况下,信号与槽的参数个数和类型需要一致,至少信号的参数不能少于槽的参数。如果不匹配,会出现编译错误或运行错误。
(5) 在使用信号与槽的类中,必须在类的定义中加入宏Q_OBJECT。
(6) 当一个信号被发射时,与其关联的槽函数通常被立即执行。只有当信号关联的所有槽函数执行完毕后,才会执行发射信号处后面的代码。
单击chkBoxUnder组件,右键调出快捷菜单,单击“Go to slot…”,选择clicked(bool),自动生成on_chkBoxUnder_clicked(bool)的框架,添加三行代码,实现文本框字体下划线控制:
void QWDialog::on_chkBoxUnder_clicked(bool checked)
QFont font = ui->txtEdit->font();
font.setUnderline(checked);
ui->txtEdit->setFont(font);
同理,完成字体的斜体、加粗的控制。
查看编译生成的ui_qwdialog.h文件,发现只是在setupUi()函数里有一条信号与槽关联的语句:
QMetaObject::connectSlotsByName(QWDialog);
以上语句将搜索QWDialog界面上的所有组件,将信号与槽函数匹配的信号和槽关联起来,它假设槽函数的名称是:
void on_<object name>_<signal name>(<signal parameters>);
对于3个RadioButton,也可以采用可视化设计的方式设计其clicked()信号的槽函数,但这样就需要3个槽函数。我们可以简化,即只设计一个槽函数。
在QWDialog类的private slots部分增加一个槽函数定义如下:
void setTextFontColor();
将鼠标光标移动到这个函数的函数名上面,单击右键调出快捷菜单,单击“Refactor”->“Add Definition in qwdialog.cpp”,就会在qwdialog.cpp文件中自动为函数setTextFontColor()生成一个函数框架。
void QWDialog::setTextFontColor()
QPalette plet = ui->txtEdit->palette();
if (ui->rBtnBlue->isChecked())
plet.setColor(QPalette::Text, Qt::blue);
else if (ui->rBtnRed->isChecked())
plet.setColor(QPalette::Text, Qt::red);
else if (ui->rBtnBlack->isChecked())
plet.setColor(QPalette::Text, Qt::black);
else
plet.setColor(QPalette::Text, Qt::white);
ui->txtEdit->setPalette(plet);
在QWDialog的构造函数中手工进行关联:
QWDialog::QWDialog(QWidget *parent): QDialog(parent), ui(new Ui::QWDialog)
ui->setupUi(this);
connect(ui->rBtnBlue, SIGNAL(clicked()), this, SLOT(setTextFontColor()));
connect(ui->rBtnRed, SIGNAL(clicked()), this, SLOT(setTextFontColor()));
connect(ui->rBtnBlack, SIGNAL(clicked()), this, SLOT(setTextFontColor()));
单击上方工具栏的“Edit Signals/Slots”,再按下鼠标左键,移动到窗体空白区域释放。
“确定”按钮(btnOK)对应:clicked(),QWDialog的槽函数accept()。
“取消”按钮(btnCancel)对应:clicked(),QWDialog的槽函数reject()。
“退出”按钮(btnClose)对应:clicked(),QWDialog的槽函数close()。
close()槽函数没有出现在弹出的列表框中,需要勾选“Show signals and slots inherited from QWidget”才会出现close()函数。
在编译生成的ui_qwdialog.h文件中多了3行代码:
QObject::connect(btnOK, SIGNAL(clicked()), QWDialog, SLOT(accept()));
QObject::connect(btnCancel, SIGNAL(clicked())::clicked, QWDialog, SLOT(reject()));
QObject::connect(btnClose, SIGNAL(clicked())::clicked, QWDialog, SLOT(close()));
现在,单击这3个按钮都会关闭程序。
代码化UI设计
Qt自带的实例基本都是用纯代码方式实现用户界面的。
创建一个Widget Application项目,不勾选“Generate form”复选框。创建后的项目文件目录树下没有.ui文件。
QWDlgManual类定义
qwdlgmanual.h
#include <QDialog>
#include <QCheckBox>
#include <QRadioButton>
#include <QPlainTextEdit>
#include <QPushButton>
class QWDlgManual : public QDialog
Q_OBJECT
private:
QCheckBox *chkBoxUnder;
QCheckBox *chkBoxItalic;
QCheckBox *chkBoxBold;
QRadioButton *rBtnBlack;
QRadioButton *rBtnRed;
QRadioButton *rBtnBlue;
QPlainTextEdit *txtEdit;
QPushButton *btnOK;
QPushButton *btnCancel;
QPushButton *btnClose;
void iniUI();// UI创建与初始化
void iniSignalSlots();// 初始化信号与槽的链接
private slots:
void on_chkBoxUnder(bool checked);// Underline的槽函数
void on_chkBoxItalic(bool checked);// Italic的槽函数
void on_chkBoxBold(bool checked);// Bold的槽函数
void setTextFontColor();// 设置字体颜色
public:
QWDlgManual(QWidget *parent = 0);
~QWDlgManual();
;
qwdlgmanual.cpp
#include "qwdlgmanual.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
void QWDlgManual::iniUI()
// 创建 Underline, Italic, Bold三个CheckBox,并水平布局
chkBoxUnder = new QCheckBox(tr("Underline"));
chkBoxItalic = new QCheckBox(tr("Italic"));
chkBoxBold = new QCheckBox(tr("Bold"));
QHBoxLayout *HLay1 = new QHBoxLayout;
HLay1->addWidget(chkBoxUnder);
HLay1->addWidget(chkBoxItalic);
HLay1->addWidget(chkBoxBold);
// 创建 Black, Red, Blue三个RadioButton,并水平布局
rBtnBlack = new QRadioButton(tr("Black"));
rBtnBlack->setChecked(true); // 缺省被选中
rBtnRed = new QRadioButton(tr("Red"));
rBtnBlue = new QRadioButton(tr("Blue"以上是关于✥2-GUI应用程序设计基础的主要内容,如果未能解决你的问题,请参考以下文章