Qt读写json、套接字通信、多线程、条件变量、QList、QFileDialog文件浏览、QDir目录操作、QListWidget

本文详细介绍了如何在Qt中进行文件I/O操作(读写JSON和文本文件)、使用HTTP协议通信、TCP套接字、加密解密技术、多线程与线程池、条件变量、QFileDialog应用、QFile和QDir目录操作以及QListWidget的删除功能。
摘要由CSDN通过智能技术生成

读取和写入json文件

#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QFile>

json操作

  • 写入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();
  • 方法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()
      {
      }	
      
  • 条件变量

    • 所需头文件
      #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,否则仍有占位
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值