目录标题
一、 QT介绍
Qt(官方发音 [kju:t],音同 cute)是一个跨平台的 C++ 开发库,主要用来开发图形用户界面(Graphical User Interface,GUI)程序
,当然也可以开发不带界面的命令行(Command User Interface,CUI)程序。
Qt4 时代的主流就是传统部件(或叫控件)编程,所用的语言一般是 C++。 Qt5 诞生之时,正是手机移动设备蓬勃发展的时候,而传统的 C++ 部件编写的界面对手机应用程序非常方便,比如手机屏幕显示随意翻转, 这在传统桌面程序里基本遇不到,谁会将 22 寸显示器翻过来转过去呢。
为了适应手机移动应用开发, Qt5 将 QML 脚本编程提到与传统 C++ 部件编程相同的高度,力推 QML 界面编程,当然 QML 主要用于手机移动应用程序。 QML 包含大量使用手机移动设备的功能模块,比如基本部件(QtQuick 模块)、GPS 定位、渲染特效、蓝牙、NFC、WebkKit 等等。
时代在变化,C++ 标准也在前进。C++ 正式公布标准有 C++98、C++03、C++11。最新的 C++11 标准是2011年8月12日公布的,在公布之前该标准原名为 C++0x 。这是一次较大的修订和扩充,建议读者专门学一下。
Qt 从 4.8 版本就开始用 C++11 新特性了。编译器里面开始支持 C++11 的版本是 MSVC 2010、GCC 4.5、Clang 3.1,这之后版本的编译器都在逐步完善对 C++11 的支持,现在新版本编译器对新标准的支持都比较全面了。
Qt 官方在编译 Qt5 库的时候都是开启 C++11 特性的,如果我们要在自己项目代码启用新标准,需要在 .pro 文件里面添加一行:
(一)下载安装
目录 | 说明 |
---|---|
archive | 各种 Qt 开发工具安装包,新旧都有(可以下载 Qt 开发环境和源代码)。 |
community_releases | 社区定制的 Qt 库,Tizen 版 Qt 以及 Qt 附加源码包。 |
development_releases | 开发版,有新的和旧的不稳定版本,在 Qt 开发过程中的非正式版本。 |
learning | 有学习 Qt 的文档教程和示范视频。 |
ministro | 迷你版,目前是针对 Android 的版本。 |
official_releases | 正式发布版,是与开发版相对的稳定版 Qt 库和开发工具(可以下载Qt开发环境和源代码)。 |
online | Qt 在线安装源。 |
snapshots | 预览版,最新的开发测试中的 Qt 库和开发工具。 |
- 安装好qt后,配置两个环境变量
//换成自己的安装目录
D:\it\qt5.9.8\5.9.8\mingw53_32\bin
D:\it\qt5.9.8\Tools\mingw530_32\bin
- 安装cmake
参考
- 配置cmake
安装好后,在win开始页面会有很多应用。介绍如下:
程序 | 说明 |
---|---|
Qt Creator 4.6.2 (Enterprise) | Qt 的集成开发环境,本教程就使用它来创建和管理 Qt 项目。 |
Assistant(Qt 助手) | 用来查看帮助文档,已被集成在 Qt Creator 中。 |
Designer(Qt 设计师) | 图形界面可视化编辑工具,已被集成在 Qt Creator 中,在 Qt Creator 中编辑或创建界面文件时,就可以自动打开。 |
Linguist(Qt 语言家) | 多国语言翻译支持工具,可以用来编辑语言资源文件,在开发多语言界面的应用程序时会用到。 |
Qt 5.11.1 for Desktop (MinGW 5.3.0 32bit) | Qt 命令行工具,用来配置 Qt 开发环境(主要是设置 PATH 变量)。 |
找到Qt Creator
并打开。
(二)集成开发环境Qt Creator使用
Qt Creator可视化页面介绍
- 打开是这个样子
二、hello world
(一)新建项目
(二)打印hello world
qt 框架封装了打印日志的方法,在QDebug类中。使用方法跟cout类似。
- mainwindow.cpp 。
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug> //导入日志包
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
//这个表示对属性ui进行初始化 初始值为:new Ui::MainWindow。用逗号分割,后面可以初始化很多属性
ui(new Ui::MainWindow)
{
// 将 mainwindow.ui 的实例对象和 当前类的对象进行关联(解析 mainwindow.ui),将这个代码放最前面
ui->setupUi(this);
//其他组件和业务代码
//一共有四种日志级别。
qDebug()<< "hello world!"<<"——————debug级别。"; //注意是qDebug() ,不是qDebug
qWarning() << "Warning:" << "错误级别。";
qInfo() << "qInfo:" << "正常输出级别。";
qCritical() << "qCritical:" ;
}
MainWindow::~MainWindow()
{
delete ui;
}
当更改代码后需要重新构建项目,代码才会是生效。
- 输出
(三)使用Qt Creator查看文档
需要哪个方法就点进去看,看不懂可以去翻译一下。
三、qt提供的数据类型
(一)字符串
- QByteArray
- QString
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QByteArray> // 导入字符串
#include <QString>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
// 将 mainwindow.ui 的实例对象和 当前类的对象进行关联(解析 mainwindow.ui),将这个代码放最前面
ui->setupUi(this);
//其他组件和业务代码
// 使用无参构造创建一个类
QByteArray s;
s.append("lihua");
qDebug()<<s;
QByteArray s1("my name is ");
s1.append("lihua");
qDebug()<<s1;
QString str("123");
str.append("-456");
qDebug()<<str;
}
MainWindow::~MainWindow()
{
delete ui;
}
(二)坐标QPoint
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPoint>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//创建一个坐标点A,并调用方法设置坐标点的x、y轴
QPoint pointA;
pointA.setX(10);
pointA.setY(20);
//调用有参构造器创建一个坐标点
QPoint pointB(40,80);
//无参构造器默认x、y轴为(0,0)
QPoint pointC;
//获取坐标点的x、y轴的值。
qDebug()<< pointA.x();
qDebug()<< pointB.y();
qDebug()<<"x=" <<pointC.x()<<"y="<<pointC.y();
}
MainWindow::~MainWindow()
{
delete ui;
}
(三)直线QLine
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPoint> //坐标
#include <QDebug>
#include <QLine> //直线,由两个坐标点构成
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//创建一个坐标点A,并调用方法设置坐标点的x、y轴
QPoint pointA;
pointA.setX(10);
pointA.setY(20);
//调用有参构造器创建一个坐标点
QPoint pointB(40,80);
//无参构造器默认x、y轴为(0,0)
QPoint pointC;
//获取坐标点的x、y轴的值。
qDebug()<< pointA.x();
qDebug()<< pointB.y();
qDebug()<<"x=" <<pointC.x()<<"y="<<pointC.y();
//QLine()
//QLine(const QPoint &p1, const QPoint &p2)
//QLine(int x1, int y1, int x2, int y2)
QLine line(pointC,pointA);
qDebug() << "直线的中点"<<line.center().x()<<","<<line.center().y();
}
MainWindow::~MainWindow()
{
delete ui;
}
(四)形容二维对象的大小QSize
QSize类使用整数点精度定义二维对象的大小。
尺寸由宽度()和高度()指定。它可以在构造函数中设置,并使用setWidth()、setHeight()或scale()函数或算术运算符进行更改。还可以通过使用rwidth()和rheight()函数检索对宽度和高度的引用来直接操作大小。最后,可以使用transpare()函数交换宽度和高度。
isValid()函数确定大小是否有效(有效大小的宽度和高度均大于或等于零)。如果宽度和高度中的任何一个小于或等于零,isEmpty()函数将返回true,而只有当宽度和高度都为零时,isNull()函数才返回true。
使用expandedTo()函数检索一个大小,该大小包含此大小和给定大小的最大高度和宽度。类似地,boundedTo()函数返回一个大小,该大小包含此大小和给定大小的最小高度和宽度。
QSize对象可以流式传输,也可以进行比较。
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QSize> // QSize 类用来形容长度和宽度
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//QSize 类用来形容长度和宽度
QSize size(10,19);
//交换宽和高,并返回他们交换后的值
QSize sizeTran = size.transposed();
qDebug()<<sizeTran.width();
qDebug()<<sizeTran.height();
}
MainWindow::~MainWindow()
{
delete ui;
}
(五)矩形QRect
QRect类使用整数精度定义平面中的矩形。矩形通常表示为左上角和一个大小。QRect的大小(宽和高)总是等价于数学矩形,构成了它的渲染基础。 QRect可以用一组左、上、宽、高整数来构造,也可以用一个QPoint和一个QSize来构造。
- 两种构建方式:
QRect r1(100, 200, 11, 16);
QRect r2(QPoint(100, 200), QSize(11, 16));
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
void paintEvent(QPaintEvent *);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPoint> //坐标
#include <QDebug>
#include <QLine> //直线,由两个坐标点构成
#include <QSize> // QSize 类用来形容长度和宽度
#include <QPainter> //画家类
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
void MainWindow::paintEvent(QPaintEvent *){
//创建一个坐标点A,并调用方法设置坐标点的x、y轴
QPoint pointA;
pointA.setX(10);
pointA.setY(20);
//调用有参构造器创建一个坐标点
QPoint pointB(40,80);
//无参构造器默认x、y轴为(0,0)
QPoint pointC;
//获取坐标点的x、y轴的值。
qDebug()<< pointA.x();
qDebug()<< pointB.y();
qDebug()<<"x=" <<pointC.x()<<"y="<<pointC.y();
//QLine()
//QLine(const QPoint &p1, const QPoint &p2)
//QLine(int x1, int y1, int x2, int y2)
QLine line(pointC,pointA);
qDebug() << "直线的中点"<<line.center().x()<<","<<line.center().y();
//QSize 类用来形容长度和宽度
QSize size(200,120);
//交换宽和高,并返回他们交换后的值
QSize sizeTran = size.transposed();
qDebug()<<sizeTran.width();
qDebug()<<sizeTran.height();
QRect rect(pointA,size);
//实例化画家对象 this指定的是绘图设备
QPainter painter(this);
//设置画笔
QPen pen(QColor(255,0,0));
//设置画笔宽度
pen.setWidth(3);
//设置画笔风格
pen.setStyle(Qt::DotLine);
//让画家 使用这个笔
painter.setPen(pen);
//设置画刷
QBrush brush(Qt::cyan);
//设置画刷风格
brush.setStyle(Qt::Dense7Pattern);
//让画家使用画刷
painter.setBrush(brush);
//画矩形
painter.drawRect(rect);
}
MainWindow::~MainWindow()
{
delete ui;
}
(六)时间QDate、QTime、QDateTime
//时间
void MainWindow::dateTest(){
//获取空日期对象,没有办法直接使用,需要自己添加日期
QDate date1;
date1.setDate(2022,10,12);
//在日期的基础上增加12天
QDate date3 = date1.addDays(12);
//通过有参构造器获取日期对象
QDate date2(2022,10,10);
//通过方法获取当前日期对象
QDate nowDate = QDate::currentDate();
//日期格式化输出
QString dateStr = nowDate.toString("yyyy-M-dd");
qDebug()<< date1;
qDebug()<< date3;
qDebug()<< date2;
qDebug()<< nowDate;
qDebug()<< dateStr;
//获取时间对象,h must be in the range 0 to 23, m and s must be in the range 0 to 59, and ms must be in the range 0 to 999.
QTime time(12,22,22,999);
//获取当前时间
QTime nowTime = QTime::currentTime();
qDebug()<<time;
qDebug()<<nowTime;
//格式化输出
qDebug()<<nowTime.toString("hh:mm:ss");
//获取日期+时间
QDateTime dateTime(QDate::currentDate(),QTime::currentTime());
qDebug()<<dateTime;
//格式化输出
qDebug()<<dateTime.toString("yyyy-MM-dd hh:mm:ss");
//获取时间戳,单位毫秒
qDebug()<< QDateTime::currentMSecsSinceEpoch();
//获取时间戳,单位秒
qDebug()<< QDateTime::currentSecsSinceEpoch();
}
四、信号\槽
信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式 (发布 - 订阅模式)。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPushButton>
#include <QTextEdit>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//创建一个按钮
QPushButton * btn = new QPushButton("get_name",this);
btn->move(10,40);
//一个文本输入框
QTextEdit *textName = new QTextEdit("getName",this);
textName->move(10,100);
//为按钮添加一个点击(鼠标点击一次就是信号)事件(点击鼠标后触发的事件就是槽),下面的是Lambda表达式
connect(btn,&QPushButton::clicked,textName,[=](){
qDebug()<<"ok";
textName->setText("lihua");
});
}
MainWindow::~MainWindow()
{
delete ui;
}
信号和槽机制其实就是发布订阅模式。
- 订阅一个信号:connect(this,&MainWindow::signalMain,thread,&MyWork::slotWork);连接函数就相当于订阅操作,订阅了这个信号signalMain 。
- 生产者发布一个信号(注意:这个发布的信号是一次性的,调用一次emit ,就发送一个。同时已经订阅了这个信号的消费者才会接受到这个信号,也就是先订阅再发布信号) emit signalMain(10); 发送信号的时候也可以传递一些参数给回调函数。比如这里的10。
- 消费者接收到这个信号,调用回调函数(槽函数)。消费者如何接收到信号的,这个是由qt框架实现的。回调函数(槽函数)&MyWork::slotWork
一个向子线程传递参数的例子:
- mywork.h
#ifndef MYWORK_H
#define MYWORK_H
#include <QObject>
#include <QThread>
/**
* @class mywork.h
*
* @brief 工作线程
*
*
* @author: lihua
* @date: 2022-10-25 15:53
*/
class MyWork : public QThread
{
Q_OBJECT
public:
explicit MyWork(QThread *parent = nullptr);
signals:
//信号由子线程发出,主线程接收(子线程返回执行结果给主线程)
void signalWork(int result);
public slots:
//信号由主线程发出,子线程接收(主线程传入参数给子线程)
void slotWork(int num);
// QThread interface
protected:
void run();
private:
int num = 0;
};
#endif // MYWORK_H
- mywork.cpp
#include "mywork.h"
#include <QDebug>
MyWork::MyWork(QThread *parent) : QThread(parent)
{
}
void MyWork::slotWork(int num)
{
//调用槽函数
qDebug()<< "子线程的槽函数被调用"<<num;
this->num = num;
}
/**
* @brief 主线程传一个初始值num给子线程,子线程在num的基础上加100
*
* @param run没有参数,num是通过槽函数获取的
*
* @return run没有返回值将计算结果通过槽函数返回给主线程
*/
void MyWork::run()
{
qDebug()<< "运行子线程<<" << QThread::currentThread();
//主线程给一个初始值num
int num = this->num;
int result = num +100;
//发出信号,订阅了此信号的订阅者的槽函数会被回调,将计算结果通过槽函数返回给主线程
qDebug()<< "结果:"<<result;
emit signalWork(result);
}
- mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "mywork.h"
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
//子线程
MyWork *thread;
// 计算结果
int result;
signals:
//信号
void signalMain(int num);
public slots:
//槽函数
void slotMain(int result);
};
#endif // MAINWINDOW_H
- mainwindow.cpp
#include "mainwindow.h"
#include "mywork.h"
#include "ui_mainwindow.h"
#include <QThread>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
qDebug()<< QThread::currentThread();
thread = new MyWork();
//订阅signalMain信号,并指定回调函数。
connect(this,&MainWindow::signalMain,thread,&MyWork::slotWork);
//订阅signalWork信号,并指定回调函数
//connect(thread,&MyWork::signalWork,this,&MainWindow::slotMain); 也可以用lamda实现
connect(thread,&MyWork::signalWork,this,[=](int result){
this->result = result;
qDebug()<< "result="<<this->result;
});
//发出一个信号,将10通过槽函数slotWork传递给run
emit signalMain(10);
thread->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::slotMain(int result)
{
this->result = result;
qDebug()<< "result="<<this->result;
}
五、Lambda 表达式
- 格式:
- 捕获列表:捕获一定范围内的变量
- [] - 不捕捉任何变量
- [&] - 捕获外部作用域中所有变量,并作为引用在函数体内使用 (按引用捕获)
- [=] - 捕获外部作用域中所有变量,并作为副本在函数体内使用 (按值捕获) 拷贝的副本在匿名函数体内部是只读的
- [=, &foo] - 按值捕获外部作用域中所有变量,并按照引用捕获外部变量 foo
- [bar] - 按值捕获 bar 变量,同时不捕获其他变量
- [&bar] - 按引用捕获 bar 变量,同时不捕获其他变量
- [this] - 捕获当前类中的 this 指针
- 让 lambda 表达式拥有和当前类成员函数同样的访问权限
- 如果已经使用了 & 或者 =, 默认添加此选项
- 参数列表:和普通函数的参数列表一样
- opt 选项 –> 可以省略
- mutable: 可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)
- exception: 指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw ();
- 返回值类型:
标识函数返回值的类型,当返回值为 void,或者函数体中只有一处 return 的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略 - 函数体:
函数的实现,这部分不能省略,但函数体可以为空。
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPushButton>
#include <QTextEdit>
#include <QDebug>
#include <QTimeEdit>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//创建一个按钮
QPushButton * btn = new QPushButton("get_name",this);
btn->move(10,40);
//一个文本输入框
QTextEdit *textName = new QTextEdit("getName",this);
textName->move(10,100);
//在mainwindow.ui文件创建一个时间编辑器。
QTimeEdit *timeEdit = new QTimeEdit(this);
timeEdit->move(10,200);
//创建一个设置时间的按钮
QPushButton *updateTime = new QPushButton("设置时间",this);
updateTime->move(150,200);
//为按钮添加一个点击(鼠标点击一次就是信号)事件(点击鼠标后触发的事件就是槽),下面的是Lambda表达式
connect(btn,&QPushButton::clicked,textName,[=](){
textName->setText("lihua");
});
//为时间编辑器创建一个事件
connect(updateTime,&QPushButton::clicked,[=](){
//设置为当前时间
timeEdit->setTime(QTime::currentTime());
});
}
MainWindow::~MainWindow()
{
delete ui;
}
六、类型转换
QObject *obj = new MyWidget;
QWidget *widget = qobject_cast<QWidget *>(obj);