1. Qt 数据类型
1.1 数字类型
-
整型: qint8、qint16、qint32、qint64
-
无符号整型: quint8、quint16、quint32、quint64
-
qintptr: 指针类型 根据系统类型不同而不同,32位系统为qint32、64位系统为qint64 (Linux)
-
浮点型 qreal;
cpp
using quint16 = unsigned short;
using uint32 = unsigned int;
// 有符号,8位长度
qint8 num1 = 127; // -128-127
qint16 num2 = 32767;
// 无符号,8位长度
quint8 num3 = 255; // 0-255
quint16 num4 = 65535;
qintptr num5 = 2147483647;
// 浮点型,6位数字
qreal num6 = 3.141592653871;
1.2 位置和尺寸
-
QPoint: QPoint类封装了我们常用到的坐标点 (x, y);
-
QLine: QLine是一个直线类, 封装了两个坐标点 (两点确定一条直线);
-
QSize: QSize类用来形容长度和宽度;
-
QRect: QRect类来描述一个矩形;
cpp
QPoint p(50, 100);
qDebug() << QString("x=%1, y=%2").arg(p.rx()).arg(p.ry());
QPoint p1(100, 50);
QLine l(p, p1);
qDebug() << l;
QSize s(200, 300);
qDebug() << s.width() << "---" << s.height();
QRect r(10, 10, 200, 300);
qDebug() << QString("x:%1,y:%2,width:%3,height:%4")
.arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height());
1.3 字符串类型
在 Qt 中使用 QString:高级类型 或 QByteArray:更偏底层,某些情况下,是可以通用的,但是在更偏底层的操作中基本都用QByteArray;例如文件读写 和 网络数据传输。
1.使用 QString 声明字符串
cpp
QString s1 = "Hello";
QString s2 = "你好";
qDebug() << s1 << s2;
2.常用方法
cpp
// 获取字符串长度
s.size();
s.length();
s.count();
// 大小写转换
s.toUpper(); // 转为大写字母
s.toLower(); // 转为小写字母
// 字符串拼接
s.append(" World"); // 向字符串最后追加字符串
s.prepend("~~~"); // 向字符串前面添加字符串
s.insert(2, "abc"); // 在索引2之前插入字符串 abc
// 处理空格
s.trimmed() // 去掉字符串左右的空格
s.simplified() // 去掉字符串左右的空格,字符串中间的多个空格合并为一个空格
// 索引相关
s.indexOf('o'); // 指定字符第一次出现的索引
s.lastIndexOf('o'); // 指定字符最后一次出现的索引
s.at(1); // 查询索引为 1 的字符,并返回
// 判断字符串为空
s.isEmpty(); // 未赋值 和 赋值为空字符串是都是 true
s.isNull(); // 未赋值为 true, 赋值为空是 false
s.contains("e") // 判断字符串 s 中是否包含 'e'
s.startsWith("H") // 判断字符串 s 是否以 'H' 开头
s.endsWith("d") // 判断字符串 s 是否以 'd' 结尾
// 字符串截取
// 参数1: 指定截取的起始点
// 参数2: 指定截取的长度。 可选参数,如果不写,截取到字符串的末尾
s.mid(2); // 从索引2开始截取到字符串最后
s.mid(3, 5); // 从索引3开始截取5个字符长度
// 字符串替换
s.replace('o', "abc"); // 将字符串中的 o 全部替换为 abc
s.section('/', 2, 3); // 使用 / 将字符串拆分,再取出索引2、3部分,再拼接成一个字符串
// 格式化字符串 %n 占位符
QString("x=%1,y=%2,z=%3").arg(30).arg(40).arg(50);
示例:
cpp
QString str1;
QString srt2 = "";
qDebug() << str1.isNull() << str1.isEmpty();
qDebug() << str2.isNull() << str2.isEmpty();
//section(参数1,参数2,参数3)
//使用参数1对字符串进行拆分 返回参数2-参数3之间的数
qDebug() << s.section("/", 2, 3); // local/bin
3.使用 QByteArray 声明字符串
cpp
QByteArray str = "Hello World";
qDebug() << str;
qDebug() << str.toLower();
qDebug() << str.mid(1, 3);
4.QString VS QByteArray
QString 相较 QByteArray 更高级更抽象,QByteArray 则更底层更具体。QString基本单位是一个个的Unicode字符,提供了对字符串操作的方法, QByteArray 的基本单位是一个个的字节,提供了对字节流操作的方法。
QByteArray类便于对数据进行存储和传输,在这样的场景时使用QByteArray类比较合适。而对于其他场景,则使用QString比较合适。因为QString存储的是Unicode字符,使得在应用程序中更容易存储非ASCII和非Latin-1字符,而且QString全部使用的是Qt的API。
-
QByteArray 转 QString : 使用 QString 包裹 QByteArray 类型数据
-
QString 转 QByteArray : 调用 toUtf8() 方法
-
QByteArray 类型调用 data 方法能去掉双引号
cpp
QByteArray s3 = "Hello World";
QByteArray s4 = "你好啊";
QString s5 = "你好!!!";
qDebug() << s3 << s4 << s5;
// QByteArray 转 QString
qDebug() << QString(s4);
// QString 转 QByteArray
qDebug() << s5.toUtf8().data();
// QByteArray 类型调用 data 方法能够去掉双引号
qDebug() << s4.data();
qDebug() << s5.toUtf8().data();
1.4 日期和时间
QDate : 日期 年-月-日
QTime: 时间 时:分:秒
QDateTime : 年-月-日 时:分:秒
cpp
// 实例化日期
QDate d(2023,10,20);
qDebug() << d.toString() << d.toString("yyyy/MM/dd");
qDebug() << QString("%1-%2-%3").arg(d.year()).arg(d.month()).arg(d.day());
// 实例化时间
QTime t(10, 12, 40);
qDebug() << t.toString() << t.toString("hh:mm:ss");
qDebug() << QString("%1:%2:%3").arg(t.hour()).arg(t.minute()).arg(t.second());
// 实例化日期时间
// QDateTime::currentDateTime : 获取当前时间
QDateTime dt = QDateTime::currentDateTime();
qDebug() << dt.toString();
qDebug() << dt.toString("yyyy-MM-dd hh:mm:ss");
qDebug() << dt.date() << dt.date().year();
qDebug() << dt.time() << dt.time().hour();
// QDateTime::currentDateTimeUtc : 获取当前时间(GMT时间)
QDateTime dt1 = QDateTime::currentDateTimeUtc();
qDebug() << dt1.toString();
qDebug() << dt1.toString("yyyy-MM-dd hh:mm:ss");
qDebug() << dt1.date() << dt1.date().year();
qDebug() << dt1.time() << dt1.time().hour();
QDateTime dt2(QDate(2023,12,12), QTime(20,10,50));
qDebug() << dt2.toString("yyyy-MM-dd hh:mm:ss");
1.5 QVariant
-
QVariant 可以存储各种数据类型(万能类型)
-
某些情况下,不知道具体的数据类型时,可以使用 QVariant
-
QVariant 实例化的变量,可以保存任何类型的数据
cpp
QVariant v;
v = 100;
qDebug() << v << v.toInt();
v = 3.1415f;
qDebug() << v << v.toFloat();
v = "Hello World";
qDebug() << v << v.toString();
v = QDate(2023, 12, 12);
qDebug() << v << v.toDate() << v.toString();
2. 容器类
-
Qt 库提供了一组通用的容器类,这些容器类可以用来存储指定类型的数据。例如,如果需要一个QString类型的可变大小的数组,那么可以使用
QList<QString>
。与STL(Standard Template Library,C++的标准模板库)中的容器类相比,Qt中的这些容器类更轻量,更安全,更容易使用。 -
Qt 提供的顺序容器有 QVector、QList、QStack(后进先出)和 QQueue(先进先出)等。由于这些容器中的数据都是一个接一个线性存储的,所以称为顺序容器。
-
Qt 提供的关联容器有 QMap、QMultiMap、QSet等。由于这些容器存储的是<键,值>对,比如QMap<Key, Value>,所以称为关联容器。其中"Multi"容器用来支持一个键多个值的情况。
2.1 QVector
-
QVector 类是顺序容器,它将自己的每一个对象存储在连续的内存中,可以使用下标(索引号)来快速访问
-
可以在任意位置插入和删除元素,但在中间位置进行插入和删除操作时的性能较差。
-
适用于需要频繁随机访问元素的场景。
示例1: QVector 创建和遍历
cpp
// 实例化一个存储 int 类型数据的 QVector, 内部有4个单元, 保存4个数据
QVector<int> intVec = {66, 10, 100, 88};
qDebug() << intVec[0];
qDebug() << intVec[1];
qDebug() << intVec[2];
qDebug() << intVec[3];
遍历方法一(使用循环)
- size()、 count() : 可以计算 QVector 的长度
cpp
for(int i = 0; i < intVec.size(); i++)
{
qDebug() << intVec[i];
}
遍历方法二 (使用迭代器)
-
begin() :返回一个STL类型的迭代器指针指向 vector 的第一个元素
-
end() : 返回一个STL类型的迭代器指针指向 vector 的最后一个元素后面的元素
cpp
// 设置迭代器
QVector<int>::iterator iter1;
for (iter1 = intVec.begin(); iter1 < intVec.end(); iter1++)
{
qDebug() << *iter1;
}
示例2: 常用方法
-
向尾部插入新数据:
-
append(const T &value)
-
push_back(const T &value)
-
-
向头部插入新数据:
-
prepend(const T &value)
-
push_front(const T &value)
-
-
插入任意位置:
- insert(int i, const T &value)
-
删除数据:
-
remove(int i , int len) 删除指定位置数据
-
pop_back() 删除最后一个单元
-
pop_front() 删除第一个单元
-
clear() 清空所有单元
-
-
单元替换:替换指定单元的值
- replace(int i, const T &value)
cpp
QVector<QString> strVec;
// 添加
strVec.append("王者荣耀");
strVec.push_back("LOL");
strVec.prepend("DNF");
strVec.insert(1, "CF");
// DNF CF 王者荣耀 LOL
// 替换
strVec.replace(1, "穿越火线");
// 删除
strVec.pop_back();
strVec.pop_front();
strVec.remove(1);
strVec.clear();
示例3: 常用函数
-
判断是否为空
- isEmpty()
-
索引操作
- indexOf、lastIndexOf
-
弹出单元
-
front() : 获取第一个单元
-
back(): 获取最后一个单元
-
cpp
QVector<QString> strVec = {"王者荣耀", "LOL", "DNF", "王者荣耀", "原神"};
// 计算长度
qDebug() << "strVec的长度为:" << strVec.count();
qDebug() << "strVec的长度为:" << strVec.capacity();
qDebug() << strVec.indexOf("王者荣耀");
qDebug() << strVec.lastIndexOf("王者荣耀");
qDebug() << strVec.back();
qDebug() << strVec.front();
qDebug() << strVec.empty(); // false
示例3: 应用复杂类型
cpp
struct UserInfo
{
qint16 id;
QString username;
QString password;
bool isMarry;
};
QVector<UserInfo> userList =
{
{1001, "admin", "123123", true},
{1002, "root", "123456", false},
{1003, "hhb", "123abc", false}
};
for(int i = 0; i < userList.size(); i++)
{
qDebug() << QString("%1-%2-%3-%4").arg(userList[i].id).arg(userList[i].username)
.arg(userList[i].password).arg(userList[i].isMarry ? "已婚" : "未婚");
}
2.2 QList
- 链表是一种在内存中非连续的存储结构,元素的逻辑顺序是通过链表中的指针链接次序实现的。每一个元素都包括两个值:自身数据 + 下一个元素的地址(指针)。插入元素比较快。
示例1:
cpp
QList<int> intList = {89, 123, 78, 63};
qDebug() << intList[0];
qDebug() << intList[1];
qDebug() << intList[2];
qDebug() << intList[3];
// 通过长度遍历
for(int i = 0; i < intList.size(); i++)
{
qDebug() << intList[i];
}
// 使用迭代器遍历
QList<int>::iterator iter;
for (iter = intList.begin(); iter < intList.end(); iter++)
{
qDebug() << *iter;
}
示例2:
cpp
QList<QString> strList;
strList.append("aaa");
strList.push_front("bbb");
strList.insert(1, "ccc");
QList<QString>::iterator iter;
for (iter = strList.begin(); iter < strList.end(); iter++)
{
qDebug() << *iter;
}
示例3:
cpp
QList<UserInfo> userList =
{
{1001, "admin", "123123", true},
{1002, "root", "123456", false},
{1003, "hhb", "123abc", false}
};
QList<UserInfo>::Iterator iter;
for (iter = userList.begin(); iter < userList.end(); iter++)
{
qDebug() << QString("%1-%2-%3-%4").arg(iter->id).arg(iter->username)
.arg(iter->password).arg(iter->isMarry ? "已婚" : "未婚");
}
for(int i = 0; i < userList.count(); i++)
{
qDebug() << QString("%1-%2-%3-%4").arg(userList[i].id).arg(userList[i].username)
.arg(userList[i].password).arg(userList[i].isMarry ? "已婚" : "未婚");
}
QVector VS QList
-
QVector 是一个基于数组的动态数组类,支持高效的随机访问。
-
可以在任意位置插入和删除元素,但在中间位置进行插入和删除操作时的性能较差。
-
适用于需要频繁随机访问元素的场景。
-
QList 是一个双向链表类,支持高效的插入和删除操作。
-
可以在任意位置插入和删除元素,而不会导致整个链表的重新分配和复制。
-
适用于需要频繁插入和删除元素的场景。
2.3 QStringList
QStringList继承自QList<QString>
, 使用方式与QList一样。但是,只能操作字符串数据
QStringList <===> QList<QString>
cpp
QStringList sl1 = {"aa", "bb", "cc"};
qDebug() << sl1[0];
qDebug() << sl1[1];
qDebug() << sl1[2];
QStringList sl2;
sl2 << "C++" << "Java" << "C#";
sl2.append("PHP");
sl2.prepend("前端");
for (int i = 0; i < sl2.size(); i++) {
qDebug() << sl2[i];
}
// 字符串分割
QStringList sl = s.split(","); // 使用 , 将字符串拆分成一个list
// 字符串拼接
QStringList sl = {"aa", "bb", "cc"};
qDebug() << sl.join("-"); // aa-bb-cc
QStringList sl = {"盗墓笔记", "鬼吹灯", "茅山后裔", "上古神迹", "宝莲灯"};
qDebug() << sl.contains("鬼吹灯"); // true
QStringList result = sl.filter("灯"); // {"鬼吹灯", "宝莲灯"}
qDebug() << sl.join("-"); // 盗墓笔记-鬼吹灯-茅山后裔-上古神迹-宝莲灯
QString str = "盗墓笔记-鬼吹灯-茅山后裔";
QStringList sl = str.split("-"); // {"盗墓笔记","鬼吹灯","茅山后裔"}
2.4 QMap
Map 类是 Qt 的关联容器,它存储(键,值)对并提供了与键相关的值的快速查找。
cpp
// 创建 Map 类型, QString 是 key 的数据类型, int 是 value 的数据类型
QMap<QString, int> m1;
m1["one"] = 111;
m1["two"] = 222; // {{"one", 111}, {"tow", 222}}
qDebug() << m1["one"];
qDebug() << m1["two"];
QMap<QString, QString> m2;
m2["id"] = "10001";
m2["name"] = "zss";
// 使用方法添加 key-value
m2.insert("isMarry", "未婚");
qDebug() << m2["id"];
qDebug() << m2["name"];
qDebug() << m2["isMarry"];
遍历:
cpp
// 创建迭代器
QMapIterator<QString, QString> iter(m2);
// hasNext 方法用来判断是否还有下一个单元
while (iter.hasNext()) {
// 调整到下一个单元
iter.next();
// key 方法用来得到当前的 key数据
// value 方法用来的到当前的 value数据
qDebug() << "key:" << iter.key() << " value:" << iter.value();
}
2.5 QHash
QHash 也是一个关联容器,功能与 QMap 几乎一样。
cpp
QHash<QString, QVariant> h;
h["id"] = 10001;
h["brand"] = "奔驰";
h["model"] = "GLC 300L";
h["price"] = 43.98f;
qDebug() << h["id"] << h["id"].toInt();
qDebug() << h["brand"] << h["brand"].toString();
qDebug() << h["model"] << h["model"].toString();
qDebug() << h["price"] << h["price"].toFloat();
QHash<QString, QVariant>::const_iterator iter;
for (iter = h.constBegin(); iter != h.constEnd(); iter++)
{
qDebug() << *iter;
}
for (auto val : carHash)
{
qDebug() << val;
}
// key() : 通过值的到key
qDebug() << carHash.key("比亚迪");
// value() : 通过key得到值
qDebug() << carHash.value("id");
qDebug() << carHash.keys(); // 获取所有的 key
qDebug() << carHash.values(); // 获取所有的 value
QMap VS QHash
-
QMap和QHash的接口相同,可直接替换使用
-
QHash的查找速度明显快于QMap
-
QHash占用的存储空间明显多于QMap
-
QHash以任意的方式存储元素
-
QMap以Key顺序存储元素
3. 窗口控件
-
Qt 中的窗口有三种: QWidget、 QMainWindow、 QDialog
-
QWidget 是空窗口
-
QMainWindow 是带有菜单栏、工具栏、状态栏等扩展功能的窗口
-
QDialog 是各种对话框的基类
-
QMainWindow 和 QDialog 继承于 QWidget
-
-
QObject 和 OPaintDevice
-
QObject 是所有Qt对象的基类
-
QPaintDevice 是所有可以绘制的对象的基类
-
4. QDiaLog
Dialog 用来创建弹出框,弹出框分为两种: 模态框 和 非模态框
4.1 模态框
-
模态框是一种弹出框, 当模态框弹出时,其他窗口无法再操作
-
QDialog 类用来创建弹出框
-
exec 方法能够显示模态框
-
模态框特点: 阻塞, 关闭时系统自动回收资源
cpp
connect(ui->btn1, &QPushButton::clicked, [=](){
// 创建弹出框
QDialog dia(this);
// 设置弹出框大小和标题栏
dia.resize(300, 300);
dia.setWindowIcon(QIcon(":/images/Jinx.jpeg"));
dia.setWindowTitle("模态框");
// 为弹出框中添加部件
QLabel *l = new QLabel("哈哈哈", &dia);
l->move(50, 50);
QPushButton *btn = new QPushButton("关闭", &dia);
btn->setFixedSize(60, 30);
btn->move(240, 0);
// 为弹出框中的按钮添加信号和槽
connect(btn, &QPushButton::clicked, &dia, &QWidget::close);
// 以模态框形式弹出, 有阻塞效果
dia.exec();
// 当关闭模态框时才会执行输出
qDebug() << "弹出模态框";
});
4.2 非模态框
-
非模态框也是一种弹出框, 当非模态框弹出时,其他窗口可以继续操作
-
QDialog 类用来创建弹出框
-
show 方法能够显示非模态框
-
非模态框特点: 不阻塞, 关闭时需要自己回收资源
-
注意事项:
-
非模态框需要使用 dia->setAttribute(Qt::WA_DeleteOnClose) 来销毁对象
-
setModel(true/false) 也能设置 模态框/非模态框
-
cpp
connect(ui->btn2, &QPushButton::clicked, [=](){
if (dia != nullptr)
{
return ;
}
dia = new QDialog(this);
dia->resize(200, 300);
// 关闭时,自动销毁该模态框
dia->setAttribute(Qt::WA_DeleteOnClose);
QLabel *l = new QLabel("非模态框测试", dia);
l->move(100, 0);
QPushButton *btn = new QPushButton("close", dia);
connect(btn, &QPushButton::clicked, [=](){
dia->close();
dia = nullptr;
});
// true 模态框 | false 非模态框
// dia->setModel(true);
// 以非模态框形式显示
dia->show();
qDebug() << "弹出非模态框";
});
5.练习
cpp
//封装一个函数,传入一个文件路径,将路径进行拆分,得到以下数据:
//1. 文件所在文件夹的绝对路径 (d:/aaa/bbb/ccc)
//2. 文件名 (a.txt)
//3. 文件后缀 (.txt)
//4. 纯文件名 (a)
//path = "d:/aaa/bbb/ccc/a.txt";
//void convert (QString path);
//可选 (账号就是 @ 前面的字符串: admin@qq.com --> admin)
//任务2: 邮箱的长度在 10到30位之间
//任务3: 密码长度在 6到18位之间
//任务4: 密码中必须有 &#$. 其中之一
//任务5: 账号为 admin,密码为 123.123
// 账号为 root,密码为 123456$
// 账号为 zss, 密码为 1232#13 则提示登录成功
// 否则,提示账号或者密码错
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QPushButton>
#include <QTime> // 时分秒
#include <QDate> // 年月日
#include <QDateTime> //年月日 + 时分秒
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QString str = "d:/aaa/bbb/ccc/a.txt";
qDebug() << str.section("/",0,3);
qDebug() << str.section("/",4);
qDebug() << str.section("/",4).mid(1,4);
qDebug() << str.section("/",4).mid(0,1);
connect(ui->pushButton,&QPushButton::clicked,[this](){
QString tmp1 = ui->linezhanghao->text().trimmed();
//tmp1 = tmp1.mid(0,tmp1.size()-1);
if(tmp1.size() < 10 || tmp1.size() > 30)
{
qDebug() << "请输入邮箱的长度在 10到30位之间";
return;
}
QString tmp2 = ui->linemima->text().trimmed();
//tmp2 = tmp2.mid(0,tmp2.size()-1);
if (tmp2.size() < 6 || tmp2.size() > 18 && tmp2.contains("&") == true
&& tmp2.contains("#") == true && tmp2.contains("$") == true && tmp2.contains(".") == true)
{
qDebug() << "请输入密码长度在 6到18位之间";
return;
}
int index = tmp1.indexOf("@");
if(tmp1.mid(0,index) == "admin" && tmp2 == "123.123")
{
qDebug()<< "登录成功";
}
else if (tmp1.mid(0,index) == "root" && tmp2 == "123456$") {
qDebug()<< "登录成功";
}
else if (tmp1.mid(0,index) == " zss" && tmp2 == "1232#13") {
qDebug()<< "登录成功";
}
else {
qDebug() << "账号或者密码错";
}
// if(tmp2.contains("&") == true)
// {
// qDebug() << "密码&正确";
// }
// else {
// qDebug() << "请输入&在密码中";
// }
});
}
Widget::~Widget()
{
delete ui;
}