Qt6.0开发 第二章 GUI程序设计基础
第二章 GUI程序设计基础
窗口相关文件
按照第一章所给提示创建一个新project,我们得到了下面的代码:
widget.h:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
main.cpp:
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
上面的内容便是一个完整的新项目的代码部分了.
下面我们将逐文件逐语句对其进行解释:
widget.h
首先对最外层的宏进行分析,不难发现,这是从C语言开始防止重复include的宏设计:
#ifndef WIDGET_H
#define WIDGET_H
//...
#endif // WIDGET_H
include语句将QWidget引入,以便对QWidget类进行继承:
#include <QWidget>
在include语句后又定义了一个继承自QWidget的自定义Widget类.
同时,其还声明了一个名称为Ui的名称空间:
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
注意:此处的Widget不是此文件中的Widget,而是ui_widget中定义的一个类,所以我们区分Ui::Widget是窗口ui类,是为了用于描述可视化设计的窗口界面的.
下面即为Widget的定义了.
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
widget.cpp
在其中,首先就和一般C++多文件程序一样,同名的.cpp文件对包含类声明与形式定义的.h文件进行include.
#include "widget.h"
接着对自定义Widget类的构造函数进行了定义:
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
结合widget.h中的声明:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
解释上面initial-list的代码,有:
:QWidget(parent)//从父QWidget继承,同时默认为nullptr
,ui(new Ui::Widget)//存在一个新分配的Ui::Widget
然后对构造函数函数体进行解释:
ui->setupUi(this);//通过Ui::Widget对自定义Widget的Ui进行装载
在setupUi()中会创建窗口上的所有界面组件,并且以Widget窗口作为所有组建的父容器.
而析构函数中则为简单的释放Ui::Widget对象.
delete ui;
注意:ui_widget.h是UI文件widget.ui被UIC编译后自动生成的文件.该文件中定义了窗口UI类.
#include "ui_widget.h"
main.cpp
开门见山,对自定义widget.h的include:
#include "widget.h"
接着由于函数体中要对QApplication进行初始化,对QApplication进行include:
#include <QApplication>
主函数体本身没有太大变化:
int main(int argc, char *argv[])
{
//do something
}
而其中每一句语句的含义见下:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);//定义并创建应用程序
Widget w;//定义并创建窗口对象
w.show();//显示窗口
return a.exec();//运行应用程序,开始应用程序的消息循环和事件处理
}
界面组件布局管理
Qt的UI设计具有布局(layout)功能.所谓布局,就是指界面上组件的排列方式.
伙伴关系
点击Qt Designer工具栏上的Edit Buddies按钮进入伙伴关系编辑状态.
在伙伴状态下可以选中一个标签,按住鼠标左键将其拖向控件.
将拥有伙伴关系的标签text属性改为<name>+(&+<char>)形式的字串.则在运行时按下alt+<char>便可跳转焦点至相关控件.
Tab顺序
点击Qt Designer工具栏上的Edit Tab Order按钮进入Tab顺序编辑状态.
Tab顺序是指程序在运行时,按下键盘上Tab键时输入焦点的移动顺序.
进入Tab顺序编辑状态后,界面上会显示具有Tab顺序的组件的Tab顺序编号,依次按希望的顺序点击组件就可以重排Tab顺序.
信号与槽
信号(signal): 特定情况下被发射的通知.GUI程序设计的主要工作就是对界面上各组件的信号进行响应.
槽(slot): 是对信号进行响应的函数.槽就是函数,所以也称槽函数.槽函数与一般函数不同的是:槽函数与信号关联,当信号被发射时,关联的槽函数被自动运行.
信号与槽是通过QObject::connect()实现的,使用connect()函数的基本格式为:
QObject::connect(sender, SIGNAL(signal()),receiver,SLOT(slot()));
其中,sender是发射信号的对象的名称;signal()是信号,信号可以看作特殊的函数,需要带有括号,有参数时还需要指名各参数类型;receiver是接收信号的对象的名称;slot()是槽函数,需要带有括号,有参数时还需要指名各参数类型.
SIGNAL和SLOT是Qt的宏,分别用于指明信号和槽函数,并将它们的参数转换为相应的字符串.
关于信号与槽的使用,有下列规则:
-
一个信号可以连接多个槽函数.
-
多个信号可以连接一个槽函数.
-
一个信号可以连接另一个信号,如:
connect(spinNum,SIGNAL(signal_1(int)),this,SIGNAL(signal_2(int)));
-
严格的情况下,信号与槽的参数个数与类型需要一致,至少信号参数不能少于槽参数.
-
在使用信号与槽的类中,必须在类的定义中插入宏Q_OBJECT.
-
当一个信号被发射时,与其关联的槽函数通常被立即运行,只有所有所有关联槽函数运行完,才运行发射信号后的代码.
Qt的界面组件都是从QWidget继承而来的,都支持信号与槽的功能.每个类都有一些内建的信号和槽函数.
信号与槽的使用
如果需要通过Qt Designer可视化设计某个组件的信号的槽函数,选择Qt Designer下侧Action编辑器旁边的 Signals and Slots Editor 进行新增即可.
如果希望手动对信号的槽函数进行定义,而非直接生成在ui_widget.h中.那么可以选择一个组件,然后右键选择 "转到槽..."
实例:第一个Qt应用程序
选择Dialog类开始,且不继承(即不需要ui界面辅助)
Dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QCheckBox>
#include <QPushButton>
#include <QRadioButton>
#include <QPlainTextEdit>
#include <QHBoxLayout>
#include <QVBoxLayout>
class QPlainTextEdit;
class QRadioButton;
class QPushButton;
class QCheckBox;
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = nullptr);
~Dialog();
private:
QCheckBox *chkBoxUl,*chkBoxIt,*chkBoxBf;
QPushButton *btnOk,*btnExit,*btnClear;
QRadioButton *radioBlack,*radioRed,*radioBlue;
QPlainTextEdit *txtEdit;
private slots:
//勾选框
void do_checkBoxUl(bool checked);
void do_checkBoxBf(bool checked);
void do_checkBoxIt(bool checked);
//按钮
void do_pushButtonOk();
void do_pushButtonExit();
void do_pushButtonClear();
//选择框
void do_radioColor();
//测试自定义信号
void do_reactionToSignal();
signals:
void do_checkedRadio();
};
#endif // DIALOG_H
Dialog.cpp
#include "dialog.h"
void Dialog::do_checkBoxUl(bool checked)
{
QFont font= txtEdit->font();
font.setUnderline(checked);
txtEdit->setFont(font);
return;
}
void Dialog::do_checkBoxIt(bool checked)
{
QFont font= this->txtEdit->font();
font.setItalic(checked);
this->txtEdit->setFont(font);
return;
}
void Dialog::do_checkBoxBf(bool checked)
{
QFont font= this->txtEdit->font();
font.setBold(checked);
this->txtEdit->setFont(font);
return;
}
void Dialog::do_pushButtonOk()
{
emit do_checkedRadio();
//this->accept();
return;
}
void Dialog::do_pushButtonExit()
{
this->close();
return;
}
void Dialog::do_pushButtonClear()
{
txtEdit->clear();
return;
}
void Dialog::do_radioColor()
{
QPalette plet= txtEdit->palette();
if(radioBlack->isChecked())
plet.setColor(QPalette::Text,Qt::black);
if(radioRed->isChecked())
plet.setColor(QPalette::Text,Qt::red);
if(radioBlue->isChecked())
plet.setColor(QPalette::Text,Qt::blue);
txtEdit->setPalette(plet);
return;
}
void Dialog::do_reactionToSignal()
{
QPalette plet= txtEdit->palette();
plet.setColor(QPalette::Text,Qt::green);
txtEdit->setPalette(plet);
return;
}
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
//新建竖向布局
QVBoxLayout *VLay= new QVBoxLayout;
//勾选框
chkBoxUl= new QCheckBox("下划线");
chkBoxIt= new QCheckBox("倾斜体");
chkBoxBf= new QCheckBox("加粗体");
QHBoxLayout *HLay1= new QHBoxLayout;
HLay1->addWidget(chkBoxUl);
HLay1->addWidget(chkBoxIt);
HLay1->addWidget(chkBoxBf);
connect(chkBoxUl,SIGNAL(clicked(bool)),this,SLOT(do_checkBoxUl(bool)));
connect(chkBoxIt,SIGNAL(clicked(bool)),this,SLOT(do_checkBoxIt(bool)));
connect(chkBoxBf,SIGNAL(clicked(bool)),this,SLOT(do_checkBoxBf(bool)));
//选择框
radioBlack= new QRadioButton("黑色");
radioRed= new QRadioButton("红色");
radioBlue= new QRadioButton("蓝色");
QHBoxLayout *HLay2= new QHBoxLayout;
HLay2->addWidget(radioBlack);
HLay2->addWidget(radioRed);
HLay2->addWidget(radioBlue);
connect(radioBlack,SIGNAL(clicked()),this,SLOT(do_radioColor()));
connect(radioRed,SIGNAL(clicked()),this,SLOT(do_radioColor()));
connect(radioBlue,SIGNAL(clicked()),this,SLOT(do_radioColor()));
//按钮
btnOk= new QPushButton("确定");
btnClear= new QPushButton("清除");
btnExit= new QPushButton("退出");
QHBoxLayout *HLay3= new QHBoxLayout;
HLay3->addWidget(btnOk);
HLay3->addWidget(btnClear);
HLay3->addWidget(btnExit);
connect(btnOk,SIGNAL(clicked()),this,SLOT(do_pushButtonOk()));
connect(btnClear,SIGNAL(clicked()),this,SLOT(do_pushButtonClear()));
connect(btnExit,SIGNAL(clicked()),this,SLOT(do_pushButtonExit()));
//文本栏
txtEdit= new QPlainTextEdit;
txtEdit->setPlainText("Qt6.0 学习\n第二章 项目二");
//测试自定义信号函数
connect(this,SIGNAL(do_checkedRadio()),this,SLOT(do_reactionToSignal()));
VLay->addLayout(HLay1);
VLay->addLayout(HLay2);
VLay->addWidget(txtEdit);
VLay->addLayout(HLay3);
//布局应用
setLayout(VLay);
}
Dialog::~Dialog() {}
main.cpp则默认即可.
最后,最重要的建议:去看视频跟着学,里面很多书里没有的操作https://www.bilibili.com/video/BV1km4y1k7CW