读取和写入json文件
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QFile>
-
写入json
-
创建json对象
QJsonObject obj; QJsonObject sub; sub.insert("IP", QJsonValue("192.168.1.25")); sub.insert("port", "9999");//第2个参数应该传QJsonValue,而QJsonValue有字符串、数字等类型的构造函数,这里隐式类型转换 obj.insert("server", QJsonValue(sub));
-
json对象转为json文档
QJsonDocument doc(obj);
-
另一种得到json文档的方法:
QMap<QString, QVariant> web_server; web_server.insert("ip", ip); web_server.insert("port", port); QMap<QString, QVariant> json; json.insert("web_server", web_server); QJsonDocument jsonDocument = QJsonDocument::fromVariant(json);
-
json文档转为json字符串
QByteArray data = doc.toJson();
-
把字符串写入文件
QFile file("temp.json"); file.open(QIODevice::WriteOnly); file.write(data); file.close();
-
-
读取json
-
从文件读取json字符串
QFile file("temp.json"); file.open(QIODevice::ReadOnly);//返回值bool表示文件是否存在 QByteArray data = file.readAll(); file.close();
-
字符串转为json文档
QJsonDocument doc = QJsonDocument::fromJson(data);
-
判断json文档是对象还是数组来的
- 如果是json对象过来的,则转为json对象,读取value
if(doc.isObject()) { QJsonObject obj = doc.object(); QJsonValue value = obj.value("server"); if(value.isObject()) { QJsonObject subobj = value.toObject(); QString ip = subobj.value("IP").toString(); QString port = subobj.value("port").toString(); qDebug()<<ip<<port; } }
- 如果是json数组,直接当作普通数组操作(其重载了[])
-
使用Http协议通信
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
-
创建一个manager对象,用于做get、post请求
QNetworkAccessManager* manager = new QNetworkAccessManager(this);
-
创建QNetworkRequest对象,指定请求头、要访问的地址
QNetworkRequest res; res.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0");//设置请求头里的useragent,来假装自己是浏览器 res.setUrl(QUrl("http://www.baidu.com:80"));//你要访问的地址
-
使用manager进行post/get请求
QNetworkReply* reply = manager->post(res, "");//第2个参数是请求体的数据 QNetworkReply* reply = manager->get(res);//get请求的请求体为空
-
读服务器返回的数据:当reply发出readyRead信号(或者是finished信号)后,就调用readAll读取数据
connect(reply, &QNetworkReply::readyRead, this, [=](){ //访问百度时会重定向到https://www.baidu.com:80,这里可以通过响应头LocationHeader得知 QVariant str = reply->header(QNetworkRequest::LocationHeader); QByteArray data = reply->readAll();//服务器返回的数据,不包含响应头 qDebug()<<data; qDebug()<<str.toString(); });
post提交数据有4种常见方式(即Content-Type的类型)
application/x-www-form-urlencoded
application/json
multipart/form-data
text/xml
TCP套接字通信
-
服务器端
- 创建套接字
QTcpServer *tcpServer = new QTcpServer(this);
- 有新连接时tcpServer发出newConnection()信号
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection_Slot()));
- 在自定义的槽函数中连接客户端套接字,有客户端新消息时tcpServer发出readyRead()信号
tcpSocket = tcpServer->nextPendingConnection();//获得连接的客户端的socket connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readyRead_Slot()));
- 在自定义的槽函数中readAll读取信息
QString buf = tcpSocket->readAll();
- 开启监听
tcpServer->listen(QHostAddress::Any, ui->portEdit->text().toUInt());
- 创建套接字
-
客户端
- 创建套接字
QTcpSocket *tcpSocket = new QTcpSocket(this);
- 建立连接
tcpSocket->connectToHost(ui->ipEdit->text(), ui->portEdit->text().toUInt());
- 连接上以后,tcpSocket会发出connected信号
connect(tcpSocket, SIGNAL(connected()), this, SLOT(connected_Slot()));
- 在自定义的槽函数中关联readyRead信号
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readyRead_Slot()));
- readAll()读取
- 创建套接字
加密解密
MD5
QByteArray arry = QCryptographicHash::hash("hello, world", QCryptographicHash::Md5);
qDebug()<<arry.toHex();//转为16进制
base64
为了避免文件传输过程中,某些字符在不同平台转码不一样,导致错误。base64将字符进行编码
//编码
QByteArray base ="dd";
base = base.toBase64();
//解码
base = QByteArray::fromBase64(base);
发布Qt程序时怎么找动态库
使用E:\az\Qtfolder\5.12.9\mingw73_32\bin下的windeployqt.exe
即:cmd执行windeployqt.exe YunDemo.exe
多线程、线程池、条件变量
-
方法1:简单,run不能带参数,子线程只能通过信号传递参数
- 继承自QThread,重写其run方法
#ifndef THREADFROMQTHREAD_H #define THREADFROMQTHREAD_H #include <QThread> class ThreadFromQThread : public QThread { private: void run(); }; #endif // THREADFROMQTHREAD_H
- 启动子线程:调用start()
ThreadFromQThread *tq = new ThreadFromQThread;
tq->start();
- 继承自QThread,重写其run方法
-
方法2:复杂,函数随便起名且可以带参数
-
任意任务类,必须继承自QObject
#ifndef YXWORK_H #define YXWORK_H #include <QObject> class YxWork : public QObject { Q_OBJECT public: explicit YxWork(QObject *parent = nullptr); void yxhardwork();//随便起名,可以带参数 signals: }; #endif // YXWORK_H
-
主线程中创建线程对象、创建任务对象、移动任务对象到子线程中、启动子线程、调用任务函数
QThread * sub = new QThread; YxWork * yx = new YxWork;//千万不要指定给创建的对象指定父对象 yx->moveToThread(sub);//移动到子线程中工作 sub->start();//启动子线程 yx->yxhardwork();//调用任务函数,该函数现在是在子线程中工作
-
为了避免主线程直接调用任务函数导致阻塞,可以使用信号来通知子线程启动
QThread * sub = new QThread; YxWork * yx = new YxWork;//千万不要指定给创建的对象指定父对象 yx->moveToThread(sub);//移动到子线程中工作 sub->start();//启动子线程 connect(yx, &YxWork::startwork, yx, [=](){ yx->yxhardwork();//调用任务函数,该函数现在是在子线程中工作 }); QThread::sleep(3); QThread * sub2 = new QThread; YxWork * yx2 = new YxWork; yx2->moveToThread(sub2); sub2->start(); connect(yx2, &YxWork::startwork, yx2, [=](){ yx2->yxhardwork(); }); emit yx->startwork(); QThread::sleep(3); emit yx2->startwork();
-
多个工作对象可以移动到同一个子线程中
-
线程资源释放
退出线程 void QThread::exit(int returnCode = 0);
等价于 [slot] void QThread::quit();
等待任务完成 bool QThread::wait(unsigned long time = ULONG_MAX);
//当前窗口析构时会发出destroyed信号,写一个槽函数来释放线程对象和任务对象 connect(this, &Widget::destroyed, this,, [=](){ sub->quit(); sub->wait(); sub->deleteLater();//等价于delete sub sub2->quit(); sub2->wait(); sub2->deleteLater(); yx->deleteLater(); yx2->deleteLater(); });
-
-
线程池
- 创建任务类继承自QRunnable
- 如果里面需要使用信号,那么还要同时继承自QObject
- QRunnable类里的run函数是public的,而QThread里的run是protected的
#ifndef MYWORK_H #define MYWORK_H #include <QObject> #include <QRunnable> //为了使用槽函数,需要继承自QObject class MyWork : public QObject, public QRunnable { Q_OBJECT public: explicit MyWork(QObject *parent = nullptr); void run(); signals: }; #endif // MYWORK_H
- 使用setAutoDelete来自动销毁任务对象
#include "mywork.h" #include <QDebug> //QRunnable这里不需要参数 MyWork::MyWork(QObject *parent) : QObject(parent),QRunnable() { setAutoDelete(true);// 任务执行完毕时,该对象自动销毁 } void MyWork::run() { qDebug()<<"run"; }
- 主线程里只需要创建任务对象,并在合适时机将其放入线程池
#include "widget.h" #include "mywork.h" #include <QThreadPool> Widget::Widget(QWidget *parent) : QWidget(parent) { MyWork * yxwork = new MyWork;//创建任务对象 QThreadPool::globalInstance()->start(yxwork);//将任务对象扔进线程池里 //使用过setAutoDelete,不需要自己来释放任务对象,而线程相关的东西都由线程池自己来管理释放了 } Widget::~Widget() { }
- 创建任务类继承自QRunnable
-
条件变量
- 所需头文件
#include <QThread> #include <QMutex> #include <QWaitCondition>
- 创建条件变量
QWaitCondition productIsNotEmpty;
- 休眠
productIsNotEmpty.wait(&mutex);
- 唤醒
productIsNotEmpty.wakeAll();
- 所需头文件
QList的使用:
创建QList:QList<int> numberlist;
尾插:numberlist.append(number);
按索引位置插入:void QList::insert(int i, const T &value)
头删:numberlist.removeFirst();
访问元素:numberlist.at(0);
- 练习:使用线程池和条件变量写一个简单的生产者消费者作为测试
-
先写一个YX类作为生产者和消费者的父类,为它们提供锁和条件变量
#ifndef YX_H #define YX_H #include <QList> #include <QMutex> #include <QWaitCondition> class YX { public: QMutex mutex;//锁 QWaitCondition productIsNotEmpty;//条件变量,未空,可消费 //提供消费、生产的方法 void insertlist(int number);//生产:添加数据到链表尾部 int getlistnumber();//消费:从链表头部取数据 int getlistsize();//获取链表元素个数 private: QList<int> numberlist;//临界资源 }; #endif // YX_H
-
为了使用线程池,消费者任务对象继承自QRunnable
#include "customer.h" #include <QThread> #include <QDebug> #include <QMutex> #include <QWaitCondition> Customer::Customer() : YX(),QRunnable() { setAutoDelete(true);// 任务执行完毕时,该对象自动销毁 } //消费 void Customer::run() { while(1) { qDebug()<<"进入消费线程"<<QThread::currentThread(); mutex.lock();//先加锁 qDebug()<<"链表:"<<getlistsize(); if(getlistsize()==0)//资源耗尽,休眠等待 { qDebug()<<"资源耗尽,等待中"; productIsNotEmpty.wait(&mutex); qDebug()<<"消费者被唤醒"; } qDebug()<<"消费中 "<<getlistnumber();//消费 qDebug()<< getlistnumber();//取链表头部元素 mutex.unlock();//临界资源访问完毕,解锁 QThread::sleep(1); } }
-
生产者同理
#include "producer.h" #include <QThread> #include <QDebug> #include <QMutex> #include <QWaitCondition> #include <QRandomGenerator> Producer::Producer() : YX(),QRunnable() { setAutoDelete(true);// 任务执行完毕时,该对象自动销毁 } //生产 void Producer::run() { while(1) { qDebug()<<"进入生产线程"<<QThread::currentThread(); mutex.lock();//先加锁 if(getlistsize() > 0)//现在链表上有未处理任务,唤醒消费者线程 { qDebug()<<"唤醒消费者"; productIsNotEmpty.wakeAll(); } insertlist(QRandomGenerator::global()->bounded(100));//生成0-99的随机整数,插入到链表尾部 qDebug()<<"生产中 "<<getlistnumber(); mutex.unlock();//临界资源访问完毕,解锁 QThread::sleep(1); } }
-
在主线程中启动生产者和消费者线程
#include "widget.h" #include "customer.h" #include "producer.h" #include <QThreadPool> #include <QThread> #include <QDebug> Widget::Widget(QWidget *parent) : QWidget(parent) { Producer * pd = new Producer; QThreadPool::globalInstance()->start(pd); Customer * cs = new Customer; QThreadPool::globalInstance()->start(cs); } Widget::~Widget() { }
-
QFileDialog文件浏览
-
方法1:使用静态函数getOpenFileName
QString fileName = QFileDialog::getOpenFileName(this, tr("Open doc"), "/D:/", tr("good(*.doc)"));
-
方法2:直接创建对象
QFileDialog dialog(this); dialog.exec();//弹出对话框
-
属性设置
//选择文件策略:可选择 AnyFile任何文件、ExistingFile单个存在的文件、Directory目录、ExistingFiles多个存在的文件 dialog.setFileMode(QFileDialog::ExistingFile);//默认AnyFile //文件过滤 dialog.setNameFilter(tr("Image Files (*.png *.jpg *.bmp)"));//默认All Files(*.*) dialog.setWindowTitle("Open Image"); //打开的目录设置 dialog.setDirectory("/D:"); //该对话框是用来打开还是保存文件:默认是打开文件。而保存文件等价于静态函数getSaveFileName dialog.setAcceptMode(QFileDialog::AcceptSave);
QFile
在使用QFileDialog得到要操作的文件名后,就使用QFile来进行后续打开、保存操作
//1.打开文件
QString file_name = fileNames.first();
QFile file(file_name);
if (file.open(QIODevice::ReadWrite) == false){
qDebug() << "打开失败";
}
//2.读取文件
QByteArray temp = file.readAll();
//3.写文件
//获取文件后缀
QString endname = file_name.split(".").back();
//指定要写入的文件
QFile newfile("E:\\downfile\\temp." + endname);
newfile.open(QIODevice::WriteOnly);
//写入
newfile.write(temp);
newfile.close();
file.close();
QDir目录操作
获取目录下所有目录和文件的名字
QDir allfile("E:/downfile");
QStringList filesAndDirs = allfile.entryList();
QListWidget 删除item
点击某项时会触发QListWidget::itemClicked信号
QListWidgetItem* item = ui.list_1->currentItem();
ui.list_1->removeItemWidget(item);
delete item;//必须delete,否则仍有占位