跳转到帖子

墨香年少

Administrators
  • 帖子数

    309
  • 注册日期

  • 上次访问

  • 得奖次数

    18

最新回复 发布由 墨香年少


  1. 使用Chrome浏览器,F12打开控制台,输入:

    [...document.getElementsByClassName("hljs")].forEach(i=>console.log(i.innerText))

    其中的prism就是

    <code class="prism language-c has-numbering" onclick="mdcp.signin(event)"

    中的code元素的class

    112233.png


  2. 先看效果图:

    微信截图_20211128164746.png

    22.png

    Toast.h

    #ifndef TOAST_H
    #define TOAST_H
    
    #include <QDialog>
    
    namespace Ui {
    class Toast;
    }
    
    class Toast : public QDialog
    {
        Q_OBJECT
    
    public:
        explicit Toast(QWidget *parent = nullptr);
        ~Toast();
        void setText(const QString& text);
        void setBgColor(const QString& color);
        void showAnimation(int timeout = 3000);
    public:
        static void tip(const QString& text);
        static void info(const QString& text);
        static void warn(const QString& text);
        static void err(const QString& text);
        static void succ(const QString& text);
    
        static void display(const QString& text,QString);
    protected:
        virtual void paintEvent(QPaintEvent *event);
    private:
        Ui::Toast *ui;
    };
    
    #endif // TOAST_H

    Toast.cpp

    #include "Toast.h"
    #include "ui_Toast.h"
    #include <QPropertyAnimation>
    #include <QScreen>
    #include <QGuiApplication>
    #include <QPainter>
    #include <QTimer>
    
    
    Toast::Toast(QWidget *parent) :
        QDialog(parent),
        ui(new Ui::Toast)
    {
        ui->setupUi(this);
    
        setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::Tool);
        setAttribute(Qt::WA_TranslucentBackground, true);
    }
    
    Toast::~Toast()
    {
        delete ui;
    }
    
    
    void Toast::setText(const QString& text)
    {
        ui->label->setText(text);
    }
    
    void Toast::setBgColor(const QString &color)
    {
        QString css = "border-radius: 26px;color: #FFFFFF;font-family: microsoft yahei;font-size: 16px;padding-left:25px;padding-right:25px;";
        if("ERROR" == color)
        {
            ui->label->setStyleSheet("background-color: #dc3545;"+css);
        }else if("SUCC" == color)
        {
            ui->label->setStyleSheet("background-color: #198754;"+css);
        }
        else if("WARNING" == color)
        {
            ui->label->setStyleSheet("background-color: #fd7e14;"+css);
        }
        else if("INFOMATION" == color)
        {
            ui->label->setStyleSheet("background-color: #009ef6;"+css);
        }
        else{
            ui->label->setStyleSheet("background-color: rgba(0,0,0,0.80);"+css);
        }
    }
    
    void Toast::showAnimation(int timeout)
    {
        // 开始动画
        QPropertyAnimation *animation = new QPropertyAnimation(this, "windowOpacity");
        animation->setDuration(1000);
        animation->setStartValue(0);
        animation->setEndValue(1);
        animation->start();
        show();
    
        QTimer::singleShot(timeout, [&]
        {
            // 结束动画
            QPropertyAnimation *animation = new QPropertyAnimation(this, "windowOpacity");
            animation->setDuration(1000);
            animation->setStartValue(1);
            animation->setEndValue(0);
            animation->start();
            connect(animation, &QPropertyAnimation::finished, [&]
            {
                close();
                deleteLater();// 关闭后析构
            });
        });
    }
    
    void Toast::tip(const QString &text)
    {
        display(text,"TIP");
    }
    
    void Toast::info(const QString &text)
    {
        display(text,"INFOMATION");
    }
    
    void Toast::warn(const QString &text)
    {
        display(text,"WARNING");
    }
    
    void Toast::err(const QString &text)
    {
        display(text,"ERROR");
    }
    
    void Toast::succ(const QString &text)
    {
        display(text,"SUCC");
    }
    
    
    void Toast::display(const QString& text,QString color)
    {
        Toast* toast = new Toast(nullptr);
        toast->setWindowFlags(toast->windowFlags() | Qt::WindowStaysOnTopHint); // 置顶
        toast->setText(text);
        toast->setBgColor(color);
        toast->adjustSize();
    
    
        // 测试显示位于主屏的70%高度位置
        QScreen* pScreen = QGuiApplication::primaryScreen();
        toast->move((pScreen->size().width() - toast->width()) / 2, pScreen->size().height() * 7 / 10);
        toast->showAnimation();
    }
    
    void Toast::paintEvent(QPaintEvent *event)
    {
    
    }

    Toast.ui

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>Toast</class>
     <widget class="QWidget" name="Toast">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>170</width>
        <height>52</height>
       </rect>
      </property>
      <property name="minimumSize">
       <size>
        <width>0</width>
        <height>52</height>
       </size>
      </property>
      <property name="maximumSize">
       <size>
        <width>16777215</width>
        <height>52</height>
       </size>
      </property>
      <property name="windowTitle">
       <string>Toast</string>
      </property>
      <layout class="QGridLayout" name="gridLayout">
       <property name="leftMargin">
        <number>0</number>
       </property>
       <property name="topMargin">
        <number>0</number>
       </property>
       <property name="rightMargin">
        <number>0</number>
       </property>
       <property name="bottomMargin">
        <number>0</number>
       </property>
       <property name="spacing">
        <number>0</number>
       </property>
       <item row="0" column="0">
        <widget class="QLabel" name="label">
         <property name="styleSheet">
          <string notr="true">background-color: rgba(0,0,0,0.80);
    border-radius: 26px;
    color: #FFFFFF;
    font-family: microsoft yahei;
    font-size: 16px;
    padding-left:25px;
    padding-right:25px;</string>
         </property>
         <property name="text">
          <string>TextLabel</string>
         </property>
         <property name="alignment">
          <set>Qt::AlignCenter</set>
         </property>
         <property name="indent">
          <number>-1</number>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
     <layoutdefault spacing="6" margin="11"/>
     <resources/>
     <connections/>
    </ui>
    • 有帮助 1

  3. HttpClient.h

    #ifndef HTTPCLIENT_H
    #define HTTPCLIENT_H
    
    #include <functional>
    #include <QMap>
    #include <QVariant>
    #include <QStringList>
    #include <QNetworkReply>
    #include <QNetworkRequest>
    #include <QNetworkAccessManager>
    
    class HttpClientPrivate;
    
    /**
    * 对 QNetworkAccessManager 简单封装的 HTTP 访问客户端,简化 GET、POST、PUT、DELETE、上传、下载等操作。
    * 在执行请求前设置需要的参数和回调函数:
    *     1. 调用 header() 设置请求头
    *     2. 调用 param() 设置参数,使用 Form 表单的方式提交请求,GET 请求的 query parameters 也可以用它设置
    *     3. 调用 json() 设置 JSON 字符串的 request body,Content-Type 为 application/json,
    *        当然也可以不是 JSON 格式,因使用 request body 的情况多数是使用 JSON 格式传递复杂对象,故命名为 json
    *     4. 调用 success() 注册请求成功的回调函数
    *     5. 调用 fail() 注册请求失败的回调函数
    *     6. 调用 complete() 注册请求结束的回调函数
    *        success(), fail(), complete() 的回调函数是可选的,根据需要注册对应的回调函数,也可以一个都不注册
    * 然后根据请求的类型调用 get(), post(), put(), remove(), download(), upload() 执行 HTTP 请求
    *
    * 默认 HttpClient 会创建一个 QNetworkAccessManager,如果不想使用默认的,调用 manager() 传入即可。
    * 调用 debug(true) 设置为调试模式,输出调试信息如 URL、参数等。
    */
    
    /**
    // 在代码块里执行网络访问,是为了测试 HttpClient 对象在被析构后,网络访问的回调函数仍然能正常执行
    {
    
        // [[1]] GET 请求无参数
        HttpClient("http://localhost:8080/device").success([](const QString &response) {
            qDebug() << response;
        }).get();
    
        // [[2]] GET 请求有参数,有自定义 header
        HttpClient("http://localhost:8080/device").success([](const QString &response) {
            qDebug() << response;
        }).param("id", "1").param("name", "诸葛亮").header("token", "123AS#D").get();
    
    
        // [[3]] POST 请求有参数,有自定义 header
        HttpClient("http://localhost:8080/device").success([](const QString &response) {
            qDebug() << response;
        }).param("id", "2").param("name", "卧龙").header("token", "DER#2J7")
        .header("content-type", "application/x-www-form-urlencoded").post();
    
        // [[4]] 每创建一个 QNetworkAccessManager 对象都会创建一个线程,当频繁的访问网络时,为了节省线程资源,调用 useManager()
        // 使用共享的 QNetworkAccessManager,它不会被 HttpClient 删除。
        // 如果下面的代码不传入 QNetworkAccessManager,从任务管理器里可以看到创建了几千个线程。
        QNetworkAccessManager *manager = new QNetworkAccessManager();
        for (int i = 0; i < 5000; ++i) {
            HttpClient("http://localhost:8080/device").success([=](const QString &response) {
                qDebug() << response << ", " << i;
            }).manager(manager).get();
        }
    }
    */
    
    class HttpClient {
    public:
        HttpClient(const QString &url);
        ~HttpClient();
        void stop2();
    
        /**
        * @brief 每创建一个 QNetworkAccessManager 对象都会创建一个线程,当频繁的访问网络时,为了节省线程资源,
        *     可以传入 QNetworkAccessManager 给多个请求共享 (它不会被 HttpClient 删除,用户需要自己手动删除)。
        *     如果没有使用 manager() 传入一个 QNetworkAccessManager,则 HttpClient 会自动的创建一个,并且在网络访问完成后自动删除它。
        *
        * @param  manager 执行 HTTP 请求的 QNetworkAccessManager 对象
        * @return 返回 HttpClient 的引用,可以用于链式调用
        */
        HttpClient& manager(QNetworkAccessManager *manager);
    
        /**
        * @brief  参数 debug 为 true 则使用 debug 模式,请求执行时输出请求的 URL 和参数等
        *
        * @param  debug 是否启用调试模式
        * @return 返回 HttpClient 的引用,可以用于链式调用
        */
        HttpClient& debug(bool debug);
    
        /**
        * @brief 添加一个请求的参数,可以多次调用添加多个参数
        *
        * @param name  参数的名字
        * @param value 参数的值
        * @return 返回 HttpClient 的引用,可以用于链式调用
        */
        HttpClient& param(const QString &name, const QVariant &value);
    
        /**
        * @brief 添加多个请求的参数
        *
        * @param ps QMap 类型的参数,key 为参数名,value 为参数值
        *           可以使用 {{"name", 1}, {"box", 2}} 的方式创建 QMap 对象
        * @return 返回 HttpClient 的引用,可以用于链式调用
        */
        HttpClient& params(const QMap<QString, QVariant> &ps);
    
        /**
        * @brief 添加请求的参数 (请求体),使用 Json 格式,例如 "{\"name\": \"Alice\"}"
        *
        * @param json 请求体 (request body) 为 Json 格式的参数字符串
        * @return 返回 HttpClient 的引用,可以用于链式调用
        */
        HttpClient& json(const QString &json);
    
        /**
        * @brief 添加请求头
        *
        * @param name  请求头的名字
        * @param value 请求头的值
        * @return 返回 HttpClient 的引用,可以用于链式调用
        */
        HttpClient& header(const QString &name, const QString &value);
    
        /**
        * @brief 添加多个请求头
        *
        * @param nameValues 请求头的名字和值对
        *                   可以使用 {{"name", 1}, {"box", 2}} 的方式创建 QMap 对象
        * @return 返回 HttpClient 的引用,可以用于链式调用
        */
        HttpClient& headers(const QMap<QString, QString> nameValues);
    
        /**
        * @brief 注册请求成功的回调函数
        *
        * @param successHandler 成功的回调函数,参数为响应的字符串
        * @return 返回 HttpClient 的引用,可以用于链式调用
        */
        HttpClient& success(std::function<void(const QString &)> successHandler);
    
        /**
        * @brief 注册请求失败的回调函数
        *
        * @param failHandler 失败的回调函数,参数为失败原因和 HTTP 状态码
        * @return 返回 HttpClient 的引用,可以用于链式调用
        */
        HttpClient& fail(std::function<void(const QString &, int)> failHandler);
    
        /**
        * @brief 注册请求结束的回调函数,不管成功还是失败请求结束后都会执行
        *
        * @param completeHandler 完成的回调函数,无参数
        * @return 返回 HttpClient 的引用,可以用于链式调用
        */
        HttpClient& complete(std::function<void()> completeHandler);
    
        /**
        * @brief 设置请求响应的字符集,默认使用 UTF-8
        *
        * @param cs 字符集
        * @return 返回 HttpClient 的引用,可以用于链式调用
        */
        HttpClient& charset(const QString &cs);
    
        /**
        * @brief 执行 GET 请求
        */
        void get();
    
        /**
        * @brief 执行 POST 请求
        */
        void post();
    
        /**
        * @brief 执行 PUT 请求
        */
        void put();
    
        /**
        * @brief 执行 DELETE 请求,由于 delete 是 C++ 的运算符,所以用同义词 remove
        *        注意: Qt 提供的 DELETE 请求是不支持传递参数的,
        *        请参考 QNetworkAccessManager::deleteResource(const QNetworkRequest &request)
        */
        void remove();
    
        /**
        * @brief 使用 GET 进行下载,下载的文件保存到 savePath
        *
        * @param savePath 下载的文件保存路径
        */
        void download(const QString &savePath);
    
        /**
        * @brief 上传单个文件
        *        使用 POST 上传,服务器端获取文件的参数名为 file
        *
        * @param path 要上传的文件的路径
        */
        void upload(const QString &path);
    
        /**
        * @brief 上传文件,文件的内容已经读取到 data 中
        *        使用 POST 上传,服务器端获取文件的参数名为 file
        *
        * @param path 要上传的文件的路径
        */
        void upload(const QByteArray &data);
    
        /**
        * @brief 上传多个文件
        *        使用 POST 上传,服务器端获取文件的参数名为 files
        *
        * @param paths 要上传的文件的路径
        */
        void upload(const QStringList &paths);
    
    private:
        HttpClientPrivate *d;
    };
    
    #endif // HTTPCLIENT_H

    HttpClient.cpp

    #include "HttpClient.h"
    
    #include <QDebug>
    #include <QFile>
    #include <QHash>
    #include <QUrlQuery>
    #include <QHttpPart>
    #include <QHttpMultiPart>
    
    /*-----------------------------------------------------------------------------|
    |                              HttpClientPrivate                              |
    |----------------------------------------------------------------------------*/
    /**
    * @brief 请求的类型
    *
    * 注: UPLOAD 不是 HTTP Method,只是为了上传时对请求进行特殊处理而定义的
    */
    enum class HttpClientRequestMethod {
        GET, POST, PUT, DELETE, UPLOAD
    };
    
    /**
    * @brief 缓存 HttpClientPrivate 的数据成员,方便在异步 lambda 中使用 = 以值的方式访问。
    */
    class HttpClientPrivateCache {
    public:
        std::function<void(const QString &)>      successHandler = nullptr;
        std::function<void(const QString &, int)>    failHandler = nullptr;
        std::function<void()>                    completeHandler = nullptr;
        bool debug = false;
        bool internal = false;
        QString charset;
        QNetworkAccessManager* manager = nullptr;
    };
    
    /**
    * @brief HttpClient 的辅助类,封装不希望暴露给客户端的数据和方法,使得 HttpClient 只暴露必要的 API 给客户端。
    */
    class HttpClientPrivate {
        friend class HttpClient;
    
        HttpClientPrivate(const QString &url);
        ~HttpClientPrivate();
        void stop1();
    
        /**
        * @brief 缓存 HttpClientPrivate 的数据成员
        *
        * @return 返回 HttpClientPrivateCache 缓存对象
        */
        HttpClientPrivateCache cache();
    
        /**
        * @brief 获取 Manager,如果传入了 manager 则返回此 manager,否则新创建一个 manager,默认会自动创建一个 manager,
        *        使用传入的 manager 则 interval 被设置为 false,自动创建的 manager 则设置 interval 为 true
        *
        * @return 返回 QNetworkAccessManager 对象
        */
        QNetworkAccessManager* getManager();
    
        /**
        * @brief 使用用户设定的 URL、请求头、参数等创建 Request
        *
        * @param d      HttpClientPrivate 的对象
        * @param method 请求的类型
        * @return 返回可用于执行请求的 QNetworkRequest
        */
        static QNetworkRequest createRequest(HttpClientPrivate *d, HttpClientRequestMethod method);
    
        /**
        * @brief 执行请求的辅助函数
        *
        * @param d      HttpClientPrivate 的对象
        * @param method 请求的类型
        */
        static void executeQuery(HttpClientPrivate *d, HttpClientRequestMethod method);
    
        /**
        * @brief 上传文件或者数据
        *
        * @param d     HttpClientPrivate 的对象
        * @param paths 要上传的文件的路径(path 和 data 不能同时使用)
        * @param data  要上传的文件的数据
        */
        static void upload(HttpClientPrivate *d, const QStringList &paths, const QByteArray &data);
    
        /**
        * @brief 使用 GET 进行下载,下载的文件保存到 savePath
        *
        * @param d        HttpClientPrivate 的对象
        * @param savePath 下载的文件保存路径
        */
        static void download(HttpClientPrivate *d, const QString &savePath);
    
        /**
        * @brief 使用 GET 进行下载,当有数据可读取时回调 readyRead(), 大多数情况下应该在 readyRead() 里把数据保存到文件
        *
        * @param readyRead 有数据可读取时的回调 lambda 函数
        */
        static void download(HttpClientPrivate *d, std::function<void(const QByteArray &)> readyRead);
    
        /**
        * @brief 读取服务器响应的数据
        *
        * @param reply   请求的 QNetworkReply 对象
        * @param charset 请求响应的字符集,默认使用 UTF-8
        * @return 返回服务器端响应的字符串
        */
        static QString readReply(QNetworkReply *reply, const QString &charset = "UTF-8");
    
        /**
        * @brief 请求结束的处理函数
        *
        * @param cache          HttpClientPrivateCache 缓存对象
        * @param reply          QNetworkReply 对象,不能为 NULL
        * @param successMessage 请求成功的消息
        * @param failMessage    请求失败的消息
        */
        static void handleFinish(HttpClientPrivateCache cache, QNetworkReply *reply, const QString &successMessage, const QString &failMessage);
    
        /////////////////////////////////////////////////// 成员变量 //////////////////////////////////////////////
        QString   url;                            // 请求的 URL
        QString   json;                           // 请求的参数使用 Json 格式
        QUrlQuery params;                         // 请求的参数使用 Form 格式
        QString   charset = "UTF-8";              // 请求响应的字符集
        QHash<QString, QString> headers;          // 请求头
        QNetworkAccessManager *manager = nullptr; // 执行 HTTP 请求的 QNetworkAccessManager 对象
        bool useJson = false;                    // 为 true 时请求使用 Json 格式传递参数,否则使用 Form 格式传递参数
        bool debug = false;                    // 为 true 时输出请求的 URL 和参数
        bool internal = true;                     // 是否使用自动创建的 manager
    
        std::function<void(const QString &)>   successHandler = nullptr; // 成功的回调函数,参数为响应的字符串
        std::function<void(const QString &, int)> failHandler = nullptr; // 失败的回调函数,参数为失败原因和 HTTP status code
        std::function<void()>                 completeHandler = nullptr; // 结束的回调函数,无参数
    };
    
    HttpClientPrivate::HttpClientPrivate(const QString &url) : url(url) { }
    
    HttpClientPrivate::~HttpClientPrivate() {
        manager = nullptr;
        successHandler = nullptr;
        failHandler = nullptr;
        completeHandler = nullptr;
    }
    
    void HttpClientPrivate::stop1()
    {
        manager->deleteLater();
    }
    
    // 缓存 HttpClientPrivate 的数据成员
    HttpClientPrivateCache HttpClientPrivate::cache() {
        HttpClientPrivateCache cache;
    
        cache.successHandler = successHandler;
        cache.failHandler = failHandler;
        cache.completeHandler = completeHandler;
        cache.debug = debug;
        cache.internal = internal;
        cache.charset = charset;
        cache.manager = getManager();
    
        return cache;
    }
    
    // 执行请求的辅助函数
    void HttpClientPrivate::executeQuery(HttpClientPrivate *d, HttpClientRequestMethod method) {
        // 1. 缓存需要的变量,在 lambda 中使用 = 捕获进行值传递 (不能使用引用 &,因为 d 已经被析构)
        // 2. 创建请求需要的变量
        // 3. 根据 method 执行不同的请求
        // 4. 请求结束时获取响应数据,在 handleFinish 中执行回调函数
    
        // [1] 缓存需要的变量,在 lambda 中使用 = 捕获进行值传递 (不能使用引用 &,因为 d 已经被析构)
        HttpClientPrivateCache cache = d->cache();
    
        // [2] 创建请求需要的变量
        QNetworkRequest request = HttpClientPrivate::createRequest(d, method);
        QNetworkReply    *reply = nullptr;
    
        // [3] 根据 method 执行不同的请求
        switch (method) {
        case HttpClientRequestMethod::GET:
            reply = cache.manager->get(request);
            break;
        case HttpClientRequestMethod::POST:
            reply = cache.manager->post(request, d->useJson ? d->json.toUtf8() : d->params.toString(QUrl::FullyEncoded).toUtf8());
            break;
        case HttpClientRequestMethod::PUT:
            reply = cache.manager->put(request, d->useJson ? d->json.toUtf8() : d->params.toString(QUrl::FullyEncoded).toUtf8());
            break;
        case HttpClientRequestMethod::DELETE:
            reply = cache.manager->deleteResource(request);
            break;
        default:
            break;
        }
    
        // [4] 请求结束时获取响应数据,在 handleFinish 中执行回调函数
        // 请求结束时一次性读取所有响应数据
        QObject::connect(reply, &QNetworkReply::finished, [=] {
            QString successMessage = HttpClientPrivate::readReply(reply, cache.charset.toUtf8());
            QString failMessage = reply->errorString();
            HttpClientPrivate::handleFinish(cache, reply, successMessage, failMessage);
        });
    }
    
    // 使用 GET 进行下载,下载的文件保存到 savePath
    void HttpClientPrivate::download(HttpClientPrivate *d, const QString &savePath) {
        // 1. 打开下载文件,如果打开文件出错,不进行下载
        // 2. 给请求结束的回调函数注入关闭释放文件的行为
        // 3. 调用下载的重载函数开始下载
        QFile *file = new QFile(savePath);
    
        // [1] 打开下载文件,如果打开文件出错,不进行下载
        if (!file->open(QIODevice::WriteOnly | QIODevice::Truncate)) {
            file->close();
            file->deleteLater();
    
            if (d->debug) {
                qDebug().noquote() << QString("[错误] 打开文件出错: %1").arg(savePath);
            }
    
            if (nullptr != d->failHandler) {
                d->failHandler(QString("[错误] 打开文件出错: %1").arg(savePath), -1);
            }
    
            return;
        }
    
        // [2] 给请求结束的回调函数注入关闭释放文件的行为
        std::function<void()> userCompleteHandler = d->completeHandler;
        std::function<void()> injectedCompleteHandler = [=]() {
            // 请求结束后释放文件对象
            file->flush();
            file->close();
            file->deleteLater();
    
            // 执行用户指定的结束回调函数
            if (nullptr != userCompleteHandler) {
                userCompleteHandler();
            }
        };
        d->completeHandler = injectedCompleteHandler;
    
        // [3] 调用下载的重载函数开始下载
        HttpClientPrivate::download(d, [=](const QByteArray &data) {
            file->write(data);
        });
    }
    
    // 使用 GET 进行下载,当有数据可读取时回调 readyRead(), 大多数情况下应该在 readyRead() 里把数据保存到文件
    void HttpClientPrivate::download(HttpClientPrivate *d, std::function<void(const QByteArray &)> readyRead) {
        // 1. 缓存需要的变量,在 lambda 中使用 = 捕获进行值传递 (不能使用引用 &,因为 d 已经被析构)
        // 2. 创建请求需要的变量,执行请求
        // 3. 有数据可读取时回调 readyRead()
        // 4. 请求结束时获取响应数据,在 handleFinish 中执行回调函数
    
        // [1] 缓存需要的变量,在 lambda 中使用 = 捕捉使用 (不能使用引用 &,因为 d 已经被析构)
        HttpClientPrivateCache cache = d->cache();
    
        // [2] 创建请求需要的变量,执行请求
        QNetworkRequest request = HttpClientPrivate::createRequest(d, HttpClientRequestMethod::GET);
        QNetworkReply    *reply = cache.manager->get(request);
    
        // [3] 有数据可读取时回调 readyRead()
        QObject::connect(reply, &QNetworkReply::readyRead, [=] {
            readyRead(reply->readAll());
        });
    
        // [4] 请求结束时获取响应数据,在 handleFinish 中执行回调函数
        QObject::connect(reply, &QNetworkReply::finished, [=] {
            QString successMessage = "下载完成"; // 请求结束时一次性读取所有响应数据
            QString failMessage = reply->errorString();
            HttpClientPrivate::handleFinish(cache, reply, successMessage, failMessage);
        });
    }
    
    // 上传文件或者数据的实现
    void HttpClientPrivate::upload(HttpClientPrivate *d, const QStringList &paths, const QByteArray &data) {
        // 1. 缓存需要的变量,在 lambda 中使用 = 捕获进行值传递 (不能使用引用 &,因为 d 已经被析构)
        // 2. 创建 Form 表单的参数 Text Part
        // 3. 创建上传的 File Part
        //    3.1 使用文件创建 File Part
        //    3.2 使用数据创建 File Part
        // 4. 创建请求需要的变量,执行请求
        // 5. 请求结束时释放 multiPart 和打开的文件,获取响应数据,在 handleFinish 中执行回调函数
    
        // [1] 缓存需要的变量,在 lambda 中使用 = 捕捉使用 (不能使用引用 &,因为 d 已经被析构)
        HttpClientPrivateCache cache = d->cache();
    
        // [2] 创建 Form 表单的参数 Text Part
        QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
        QList<QPair<QString, QString> > paramItems = d->params.queryItems();
        for (int i = 0; i < paramItems.size(); ++i) {
            QString name = paramItems.at(i).first;
            QString value = paramItems.at(i).second;
    
            QHttpPart textPart;
            textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"%1\"").arg(name));
            textPart.setBody(value.toUtf8());
            multiPart->append(textPart);
        }
    
        if (paths.size() > 0) {
            // [3.1] 使用文件创建 File Part
            QString inputName = paths.size() == 1 ? "file" : "files"; // 一个文件时为 file,多个文件时为 files
    
            for (const QString &path : paths) {
                // path 为空时,不上传文件
                if (path.isEmpty()) {
                    continue;
                }
    
                // We cannot delete the file now, so delete it with the multiPart
                QFile *file = new QFile(path, multiPart);
    
                // 如果文件打开失败,则释放资源返回,终止上传
                if (!file->open(QIODevice::ReadOnly)) {
                    QString failMessage = QString("打开文件失败[%2]: %1").arg(path).arg(file->errorString());
    
                    if (cache.debug) {
                        qDebug().noquote() << failMessage;
                    }
    
                    if (nullptr != cache.failHandler) {
                        cache.failHandler(failMessage, -1);
                    }
    
                    multiPart->deleteLater();
                    return;
                }
    
                // 单个文件时,name 为服务器端获取文件的参数名,为 file
                // 多个文件时,name 为服务器端获取文件的参数名,为 files
                // 注意: 服务器是 Java 的则用 form-data
                // 注意: 服务器是 PHP  的则用 multipart/form-data
                QString disposition = QString("form-data; name=\"%1\"; filename=\"%2\"").arg(inputName).arg(file->fileName());
                QHttpPart filePart;
                filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(disposition));
                filePart.setBodyDevice(file);
                multiPart->append(filePart);
            }
        }
        else {
            // [3.2] 使用数据创建 File Part
            QString   disposition = QString("form-data; name=\"file\"; filename=\"no-name\"");
            QHttpPart dataPart;
            dataPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(disposition));
            dataPart.setBody(data);
            multiPart->append(dataPart);
        }
    
        // [4] 创建请求需要的变量,执行请求
        QNetworkRequest request = HttpClientPrivate::createRequest(d, HttpClientRequestMethod::UPLOAD);
        QNetworkReply    *reply = cache.manager->post(request, multiPart);
    
        // [5] 请求结束时释放 multiPart 和文件,获取响应数据,在 handleFinish 中执行回调函数
        QObject::connect(reply, &QNetworkReply::finished, [=] {
            multiPart->deleteLater(); // 释放资源: multiPart + file
    
            QString successMessage = HttpClientPrivate::readReply(reply, cache.charset); // 请求结束时一次性读取所有响应数据
            QString failMessage = reply->errorString();
            HttpClientPrivate::handleFinish(cache, reply, successMessage, failMessage);
        });
    }
    
    // 获取 Manager,如果传入了 manager 则返回此 manager,否则新创建一个 manager,默认会自动创建一个 manager
    QNetworkAccessManager* HttpClientPrivate::getManager() {
        return internal ? new QNetworkAccessManager() : manager;
    }
    
    // 使用用户设定的 URL、请求头、参数等创建 Request
    QNetworkRequest HttpClientPrivate::createRequest(HttpClientPrivate *d, HttpClientRequestMethod method) {
        // 1. 如果是 GET 请求,并且参数不为空,则编码请求的参数,放到 URL 后面
        // 2. 调试时输出网址和参数
        // 3. 设置 Content-Type
        // 4. 添加请求头到 request 中
    
        bool get = method == HttpClientRequestMethod::GET;
        bool upload = method == HttpClientRequestMethod::UPLOAD;
        bool withForm = !get && !upload && !d->useJson; // PUT、POST 或者 DELETE 请求,且 useJson 为 false
        bool withJson = !get && !upload &&  d->useJson; // PUT、POST 或者 DELETE 请求,且 useJson 为 true
    
                                                        // [1] 如果是 GET 请求,并且参数不为空,则编码请求的参数,放到 URL 后面
        if (get && !d->params.isEmpty()) {
            d->url += "?" + d->params.toString(QUrl::FullyEncoded);
        }
    
        // [2] 调试时输出网址和参数
        if (d->debug) {
            qDebug().noquote() << "[网址]" << d->url;
    
            if (withJson) {
                qDebug().noquote() << "[参数]" << d->json;
            }
            else if (withForm || upload) {
                QList<QPair<QString, QString> > paramItems = d->params.queryItems();
                QString buffer; // 避免多次调用 qDebug() 输入调试信息,每次 qDebug() 都有可能输出行号等
    
                                // 按键值对的方式输出参数
                for (int i = 0; i < paramItems.size(); ++i) {
                    QString name = paramItems.at(i).first;
                    QString value = paramItems.at(i).second;
    
                    if (0 == i) {
                        buffer += QString("[参数] %1=%2\n").arg(name).arg(value);
                    }
                    else {
                        buffer += QString("       %1=%2\n").arg(name).arg(value);
                    }
                }
    
                if (!buffer.isEmpty()) {
                    qDebug().noquote() << buffer;
                }
            }
        }
    
        // [3] 设置 Content-Type
        // 如果是 POST 请求,useJson 为 true 时添加 Json 的请求头,useJson 为 false 时添加 Form 的请求头
        if (withForm) {
            d->headers["Content-Type"] = "application/x-www-form-urlencoded";
        }
        else if (withJson) {
            d->headers["Content-Type"] = "application/json; charset=utf-8";
        }
    
        // [4] 添加请求头到 request 中
        QNetworkRequest request(QUrl(d->url));
        for (auto i = d->headers.cbegin(); i != d->headers.cend(); ++i) {
            request.setRawHeader(i.key().toUtf8(), i.value().toUtf8());
        }
    
        return request;
    }
    
    // 读取服务器响应的数据
    QString HttpClientPrivate::readReply(QNetworkReply *reply, const QString &charset) {
        QTextStream in(reply);
        QString result;
        in.setCodec(charset.toUtf8());
    
        while (!in.atEnd()) {
            result += in.readLine();
        }
    
        return result;
    }
    
    // 请求结束的处理函数
    void HttpClientPrivate::handleFinish(HttpClientPrivateCache cache, QNetworkReply *reply, const QString &successMessage, const QString &failMessage) {
        // 1. 执行请求成功的回调函数
        // 2. 执行请求失败的回调函数
        // 3. 执行请求结束的回调函数
        // 4. 释放 reply 和 manager 对象
    
        if (reply->error() == QNetworkReply::NoError) {
            if (cache.debug) {
                qDebug().noquote() << QString("[结束] 成功: %1").arg(successMessage);
            }
    
            // [1] 执行请求成功的回调函数
            if (nullptr != cache.successHandler) {
                cache.successHandler(successMessage);
            }
        }
        else {
            if (cache.debug) {
                qDebug().noquote() << QString("[结束] 失败: %1").arg(failMessage);
            }
    
            // [2] 执行请求失败的回调函数
            if (nullptr != cache.failHandler) {
                cache.failHandler(failMessage, reply->error());
            }
        }
    
        // [3] 执行请求结束的回调函数
        if (nullptr != cache.completeHandler) {
            cache.completeHandler();
        }
    
        // [4] 释放 reply 和 manager 对象
        if (nullptr != reply) {
            reply->deleteLater();
        }
    
        if (cache.internal && nullptr != cache.manager) {
            cache.manager->deleteLater();
        }
    }
    
    /*-----------------------------------------------------------------------------|
    |                                 HttpClient                                  |
    |----------------------------------------------------------------------------*/
    
    // 注意: 在异步请求中 HttpClient 的 HttpClientPrivate 成员变量 d 已经被析构,所以需要先缓存相关变量为栈对象,使用 = 以值的方式访问
    HttpClient::HttpClient(const QString &url) : d(new HttpClientPrivate(url)) { }
    
    HttpClient::~HttpClient() {
        delete d;
    }
    
    void HttpClient::stop2()
    {
        d->stop1();
    }
    
    // 传入 QNetworkAccessManager 给多个请求共享
    HttpClient& HttpClient::manager(QNetworkAccessManager *manager) {
        d->manager = manager;
        d->internal = (nullptr == manager);
    
        return *this;
    }
    
    // 传入 debug 为 true 则使用 debug 模式,请求执行时输出请求的 URL 和参数等
    HttpClient& HttpClient::debug(bool debug) {
        d->debug = debug;
    
        return *this;
    }
    
    // 添加一个请求的参数,可以多次调用添加多个参数
    HttpClient& HttpClient::param(const QString &name, const QVariant &value) {
        d->params.addQueryItem(name, value.toString());
    
        return *this;
    }
    
    // 添加多个请求的参数
    HttpClient& HttpClient::params(const QMap<QString, QVariant> &ps) {
        for (auto iter = ps.cbegin(); iter != ps.cend(); ++iter) {
            d->params.addQueryItem(iter.key(), iter.value().toString());
        }
    
        return *this;
    }
    
    // 添加请求的参数 (请求体),使用 Json 格式,例如 "{\"name\": \"Alice\"}"
    HttpClient& HttpClient::json(const QString &json) {
        d->json = json;
        d->useJson = true;
    
        return *this;
    }
    
    // 添加请求头
    HttpClient& HttpClient::header(const QString &name, const QString &value) {
        d->headers[name] = value;
    
        return *this;
    }
    
    // 添加多个请求头
    HttpClient& HttpClient::headers(const QMap<QString, QString> nameValues) {
        for (auto i = nameValues.cbegin(); i != nameValues.cend(); ++i) {
            d->headers[i.key()] = i.value();
        }
    
        return *this;
    }
    
    // 注册请求成功的回调函数
    HttpClient& HttpClient::success(std::function<void(const QString &)> successHandler) {
        d->successHandler = successHandler;
    
        return *this;
    }
    
    // 注册请求失败的回调函数
    HttpClient& HttpClient::fail(std::function<void(const QString &, int)> failHandler) {
        d->failHandler = failHandler;
    
        return *this;
    }
    
    // 注册请求结束的回调函数,不管成功还是失败都会执行
    HttpClient& HttpClient::complete(std::function<void()> completeHandler) {
        d->completeHandler = completeHandler;
    
        return *this;
    }
    
    // 设置请求响应的编码
    HttpClient& HttpClient::charset(const QString &cs) {
        d->charset = cs;
    
        return *this;
    }
    
    // 执行 GET 请求
    void HttpClient::get() {
        HttpClientPrivate::executeQuery(d, HttpClientRequestMethod::GET);
    }
    
    // 执行 POST 请求
    void HttpClient::post() {
        HttpClientPrivate::executeQuery(d, HttpClientRequestMethod::POST);
    }
    
    // 执行 PUT 请求
    void HttpClient::put() {
        HttpClientPrivate::executeQuery(d, HttpClientRequestMethod::PUT);
    }
    
    // 执行 DELETE 请求
    void HttpClient::remove() {
        HttpClientPrivate::executeQuery(d, HttpClientRequestMethod::DELETE);
    }
    
    // 使用 GET 进行下载,下载的文件保存到 savePath
    void HttpClient::download(const QString &savePath) {
        HttpClientPrivate::download(d, savePath);
    }
    
    // 上传文件
    void HttpClient::upload(const QString &path) {
        QStringList paths = { path };
        HttpClientPrivate::upload(d, paths, QByteArray());
    }
    
    // 上传文件,文件的内容以及读取到 data 中
    void HttpClient::upload(const QByteArray &data) {
        HttpClientPrivate::upload(d, QStringList(), data);
    }
    
    // 上传多个文件
    void HttpClient::upload(const QStringList &paths) {
        HttpClientPrivate::upload(d, paths, QByteArray());
    }

    用法:

    #include "Component/HttpClient.h"
      
    HttpClient("https://www.abc.com/api.php").success([this](const QString &res) {
    	//逻辑处理
    }).param("参数1", 参数1).param("USER_TOKEN", USER_TOKEN).post();

     

    • 有帮助 1

  4. #include <windows.h>
    //设置当前程序开机启动
    void AutoPowerOn()
    {
        HKEY hKey;
        //std::string strRegPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
     
     
        //1、找到系统的启动项  
        if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"), 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) ///打开启动项       
        {
            //2、得到本程序自身的全路径
            TCHAR strExeFullDir[MAX_PATH];
            GetModuleFileName(NULL, strExeFullDir, MAX_PATH);
     
     
            //3、判断注册表项是否已经存在
            TCHAR strDir[MAX_PATH] = {};
            DWORD nLength = MAX_PATH;
            long result = RegGetValue(hKey, nullptr, _T("GISRestart"), RRF_RT_REG_SZ, 0, strDir, &nLength);
     
     
            //4、已经存在
            if (result != ERROR_SUCCESS || _tcscmp(strExeFullDir, strDir) != 0)
            {
                //5、添加一个子Key,并设置值,"GISRestart"是应用程序名字(不加后缀.exe) 
                RegSetValueEx(hKey, _T("GISRestart"), 0, REG_SZ, (LPBYTE)strExeFullDir, (lstrlen(strExeFullDir) + 1)*sizeof(TCHAR));
     
     
                //6、关闭注册表
                RegCloseKey(hKey);
            }
        }
    }
     
     
    //取消当前程序开机启动
    void CanclePowerOn()
    {
        HKEY hKey;
        //std::string strRegPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
     
     
        //1、找到系统的启动项  
        if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"), 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
        {
            //2、删除值
            RegDeleteValue(hKey, _T("GISRestart"));
     
     
            //3、关闭注册表
            RegCloseKey(hKey);
        }
    }

    亲测有用

    • 有帮助 1

  5. watcherthread.h

    #ifndef WATCHERTHREAD_H
    #define WATCHERTHREAD_H
    
    #include <QThread>
    #include <QAtomicInt>
    #include <windows.h>
    
    class WatcherThread : public QThread
    {
        Q_OBJECT
    public:
        WatcherThread(const QString &path) : QThread(), _path(path + (path.endsWith(QLatin1Char('/')) ? QString() : QStringLiteral("/"))), _directory(0), _resultEvent(0), _stopEvent(0), _done(false){}
        ~WatcherThread();
        void stop();
    
    protected:
        QString longWinPath(const QString &inpath);
        QString pathtoUNC(const QString &_str);
        void run();
        void watchChanges(size_t fileNotifyBufferSize,bool *increaseBufferSize);
        void closeHandle();
    
    signals:
        void changed(const QString &path,FILE_NOTIFY_INFORMATION *);
        void lostChanges();
        void ready();
    
    private:
        QString _path;
        HANDLE _directory;
        HANDLE _resultEvent;
        HANDLE _stopEvent;
        QAtomicInt _done;
        FILE_NOTIFY_INFORMATION *pFileNotifyBuffer;
    };
    
    #endif // WATCHERTHREAD_H

    watcherthread.cpp

    #include "watcherthread.h"
    
    #include <QDir>
    #include <QUrl>
    #include <QFile>
    
    #ifdef Q_OS_WIN
    #include <windows.h>
    #include <windef.h>
    #include <winbase.h>
    #include <fcntl.h>
    #include <io.h>
    #endif
    
    #include <QtDebug>
    
    
    void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
                                     bool *increaseBufferSize)
    {
        *increaseBufferSize = false;
        const QString longPath = _path;
    
        _directory = CreateFileW(
                    longPath.toStdWString().data(),
                    FILE_LIST_DIRECTORY,
                    FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
                    nullptr,
                    OPEN_EXISTING,
                    FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
                    nullptr);
    
        if (_directory == INVALID_HANDLE_VALUE) {
            const auto error = GetLastError();
            qDebug() << "Failed to create handle ," << error;
            _directory = 0;
            return;
        }
    
        OVERLAPPED overlapped;
        overlapped.hEvent = _resultEvent;
    
        // QVarLengthArray ensures the stack-buffer is aligned like double and qint64.
        QVarLengthArray<char, 4096 * 10> fileNotifyBuffer;
        fileNotifyBuffer.resize(static_cast<int>(fileNotifyBufferSize));
    
        const size_t fileNameBufferSize = 4096;
        TCHAR fileNameBuffer[fileNameBufferSize];
    
    
        pFileNotifyBuffer = reinterpret_cast<FILE_NOTIFY_INFORMATION *>(fileNotifyBuffer.data());
    
        while (!_done) {
            ResetEvent(_resultEvent);
            DWORD dwBytesReturned = 0;
            if (!ReadDirectoryChangesW(_directory, pFileNotifyBuffer,
                                       static_cast<DWORD>(fileNotifyBufferSize), true,
                                       FILE_NOTIFY_CHANGE_FILE_NAME
                                       | FILE_NOTIFY_CHANGE_DIR_NAME
                                       | FILE_NOTIFY_CHANGE_LAST_WRITE
                                       | FILE_NOTIFY_CHANGE_ATTRIBUTES, // attributes are for vfs pin state changes
                                       &dwBytesReturned,
                                       &overlapped,
                                       nullptr)) {
                const DWORD errorCode = GetLastError();
                if (errorCode == ERROR_NOTIFY_ENUM_DIR) {
                    qDebug() << "The buffer for changes overflowed! Triggering a generic change and resizing";
                    emit changed(_path,pFileNotifyBuffer);
                    *increaseBufferSize = true;
                } else {
                    qDebug() << "ReadDirectoryChangesW error";
                }
                break;
            }
    
            emit ready();
    
            HANDLE handles[] = { _resultEvent, _stopEvent };
            DWORD result = WaitForMultipleObjects(
                        2, handles,
                        false, // awake once one of them arrives
                        INFINITE);
            const auto error = GetLastError();
            if (result == 1) {
                qDebug() << "Received stop event, aborting folder watcher thread";
                break;
            }
            if (result != 0) {
                qDebug() << "WaitForMultipleObjects failed";
                break;
            }
    
            bool ok = GetOverlappedResult(_directory, &overlapped, &dwBytesReturned, false);
            if (!ok) {
                const DWORD errorCode = GetLastError();
                if (errorCode == ERROR_NOTIFY_ENUM_DIR) {
                    qDebug() << "The buffer for changes overflowed! Triggering a generic change and resizing";
                    emit lostChanges();
                    emit changed(_path,pFileNotifyBuffer);
                    *increaseBufferSize = true;
                } else {
                    qDebug() << "GetOverlappedResult error";
                }
                break;
            }
    
            FILE_NOTIFY_INFORMATION *curEntry = pFileNotifyBuffer;
            forever {
                const int len = curEntry->FileNameLength / sizeof(wchar_t);
                QString longfile = longPath + QString::fromWCharArray(curEntry->FileName, len);
    
                // Unless the file was removed or renamed, get its full long name
                // TODO: We could still try expanding the path in the tricky cases...
                if (curEntry->Action != FILE_ACTION_REMOVED
                        && curEntry->Action != FILE_ACTION_RENAMED_OLD_NAME) {
                    const auto wfile = longfile.toStdWString();
                    const int longNameSize = GetLongPathNameW(wfile.data(), fileNameBuffer, fileNameBufferSize);
                    const auto error = GetLastError();
                    if (longNameSize > 0) {
                        longfile = QString::fromWCharArray(fileNameBuffer, longNameSize);
                    } else {
                        qDebug() << "Error converting file name" << longfile << "to full length, keeping original name.";
                    }
                }
    
    #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
                // The prefix is needed for native Windows functions before Windows 10, version 1607
                const bool hasLongPathPrefix = longPath.startsWith(QStringLiteral("\\\\?\\"));
                if (hasLongPathPrefix)
                {
                    longfile.remove(0, 4);
                }
    #endif
                longfile = QDir::cleanPath(longfile);
    
                // Skip modifications of folders: One of these is triggered for changes
                // and new files in a folder, probably because of the folder's mtime
                // changing. We don't need them.
                const bool skip = curEntry->Action == FILE_ACTION_MODIFIED
                        && QFileInfo(longfile).isDir();
    
                if (!skip) {
                    emit changed(longfile,curEntry);
                } else {
                    //qDebug() << "Skipping syncing of" << longfile;
                }
    
                if (curEntry->NextEntryOffset == 0) {
                    break;
                }
                // FILE_NOTIFY_INFORMATION has no fixed size and the offset is in bytes therefor we first need to cast to char
                curEntry = reinterpret_cast<FILE_NOTIFY_INFORMATION *>(reinterpret_cast<char*>(curEntry) + curEntry->NextEntryOffset);
            }
        }
    
        CancelIo(_directory);
        closeHandle();
    }
    
    void WatcherThread::closeHandle()
    {
        if (_directory) {
            CloseHandle(_directory);
            _directory = nullptr;
        }
    }
    
    void WatcherThread::run()
    {
        _resultEvent = CreateEvent(nullptr, true, false, nullptr);
        _stopEvent = CreateEvent(nullptr, true, false, nullptr);
    
        // If this buffer fills up before we've extracted its data we will lose
        // change information. Therefore start big.
        size_t bufferSize = 4096 * 10;
        const size_t maxBuffer = 64 * 1024;
    
        while (!_done) {
            bool increaseBufferSize = false;
            watchChanges(bufferSize, &increaseBufferSize);
    
            if (increaseBufferSize) {
                bufferSize = qMin(bufferSize * 2, maxBuffer);
            } else if (!_done) {
                // Other errors shouldn't actually happen,
                // so sleep a bit to avoid running into the same error case in a
                // tight loop.
                sleep(2);
            }
        }
    }
    
    WatcherThread::~WatcherThread()
    {
        closeHandle();
    }
    
    void WatcherThread::stop()
    {
        _done = 1;
        SetEvent(_stopEvent);
    }

    Dlg.h

    #ifndef DLG_H
    #define DLG_H
    
    #include <QDialog>
    #include <QDir>
    #include <QSet>
    #include <windows.h>
    #include "watcherthread.h"
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class Dlg; }
    QT_END_NAMESPACE
    
    class Dlg : public QDialog
    {
        Q_OBJECT
    
    public:
        Dlg(QWidget *parent = nullptr);
        ~Dlg();
    
        void msg(QString);
    signals:
        /** Emitted when one of the watched directories or one
         *  of the contained files is changed. */
        void pathChanged(const QString &path);
    
    private slots:
        void on_btn_start_clicked();
        void changeDetected(const QString &,FILE_NOTIFY_INFORMATION *);
        void lostChanges();
    
        void on_btn_stop_clicked();
    
        void changeDetected(const QStringList &paths);
    
    private:
        Ui::Dlg *ui;
        WatcherThread *_thread;
    
        QElapsedTimer _timer;
        QSet<QString> _lastPaths;
    
        void appendSubPaths(QDir dir, QStringList& subPaths);
    
    
    };
    #endif // DLG_H

    Dlg.cpp

    #pragma execution_character_set("utf-8")
    
    #include "Dlg.h"
    #include "ui_Dlg.h"
    
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <tchar.h>
    #include <qDebug>
    #include <QFileInfo>
    #include <QDir>
    #include <QSet>
    
    Dlg::Dlg(QWidget *parent)
        : QDialog(parent)
        , ui(new Ui::Dlg)
    {
        ui->setupUi(this);
    
    
    }
    
    Dlg::~Dlg()
    {
        delete ui;
    }
    
    void Dlg::msg(QString txt)
    {
        ui->plainTextEdit->appendHtml(txt+"\n");
    }
    
    
    void Dlg::on_btn_start_clicked()
    {
        qDebug() << "开始......";
        _thread = new WatcherThread("D:\\TEST\\");
    
        connect(_thread, SIGNAL(changed(const QString &,FILE_NOTIFY_INFORMATION *)),this, SLOT(changeDetected(const QString &,FILE_NOTIFY_INFORMATION *)));
        connect(_thread, SIGNAL(lostChanges()),this, SLOT(lostChanges()));
        //connect(_thread, &WatcherThread::ready,this, [this]() { qDebug() << "触发了 ready"; });
    
        _thread->start();
        _timer.start();
    }
    
    void Dlg::changeDetected(const QString & path,FILE_NOTIFY_INFORMATION * curEntry)
    {
    
    
        switch(curEntry->Action)
        {
        case FILE_ACTION_ADDED:  //1
            qDebug() << "文件被创建" << path;
            break;
        case FILE_ACTION_REMOVED: //2
            qDebug() << "文件被删除" << path;
            break;
        case FILE_ACTION_MODIFIED: //3
            qDebug() << "文件被修改" << path;
            break;
        case FILE_ACTION_RENAMED_OLD_NAME: //4
            qDebug() << "旧文件名:" << path;
            break;
        case FILE_ACTION_RENAMED_NEW_NAME: //5
            qDebug() << "新文件名:" << path;
            break;
        default:
            qDebug() << "未知操作";
            break;
        }
    
            QFileInfo fileInfo(path);
            QStringList paths(path);
            if (fileInfo.isDir()) {
                QDir dir(path);
                appendSubPaths(dir, paths);
            }
            changeDetected(paths);
    }
    
    void Dlg::lostChanges()
    {
        qDebug() << "触发了 lostChanges";
    }
    
    
    void Dlg::on_btn_stop_clicked()
    {
        _thread->stop();
        qDebug() << "结束......";
    }
    
    
    void Dlg::changeDetected(const QStringList &paths)
    {
        // TODO: this shortcut doesn't look very reliable:
        //   - why is the timeout only 1 second?
        //   - what if there is more than one file being updated frequently?
        //   - why do we skip the file altogether instead of e.g. reducing the upload frequency?
    
        // Check if the same path was reported within the last second.
        QSet<QString> pathsSet = paths.toSet();
        if (pathsSet == _lastPaths && _timer.elapsed() < 1000) {
            // the same path was reported within the last second. Skip.
            return;
        }
        _lastPaths = pathsSet;
        _timer.restart();
    
        QSet<QString> changedPaths;
    
        // ------- handle ignores:
        for (int i = 0; i < paths.size(); ++i) {
            QString path = paths[i];
            changedPaths.insert(path);
        }
        if (changedPaths.isEmpty()) {
            return;
        }
    
    
        foreach (const QString &path, changedPaths) {
            //qDebug() << "已修改:" << path;
        }
    }
    
    void Dlg::appendSubPaths(QDir dir, QStringList& subPaths) {
        QStringList newSubPaths = dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files);
        for (int i = 0; i < newSubPaths.size(); i++) {
            QString path = dir.path() + "/" + newSubPaths[i];
            QFileInfo fileInfo(path);
            subPaths.append(path);
            if (fileInfo.isDir()) {
                QDir dir(path);
                appendSubPaths(dir, subPaths);
            }
        }
    }

    QQ图片20211118103832.jpg


  6. 不要直接使用QLable,自己写一个类集成QLabel

    头文件:

    #ifndef LABEL_H
    #define LABEL_H
    
    #include <QLabel>
    #include <QWidget>
    #include <Qt>
    
    class Label : public QLabel {
        Q_OBJECT
    
    public:
        explicit Label(QWidget* parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
        ~Label();
    
    signals:
        void clicked();
    
    protected:
        void mousePressEvent(QMouseEvent* event);
    
    };
    
    #endif // LABEL_H

    cpp文件

    #include "Label.h"
    
    Label::Label(QWidget* parent, Qt::WindowFlags f)
        : QLabel(parent) {
    
    }
    
    Label::~Label() {}
    
    void Label::mousePressEvent(QMouseEvent* event) {
        emit clicked();
    }

    使用:

    connect(Label实例化对象,SIGNAL(clicked()),this,SLOT(关联的槽函数()));

     


  7. 添加一行

    int i = ui->tableWidget->rowCount();
    ui->tableWidget->insertRow(i);
    ui->tableWidget->setItem(i,0,new QTableWidgetItem("AAA"+QString::number(i)));
    ui->tableWidget->setItem(i,1,new QTableWidgetItem("BBB"));
    ui->tableWidget->setItem(i,2,new QTableWidgetItem("CCC"));
    ui->tableWidget->setItem(i,3,new QTableWidgetItem("DDD"));

    删除一行

    QTableWidgetItem* item = ui->tableWidget->item(i,0);
    if("AAA5" == item->text())
    {
        ui->tableWidget->removeRow(i);
    }

    下面是我的方法代码:

    //点击了toolbar
    void ServerMain::action_triggered(QAction * _action)
    {
        QString text = _action->text();
        if("开启服务" == text)
        {
            this->startService();
        }
        else if("测试添加" == text)
        {
            int i = ui->tableWidget->rowCount();
            ui->tableWidget->insertRow(i);
            ui->tableWidget->setItem(i,0,new QTableWidgetItem("AAA"+QString::number(i)));
            ui->tableWidget->setItem(i,1,new QTableWidgetItem("BBB"));
            ui->tableWidget->setItem(i,2,new QTableWidgetItem("CCC"));
            ui->tableWidget->setItem(i,3,new QTableWidgetItem("DDD"));
        }else if("测试删除" == text){
            for (int i = 0; i < ui->tableWidget->rowCount(); i++) {
                QTableWidgetItem* item = ui->tableWidget->item(i,0);
                if("AAA5" == item->text())
                {
                    ui->tableWidget->removeRow(i);
                }
            }
        }
    }

    112233.png


  8. QTabWidget::pane {
      border: 1px solid #c0c6cc;
      top:-1px; 
      background: #ffffff; 
    } 
    
    QTabBar::tab {
    width: 80px;
    height: 35px;
    font-weight: 400;
    background-color: #F1F1F1;
    border: 1px solid #ccc;
    } 
    
    QTabBar::tab:selected { 
    color: #0064c8;
    margin-right: -2px; 
    background-color: #fff;
    border-bottom:1px solid #fff;
    }

     


  9. public function __construct()
    	{
    		$origin = isset($_SERVER['HTTP_ORIGIN'])? $_SERVER['HTTP_ORIGIN'] : '';
    		$allow_origin = array(
    			'https://abc.qq.com','https://123.xpgo.net','https://php.thinkh5.com',
    		); 
    		if(in_array($origin, $allow_origin)){  
    			header('Access-Control-Allow-Origin:'.$origin);       
    		}
    	}

    这样就可以设置多个跨域域名了,亲测有效


  10. <script>
    /* 
    * 替换URL的参数值
    * url 目标url
    * arg 需要替换的参数名称 
    * arg_val 替换后的参数的值 
    * return url 参数替换后的url 
    */
    function changeURLArg(url, arg, arg_val) {
        var pattern = arg + '=([^&]*)';
        var replaceText = arg + '=' + arg_val;
        if (url.match(pattern)) {
            var retuenUrl = url;
            var temp = '/(\&' + arg + '=)([^&]*)/gi';
            if (eval(temp).test(retuenUrl)) {
            retuenUrl = retuenUrl.replace(eval(temp), '&' + replaceText);
            }
            var temps = '/([\?])(' + arg + '=)([^&]*)/gi';
            if (eval(temps).test(retuenUrl)) {
            retuenUrl = retuenUrl.replace(eval(temps), '?' + replaceText);
            }
            return retuenUrl;
        }
        else {
            // if (url.match('[\?]')) {
            if (url.indexOf('?') > 0) {
                return url + "&" + replaceText;
            } else {
                return url + "?" + replaceText;
            }
        }
        return url + '\n' + arg + '\n' + arg_val;
    }
    document.write(changeUrlArg('http://www.abcd.com/test.testhtml?typeId=3&id=2&user_id=1', 'id', 5));
    </script>

     


  11. 1.创建React项目

    npx create-react-app my-app

    2.添加微软的FlumentUI

    yarn add @fluentui/react

    或者

    npm install @fluentui/react

     

    3.添加路由

    npm install react-router-dom@5.2.0

    4.为了使用ajax

    yarn add axios

    5.跨组件沟通事件

    yarn add pubsub.js
    npm install pubsub-js --save

    6.使用图表

    npm install echarts --save

    7.使用md5等加密

    npm install crypto-js

    8.开启项目

    yarn start

    9.编译

    yaarn build

    10.无刷新上传

    npm install plupload@2.3.7

     


  12. How to get user in TokenListener in symfony3.x?

    1.打开文件:

    \vendor\symfony\symfony\src\Symfony\Component\Security\Core\Authorization\AuthorizationChecker.php

    增加一个方法:

    final public function getUser()
    {
        return $this->tokenStorage->getToken()->getUser();
    }

    2.在TokenListener中

    $user = $this->container->get('security.authorization_checker')->getUser();

    这样就可以获取到用户的信息了!


  13. using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace WindowsFormsApp4
    {
        public delegate void DelReadStdOutput(string result);
        public delegate void DelReadErrOutput(string result);
    
        public partial class Form1 : Form
        {
            public event DelReadStdOutput ReadStdOutput;
            public event DelReadErrOutput ReadErrOutput;
    
            public Form1()
            {
                InitializeComponent();
                Init();
            }
            private void Init()
            {
                //3.将相应函数注册到委托事件中
                ReadStdOutput += new DelReadStdOutput(ReadStdOutputAction);
                ReadErrOutput += new DelReadErrOutput(ReadErrOutputAction);
            }
            private void RealAction(string StartFileName, string StartFileArg)
            {
                Process CmdProcess = new Process();
                CmdProcess.StartInfo.FileName = StartFileName;      // 命令
                CmdProcess.StartInfo.Arguments = StartFileArg;      // 参数
    
                CmdProcess.StartInfo.CreateNoWindow = true;         // 不创建新窗口
                CmdProcess.StartInfo.UseShellExecute = false;
                CmdProcess.StartInfo.RedirectStandardInput = true;  // 重定向输入
                CmdProcess.StartInfo.RedirectStandardOutput = true; // 重定向标准输出
                CmdProcess.StartInfo.RedirectStandardError = true;  // 重定向错误输出
                                                                    //CmdProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    
                CmdProcess.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
                CmdProcess.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);
    
                CmdProcess.EnableRaisingEvents = true;                      // 启用Exited事件
                CmdProcess.Exited += new EventHandler(CmdProcess_Exited);   // 注册进程结束事件
    
                CmdProcess.Start();
                CmdProcess.BeginOutputReadLine();
                CmdProcess.BeginErrorReadLine();
    
                // 如果打开注释,则以同步方式执行命令,此例子中用Exited事件异步执行。
                // CmdProcess.WaitForExit();     
            }
    
            private void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
            {
                if (e.Data != null)
                {
                    // 4. 异步调用,需要invoke
                    this.Invoke(ReadStdOutput, new object[] { e.Data });
                }
            }
    
            private void p_ErrorDataReceived(object sender, DataReceivedEventArgs e)
            {
                if (e.Data != null)
                {
                    this.Invoke(ReadErrOutput, new object[] { e.Data });
                }
            }
    
            private void ReadStdOutputAction(string result)
            {
                this.richTextBox1.AppendText(result + "\r\n");
            }
    
            private void ReadErrOutputAction(string result)
            {
                this.richTextBox1.AppendText(result + "\r\n");
            }
    
            private void CmdProcess_Exited(object sender, EventArgs e)
            {
                // 执行结束后触发
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                RealAction("adb.exe", "devices");
            }
    
            private void button2_Click(object sender, EventArgs e)
            {
                RealAction("adb.exe", "shell /system/bin/uiautomator dump --compressed /data/local/tmp/uidump.xml");
            }
    
            private void button3_Click(object sender, EventArgs e)
            {
                RealAction("adb.exe", "pull /data/local/tmp/uidump.xml C:/Users/m/Desktop/DATA/uidump.xml");
            }
        }
    }

    微信截图_20210602161015.png


  14. #include <iostream>
    #include "Markup.h"
    
    using namespace std;
    
    int main()
    {
    	CMarkup xml;
    	xml.Load("D:\\PROJECTS\\TestXML\\Debug\\MyTest.xml");
    	BOOL findEle = xml.FindElem();
    	if (!findEle)
    	{
    		cerr << "can not find element!";
    		return 1;
    	}
    	xml.IntoElem();
    	
    	//<Person name="duqingnian" age="36" address="江苏省" />
    	BOOL b = xml.FindElem("Person");
    	if (!b)
    	{
    		cout << "lost Person !!!" << endl;
    	}
    
    	cout << "name=" << xml.GetAttrib("name") << ", age=" << xml.GetAttrib("age") << ",addres=" << xml.GetAttrib("address") << endl;;
    
    
    
    	if (true)
    	{
    		BOOL p = xml.FindElem("Phone");
    		if (!p)
    		{
    			cout << "lost phone!!" << endl;
    		}
    		xml.IntoElem();
    		xml.FindElem("Disk");
    		cout << xml.GetAttrib("size") << endl;
    	}
    	xml.OutOfElem();
    
    
    
    	//<Hello ID="HID">nihao</Hello>
    	if (true)
    	{
    		BOOL a = xml.FindElem("Hello");
    		if (!a)
    		{
    			cout << "lost Hello!!" << endl;
    		}
    		cout << "data="  << xml.GetData() << ",id=" << xml.GetAttrib("ID") << endl;
    	}
    
    
    	cout << "over" << endl;
    	return 0;
    }
    
    //XML
    /**
    <?xml version="1.0" encoding="UTF-8"?>
    <Window>
      <Person name="duqingnian" age="36" address="江苏省" />
      <Phone>
    	<Cpu stype="intel" />
    	<Memory size="16GB" />
    	<Disk size="512GB" type="固态" />
      </Phone>
      <Hello ID="HID">nihao</Hello>
    </Window>
    */
    
    //OUTPUT:
    /**
    name=duqingnian, age=36,addres=江苏省
    512GB
    data=nihao,id=HID
    over
    */

    CMarkup

×
×
  • 创建新的...

重要信息

注册必须使用2-8个中文汉字作为账号