CMake+QT+大漠插件的桌面应用开发(QThread)
文章目录
- CMake+QT+大漠插件的桌面应用开发(QThread)
- 简介
- 环境
- 项目结构
- 配置编译环境
- 代码
CMake+QT+大漠插件的桌面应用开发(QThread)
简介
-
在CMake+QT+大漠插件的桌面应用开发中已经给出了QT配合大漠插件开发桌面应用的样例
-
不过由于主窗口的UI操作和大漠的调用是在一个线程里面的,所以当大漠调用时间过长时会出现UI界面卡顿的现象
-
我们可以利用子线程处理耗时操作,处理完后再由主线程(UI线程)更新界面,这样界面就不会出现卡顿。
-
在这里,我们将会用到QThread,调整后的QT主线程与子线程交互逻辑图如下:
-
交互逻辑描述
- 当点击“注册”选项时,会发出
regDM
信号,子线程接收到该信号会执行MyMainWorker中的doRegDM
方法,执行完成后会发出regDMReady
信号,主线程接收到该信号会执行更新UI的操作 - 当点击“搜索”按钮时,同理
- 当点击“截图”按钮时,同理
- 当点击“注册”选项时,会发出
环境
版本/规范 | 备注 | |
---|---|---|
平台 | win32 | 操作系统为Windows10 |
CMake | 3.27.8 | CLion自带 |
C++ | 17 | |
Toolchain | VisualStudio 2022 | 只用其工具链,记得先安装好 |
QT | 5.12.12 | 安装时选择msvc2017,不要64位的 |
DM | 7.2353 | |
CLion | 2023.3.2 | 你也可以用其他IDE工具 |
- 启动IDE时,记得以管理员模式启动
项目结构
- 新建一个项目 qt_dm_demo_x_02
- 目录同CMake+QT+大漠插件的桌面应用开发中一致,会多出MyMainWorker,用于处理子线程逻辑
qt_dm_demo_x_02 # 项目目录
-- ......
--MyMainWorker.cpp
--MyMainWorker.h
-- ......
配置编译环境
- 其他同CMake+QT+大漠插件的桌面应用开发中一致
- CMakeLists.txt 文件中生成可执行文件时,会多出MyMainWorker.cpp、MyMainWorker.h
# 生成可执行文件
add_executable(${PROJECT_NAME} main.cppstrutils.cpp strutils.hdmutil.cpp dmutil.hmymainwindow.cpp mymainwindow.h mymainwindow.uiMyMainWorker.cpp MyMainWorker.h
)
代码
- dmutil.h、dmutil.cpp、strutils.h、strutils.cpp、mymainwindow.ui、main.cpp同CMake+QT+大漠插件的桌面应用开发中一致
- mymainwindow.h
#ifndef QT_DM_DEMO_X_MYMAINWINDOW_H
#define QT_DM_DEMO_X_MYMAINWINDOW_H#include <QMainWindow>
#include <QTextEdit>
#include <QThread>#include "dmutil.h"QT_BEGIN_NAMESPACE
namespace Ui { class MyMainWindow; }
QT_END_NAMESPACEclass MyMainWindow : public QMainWindow {
Q_OBJECTQThread workerThread;
public:explicit MyMainWindow(QWidget *parent = nullptr);~MyMainWindow() override;public:void showInfo(const QString &message, const QString &title = "提示");void showWarn(const QString &message, const QString &title = "告警");signals:void regDM(Idmsoft **pDm);void findWindow(Idmsoft *pDm, const QString &title);void captureWindow(Idmsoft *pDm, const long hwnd);public slots:void showMessageBox(bool result, const QString &message);void showTable(bool result, const QString &msg, const vector<MyWindow> &windowVec);private:Ui::MyMainWindow *ui;Idmsoft *pCommonDm = nullptr;
};#endif //QT_DM_DEMO_X_MYMAINWINDOW_H
- mymainwindow.cpp
// You may need to build the project (run Qt uic code generator) to get "ui_MyMainWindow.h" resolved#include <QFont>
#include <QHeaderView>
#include <QMessageBox>
#include <QPushButton>
#include <QAction>
#include <QString>
#include <QTableWidgetItem>
#include <QObject>
#include <QVector>
#include <iostream>
#include "mymainwindow.h"
#include "ui_MyMainWindow.h"
#include "MyMainWorker.h"using namespace std;MyMainWindow::MyMainWindow(QWidget *parent) :QMainWindow(parent), ui(new Ui::MyMainWindow) {ui->setupUi(this);qRegisterMetaType<QVector<int>>("QVector<int>");qRegisterMetaType<vector<MyWindow>>("vector<MyWindow>");// Init ViewssetFixedSize(1280, 720);ui->tableWidget->setColumnCount(3);ui->tableWidget->setHorizontalHeaderLabels(QStringList() << "进程ID" << "句柄" << "标题");ui->tableWidget->horizontalHeader()->setStretchLastSection(true); // 最后一列自动铺满表格// ui->tableWidget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);ui->tableWidget->horizontalHeader()->setHighlightSections(false);ui->tableWidget->horizontalHeader()->setStyleSheet("QHeaderView::section{background:gray;}");ui->tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);QFont font = ui->tableWidget->horizontalHeader()->font();font.setBold(true);ui->tableWidget->horizontalHeader()->setFont(font);ui->tableWidget->setStyleSheet("QTableWidget::item:hover { background-color: lightblue; }");ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); // 禁止编辑ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); // 选中整行// Init Listenerauto worker = new MyMainWorker;worker->moveToThread(&workerThread);connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);// 注册大漠connect(ui->actionReg, &QAction::triggered, [this]() {ui->actionReg->setEnabled(false);emit this->regDM(&this->pCommonDm);});connect(this, &MyMainWindow::regDM, worker, &MyMainWorker::doRegDM);connect(worker, &MyMainWorker::regDMReady, this, &MyMainWindow::showMessageBox);// 查找窗口connect(ui->btnQuery, &QPushButton::clicked, [this]() {ui->btnQuery->setEnabled(false);emit this->findWindow(this->pCommonDm, ui->edtTitle->text());});connect(this, &MyMainWindow::findWindow, worker, &MyMainWorker::doFindWindow);connect(worker, &MyMainWorker::findWindowReady, this, &MyMainWindow::showTable);// 截图connect(ui->btnCapture, &QPushButton::clicked, [this]() {ui->btnCapture->setEnabled(false);// 获取选中行的句柄列的字段const QList<QTableWidgetItem *> &selectedItems = ui->tableWidget->selectedItems();if (selectedItems.size() >= 2) {QTableWidgetItem *item = selectedItems.at(1);const QString &hwnd = item->data(Qt::DisplayRole).toString();bool res = false;long hwndL = hwnd.toLong(&res, 0);cout << res << endl;if (res) {emit this->captureWindow(this->pCommonDm, hwndL);} else {ui->btnCapture->setEnabled(true);this->showWarn("选中行的窗口句柄解析异常!");}} else {ui->btnCapture->setEnabled(true);this->showWarn("请选中列表中的其中一行!");}});connect(this, &MyMainWindow::captureWindow, worker, &MyMainWorker::doCaptureWindow);connect(worker, &MyMainWorker::captureWindowReady, this, &MyMainWindow::showMessageBox);workerThread.start();
}MyMainWindow::~MyMainWindow() {delete ui;workerThread.quit();workerThread.wait();
}void MyMainWindow::showInfo(const QString &message, const QString &title) {QMessageBox::information(this, title, message);
}void MyMainWindow::showWarn(const QString &message, const QString &title) {QMessageBox::critical(this, title, message);
}void MyMainWindow::showMessageBox(const bool result, const QString& message) {ui->actionReg->setEnabled(true);ui->btnCapture->setEnabled(true);if (result) {this->showInfo(message);} else {this->showWarn(message);}
}void MyMainWindow::showTable(const bool result, const QString &msg, const vector<MyWindow> &windowVec) {ui->btnQuery->setEnabled(true);if (result) {auto rowNum = windowVec.size();ui->tableWidget->setRowCount(rowNum);for (int i = 0; i < rowNum; ++i) {const MyWindow &item = windowVec[i];ui->tableWidget->setItem(i, 0, new QTableWidgetItem(QString::number(item.processId)));ui->tableWidget->setItem(i, 1, new QTableWidgetItem(QString::number(item.hwnd)));ui->tableWidget->setItem(i, 2, new QTableWidgetItem(QString::fromStdWString(item.title)));}} else {this->showWarn(msg);}
}
- MyMainWorker.h
#ifndef QT_DM_DEMO_X_MYMAINWORKER_H
#define QT_DM_DEMO_X_MYMAINWORKER_H#include <QObject>
#include "dmutil.h"class MyMainWorker: public QObject {
Q_OBJECT
signals:void regDMReady(const bool result, const QString &msg);void findWindowReady(const bool result, const QString &msg, const vector <MyWindow> &windowVec);void captureWindowReady(const bool result, const QString &msg);public slots:/*** 注册大漠* @param pDm 大漠插件,待赋值*/void doRegDM(Idmsoft **pDm);/*** 查询匹配的窗口* @param pDm 大漠插件* @param title 窗口标题(模糊查询)*/void doFindWindow(Idmsoft *pDm, const QString &title);/*** 对窗口截图* @param pDm 大漠插件* @param hwnd 窗口句柄*/void doCaptureWindow(Idmsoft *pDm, long hwnd);
};#endif //QT_DM_DEMO_X_MYMAINWORKER_H
- MyMainWorker.cpp
#include <iostream>#include "MyMainWorker.h"using namespace std;void MyMainWorker::doRegDM(Idmsoft **pDm) {cout << "========== Initial DM ............ ==========" << endl;*pDm = initialDMAndRegVIP();if (*pDm == nullptr) {cout << "========== Initial DM <Failed> ==========" << endl;emit this->regDMReady(false, "DM 注册失败!");return;}cout << "========== Initial DM <Successful> ==========" << endl;cout << endl;emit this->regDMReady(true, "DM 注册完成!");
}void MyMainWorker::doFindWindow(Idmsoft *pDm, const QString &title) {vector<MyWindow> windowVec;if (pDm == nullptr) {cout << "this->pCommonDm == nullptr" << endl;emit this->findWindowReady(false, "请先在菜单中完成注册!", windowVec);return;}// 找一下包含title的窗口getMatchedWindows(windowVec, pDm, title.toStdWString());if (windowVec.empty()) {cout << "can not find such window" << endl;emit this->findWindowReady(false, "没有找到包含该标题的窗口!", windowVec);return;}emit this->findWindowReady(true, "成功!", windowVec);
}void MyMainWorker::doCaptureWindow(Idmsoft *pDm, long hwnd) {if (pDm == nullptr) {cout << "this->pCommonDm == nullptr" << endl;emit this->captureWindowReady(false, "请先在菜单中完成注册!");return;}// 绑定窗口句柄long dmBind = pDm->BindWindowEx(hwnd,"normal","normal","normal","",0);if (dmBind == 1) {// 恢复并激活指定窗口,置顶窗口,pDm->SetWindowState(hwnd, 12);pDm->SetWindowState(hwnd, 8);pDm->delay(600);// 延迟一下截图,存到相对路径wstring filename = wstring(L"./capture_window_").append(std::to_wstring(hwnd)).append(L".bmp");long retCap = pDm->Capture(0, 0, 2000, 2000, filename.c_str());if (retCap != 1) {cout << "capture failed" << endl;emit this->captureWindowReady(false, "截图失败!");} else {cout << "capture success" << endl;emit this->captureWindowReady(true, QString::fromStdWString(L"截图成功,保存地址为: " + filename));}// 取消置顶窗口pDm->SetWindowState(hwnd, 9);} else {cout << "DM BindWindow failed" << endl;emit this->captureWindowReady(false, "绑定窗口异常!");}pDm->UnBindWindow();
}
相关文章:

CMake+QT+大漠插件的桌面应用开发(QThread)
文章目录 CMakeQT大漠插件的桌面应用开发(QThread)简介环境项目结构配置编译环境代码 CMakeQT大漠插件的桌面应用开发(QThread) 简介 在CMakeQT大漠插件的桌面应用开发中已经给出了QT配合大漠插件开发桌面应用的样例 不过由于主…...
【笔记】Helm-3 主题-9 Helm高级技术
Helm高级技术 这部分解释说明了使用Helm的各种高级特性和技术。这部分旨在为Helm的高级用户提供高度自定义和操作chart及发布的信息。每个高级特性都会有它自己的权衡利弊, 因此每个使用它们的都要有Helm的深度知识并小心使用。或者换言之,谨记 Peter Pa…...

YOLOv5改进 | 主干篇 | 华为GhostnetV1一种移动端的专用特征提取网络
一、本文介绍 本文给大家带来的改进机制是华为移动端模型Ghostnetv1,华为GhostnetV1一种移动端的专用特征提取网络,旨在在计算资源有限的嵌入式设备上实现高性能的图像分类。GhostNet的关键思想在于通过引入Ghost模块,以较低的计算成本增加了特征图的数量,从而提高了模型的…...

特斯拉FSD的神经网络(Tesla 2022 AI Day)
这是特斯拉的全自动驾驶(Full Self Driver)技术结构图,图中把自动驾驶模型拆分出分成了几个依赖的模块: 技术底座:自动标注技术处理大量数据,仿真技术创造图片数据,大数据引擎进不断地更新&…...

LLM自回归解码
在自然语言处理(NLP)中,大型语言模型(LLM)如Transformer进行推理时,自回归解码是一种生成文本的方式。在自回归解码中,模型在生成下一个单词时会依赖于它之前生成的单词。 使用自回归解码的公式…...
#Uniapp:uni.request(OBJECT)
uni.request(OBJECT) 发起网络请求。 示例 uni.request({url: https://www.example.com/request, //仅为示例,并非真实接口地址。data: {text: uni.request},header: {custom-header: hello //自定义请求头信息},success: (res) > {console.log(res.data);thi…...

旅游项目day14
其他模块数据初始化 搜索实现 请求一样,但是参数不一样,根据type划分。 后台需要提供一个搜索接口。 请求分发器: 全部搜索 目的地搜索 精确搜索、无高亮展示 攻略搜索 全文搜索、高亮显示、分页 游记搜搜 用户搜索 丝袜哥...

关于缓存 db redis local 取舍之道
文章目录 前言一、影响因素二、db or redis or local1.db2.redis3. local 三、redisson 和 CaffeineCache 封装3.1 redisson3.1.1 maven3.1.2 封装3.1.3 使用 3.2 CaffeineCache3.1.1 maven3.1.2 封装3.1.3 使用 总结 前言 让我们来聊一下数据缓存,它是如何为我们带…...

imgaug库图像增强指南(33):塑造【云层】效果的视觉魔法
引言 在深度学习和计算机视觉的世界里,数据是模型训练的基石,其质量与数量直接影响着模型的性能。然而,获取大量高质量的标注数据往往需要耗费大量的时间和资源。正因如此,数据增强技术应运而生,成为了解决这一问题的…...
树莓派ubuntu:CSI接口摄像头安装驱动程序及测试
树莓派中使用OV系列摄像头,网上能搜到的文章资源太老了,文章中提到的摄像头配置选项在raspi-config中并不存在。本文重新测试整理树莓派摄像头的驱动安装、配置、测试流程说明。 libcamera 新版本中使用libcamera作为摄像头驱动程序。 libcamera是一个…...
Webpack5入门到原理6:处理图片资源
处理图片资源 过去在 Webpack4 时,我们处理图片资源通过 file-loader 和 url-loader 进行处理 现在 Webpack5 已经将两个 Loader 功能内置到 Webpack 里了,我们只需要简单配置即可处理图片资源 1. 配置 const path require("path");modul…...
大语言模型(LLM)有哪些?
国际大语言模型 目前国际上有以下几个知名的大语言模型: GPT-4 GPT-4由OpenAI团队开发,是闭源的。GPT(Generative Pre-trained Transformer)系列是目前最著名的大语言模型之一。最早的版本是GPT-1,之后发展到了GPT-2和GPT-3&…...

2 - 部署Redis集群架构
部署Redis集群架构 部署Redis集群部署管理主机第一步 准备ruby脚本的运行环境第二步 创建脚本第三步 查看脚本帮助信息 配置6台Redis服务器第一步 修改配置文件启用集群功能第二步 重启redis服务第三步 查看Redis-server进程状态(看到服务使用2个端口号为成功&#…...

NOIP2003提高组T1:神经网络
题目链接 [NOIP2003 提高组] 神经网络 题目背景 人工神经网络(Artificial Neural Network)是一种新兴的具有自我学习能力的计算系统,在模式识别、函数逼近及贷款风险评估等诸多领域有广泛的应用。对神经网络的研究一直是当今的热门方向&am…...

Doris数据库误删除恢复
如果不小心误删除了表,doris提供了恢复机制,但时间间隔不能超过一天,记得要迅速 首先查看当前能恢复的记录有那些 可以通过 SHOW CATALOG RECYCLE BIN 来查询当前可恢复的元信息,也可以在语句后面加 WHERE NAME XXX 来缩小查询…...
C# byte转int:大小端读取
参考:byte[]数组和int之间的转换 文章目录 Byte转为INT小端存储方式转int大端存储方式转int 大端模式和小端模式是计算机存储多字节数据时的两种方式。内存地址从小往大增长。 大端模式:最高有效(最高位)的字节存放在最小地址上&…...

安全通信网络
1.网络架构 1)应保证网络设备的业务处理能力满足业务高峰期需要。 设备CPU和内存使用率的峰值不大于设备处理能力的70%。 在有监控环境的条件下,应通过监控平台查看主要设备在业务高峰期的资源(CPU、内存等)使用 情况ÿ…...

深度学习笔记(九)——tf模型导出保存、模型加载、常用模型导出tflite、权重量化、模型部署
文中程序以Tensorflow-2.6.0为例 部分概念包含笔者个人理解,如有遗漏或错误,欢迎评论或私信指正。 本篇博客主要是工具性介绍,可能由于软件版本问题导致的部分内容无法使用。 首先介绍tflite: TensorFlow Lite 是一组工具,可帮助开…...
七Docker可视化管理工具
Docker可视化管理工具 本节介绍几款Docker可视化管理工具。 DockerUI(ui for Docker) 官方GitHub:https://github.com/kevana/ui-for-docker 项目已废弃,现在转投Portainer项目,不建议使用。 Portainer 简介:Portainer是一个…...
vue和react的差异梳理
特性VueReact响应式系统使用Object.defineProperty()或Proxy使用不可变数据流和状态提升模板系统HTML模板语法JSX(JavaScript扩展语法)组件作用域样式支持scoped样式需要CSS-in-JS库(如styled-components)状态管理Vuex(…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...