一、工具

1 什么是 Qt

Qt 是一个跨平台的 C++图形用户界面应用程序框架。

它为应用程序开发者提供建立艺术级图形界面所需的所有功能。

它是完全面向对象的,很容易扩展,并且允许真正的组件编程。

2 Qt 的优点

跨平台,几乎支持所有的平台

接口简单,容易上手,学习 QT 框架对学习其他框架有参考意义。一定程度上简化了内存回收机制

开发效率高,能够快速的构建应用程序。

有很好的社区氛图,市场份额在缓慢上升。

可以进行嵌入式开发。

3 介绍QT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1. QT主要用于图形化界面的开发, QT是基于C++编写的一套界面相关的类库,
进程线程库,网络编程的库,数据库操作的库,文件操作的库.....

2. 学习QT,
掌握QT中的类库是做什么,如何使用这个类库
类库实例化对象(构造函数) --> 学习类库中方法(函数)的使用 --> 后台逻辑的实现

3. QT是一个跨平台的GUI图形化界面开发工具

4. QT的使用场合
汽车仪表盘
打印机
医疗器械
自动化的大型设备

5. QT的优点
1.跨平台,具有较为完备的图形开发库,你所能想到的的图形的实现基本都有,比window的MFC的库更强大。所以很多之前做桌面开发用MFC的都转了Qt。
2.接口的封装性好,易上手,学习QT框架对学习计算机图形框架有很重要的参考意义。
3.Qt内部基本上有一套自己的简易好用的内存回收机制,对提高C++水平有帮助。
4.有很好的社区环境,市场份额在缓慢上升。
5.轻量级的开发环境,可以做嵌入式开发

4 QT工具介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1. Assistant  ---> QT类库的帮助手册的工具

2. Designer ---> 用来设计图形化界面
对应的界面文件为***.ui (ui文件中的内容是一种标记性的语言)

3. uic.exe ---> 将***.ui文件转换为标准的C++的代码 ui_***.h
C:\Qt\5.15.2\mingw81_64\bin\uic.exe

在cmd终端下输入以下命令:
C:\Qt\5.15.2\mingw81_64\bin\uic.exe designer.ui -o ui_designer.h

4. moc.exe ---> 元对象编辑器工具
C:\Qt\5.15.2\mingw81_64\bin\moc.exe
将QT中非标准的信号和槽,转换为标准的C++的代码

5. rcc.exe ---> 资源管理器
C:\Qt\5.15.2\mingw81_64\bin\rcc.exe
将QT资源文件(图片,音频文件,视频文件),转换为标准的C++代码

6. qmake ---> 工程管理的工具
QT工程文件的后缀为***.pro工程文件,
qmake工具可以根据***.pro文件,生成Makefile文件,
通过Makefile文件编译C++的代码。

7. QTcreater --> QT集成开发环境工具(IDE)
将上边的所有的工具都集成到一起了。

二、如何新建一个项目工程

img

img

img

img

img

img

img

三、各个文件中的相关介绍

项目工程文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#项目工程文件


QT += core gui


greaterThan(QT_MAJOR_VERSION, 4): QT += widgets


CONFIG += c++11


# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS


# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0


SOURCES += \
main.cpp \
mywidget.cpp


HEADERS += \
mywidget.h


# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "mywidget.h"


MyWidget::MyWidget(QWidget *parent) //构造函数的实现
: QWidget(parent)
{


}


MyWidget::~MyWidget() //析构函数的实现
{
}




头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef MYWIDGET_H
#define MYWIDGET_H


#include <QWidget>


class MyWidget : public QWidget
{
Q_OBJECT //宏 用于信号和槽


public:
MyWidget(QWidget *parent = nullptr); //构造函数
~MyWidget();//析构函数
};
#endif // MYWIDGET_H


main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include "mywidget.h" //包含了 我的窗口 头文件


#include <QApplication> //包含了应用程序类的头文件


//主函数 编译的入口 argc:命令行变量的个数 argv:命令行变量的列表
int main(int argc, char *argv[])
{
QApplication a(argc, argv); //应用程序类 实例化一个a对象 该对象在程序中有且仅有一个


//我的窗口类 实例化一个w对象
MyWidget w; //自动调用构造函数(无参)


//窗口默认不显示 需要调用show显示
w.show();




//是消息进入阻塞状态
return a.exec();
}




四、 Qt中的信息调试类 (输出类) QDebug

1
2
3
4
5
6
7
8
9
10
11
//1.类似与printf
qDebug("%s","hello kittiy");


//2. 类似与cout 默认有换行 比较常用的方式
qDebug() << "你好" ;


//3. qt中的字符串 是 QString ,其中有一个成员函数 arg比较厉害
//QString s = QString("%1,%2,%3").arg(100).arg(200).arg(300);
qDebug() << QString("%1,%2,%3").arg(100).arg(200).arg(300);

img

五、窗口的相关设置

5.1 窗口的尺寸、标题、背景颜色等设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
{


//设置窗口标题
this->setWindowTitle("我的窗口");


//设置窗口图标
this->setWindowIcon(QIcon("C:\\Users\\admin\\Desktop\\pictrue\\qq.png"));
//this->setWindowIcon(QIcon("C:/Users/admin/Desktop/pictrue/qq.png"))


//设置背景颜色
//this->setStyleSheet("background-color:pink");
this->setStyleSheet("background-color:rgb(255,255,255)");


//重新设置窗口大小
this->resize(540,410);


}

5.2 求出当前窗口的大小

1
2
3
4
5
6
7
qDebug() << this->size() << endl; //输出 宽、高
qDebug() << this->geometry().width() << endl; //宽
qDebug() << this->geometry().height() << endl; //高
qDebug() << this->width() << endl;
qDebug() << this->height() << endl;
qDebug() << width() << endl;
qDebug() << height() << endl;

六、常用类以及组件的使用

6.1 按钮类 QPushBtutton

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
{
//=======创建第一个按钮======
QPushButton *btn1 = new QPushButton;
//btn1->show(); //show会使组件独立显示


//如果想让一个组件,依赖于窗口显示,则需要对其设置父组件
btn1->setParent(this);
//给按钮设置文本
btn1->setText("第一个按钮");
//给按钮设置背景颜色
btn1->setStyleSheet("background-color:pink");
//给按钮设置图标
btn1->setIcon(QIcon("C:\\Users\\admin\\Desktop\\pictrue\\qq.png"));


//======创建第二个按钮======
QPushButton *btn2 = new QPushButton("第二个按钮",this);
//移动按钮
btn2->move(200,100);
//重新设置大小
btn2->resize(100,50);


//======创建第三个按钮======
QPushButton *btn3 = new QPushButton(QIcon("C:\\Users\\admin\\Desktop\\pictrue\\qq.png"),"第三个按钮",this);


btn3->move(300,300);


//将按钮设置成不可以状态
btn3->setEnabled(false);
}

img

6.2 行编辑器类 QLineEdit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//创建第一个行编辑器
QLineEdit *edit1 = new QLineEdit;
//指定父组件
edit1->setParent(this);
//设置显示模式 密码
edit1->setEchoMode(QLineEdit::Password);


//创建第二个行编辑器
QLineEdit *edit2 = new QLineEdit("张三",this);
edit2->move(200,300);


//创建第三个行编辑器
QLineEdit *edit3 = new QLineEdit(this);
//设置占位
edit3->setPlaceholderText("账号");
edit3->move(100,100);


}

img

6.3 标签类 QLabel

标签一般用来显示图像或者文本的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->resize(600,400);
this->setFixedSize(600,400);

//创建一个标签
QLabel *lab1 = new QLabel;
lab1->setParent(this);
lab1->setText("我是一个标签");
lab1->resize(100,200);
lab1->setStyleSheet("background-color:green");

//创建第二个标签
QLabel *lab2 = new QLabel("标签",this);
lab2->move(200,200);
lab2 ->resize(100,100);
//设置图片
lab2->setPixmap(QPixmap("C:\\Users\\admin\\Desktop\\pictrue\\logo.png"));
//让图片自动适应于标签
lab2->setScaledContents(true);

}

img

七、对象树

qt引以为傲的核心机制:对象树、信号和槽、事件机制

当一个组件指定父组件(父对象)时,就可以不用管它的释放操作,因为父对象会将子对象丢到对象树上,父对象在释放之前,会确保子对象释放后,再释放自己。

注意:对象之间的父子关系 和 继承之间的父子关系 不是一回事

img

一定程度上简化了内存回收机制。

八、帮助手册的使用

img

登录界面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include "widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//====================窗口相关设置
this->setWindowTitle("QQ快聊");
this->setWindowIcon(QIcon("C:\\Users\\admin\\Desktop\\pictrue\\qq.png"));


this->setWindowFlag(Qt::FramelessWindowHint);


this->resize(540,410);


this->setStyleSheet("background-color:white");


//====================标签相关设置
QLabel *lab1 = new QLabel(this);
lab1->resize(540,160);
lab1->setStyleSheet("background-color:green");


//动图类 QMovie
QMovie *mv = new QMovie("C:\\Users\\admin\\Desktop\\pictrue\\qq.gif");
mv->setParent(this);
//将动图设置到标签中
lab1->setMovie(mv);
//让动图动起来
mv->start();
lab1->setScaledContents(true);


QLabel *lab2 = new QLabel(this);
lab2->resize(35,35);
lab2->move(120,210);
lab2->setPixmap(QPixmap("C:\\Users\\admin\\Desktop\\pictrue\\wodepeizhenshi.png"));
lab2->setScaledContents(true);


QLabel *lab3 = new QLabel(this);
lab3->resize(35,35);
lab3->move(120,250);
lab3->setPixmap(QPixmap("C:\\Users\\admin\\Desktop\\pictrue\\passwd.jpg"));
lab3->setScaledContents(true);






//===============行编辑器相关设置
QLineEdit *edit1 = new QLineEdit(this);
edit1->move(165,210);
edit1->resize(270,35);
edit1->setPlaceholderText("QQ号/手机号码/邮箱");


QLineEdit *edit2 = new QLineEdit(this);
edit2->move(165,250);
edit2->resize(270,35);
edit2->setPlaceholderText("密码");
edit2->setEchoMode(QLineEdit::Password);


//================按钮相关设置
QPushButton *btn = new QPushButton("登录",this);
btn->move(120,290);
btn->resize(315,45);
btn->setStyleSheet("background-color:rgb(31,200,253);color:white;border-radius:10px");




}


Widget::~Widget()
{
}




img

九、QMainWindow中常用的类

继承于QMainWindow类,原因该类提供了QWidget没有提供的成员函数。

菜单栏、工具栏、状态栏、浮动窗口(铆接部件)、核心部件

9.1 菜单栏 QMenuBar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
this->resize(600,400);
this->setFixedSize(600,400);


//创建菜单栏 QMenuBar 最多只能有一个
QMenuBar *mbar = menuBar();
//将菜单栏放入窗口中
this->setMenuBar(mbar);
//往菜单栏里增加菜单
QMenu *file = mbar->addMenu("文件");
QMenu *edit = mbar->addMenu("编辑");
//往菜单中增加菜单项
file->addAction("新建");
//增加分隔符
file->addSeparator();
file->addAction("打开");


}

9.2 工具栏 QToolBar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//创建工具栏 QToolBar  可以有多个
QToolBar *tbar = new QToolBar(this);
//将工具栏放入窗口中 设置停靠区域
this->addToolBar(Qt::LeftToolBarArea,tbar);
//后期设置 只允许左右停靠
tbar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);
//不允许浮动
tbar->setFloatable(false);
//往工具栏中增加工具项
tbar->addAction("欢迎");


//实例化一个按钮
QPushButton *btn = new QPushButton(this);
btn->setText("编辑");
//将按钮放入工具栏中
tbar->addWidget(btn);


9.3 状态栏 QStatusBar

1
2
3
4
5
6
7
8
9
10
11
//创建状态栏 QStatusBar  最多只能有一个
QStatusBar *sbar = statusBar();
//将状态栏放入窗口中
this->setStatusBar(sbar);


//实例化一个标签
QLabel *lab = new QLabel("问题",this);
//将标签放入状态栏中
//sbar->addWidget(lab);//默认从左边显示
sbar->addPermanentWidget(lab); //默认从右边显示

9.4 浮动窗口 QDockWidget

1
2
3
4
5
6
7
8
9
10
11
12
//创建浮动窗口  QDockWidget 可以有多个
QDockWidget *dock = new QDockWidget("浮动窗口",this);
//将浮动窗口放入界面中
this->addDockWidget(Qt::BottomDockWidgetArea,dock);
//后期设置 只允许左右停靠
dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);


//创建中心部件 最多只能有一个
QTextEdit *text_edit = new QTextEdit(this);
//将中心部件放入窗口中
this->setCentralWidget(text_edit);

img

十、ui界面文件

1> ui界面文件是在项目工程文件的Forms文件夹下(XXX.ui)

2> ui界面上可以通过ui指针访问到ui界面上的所有组件。

img

十一、资源文件的添加

1> 目的

考虑项目的兼容性,将自己发布的软件或者将项目拷贝给别人,能够保证项目可以正常运行起来。

2> 资源文件添加的步骤:

1> 将资源文件放入项目中

img

img

2> 给qt项目增加一个资源文件,存放资源

img

img

img

img

img

3> 锤子锤一下(编译)

使用ui界面,升级优化登录界面

十二、信号与槽

qt以引为傲的核心机制:对象树、信号与槽、事件机制

1> 信号和槽是qt的核心机制之一,可以实现多个组件之间的通信

2> 信号:信号函数。信号定义在类体的signals权限下,信号函数是一个不完整的函数,只需要声明,不需要实现。

3> 槽:槽函数。槽定义在类体的slots权限下,槽函数是一个完整的函数,既要有声明,也要有定义。该函数可以当成普通函数使用。

4> 任意一个组件都有系统提供的信号和槽。

12.1 带有信号和槽的类体定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Widget : public QWidget
{
Q_OBJECT //宏 信号和槽


public:
Widget(QWidget *parent = nullptr);
~Widget();



signals: //信号权限,该权限下函数 都是信号
void my_signal(); //只需要声明 不需要实现


public slots: //槽函数权限 ,该权限下函数 都是槽函数
void my_slot(); //只需要声明 也要实现


private:
Ui::Widget *ui;
};
#endif // WIDGET_H

12.2 信号和槽的连接方式

1)基于ui界面上的连接,在ui界面下的信号和槽区,将系统提供的信号和槽进行连接。

该连接,无需手动书写信号函数、槽函数、连接函数

img

2) 基于ui界面上的连接,在组件上,右击转到槽,选中要发射的信号,在槽函数中处理相关逻辑代码。

该连接,无需手动书写信号函数、连接函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//按钮2 对应的槽函数处理    当按钮2被发射一个点击,就会自动执行该槽函数
void Widget::on_Btn2_clicked()
{
static int num = 0;

if(num%3 == 0)
{
ui->Btn1->setStyleSheet("background-color:red");
}
else if(num%3 == 1)
{
ui->Btn1->setStyleSheet("background-color:green");
}
else if(num%3 == 2)
{
ui->Btn1->setStyleSheet("background-color:yellow");
}
num++;
}

3) 手动连接信号和槽,基于qt4版本的连接,该连接是不友好的连接。

需要使用connect连接函数进行连接,在QObject类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[static] QMetaObject::Connection //函数的返回值类型  静态成员函数
QObject::connect( //函数名
const QObject *sender, //发送者 组件的指针
const char *signal, //发送的信号 信号的函数 应该用函数指针接收 不能用char *接收 所以需要使用宏函数转换
const QObject *receiver, //接受者 组件的指针
const char *method, // 槽函数 应该用函数指针接收 不能用char *接收 所以需要使用宏函数转换
)

信号宏函数 SIGNAL()
槽宏函数 SLOT()

举个例子:
QLabel *label = new QLabel;
QScrollBar *scrollBar = new QScrollBar;
QObject::connect(scrollBar, SIGNAL(valueChanged(int)),
label, SLOT(setNum(int)));

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);


//给按钮3实例化空间
Btn3 = new QPushButton("按钮3",this);
Btn3->resize(ui->Btn2->width(),ui->Btn2->height());
Btn3->move(ui->Btn2->x(),ui->Btn2->y()+ui->Btn2->height()+10);




//手动连接系统提供的信号和槽 基于qt4版本连接 该连接是不友好
connect(Btn3,SIGNAL(clicked()),this,SLOT(close()));


}

4)手动连接信号和槽,基于qt5版本的连接,该连接就是友好的连接

需要使用connect连接函数进行连接,在QObject类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[static] QMetaObject::Connection //函数返回值类  静态函数
QObject::connect( //函数名
const QObject *sender, //信号的发送者 组件的指针
PointerToMemberFunction signal, //信号函数 可以直接传函数名 原因是函数指针接收
const QObject *receiver, //信号的接收者 组件的指针
PointerToMemberFunction method, //槽函数 可以直接传函数名 原因是函数指针接收
)

举个例子:
QLabel *label = new QLabel;
QLineEdit *lineEdit = new QLineEdit;
QObject::connect(lineEdit, &QLineEdit::textChanged,
label, &QLabel::setText);

实例:

img

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#ifndef WIDGET_H
#define WIDGET_H


#include <QWidget>
#include<QPushButton> //按钮类
#include<QTextToSpeech> //语音播报类


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE


class Widget : public QWidget
{
Q_OBJECT //宏 信号和槽

public:
Widget(QWidget *parent = nullptr);
~Widget();


signals: //信号权限,该权限下函数 都是信号
void my_signal(); //只需要声明 不需要实现


public slots: //槽函数权限 ,该权限下函数 都是槽函数
void my_slot(); //只需要声明 也要实现


private slots:
void on_Btn2_clicked();


private:
Ui::Widget *ui;

QPushButton *Btn3;

//实例化一个语言播报者
QTextToSpeech *speecher;
};
#endif // WIDGET_H

源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);


//给按钮3实例化空间
Btn3 = new QPushButton("按钮3",this);
Btn3->resize(ui->Btn2->width(),ui->Btn2->height());
Btn3->move(ui->Btn2->x(),ui->Btn2->y()+ui->Btn2->height()+10);


//给语音播报者实例化空间
speecher = new QTextToSpeech(this);




//手动连接系统提供的信号和槽 基于qt4版本连接 该连接是不友好
connect(Btn3,SIGNAL(clicked()),this,SLOT(close()));


//手动连接系统提供的信号和自定义的槽函数 基于qt5版本连接 该连接是友好的连接
connect(ui->Btn4, &QPushButton::clicked, this, &Widget::my_slot);


}


void Widget::my_slot()
{
static int num = 0;


if(num%3 == 0)
{
speecher->say(ui->Btn1->text());
}
else if(num%3 == 1)
{
speecher->say(ui->Btn2->text());
}
else if(num%3 == 2)
{
speecher->say(Btn3->text());
}
num++;
}


//按钮2 对应的槽函数处理 当按钮2被发射一个点击,就会自动执行该槽函数
void Widget::on_Btn2_clicked()
{
static int num = 0;


if(num%3 == 0)
{
ui->Btn1->setStyleSheet("background-color:red");
}
else if(num%3 == 1)
{
ui->Btn1->setStyleSheet("background-color:green");
}
else if(num%3 == 2)
{
ui->Btn1->setStyleSheet("background-color:yellow");
}
num++;
}
Widget::~Widget()
{
delete ui;
}

5) 手动连接信号和功能函数,如果信号一旦被触发,则自动执行功能函数中的内容。

​ 功能函数:全局函数、lambda表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[static] QMetaObject::Connection 
QObject::connect(
const QObject *sender,
PointerToMemberFunction signal,
Functor functor//功能函数
)
举个例子:
全局函数:
void someFunction();
QPushButton *button = new QPushButton;
QObject::connect(button, &QPushButton::clicked, someFunction);

lambda表达式:
QByteArray page = ...;
QTcpSocket *socket = new QTcpSocket;
socket->connectToHost("qt-project.org", 80);
QObject::connect(socket, &QTcpSocket::connected, [=] () {
socket->write("GET " + page + "\r\n");
});

案例:

1
2
3
4
// 手动连接系统提供的信号和lambda表达式
connect(ui->Btn5, &QPushButton::clicked,[&](){
ui->Btn5->setText("aaa");
});

12.3 自定义的信号

自定义的信号,定义在signals权限下,是一个不完整的函数,只要声明,不要实现。

自定义 的信号,需要手动触发,使用emit关键字进行发射信号,如果自定义的信号一旦被发射,则会自动执行对应的槽函数处理

img

12.4 断开连接

disconnect()和connect()函数的参数一样

img

12.5 小结

1> 一个信号可以连接多个槽

2> 一个槽函数可以连接多个信号

3> 信号的参数类型和槽函数参数类型必须保持一致

4> 信号的参数个数>=槽函数的参数个数

5> 信号可以连接信号

1
2
3
4
5
6
7
8
9
10
11
12
13
1、信号函数和槽函数进行链接时,一般要求信号函数和槽函数的参数保持一致
connect(信号发送者, SIGNAL(signalFun()),信号接收者, SLOT(slotFun())); //Ok
connect(信号发送者, SIGNAL(signalFun(int)),信号接收者, SLOT(slotFun(int))); //Ok
connect(信号发送者, SIGNAL(signalFun(int, char)),信号接收者, SLOT(slotFun(int, char))); //Ok
connect(信号发送者, SIGNAL(signalFun(char, int)),信号接收者, SLOT(slotFun(int, char))); //False
connect(信号发送者, SIGNAL(signalFun(int)),信号接收者, SLOT(slotFun(char))); //False
2、当信号函数的参数大于槽函数的参数时
connect(信号发送者, SIGNAL(signalFun(int, char)),信号接收者, SLOT(slotFun())); //Ok
connect(信号发送者, SIGNAL(signalFun(int, char)),信号接收者, SLOT(slotFun(int))); //Ok
connect(信号发送者, SIGNAL(signalFun(int, char)),信号接收者, SLOT(slotFun(char))); //False
3、当信号函数的参数小于槽函数的参数时
connect(信号发送者, SIGNAL(signalFun(int)),信号接收者, SLOT(slotFun(int, char))); //False
connect(信号发送者, SIGNAL(signalFun(int)),信号接收者, SLOT(slotFun(int, char=0))); //Ok

十三、使用信号和槽实现多个界面跳转

1> 准备两个界面

img

img

img

img

2> 第一个界面准备signal

3> 第二个界面准备slot

4> 将第一个界面的信号和槽进行连接

实例:

第一个界面的ui

img

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#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();


signals:
void my_jump(); //第一个界面信号的声明


private slots:
void on_pushButton_clicked();


private:
Ui::Widget *ui;
};
#endif // WIDGET_H


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}


Widget::~Widget()
{
delete ui;
}


//登录按钮对应的槽函数处理
void Widget::on_pushButton_clicked()
{
this->close(); //关闭界面
emit my_jump(); //触发第一个界面的信号
}


第二界面的ui

img

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef SECOND_H
#define SECOND_H


#include <QWidget>


namespace Ui {
class Second;
}


class Second : public QWidget
{
Q_OBJECT


public:
explicit Second(QWidget *parent = nullptr);
~Second();


public slots:
void my_jump_slot();//第二个界面的槽函数声明


private:
Ui::Second *ui;
};


#endif // SECOND_H


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "second.h"
#include "ui_second.h"

Second::Second(QWidget *parent) :
QWidget(parent),
ui(new Ui::Second)
{
ui->setupUi(this);
}


Second::~Second()
{
delete ui;
}

//第二个界面槽函数的实现
void Second::my_jump_slot()
{
this->show(); //显示界面
}


主函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include "widget.h"
#include"second.h"


#include <QApplication>


int main(int argc, char *argv[])
{
QApplication a(argc, argv);


//实例化第一个界面
Widget w;
w.show();


//实例化第二个界面
Second s;


//手动连接
QObject::connect(&w,&Widget::my_jump, &s, &Second::my_jump_slot);


return a.exec();
}


十四、qss登录界面升级优化

14.1概念

Qss是Qt程序界面中用来设置控件的背景图片、大小、字体颜色、字体类型、按钮状态变化等属性,它是用来美化UI界面。实现界面和程序的分离,快速切换界面。

优点:实现简单、便捷

14.2语法

img

img

img

主要函数:

img

十五、发布软件

1> 配置发布软件的环境变量,将qt安装路径下的bin目录放入到系统环境变量中

D:\Qt\Qt5.14.2\5.14.2\mingw73_64\bin

D:\Qt\Qt5.14.2\Tools\mingw730_64\bin

2> 在编译器中,将项目以release形式运行一次

3> 将影子目录中的可执行程序放入一个新的文件夹中

4> 在新文件夹中的空白处,按shift+右键 —>在此处打开powershell窗口

5> 输入指令:windeployqt.exe 可执行程序名.exe即可完成发布工作

如果想将快捷方式的图标进行修改,则需要将将一张图片用画图另存为BMP格式,再将该BMP图片重命名为ico类型的图片。

十六、对话框

对话框的种类:消息对话框、字体对话框、颜色对话框、文件对话框

16.1 消息对话框 QMessageBox

该类给用户提供了一种交互式的弹窗,提供了两种实现版本,分别是基于属性版本和基于静态成员函数版本。

消息对话框种类: 警告对话框、问题对话框、信息对话框、错误对话框

16.1.1 基于属性版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
需要用这样的类 实例化对象
用该对象调用成员函数进行相关设置
需要调用exec()函数 弹出对话框

举个例子:
QMessageBox msgBox;
msgBox.setText("The document has been modified.");
msgBox.setInformativeText("Do you want to save your changes?");
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Save);
int ret = msgBox.exec();


有参构造函数:
QMessageBox::QMessageBox(
QMessageBox::Icon icon, //对话框 图标
const QString &title, //对话框标题
const QString &text, //对话框文本
QMessageBox::StandardButtons buttons = NoButton, //对话框提供的按钮
QWidget *parent = nullptr, //指定父组件 父对象
)

参数1
QMessageBox::NoIcon //无图标
QMessageBox::Question //问题图标
QMessageBox::Information //信息图标
QMessageBox::Warning //警告图标
QMessageBox::Critical //错误图标

参数3
QMessageBox::Ok //ok按钮
QMessageBox::Open //打开按钮
QMessageBox::Save //保存按钮
QMessageBox::Cancel //取消按钮
QMessageBox::Close //关闭按钮
QMessageBox::Discard //不保存按钮

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//警告按钮对应的槽函数处理
void Widget::on_warBtn_clicked()
{
//弹出警告对话框
QMessageBox msg(
QMessageBox::Warning,//对话框图标
"警告",//对话框标题
"放学后,你等着",//提示文本
QMessageBox::Yes | QMessageBox::No, //提供的按钮
this); //指定父组件


//调用exec()弹出对话框
int ret = msg.exec();


//根据用户的选择 执行不同的功能
if(ret == QMessageBox::Yes)
{
qDebug() << "等着就等着,谁怕谁";
}
else
{
qDebug() << "这是干啥呀,你永远是我大哥";
}


}

16.1.2 基于静态成员函数版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
不需要实例化对象,直接使用类调用静态成员函数弹出对话框
该类给用户提供了四种静态成员函数:warning、question、information、critical
无需调用exec()

以question为例:

[static] QMessageBox::StandardButton //返回值类型 返回的是用户选中的按钮 静态成员函数
QMessageBox::question( //函数名
QWidget *parent, //指定父组件
const QString &title, //标题
const QString &text, //文本
QMessageBox::StandardButtons buttons = StandardButtons(Yes | No), //提供的按钮
)

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//问题按钮对应的槽函数处理
void Widget::on_quesBtn_clicked()
{
//调用QMessageBox的静态成员函数 弹出对话框
int ret = QMessageBox::question(this, //指定父组件
"问题",//标题
"同学们,你们饿吗?",//文本
QMessageBox::Yes | QMessageBox::No);//提供按钮


//根据用户选中的按钮 执行不同的功能
if(ret == QMessageBox::Yes)
{
qDebug() << "好饿,赶紧下课,干饭干饭";
}
else
{
qDebug() << "我爱学习,饿是什么";
}


}

16.2 字体对话框 QFontDialog

该类给用户提供了一个字体对话框,直接调用该类的一个静态成员函数getFont()即可弹出字体对话框。

1
2
3
4
5
6
7
8
9
10
11
12
[static] QFont //返回值类型 返回的是用户选中的字体  静态函数
QFontDialog::getFont( //函数名
bool *ok, //判断用户是否选中字体
const QFont &initial, //初始字体
QWidget *parent = nullptr, //指定父组件
const QString &title = QString(), //对话框标题
)
参数2
QFont::QFont(const QString &family, int pointSize = -1, int weight = -1, bool italic = false)

弹出字体对话框所需要的类: QFontDialog、QFont

案例:文本编辑器

img

img

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#ifndef WIDGET_H
#define WIDGET_H


#include <QWidget>
#include<QFontDialog> //字体对话框类
#include<QFont> //字体类
#include<QMessageBox> //消息对话框类


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE


class Widget : public QWidget
{
Q_OBJECT


public:
Widget(QWidget *parent = nullptr);
~Widget();


private slots:
void on_fontBtn_clicked();


private:
Ui::Widget *ui;
};
#endif // WIDGET_H


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}


Widget::~Widget()
{
delete ui;
}


//字体按钮对应的槽函数处理
void Widget::on_fontBtn_clicked()
{
bool ok;


//弹出字体对话框
QFont f = QFontDialog::getFont(
&ok, //判断用户是否选中字体
QFont("楷体",8,10),//初始字体
this,//指定父组件
"字体对话框" //对话框标题
);


//判断用户是否选中有效字体
if(ok)
{
//说明用户选中字体
//将ui界面上的文本设置成用户选中的字体
//ui->textEdit->setFont(f); //将文本全部设置成该字体
ui->textEdit->setCurrentFont(f); //将当前的文本设置成该字体


}
else
{
//说明用户没有选中字体
QMessageBox::information(this,"","你没有选中字体");
}


16.3 颜色对话框 QColorDialog

该类给用户提供了一个颜色对话框,直接调用该类的一个静态成员函数getColor()即可直接弹出颜色对话框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[static] QColor //返回值类型  返回的是用户选中的颜色  静态函数
QColorDialog::getColor(//函数名
const QColor &initial = Qt::white, //初始颜色
QWidget *parent = nullptr, //指定父对象
const QString &title = QString(), //对话框标题
)

弹出颜色对话框需要的类:QColorDialog、QColor

参数1
QColor::QColor(int r, int g, int b, int a = ...)


通过isValid()判断用户是否选中颜色 如果选中颜色返回true,否则false
bool QColor::isValid() const
Returns true if the color is valid; otherwise returns false.

案例:

img

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifndef WIDGET_H
#define WIDGET_H


#include <QWidget>
#include<QFontDialog> //字体对话框类
#include<QFont> //字体类
#include<QMessageBox> //消息对话框类
#include<QColorDialog> //颜色对话框类
#include<QColor> //颜色类


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE


class Widget : public QWidget
{
Q_OBJECT


public:
Widget(QWidget *parent = nullptr);
~Widget();


private slots:
void on_fontBtn_clicked();


void on_colorBtn_clicked();


private:
Ui::Widget *ui;
};
#endif // WIDGET_H


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//颜色按钮对应的槽函数处理
void Widget::on_colorBtn_clicked()
{
//弹出颜色对话框
QColor c = QColorDialog::getColor(QColor(0,0,255),//初始颜色
this,//指定父组件
"颜色对话框" //对话框标题
);


//判断用户是否选中颜色
if(c.isValid())
{
//说明用户选中了颜色
//将ui界面上的文本设置成该颜色
//ui->textEdit->setTextColor(c);//设置前景色
ui->textEdit->setTextBackgroundColor(c);
}
else
{
//说明用户没有选中颜色
QMessageBox::information(this,"","你没有选中颜色");
}


}

16.4 文件对话框 QFileDialog

该类给用户提供了一个文件对话框,可以获取文件路径,可以直接调用该类里的两个静态成员函数弹出文件对话框,分别是getOpenFileName()、getSaveFileName()。

getOpenFileName()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[static] QString //函数返回值类  返回的是文件所在的路径 静态函数
QFileDialog::getOpenFileName( //函数名
QWidget *parent = nullptr, //指定父组件
const QString &caption = QString(),//对话框的标题
const QString &dir = QString(), //起始路径
const QString &filter = QString(), //过滤器
QString *selectedFilter = nullptr, //选中的过滤器
)

举个例子:

QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
"/home",
tr("Images (*.png *.xpm *.jpg)"));

"Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"

案例:

img

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#ifndef WIDGET_H
#define WIDGET_H


#include <QWidget>
#include<QFontDialog> //字体对话框类
#include<QFont> //字体类
#include<QMessageBox> //消息对话框类
#include<QColorDialog> //颜色对话框类
#include<QColor> //颜色类
#include<QFileDialog> //文件对话框类
#include<QDebug>
#include<QFile> //文件类


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE


class Widget : public QWidget
{
Q_OBJECT


public:
Widget(QWidget *parent = nullptr);
~Widget();


private slots:
void on_fontBtn_clicked();


void on_colorBtn_clicked();


void on_openBtn_clicked();


private:
Ui::Widget *ui;
};
#endif // WIDGET_H


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//打开按钮对应的槽函数处理
void Widget::on_openBtn_clicked()
{
//弹出一个文件对话框
QString fileName = QFileDialog::getOpenFileName(this,//父组件
"文件对话框",
"./",//起始位置
"ALL(*.*);;Image(*.png *.jpg);;文本(*.txt)"//过滤器
);
qDebug() << fileName ;


//实例化一个文件对象
QFile file(fileName);


//打开文件
file.open(QFile::ReadWrite); //以读写方式打开


//读取数据
QByteArray msg = file.readAll();


//将读取到的数据放入到ui界面上
//ui->textEdit->setText(QString::fromLocal8Bit(msg));
ui->textEdit->setText(msg);


//关闭文件
file.close();
}


getSaveFileName()

1
2
3
4
5
6
7
8
[static] QString 
QFileDialog::getSaveFileName(
QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &dir = QString(),
const QString &filter = QString(),
QString *selectedFilter = nullptr,
QFileDialog::Options options = Options())

案例:

img

源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//保存按钮对应的槽函数处理
void Widget::on_saveBtn_clicked()
{
//弹出文件对话框
QString fileName = QFileDialog::getSaveFileName();

//获取ui界面上的文本
QString msg = ui->textEdit->toPlainText(); // == text

//实例一个文件对象
QFile file(fileName);

//打开文件
file.open(QFile::WriteOnly); //只写的方式打开

//写入数据
file.write(msg.toLocal8Bit());


//关闭文件
file.close();

}

16.5 Qt中文件读写 QFile

1> 用QFile类实例化一个对象,实例化对象时给个路径

2> 使用open()打开文件, 打开方式有读写

3> 使用write()写入数据,调用read()、readLine()、readAll()读取数据

4> close()关闭文件

十七、事件机制

引以为傲的核心机制之一。

17.1概念

当这件事情发生时,自动执行某块功能代码,该某块功能代码都是系统提供的虚函数,我们只需要将该虚函数重写即可。

17.2事件处理简介

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1. 什么是事件?  (重点)
事件是由窗口系统或者自身产生的,用以响应所发生的各类事情,比如用户按下并释放了键盘或者鼠标、窗口因暴露而需要重绘、定时器到时而应有所动作,等等

从某种意义上讲,事件比信号更原始,甚至可以认为大多数信号其实都是由事件产生的。比如一个下压式按钮首先感受到的是鼠标事件,
在进行必要的处理以产生按钮下沉继而弹起的视觉效果之后,才会发射 clicked()信号

2. 如何处理事件? (重点)
myWnd(自定义类) -继承-> QWidget -继承-> QObject
1> 当事件发生时,首先被调用的是QObject类中的虚函数event(),
其 QEvent型参数标识了具体的事件类型
bool QObject:: event (QEvent* e)
{
if (e == mouseEvent)
{
void QWidget::mousePressEvent (QMouseEvent* e)
void QWidget:: mouseReleaseEvent (QMouseEvent* e)
}
if(e == keyEvent){
void QWidget::keyPressEvent (QMouseEvent* e)
void QWidget:: keyReleaseEvent (QMouseEvent* e)
}
}
2> 作为QObject类的子类, QWidget类覆盖了其基类中的
event()虚函数,并根据具体事件调用具体事件处理函数
void QWidget::mousePressEvent (QMouseEvent* e)
void QWidget::mouseReleaseEvent (QMouseEvent* e)
void QWidget::keyPressEvent (QMouseEvent* e)
void QWidget:: keyReleaseEvent (QMouseEvent* e)
void QWidget::paintEvent (QPaintEvent* e):
3> 而这些事件处理函数同样也是虚函数,也可以被 QWidget类
的子类覆盖,以提供针对不同窗口部件类型的事件处理

4> 组件的使用者所关心的往往是定义什么样的槽处理什么样的信号,
而组件的实现者更关心覆盖哪些事件处理函数


17.3 事件处理函数由来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
QObject类 提供了那些可以重写的虚函数
[virtual] bool QObject::event(QEvent *e)
// 参数:事件的类型

QWidgets类, 提供了那些可以重写的虚函数
[override virtual protected] bool QWidget::event(QEvent *event)

[virtual protected] void QWidget::keyPressEvent(QKeyEvent *event)
[virtual protected] void QWidget::keyReleaseEvent(QKeyEvent *event)
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event)
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event)
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event)
[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event)
[virtual protected] void QObject::timerEvent(QTimerEvent *event)

QPainter类 ---> 画家类
void SimpleExampleWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setPen(Qt::blue);
painter.setFont(QFont("Arial", 30));
painter.drawText(rect(), Qt::AlignCenter, "Qt");
}

十八、定时器事件 QTimerEvent

qt的定时器事件提供了两种实现版本,分别是基于属性版本、基于事件函数版本。

18.1 基于事件函数

功能: 让系统每隔一定的时间,自动执行某块功能代码 (函数)

1
2
3
4
所需要的函数:
startTimer(int sec); //启动一个定时器,给定时间,其时间以毫秒为单位 该函数返回定时器的Id
killTimer(int id); //关闭一个定时器 根据定时器的id进行关闭
timerEvent(QTimerEvent *e); //当定时器超时时,自动执行该函数的内容

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//当定时器超时时 自动执行的函数
void Widget::timerEvent(QTimerEvent *e)
{
static int num = 0;
ui->Lab1->setNum(++num);


}


//启动按钮对应的槽函数处理
void Widget::on_Btn1_clicked()
{
//一扭对用
if(ui->Btn1->text() == "启动")
{
//启动一个定时器
id = startTimer(1000); //让系统每隔1秒钟,自动执行timerEvent()函数


//将按钮上的文本设置成 "关闭"
ui->Btn1->setText("关闭");
}
else
{
//关闭一个定时器
killTimer(id);
//将按钮上的文本设置成 "启动"
ui->Btn1->setText("启动");
}


}

17.2 基于属性版本

1
2
3
4
5
需要实例化一个对象  所用到的类 QTimer
调用成员函数start(int sec),给定毫秒数,每隔一定时间,自动触发timeout()信号
我们就可以将该信号连接到自定义的槽函数中,在槽函数中处理逻辑代码。
调用成员函数stop(),断开信号和槽的连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);


//实例化空间
timer = new QTimer(this);


//将系统提供的timeout信号和自定的槽进行连接
connect(timer, &QTimer::timeout, this, &Widget::timeout_slot);
}


Widget::~Widget()
{
delete ui;
}


//当定时器超时时 自动执行的函数
void Widget::timerEvent(QTimerEvent *e)
{




//判断哪个定时器超时
if(e->timerId() == id)
{
// static int num = 0;
// ui->Lab1->setNum(++num);


//获取系统时间
QTime sys_time = QTime::currentTime();


//将系统时间转换成字符串类型
QString t = sys_time.toString("hh::mm::ss");


//将系统时间放入ui界面上
ui->Lab1->setText(t);


//居中显示
ui->Lab1->setAlignment(Qt::AlignCenter);
}


}


//启动按钮对应的槽函数处理
void Widget::on_Btn1_clicked()
{
//一扭对用
if(ui->Btn1->text() == "启动")
{
//启动一个定时器
id = startTimer(1000); //让系统每隔1秒钟,自动执行timerEvent()函数


//将按钮上的文本设置成 "关闭"
ui->Btn1->setText("关闭");




}
else
{
//关闭一个定时器
killTimer(id);
//将按钮上的文本设置成 "启动"
ui->Btn1->setText("启动");
}


}


//启动按钮2对应的槽函数处理
void Widget::on_Btn1_2_clicked()
{
if(ui->Btn1_2->text() == "启动")
{
timer->start(1000); //每隔1秒钟 自动触发timeout信号


//将按钮上的文本设置成 "关闭"
ui->Btn1_2->setText("关闭");




}
else
{


timer->stop();


//将按钮上的文本设置成 "启动"
ui->Btn1_2->setText("启动");
}
}


//timeout信号对应的槽函数处理
void Widget::timeout_slot()
{
//获取系统时间
QTime sys_time = QTime::currentTime();


//将系统时间转换成字符串类型
QString t = sys_time.toString("hh::mm::ss");


//将系统时间放入ui界面上
ui->Lab1_2->setText(t);


//居中显示
ui->Lab1_2->setAlignment(Qt::AlignCenter);
}

十九、键盘事件 QKeyEvent

19.1 功能

当程序员使用键盘时(按下、抬起)时,自动执行某块功能代码。

19.2 重写的函数

1
2
[virtual protected] void QWidget::keyPressEvent(QKeyEvent *event); //键盘按下的虚函数
[virtual protected] void QWidget::keyReleaseEvent(QKeyEvent *event);//键盘抬起的虚函数

19.3 QKeyEvent常用的函数

1
2
key()  ----->键盘上对应的键值
text() ------>键盘上对应的文本(eg:有大小写字母)

案例:

img

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#ifndef WIDGET_H
#define WIDGET_H


#include <QWidget>
#include<QKeyEvent> //键盘事件类


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE


class Widget : public QWidget
{
Q_OBJECT


public:
Widget(QWidget *parent = nullptr);
~Widget();
void keyPressEvent(QKeyEvent *event) override;//键盘按下事件函数的声明
void keyReleaseEvent(QKeyEvent *event) override;//键盘抬起事件函数的声明


private:
Ui::Widget *ui;
};
#endif // WIDGET_H


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}


Widget::~Widget()
{
delete ui;
}
//键盘按下事件函数的实现
void Widget::keyPressEvent(QKeyEvent *event)
{
//在ui界面上显示哪个键被按下了
ui->label->setText(event->text()+"被按下了");
//居中显示
ui->label->setAlignment(Qt::AlignCenter);




//判断是哪个键被按下
switch (event->key())
{
case 'W':
{
if(ui->label->y() < 0-ui->label->height())
{
ui->label->move(ui->label->x(),this->height());
}
ui->label->move(ui->label->x(), ui->label->y()-10);
}
}
}


//键盘抬起事件函数的实现
void Widget::keyReleaseEvent(QKeyEvent *event)
{
//在ui界面上显示哪个键被按下了
ui->label->setText(event->text()+"被抬起了");
//居中显示
ui->label->setAlignment(Qt::AlignCenter);
}




二十、鼠标事件 QMouseEvent

20.1 功能

当程序员使用鼠标时,自动执行某块功能代码。

20.2 重写函数的种类

1
2
3
4
[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event);//鼠标双击事件函数的声明
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);//鼠标移动事件函数的声明
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);//鼠标按下事件函数的声明
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);//鼠标抬起事件函数的声明

20.3 QMouseEvent常用的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
 
Qt::MouseButton button() const ---->判断鼠标哪个键被按下

Qt::MouseButtons buttons() const ---->对于鼠标移动是,判断鼠标哪个键被按下

x() --->x轴坐标
y() --->y轴坐标

pos() --->在当前窗口的坐标点

globapos() ---->在全局窗口的坐标点


案例:

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#ifndef WIDGET_H
#define WIDGET_H


#include <QWidget>
#include<QKeyEvent> //键盘事件类


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE


class Widget : public QWidget
{
Q_OBJECT


public:
Widget(QWidget *parent = nullptr);
~Widget();
void keyPressEvent(QKeyEvent *event) override;//键盘按下事件函数的声明
void keyReleaseEvent(QKeyEvent *event) override;//键盘抬起事件函数的声明


void mouseMoveEvent(QMouseEvent *event) override; //鼠标移动事件函数的声明
void mousePressEvent(QMouseEvent *event) override;//鼠标按下事件函数的声明
void mouseReleaseEvent(QMouseEvent *event) override;//鼠标抬起事件函数的声明
void mouseDoubleClickEvent(QMouseEvent *event) override;//鼠标双击事件函数的声明


private:
Ui::Widget *ui;
};
#endif // WIDGET_H


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}


Widget::~Widget()
{
delete ui;
}
//键盘按下事件函数的实现
void Widget::keyPressEvent(QKeyEvent *event)
{
//在ui界面上显示哪个键被按下了
ui->label->setText(event->text()+"被按下了");
//居中显示
ui->label->setAlignment(Qt::AlignCenter);




//判断是哪个键被按下
switch (event->key())
{
case 'W':
{
if(ui->label->y() < 0-ui->label->height())
{
ui->label->move(ui->label->x(),this->height());
}
ui->label->move(ui->label->x(), ui->label->y()-10);
}
}
}


//键盘抬起事件函数的实现
void Widget::keyReleaseEvent(QKeyEvent *event)
{
//在ui界面上显示哪个键被按下了
ui->label->setText(event->text()+"被抬起了");
//居中显示
ui->label->setAlignment(Qt::AlignCenter);
}


//鼠标移动事件函数的实现
void Widget::mouseMoveEvent(QMouseEvent *event)
{


}
//鼠标按下事件函数的实现
void Widget::mousePressEvent(QMouseEvent *event)
{
//判断鼠标哪个键被按下
if(event->button() == Qt::LeftButton)
{


ui->label->setText("左键被按下"+QString(" (x:%1,y:%2) ").arg(event->x()).arg(event->y()));


}
else if(event->button() == Qt::RightButton)
{
ui->label->setText("右键被按下"+QString(" (x:%1,y:%2) ").arg(event->x()).arg(event->y()));
}
else if(event->button() == Qt::MidButton)
{
ui->label->setText("中间键被按下"+QString(" (x:%1,y:%2) ").arg(event->x()).arg(event->y()));
}
}
//鼠标抬起事件函数的实现
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{


ui->label->setText("左键被抬起"+QString(" (x:%1,y:%2) ").arg(event->x()).arg(event->y()));


}
else if(event->button() == Qt::RightButton)
{
ui->label->setText("右键被抬起"+QString(" (x:%1,y:%2) ").arg(event->x()).arg(event->y()));
}
else if(event->button() == Qt::MidButton)
{
ui->label->setText("中间键被抬起"+QString(" (x:%1,y:%2) ").arg(event->x()).arg(event->y()));
}
}
//鼠标双击事件函数的实现
void Widget::mouseDoubleClickEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{


ui->label->setText("左键被双击"+QString(" (x:%1,y:%2) ").arg(event->x()).arg(event->y()));


}
else if(event->button() == Qt::RightButton)
{
ui->label->setText("右键被双击"+QString(" (x:%1,y:%2) ").arg(event->x()).arg(event->y()));
}
else if(event->button() == Qt::MidButton)
{
ui->label->setText("中间键被双击"+QString(" (x:%1,y:%2) ").arg(event->x()).arg(event->y()));
}
}




20.4 纯净窗口移动

img

案例:

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#ifndef WIDGET_H
#define WIDGET_H


#include <QWidget>
#include<QMouseEvent>
#include<QPoint> //向量类


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE


class Widget : public QWidget
{
Q_OBJECT


public:
Widget(QWidget *parent = nullptr);
~Widget();
void mousePressEvent(QMouseEvent *event) override; //鼠标按下
void mouseMoveEvent(QMouseEvent *event) override; //鼠标移动


private:
Ui::Widget *ui;


QPoint p; //存放坐标
};
#endif // WIDGET_H


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);


//纯净窗口 去掉头部
this->setWindowFlag(Qt::FramelessWindowHint);

}


Widget::~Widget()
{
delete ui;
}
//鼠标按下
void Widget::mousePressEvent(QMouseEvent *event)
{

p = event->pos(); //求出当前窗口的坐标点
}
//鼠标移动
void Widget::mouseMoveEvent(QMouseEvent *event)
{
if(event->buttons() == Qt::LeftButton)
{
this->move(event->globalPos() - p);
// 全局的坐标(向量) 当前窗口的坐标(向量)
}
}

二一、绘制事件

1> 绘制事件是qt提供的二维图形引擎,能够让用户绘制各种图形,例如:适量文字、绘制图形、图像。。。

2> 绘制事件处理函数触发情况:窗口第一次展示、窗口最小化、最大化、窗口从覆盖状态显示出来、手动拖动窗口调大小、调用update函数

3> 绘制事件,依赖于画家类(QPainter)实现相关绘制工作

1
[virtual protected] void QWidget::paintEvent(QPaintEvent *event);//绘制事件函数

4> 案例

案例:水印

img

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#ifndef WIDGET_H
#define WIDGET_H


#include <QWidget>
#include<QPaintEvent> //绘制事件类
#include<QDebug>
#include<QPainter> //画家类


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE


class Widget : public QWidget
{
Q_OBJECT


public:
Widget(QWidget *parent = nullptr);
~Widget();
void paintEvent(QPaintEvent *event) override; //绘制事件函数的声明


private:
Ui::Widget *ui;
};
#endif // WIDGET_H


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}


Widget::~Widget()
{
delete ui;
}


void Widget::paintEvent(QPaintEvent *event)
{
// static int num = 0;
// qDebug() << ++num;


//实例化一个画家
QPainter painter(this); //画家在窗口上画画


//给画家设置笔
painter.setPen(QColor("pink"));


//给画家的画笔设置字体样式
painter.setFont(QFont("隶书",30,45));


//让画家画画
painter.drawText(this->rect(),Qt::AlignCenter,"好好学习,天天向上");


}




案例:画图板

img

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#ifndef WIDGET_H
#define WIDGET_H


#include <QWidget>
#include<QMouseEvent> //鼠标事件类
#include<QPaintEvent> //绘制事件类
#include<QPoint> //向量类
#include<QPainter> //画家类
#include<QPixmap> //图像容器
#include<QPen> //画笔类


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE


class Widget : public QWidget
{
Q_OBJECT


public:
Widget(QWidget *parent = nullptr);
~Widget();
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void paintEvent(QPaintEvent *event) override;


private:
Ui::Widget *ui;
QPoint p; //记录画画的起始位置


QPixmap *pix; //实例化一个图像容器
};
#endif // WIDGET_H


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);


//给图像容器实例化空间
pix = new QPixmap(this->width(),this->height());
pix->fill();//给图像容器填充颜色 ---> 白色
}


Widget::~Widget()
{
delete ui;
}


//鼠标按下事件函数的重写
void Widget::mousePressEvent(QMouseEvent *event)
{
p = event->pos(); //起始点
}


//鼠标移动事件函数的重写
void Widget::mouseMoveEvent(QMouseEvent *event)
{
//实例化一个画家
QPainter painter(pix); //让画家在pix上画画


//实例化一个画笔
QPen pen(QColor("green"));


pen.setWidth(15); //笔的粗细


//把画笔给画家
painter.setPen(pen);


//让画家将起始点和当前位置画起来
painter.drawLine(p, event->pos());


//更新起始点
p = event->pos();


this->update(); //触发绘制事件


}


//绘制事件函数的重写
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this); //这个画家在窗口上画画


painter.drawPixmap(QPoint(0,0),*pix);
//窗口坐标原点 图像
}




二二、QT实现连接TCP协议

22.1 基于TCP的通信原理(复习)

img

22.2 QT中实现服务器原理

img

22.3 QT中实现客户端原理

img

22.4 网络聊天室服务器实现

1> 使用QTcpSever实例化一个服务器对象

2> 设置监听,通过listen()函数,监听时,可以监听指定主机,也可以监听任意主机,设置端口号,可以是系统提供的端口号,也可以是指定的端口号

3> 如果有客户端发来连接请求,那么服务器端就是自动发射一个newConnection信号,我们就可以将该信号连接到自定义的槽函数中,在槽函数中获取客户端的套接字,将客户端的套接字存放到客户端容器中。

4> 调用nextPaddingConnection函数,可以获取最新连接的客户端套接字。

5> 当客户端向服务器发来数据时,那么客户端就会自动发射一个readyRead信号,我们就可以将该信号连接到自定义的槽函数中读取数据。

6> 使用read()、readLine()、readAll()读取套接字中数据,使用write()往套接字中写入数据

7> 调用close()关闭服务器

实例:

img

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#ifndef WIDGET_H
#define WIDGET_H


#include <QWidget>
#include<QTcpServer> //服务器端类
#include<QMessageBox> //消息对话框
#include<QTcpSocket> //客户端类
#include<QList> //链表容器 ---> 存放客户端的容器




QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE


class Widget : public QWidget
{
Q_OBJECT


public:
Widget(QWidget *parent = nullptr);
~Widget();


private slots:
void on_startBtn_clicked();


public slots:
void newConnection_slot(); //newconnection信号对应的槽函数声明
void readyRead_slot(); //readyRead信号对应的槽函数声明


private:
Ui::Widget *ui;


//实例化一个服务器指针
QTcpServer *server;


//定义一个容器存放客户端
QList<QTcpSocket *> socketList;


};
#endif // WIDGET_H


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)


{
ui->setupUi(this);


//给服务器指针实例化空间
server = new QTcpServer(this);
}


Widget::~Widget()
{
delete ui;
}


//启动按钮对应的槽函数处理
void Widget::on_startBtn_clicked()
{
//获取ui界面上的端口号
quint16 port = ui->portEdit->text().toUInt(); //将字符串转换成整型






//设置监听
//函数原型:bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
//参数1: 监听的主机地址
//参数2: 端口号
//返回值:如果监听成功返回true,否则false
if( server->listen(QHostAddress::Any,port) )
{
QMessageBox::information(this,"","启动服务器成功!");
}
else
{
QMessageBox::information(this,"","启动服务器失败!");
return;
}


//程序运行至此,说明服务器启动成功,如果有客户端向服务器发来连接请求,那么服务器端就会自动发射一个newConnection()信号
//将该信号连接到自定义的槽函数中处理逻辑代码
connect(server, &QTcpServer::newConnection, this, &Widget::newConnection_slot);




}




//newconnection信号对应的槽函数实现
void Widget::newConnection_slot()
{
//获取最新连接的客户端套接字
//函数原型:virtual QTcpSocket *nextPendingConnection();
QTcpSocket *s = server->nextPendingConnection();
//将客户端套接字放入容器链表中
socketList.push_back(s);


//程序运行至此,说明客户端和服务器端已经建立了连接,如果有客户端向服务器发送来数据,那么客户端就会自动发射一个readyRead信号
//我们就可以将该信号连接到自定义的槽函数中处理逻辑代码
connect(s,&QTcpSocket::readyRead, this, &Widget::readyRead_slot);


}


//readyRead信号对应的槽函数实现
void Widget::readyRead_slot()
{
//读取客户端的数据
//移除无效客户端
for(int i=0; i<socketList.count(); i++)
//容器的元素个数
{
//判断客户端与服务器的连接状态
//函数原型:SocketState state() const;
//未连接的枚举值 0
if( socketList.at(i)->state() == 0 )
{
socketList.removeAt(i); //通过下标删除该元素
}
}


//遍历有效客户端中哪些客户端有数据待读
for(int i=0; i<socketList.count(); i++)
{
//判断套接字中有没有数据
//函数原型:qint64 bytesAvailable() const override;
if( socketList.at(i)->bytesAvailable() !=0 )
{
//读取客户端中数据
QByteArray msg = socketList.at(i)->readAll();


//将读取到的信息放入ui界面上
ui->msgWidget->addItem(QString::fromLocal8Bit(msg)); // == setText


//将该数据内容广播给所有人
for(int j=0; j<socketList.count();j++)
{
//将数据写入所有客户端的套接字中
socketList.at(j)->write(msg);
}
}
}
}

22.5 网络聊天室客户端实现

1> 使用QTcpSocket实例化一个客户端对象

2> 将客户端连接服务器主机,使用connectToHost()函数,给定服务器主机地址和端口号

3> 如果客户端成功连接到服务器,那么客户端就会自动发射一个connected信号,我们就可以将该信号连接到自定义的槽函数中,在槽函数中处理逻辑代码。

4> 此时说明客户端和服务器建立了连接,如果服务器发来数据,那么客户端就会自动发射一个readyRead信号,我们就可以将该信号连接到自定义的槽函数中,读取服务器发来的数据。

5> 调用read()、readLine()、readAll()读取数据,调用write()写入数据。

6>可以使用disconnectFromHost函数断开与服务器的连接,如果与服务器成功断开连接,那么客户端就会自动发射一个disconnected信号,我们就可以将该信号连接到自定义的槽函数中,处理相关逻辑代码。

案例:

img

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#ifndef WIDGET_H
#define WIDGET_H


#include <QWidget>
#include<QTcpSocket> //客户端类
#include<QMessageBox> //消息对话框类


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE


class Widget : public QWidget
{
Q_OBJECT


public:
Widget(QWidget *parent = nullptr);
~Widget();


private slots:
void on_connectBtn_clicked();
void on_sendBtn_clicked();


void on_disconnectBtn_clicked();


public slots:
void connected_slot(); //connected信号对应的槽函数声明
void readyRead_slot(); //readyRead信号对应的槽函数声明
void disconnected_slot(); //disconnected信号对应的槽函数声明


private:
Ui::Widget *ui;


//实例化一个客户端指针
QTcpSocket *socket;


QString userName; //存放用户名
};
#endif // WIDGET_H


源文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//给客户端指针实例化空间
socket = new QTcpSocket(this);


//设置初始化界面
ui->msgEdit->setEnabled(false); //不可以
ui->sendBtn->setEnabled(false);
ui->disconnectBtn->setEnabled(false);




//如果客户端成功与服务器连接,那么客户端就会自动发射一个connected信号
//我们就可以将该信号连接到自定义的槽函数中,处理逻辑代码,由于只需要连接一次,所以连接函数写在构造函数中。
connect(socket, &QTcpSocket::connected, this, &Widget::connected_slot);


//程序运行至此,说明客户端和服务器已经成功建立连接,如果服务器发来数据,那么客户端就会自动发射一个readyRead信号
//我们就可以将该信号连接带自定义的槽函数中读取数据,由于只需要连接一次,所以连接函数写在构造函数中
connect(socket, &QTcpSocket::readyRead, this, &Widget::readyRead_slot);


//如果客户端和服务器成功断开连接,那么客户端就会自动发射一个disconnected信号
//我们就可以将该信号连接到自定义的槽函中,处理后续的逻辑代码 由于只需要连接一次 所以书写连接函数写在构造函数中
connect(socket, &QTcpSocket::disconnected, this, &Widget::disconnected_slot);


}


Widget::~Widget()
{
delete ui;
}


//连接服务器按钮对应的槽函数处理
void Widget::on_connectBtn_clicked()
{
//获取ui界面上的ip和端口号
QString ip = ui->ipEdit->text();
quint16 port = ui->portEdit->text().toUInt(); //将字符串转换成整型


//连接服务器
socket->connectToHost(ip, port);


//如果客户端成功与服务器连接,那么客户端就会自动发射一个connected信号
//我们就可以将该信号连接到自定义的槽函数中,处理逻辑代码,由于只需要连接一次,所以连接函数写在构造函数中。


}


//connected信号对应的槽函数声明
void Widget::connected_slot()
{
QMessageBox::information(this,"","连接服务器成功!");


//告诉服务器 我来了
userName = ui->userNameEdit->text();


QString msg = userName + ": 进入聊天室";


socket->write(msg.toLocal8Bit());


ui->msgEdit->setEnabled(true); //可用
ui->sendBtn->setEnabled(true);
ui->disconnectBtn->setEnabled(true);


ui->userNameEdit->setEnabled(false);
ui->ipEdit->setEnabled(false);
ui->portEdit->setEnabled(false);
ui->connectBtn->setEnabled(false);




//程序运行至此,说明客户端和服务器已经成功建立连接,如果 服务器发来数据,那么客户端就会自动发射一个readyRead信号
//我们就可以将该信号连接带自定义的槽函数中读取数据,由于只需要连接一次,所以连接函数写在构造函数中


}




void Widget::readyRead_slot() //readyRead信号对应的槽函数实现
{
//读取数据
QByteArray msg = socket->readAll();


//将读取到的信息放到ui界面上
ui->msgWidget->addItem(QString::fromLocal8Bit(msg));


}




//发送按钮对应的槽函数处理
void Widget::on_sendBtn_clicked()
{
//将msgEdit的文本发送给服务器
QString msg = userName + ": " + ui->msgEdit->text();


//发送给服务器
socket->write(msg.toLocal8Bit());


ui->msgEdit->clear(); //清空内容




}


//断开连接按钮对应的槽函数处理
void Widget::on_disconnectBtn_clicked()
{
//告诉服务器 我走了
QString msg = userName + ": 离开聊天室";
socket->write(msg.toLocal8Bit());


//断开连接
//函数原型:virtual void disconnectFromHost();


socket->disconnectFromHost();


//如果客户端和服务器成功断开连接,那么客户端就会自动发射一个disconnected信号
//我们就可以将该信号连接到自定义的槽函中,处理后续的逻辑代码 由于只需要连接一次 所以书写连接函数写在构造函数中


}


//disconnected信号对应的槽函数实现
void Widget::disconnected_slot()
{
QMessageBox::information(this,"","断开连接");


ui->msgEdit->setEnabled(false); //不可用
ui->sendBtn->setEnabled(false);
ui->disconnectBtn->setEnabled(false);


ui->userNameEdit->setEnabled(true);
ui->ipEdit->setEnabled(true);
ui->portEdit->setEnabled(true);
ui->connectBtn->setEnabled(true);


}




二三、QT实现连接数据库

23.1 QT将数据库分为三个层次

1> 数据库驱动层:QSqlDriver、QSqlDriverCreator、QSqlDriverCreatorBase、QSqlDriverPlugin

2> sql接口层:**QSqlDatabase、QSqlQuery、QSqlRecord、**QSqlError

3> 用户接口层**:提供一些模型QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel**

23.2 实现数据库操作的相关方法

1
2
3
4
5
6
7
8
9
10
11
1、添加数据库:[static] QSqlDatabase QSqlDatabase::addDatabase(QSqlDriver *driver, const QString &connectionName = QLatin1String(defaultConnection))
QSQLITE SQLite version 3 or above
2、设置数据库名称:void QSqlDatabase::setDatabaseName(const QString &name)
3、包含数据库:bool QSqlDatabase::contains(const QString &connectionName = QLatin1String(defaultConnection))
4、打开数据库:bool QSqlDriver::open(const QString &db)
5、关闭数据库:void QSqlDatabase::close()
6、错误信息:QSqlError QSqlDatabase::lastError()

7、sql语句执行:构造一个QSqlQuery类对象,调用其成员函数exec,执行sql语句:bool QSqlQuery::exec(const QString &query)
8、bool QSqlQuery::next():遍历查询结果的函数