Qt学习笔记
一、qt概述
qt(跨平台的C++ GUI应用程序开发框架)——WPS
1. qt发展历史
- 1991年诞生
- 1994年创立奇趣科技(Trolltech)公司
- 2005年qt4.0发布(500+类,9000+函数)
- 2008年被诺基亚收购
- 2009年将qt源代码开放
- 2012年qt被出售给digia公司
- 2013年5.0发布
2. 相关工具介绍
- qt助手//帮助手册
- qt构建器(qmake)//构建qt应用程序
- qt设计师(designer)//图形界面编辑器
- qt界面编辑器(uic)//将设计师得到的xml(.ui)文件转换为C++文件(.h)
- qt元对象编译器(moc)//将qt中语法扩展还原为标准C++代码
- qt资源编译器(rcc)//将图片资源转换为C++文件(.cpp)
- qt创造器(qtcreator)//qt的集成开发环境(IDE),包含上面所有工具
3. 助手中常用的模块
- qt SQL //数据库
- qt network //网络
- qt widgets //控件
- qt core //代码
- qt GUI //图形界面
- qt serial port //串口编写
例如
类名:QApplication/QLabel
- 先看第一句,了解该类的功能,若是不理解,就在”more”中查看详细介绍。
- 了解头文件(和类名一致),构建选项(QT+=widgets),继承关系。
- List of all menbers //列出所有的函数名
- obsolete members //列出过时的函数,这些函数可能会在以后的版本中删除,同时里面有相应的替换函数
- 类中的成员变量属性(properties)
- 公用的成员函数(public functions)
- 公有虚函数(Reimplemented Public Functions)
- 槽函数(Public Slots) //当有个对象发生状态改变,会发射信号,发生某个特定的动作,是qt的语法扩展
- 信号函数(signals)
- 静态公有成员(Static Public Members) //相当于全局
第一个qt程序
- 创建工程目录(最好开头大写)
注:每个qt应用程序都应该放在独立的工程目录下 - 进入工程目录编写代码(C++)
- 运行前需要qmake(构建)
注:需要在Pro里面添加“QT += widgets”
qt字符串(QString)和字符编码
- 常见编码
- Windows默认使用ANSI(中文gbk编码,英文ACKII)
- Linux默认使用Unicode(utf-8)
- Qt字符串QString内部默认使用Unicode(utf-16)用QString()将字符串用utf-8来编码
解决字符串乱码——编码转换(QTextCodec)
父窗口(容器窗口)
- 常用的父窗口类
QWidget //控件基类
QDialog //对话框,QWidget的子类 //相对简单的界面
QMainWindow //主窗口,QWidget的子类 //相对复杂的界面 - QWidget中两个常用的函数
调整控件或父窗口的位置
void move(int x, int y); //相对于左上角的位置
调整控制或父窗口的大小
void resize(int w, int h) //相对于电脑的像素
二、信号和槽机制
1. 概念
信号和槽是Qt中自定义的一种通信机制,实现对象之间的交互,当某个对象发生改变时将会发送信号,该信号可以被其他对象接收,接收以后将执行一个执行的成员函数(槽函数)。
2. 定义
- 包括信号或槽的类必须是Object的子类
- 信号使用“signals:”标记,信号函数只需声明,不能写定义
- 槽使用“slots:”标记,槽函数可以与某个信号建立连接,通过某个信号触发槽函数的执行;另外槽函数也可以当做是普通的成员函数,直接调用
- 包含信号或槽的类,前面需要添加宏“Q_OBJECT”,将来构建工程时,会调用moc(元对象编译器)将语法扩展的信号或槽还原为标准的C++代码。
1
2
3
4
5
6
7class XX::public Object{
Q_OBJECT //moc
signals:
void sigFunc(void);//信号函数
public slots:
void slotFunc(void){…}//槽函数
}3. 信号和槽连接
注: SIGNAL(信号函数(参数表)) //将信号函数转换为const char*1
2
3
4
5QObject::connect(
const QObject* sender, //信号发送对象,可以是QObject所有的子类类型
const QObject* signal, //信号函数
const QObject* receiver, //信号的接收对象,可以是QObject所有的子类类型
const QObject*method); //槽函数
SLOT(槽函数(参数表)) //将槽函数转换为const char*4. 信号与槽连接的语法要求
- 信号和槽参数要一致(绝大多数情况下)
1
2QObject::connect(A,SIGNAL(sigFunc(int)),B,SLOT(slotFunc(int)));//ok
QObject::connect(A,SIGNAL(sigFunc(void)),B,SLOT(slotFunc(int)));//error - 可以带有缺省参数,多余参数将被忽略
- 信号函数的参数可以比槽函数参数多,多余参数将会被忽略
- 一个信号可以同时连接多个槽函数(一对多)(同一线程上,按顺序执行)
1
2QObject::connect(A,SIGNAL(sigFunc(int)),B1,SLOT(slotFunc1(int)));//ok
QObject::connect(A,SIGNAL(sigFunc(int)),B2,SLOT(slotFunc2(int)));//ok - 多个信号可以同时连接到同一个槽函数(多对一)注:无论A1或A2发送信号,B的槽函数都会被执行
1
2QObject::connect(A1,SIGNAL(sigFunc1(int)),B,SLOT(slotFunc(int)));//ok
QObject::connect(A2,SIGNAL(sigFunc2(int)),B,SLOT(slotFunc(int)));//ok - 两个信号函数可以直接连接(信号串联)//了解注:当A1发送信号时,所连接的A2对象的信号也将被发送
1
QObject::connect(A1,SIGNAL(sigFunc1(int)), A2,SIGNAL(sigFunc2(int)));//ok
- *A1->A2->B**
5. QSlider(滑块)/QSpinBox(选值框)
案例:
代码实现
main函数
1 | #include "dialog.h" |
效果展示
三、面向对象的Qt编程
1. 基于对象的Qt编程(之前的编写方式)//不推荐
2. 面向对象的Qt编程
案例:实现加法计算器
QLineEdit(行编辑)
- 左右操作数只能输入数字形式的内容
- 初始化等号按钮为禁用状态,等到左右操作数都输入有效数据再恢复按钮为正常状态
- 点击等号按钮时,计算和显示结果
1
2
3
4
5
6
7
8
9
10
11思路:
class CalculatorDialog:public QDialog{
Q_OBJECT//moc
public:
构造函数(){界面初始化,信号与槽连接}
public slots:
void 恢复等号按钮正常状态的槽函数(){}
void 计算和显示结果的槽函数(){}
private:
QLineEdit、QLabel、QPushButton
}; - 自定义信号、自定义槽函数
signals: public slots:
代码实现
头文件
1 | #ifndef __CALCULATORDIALOG_H |
实现
1 | #include "CalculatorDialog.h" |
效果展示
四、qt设计师使用(designer)
案例:使用设计师重构加法计算器
创建工程目录
进入工程目录,启动设计师
- 新建窗体
- 在设计师界面中完成ui设计
- 从“widget box”中拖拽出相应的控件到父窗口
- 设置父窗口和每个控件的属性
父窗口:objectname(对象名)
注:将来会根据父窗口的对象名生成一个名字相同的类,包含在设计师完成的所有代码 - 调整父窗口和控件大小和位置
方法一:鼠标拖拽
方法二:键盘,调整位置(方向键(一次十个像素),Ctrl+方向键(一次一个像素))
方法三:设置几何属性(geometry),调整位置(x,y)大小(宽度,高度)调整大小(shift+方向键(一次十个像素),Ctrl+shift+方向键(一次一个像素))(左上角点不动)
方法四:使用布局器(右键选择布局)//推荐
- 使用界面编辑器(uic),将ui文件转换为C++.h文件
ui_*.h文件中的内容
1 | class CalculatorDialog:public Ui_CaculatorDialog(){ |
注:Ui名称空间的子类(Ui::CalculatorDialog)和上面基类(Ui_CalculatorDialog)相同
- 使用“ui_CaculatorDialog.h”文件,复用里面代码方法
- 方法1:继承
1
2
3class MyClass:public Ui::CalculatorDialog{
//将界面相关代码继承过来直接使用
}; - 方法2:组合
1
2
3
4
5
6class MyClass{
public:
MyClass():ui(new Ui::CalculatorDialog){}
private:
Ui::CalculatorDialog* ui;//之后可以通过“ui->”访问和界面相关代码
}; - *案例:登录对话框**
- 创建工程目录
- 进入工程目录,启动设计师,完成ui设计
echoMode: Password//用密码的方式显示文本
layoutDirection: LeftToRight//改为从左向右的布局方式
代码实现
头文件
1 | #ifndef __LOGINDIALOG_H |
实现
1 | #include "LoginDialog.h" |
效果展示
五、Qt创造器使用(qtcreator)
使用技巧
F4可以实现在头文件与源文件之间快速切换
对象用.
对象指针用->
QString str;
qDebug() << str;//类似C++的信息流,打印消息到通知栏
案例:获取系统时间
代码实现
头文件
1 | #ifndef __timeDIALOG_H |
实现
1 | #include "timedialog.h" |
效果展示
六、资源和图像
- 资源编译器(rcc)
- 创建资源文件(.qrc)
- 将资源文件中描述的图片资源编辑器转换为C++源代码
2.绘图事件(paintEvent)和画家类(QPainter)
- 当应用程序窗口改变时(启动,拉伸,拖拽,最大化最小化…)将会触发绘图事件,对应的事件处理函数paintEvent将会被执行。
virtual void QWidget::paintEvent(QPaintEvent*); - 通过调用update()/repaint()函数也可以手动触发绘图事件
- 绘图事件处理函数是虚函数,如果希望在自定义子类中实现指定的图像绘制,可以重写绘图事件处理函数,对基类中的版本形成覆盖,并在其中通过画家类完成绘制操作。
- QPainter是Qt中二维图形引擎,可以实现各种图像、图片的绘制。
案例:图图秀
- 使用qt创造器创建工程
- 工程名:ShowImage
- 类名:ShowImageDialog
- 向工程中添加资源
- 将需要使用资源图片(images)拷贝到工程目录下
- 添加新文件:qt-》qt resource file
- 指定资源文件名字:showImage(将来会自动生成showImage.qrc的资源文件)
- 下一步完成,默认进入文件编辑界面
- 添加:添加前缀-》添加文件,选择“image”目录下的所有图片并打开
- 双击.ui文件,进入设计模式,完成界面设计
- 拖拽需要使用的控件
frame(显示框架)pushbutton(实现图片上一张下一张的切换) - 设置属性
1
2
3frame:
sizePolicy:垂直策略(Expanding)//让控件在设置布局的过程中,尽可能的多占据空间,而不是跟其他的控件平均分配空间
FrameShape:Box//一种窗口的显示主题 - 使用布局器调整大小和位置
- 编码、测试
代码实现
头文件
1 | #ifndef DIALOGSHOWIMAGEDIALOG_H |
实现
1 | #include "dialogshowimagedialog.h" |
效果展示
七、目录(QDir)和定时器(QTime)
- 目录QDir
- 创建目录对象
QDir dir(“/home/tarena/csd1911/qt/photos”);//绝对路径
QDir dir(“./photos”);//相对路径 - 遍历目录下内容
QStringList list1 = dir.entryList(QDir::dirs|QDir::NoDotAndDotDot);//遍历子目录,并且排除.与..目录
QStringList list1 = dir.entryList(QDir::Files);//遍历普通文件
- 定时器
定时器事件:timerEvent
//定时器事件处理函数,定时器到时后将自动触发执行
virtual void timeEvent(QTimerEvent*);
//开启定时器,参数定时器到时的间隔时间(ms,是连续触发的,所以需要关闭),返回定时器ID
int startTimer(int interval);
//关闭定时器
void killTimer(int id);定时器类:QTimer(对上一个事件进行封装)(实现循环操作,相当于while(1),但是while会使窗口卡死)(比定时器事件更灵活)
//创建定时器对象
QTimer timer;
//连接定时器信号和槽函数
connect(&timer,SIGNAL(timeout()),this,SLOT(自定义定时器处理槽函数()));
//开启定时器
timer.start(int msec);
//关闭定时器
timer.stop();
案例:摇摇乐
- 工程名:Lottery
- 类名:LotteryDialog
- 将“photos”目录拷贝到工程目录下,然后到qt创造器的“项目”模式中,去掉Shadow build选项(因为要用相对路径)
- 双击“.ui”文件,进入设计模式,完成ui设计
代码实现
头文件
1 | #ifndef LOTTERYDIALOG_H |
实现
1 | #include "lotterydialog.h" |
效果展示
八、鼠标和键盘
- 鼠标事件
#include <QMouseEvent>
//鼠标按下时执行的事件处理函数
virtual void mousePressEvent(QMouseEvent *);
//鼠标抬起时执行的事件处理函数
virtual void mouseReleaseEvent(QMouseEvent *);
//鼠标移动时执行的事件处理函数
virtual void mouseMoveEvent(QMouseEvent *);
//鼠标双击时执行的事件处理函数
virtual void mouseDoubleClickEvent(QMouseEvent *);
QRect(x,y,w,h);//矩形区域
QPoint(x,y);//位置
QSize(w,h);//大小
案例:鼠标测试,实现用鼠标左键拖拽“标签”移动
- 工程名:Mouse
- 类名:MouseDialog
- 界面设计:设置label控件背景颜色
方法1:样式表(styleSheet)
方法2:调色板(palette)(只能二选一)
autoFillBackground:勾选
palette:点击“继承”->“改变调色板”
编辑调色板:选择颜色
代码实现
头文件
1 | #ifndef MOUSEDIALOG_H |
实现
1 | #include "mousedialog.h" |
效果展示
- 键盘事件
#include <QKeyEvent>
virtual void keyPressEvent (QKeyEvent *);//按键按下时执行的事件处理函数
virtual void keyReleaseEvent(QKeyEvent *);//按键抬起时执行的事件处理函数
案例:键盘按键测试,实现通过键盘方向键控制label移动
- 工程名:Keyboard
- 类名:KeyboardDialog
代码实现
头文件
1 | #ifndef KEYBOARDDIALOG_H |
实现
1 | #include "keyboarddialog.h" |
效果展示
九、Qt多线程(QThread)
创建线程方法1:QObject::moveToThread
class Myclass:public QObject{
Q_OBJECT
public slots:
void func(void){//耗时或阻塞操作,需要放在子线程中执行}
};
QThread thread;//子线程对象
Myclass myobject;//需要放在子线程中工作的对象
myobject.moveToThread(&thread);//将myobject对象移动到子线程中
connect(xx,SIGNAL(XX),&myobject,SLOT(func()));//连接信号和槽函数
thread.start();//开启子线程(exec),麻烦,但是更灵活创建线程方法2:继承QThread,重写线程入口函数run
1
2
3
4
5
6
7
8class MyClass::public QThread{
protected:
virtual void run(void){
//耗时或阻塞操作,需要放在子线程中执行
}
};
MyClass mythread;//创建子线程对象
mythread.start();//开启子线程,子类中重写run函数将会在子线程中执行(不够灵活,但是简单)
案例:多线程打印消息
- 方法1:工程(Thread1)
- 方法2:工程(Thread2)
- 线程同步
- 互斥锁:QMutex(加锁lock()会让其他线程在同样的事件中,进入阻塞…)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16eg:
QMutex mutex;
void run(void){
mutex.lock();//加锁
//访问共享资源start
if("Error1"){
mutex.unlock();//解锁
return;
}
else if("Error2"){
mutex.unlock();//解锁
return;
}
//访问共享资源end
mutex.unlock();//解锁
}
- QMutexLocker简化加锁和解锁过程;
- 互斥锁:QMutex(加锁lock()会让其他线程在同样的事件中,进入阻塞…)
1
2
3
4
5
6
7
8
9
10
11
12
13eg:
QMutex mutex;
void run(void){
QMutexLocker locker(&mutex);//加锁(不需要写解锁)
//访问共享资源start
if("Error1"){
return;
}
else if("Error2"){
return;
}
//访问共享资源end
}//析构locker时,在析构函数中自动解锁 - 信号量:QSemaphore
//初始化信号量计数值5,表示有5个可用共享资源
QSemaphore sem(5); // sem.available() == 5
//获取3个共享资源,剩余2个可用的共享资源
sem.acquire(3); // sem.available() == 2
sem.acquire(2); // sem.available() == 0
//释放5个共享资源,剩余5个可用的共享资源
sem.release(5); // sem.available() == 5
sem.release(5); // sem.available() == 10
//尝试获取1个共享资源,剩余9个可用的共享资源,获取成功
sem.tryAcquire(1); // sem.available() == 9, returns true
//尝试获取250个共享资源,获取失败,会返回错误
sem.tryAcquire(250); // sem.available() == 9, returns false
案例:生产者和消费者
1 | #include <QtCore> |
十、Qt数据库
- 数据库简介
- 概念
数据库是以一定的方式存储在一起、能为用户共享、具有尽可能小的冗余特性,是与应用程序彼此独立的数据集合。 - 相关术语
DB:数据库(database)
DBA:数据库管理员
RDB:关系式数据库
DBMS:数据库管理系统 - 常见的数据库
//大型的商业数据库
甲骨文的Oracle
IBM的DB2
微软的Sqlserver
//免费的数据库
Sun的Mysql
开源的Sqlite - 数据库操作语言(SQL)
- Sqlite数据库
Windows安装Sqlite
请访问 SQLite 下载页面https://www.sqlite.org/download.html,从 Windows 区下载预编译的二进制文件。
需要下载 sqlite-tools-win32-.zip 和 sqlite-dll-win32-.zip 压缩文件。
创建文件夹 C:\sqlite,并在此文件夹下解压上面两个压缩文件,将得到 sqlite3.def、sqlite3.dll 和 sqlite3.exe 文件。
添加 C:\sqlite 到 PATH 环境变量,最后在命令提示符下,使用 sqlite3 命令,将显示如下结果。Linux安装SQLite
目前,几乎所有版本的 Linux 操作系统都附带 SQLite。所以,只要使用下面的命令来检查您的机器上是否已经安装了 SQLite。
$ sqlite3测试
在“sqlite>”命令界面中可以输入两种指令,一种指令是以“.”开头,可以实现对数据库配置和设置数据显示方式;
另一种指令是“Sql语句”,这些指令以“;”结尾,实现对数据的增删改查等管理操作。常用的sqlite自身配置和格式显示相关命令
.help//打开帮助
.exit//退出数据库
.database//查看当前数据库名称
.open testDB.db//打开数据库文件.db
.table//查看数据库中数据表的名字
.mode MODE//设置数据表显示模式,MODE:list(默认)/column/tab/html…
.header on//显示数据的表头
.schema//查看数据表创建时的详细信息
.nullvalue “NULL”//设置数据表空白位置显示数值“NULL”
注:清屏“Ctrl+L”
注:执行“SELECT * FROM company;”可以查看company数据中所有的数据。
注:可以将上述指令写入配置文件中,将来重新进入数据库界面时会自动执行。
- 使用SQL语言操作数据库//重点
- 创建数据表
语法:
CREATE TABLE 表名 (列名1 数据类型 [约束], 列名2 数据类型 [约束],…);
常用数据类型:
INT(整型数)TEXT(字符串)REAL(浮点数)
常用约束:
NOT NULL:非空约束,表示该类数据不能为空
PRIMARY KEY:主键约束,表示该列数据唯一,可以加快对数据的访问 - 删除数据表
语法:
DROP TABLE 表名;
注:慎用,数据表一旦删除,里面所包含的数据也将随之消失! - 向数据表插入数据
语法:
INSERT INTO 表名(列名1,列名2,…) VALUES(数值1,数值2,…); - 从数据表中删除数据
语法:
DELETE FROM 表名WHERE 条件表达式;//删除满足条件的若干条数据
DELETE FROM 表名WHERE 条件1 and 条件2;//删除同时满足两个条件的数据
DELETE FROM 表名WHERE 条件1 or 条件2;//删除满足条件1或条件2的数据 - 修改数据表中已存在的数据
语法:
UPDATE 表名 SET 列名1=新数值,列名2=新数值,… WHERE 条件表达式;//条件表达式中一般是要修改数据的id号 - 查询数据表中的数据
语法:
SELET 列名1,列名2,… FROM 表名;
SELET 列名1,列名2,… FROM 表名 WHERE 条件表达式;
SELET 列名1,列名2,… FROM 表名 WHERE 条件表达式 ORDER BY 列名 排列方式;
注:排序方式关键字ASC(升序)/DESC(降序)
- 在Qt应用程序中使用Sqlite数据库(QT += sql)
建立Qt应用程序和数据库连接 QSqlDatabase
//添加数据库驱动
QSqlDatabase db = QSqlDatabase: :addDatabase (“OSQLITE”‘);
//设置数据库名字(对于sqlite就是数据库文件名)
db.setDatabaseName ( “testDB.db” ) ;
//打开数据库
bool ok = db.open () ;执行SQL语句 QSqlQuery
//创建QsqlQuery对象
QsqlQuery query;
//准备要执行的sQL语句字符串
QString str = Qstring ( “SQL语句”);//执行sql语句
query.exec (str) ;保存查询结果集 QSqlQueryModel
//创建QSqlQueryModel对象
QSqlQueryModel *model = new QSqlQueryModel;1l执行SELECT语句并保存结果集到model
model->setQuery ( “SELECT语句”);
/将model保存结果显示图形控件(QTableView)上QTableview *view = new QTableview ;
view->setModel (model) ;
view->show () ;
案例:学生成绩管理系统
- 工程名:Student
- 类名:StudentDailog
- .pro文件中QT += sql
- 界面设计
拖拽需要使用的控件
ComboBox(2个,组合框)
PushButton(4个)
Label(3个)
LineEdit(3个)
TableView(1个,表格:用于显示查询结果集)
设置对象名
ComboBox:valueComboBox condComboBox
PushButton:sortButton insertButton updateButton deleteButton
LineEdit:idEdit nameEdit scoreEdit
Label:默认
TableView:默认
十一、Qt网络编程
- 网络编程基础
- 网络协议模型(OSI七层)
应用层:HTTP
表示层
会话层
传输层:TCP、UDP(TCP安全、UDP效率高)
网络层:IP(IPV4/IPV6)
数据链路层
物理层
- IP地址
- 概念:
互联网中的唯一地址标识 - IP地址表示方式:
IPV4(32位整数)、IPV6(128位整数)
点分十进制:“192.168.15.100”
无符号整数:0xC0A80F64 - 查看IP地址命令
windows系统:ipconfig
linux系统:ifconfig - 通过IP地址判断两台主机能否通信
ping 对方的IP地址; - 特殊的IP地址
“0.0.0.0”//任意地址,INADDR_ANY,常用于服务器
“127.0.0.1”//本地环回地址,常用于测试
“255.255.255.255”//广播地址
- Qt中和网络编程相关类(Qt+= network)
- 套接字基类:QAbstractsocket
- TCP通信套接字:Tcpsocket
- TCP服务器:TcpServer
- UDP通信套接字:QUdpsocket
- IP地址:QHostAddress
- 常见问题
- windows空闲端口号查询方式
如下图所示,在cmd中使用指令查询所有端口号
如下图所示,在cmd中使用指令查询所需端口号是否为空闲状态
qt同时运行多个实例时,需要将工具-选项-构建和运行中的stop application before building设置为None
案例:基于TCP协议的网络聊天室
- TCP服务器(Server)(服务器:IP、port固定)
使用QTcpServer创建服务器
响应客户端连接请求,保存和客户端通信套接字(socket)
实时接收客户端发来的消息
转发消息给所有的客户端
代码实现
头文件
1 | #ifndef SERVERDIALOG_H |
实现
1 | #include "serverdialog.h" |
- TCP客户端(Client)
使用QTcpSocket创建和服务器通信的套接字
向服务器发送连接请求
输入聊天消息,发送到服务器
接收服务器转发的消息并显示
代码实现
头文件
1 | #ifndef CLIENTDIALOG_H |
实现
1 | #include "clientdialog.h" |
效果展示