最新回复 发布由 墨香年少
-
-
使用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
-
先看效果图:
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
-
QDesktopServices::openUrl(QUrl::fromLocalFile(filepath));
-
du -lh --max-depth=1
- 1
-
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
-
#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
-
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); } } }
-
不要直接使用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(关联的槽函数()));
-
添加一行
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); } } } }
-
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; }
-
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); } }
这样就可以设置多个跨域域名了,亲测有效
-
网上很多方案很不靠谱。
这里只提出apache的解决方案,nginx请自行解决
apache解决方案如下:
1.开启伪静态
2.项目目录下,建立.htaccess
文件内容如下:
Options -MultiViews RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.html [QSA,L]
再刷新下试试看
-
<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>
-
control.ImeMode = System.Windows.Forms.ImeMode.Disable;
这样默认就是英文输入法,不会出现每次都需要从中文切换到英文的操作
对于需要从扫码器读取输入的程序很有用!
-
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
-
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();
这样就可以获取到用户的信息了!
-
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"); } } }
-
官方下载链接:https://download3.vmware.com/software/wkst/file/VMware-workstation-full-15.0.0-10134415.exe
永久许可证:ZC10K-8EF57-084QZ-VXYXE-ZF2XF -
-
#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 */
-
效果图:
下载地址:
源码:github
-
ZF3R0-FHED2-M80TY-8QYGC-NPKYF
YF390-0HF8P-M81RQ-2DXQE-M2UT6
ZF71R-DMX85-08DQY-8YMNC-PPHV8 -
ZF3R0-FHED2-M80TY-8QYGC-NPKYF
YF390-0HF8P-M81RQ-2DXQE-M2UT6
ZF71R-DMX85-08DQY-8YMNC-PPHV8 -
Qt如何清空Layout下的所有元素
在 Qt
发布于
亲测好用