CMake+QT+大漠插件的桌面应用开发(QThread)

2024-01-18 06:44

本文主要是介绍CMake+QT+大漠插件的桌面应用开发(QThread),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • CMake+QT+大漠插件的桌面应用开发(QThread)
    • 简介
    • 环境
    • 项目结构
    • 配置编译环境
    • 代码

CMake+QT+大漠插件的桌面应用开发(QThread)

简介

  • 在CMake+QT+大漠插件的桌面应用开发中已经给出了QT配合大漠插件开发桌面应用的样例

  • 不过由于主窗口的UI操作和大漠的调用是在一个线程里面的,所以当大漠调用时间过长时会出现UI界面卡顿的现象

  • 我们可以利用子线程处理耗时操作,处理完后再由主线程(UI线程)更新界面,这样界面就不会出现卡顿。

  • 在这里,我们将会用到QThread,调整后的QT主线程与子线程交互逻辑图如下:
    QT主线程与子线程的交互逻辑图

  • 交互逻辑描述

    • 当点击“注册”选项时,会发出regDM信号,子线程接收到该信号会执行MyMainWorker中的doRegDM方法,执行完成后会发出regDMReady信号,主线程接收到该信号会执行更新UI的操作
    • 当点击“搜索”按钮时,同理
    • 当点击“截图”按钮时,同理

环境

版本/规范备注
平台win32操作系统为Windows10
CMake3.27.8CLion自带
C++17
ToolchainVisualStudio 2022只用其工具链,记得先安装好
QT5.12.12安装时选择msvc2017,不要64位的
DM7.2353
CLion2023.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.cppMyMainWorker.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)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/618309

相关文章

Python+PyQt5开发一个Windows电脑启动项管理神器

《Python+PyQt5开发一个Windows电脑启动项管理神器》:本文主要介绍如何使用PyQt5开发一款颜值与功能并存的Windows启动项管理工具,不仅能查看/删除现有启动项,还能智能添加新... 目录开篇:为什么我们需要启动项管理工具功能全景图核心技术解析1. Windows注册表操作2. 启动文件

Python datetime 模块概述及应用场景

《Pythondatetime模块概述及应用场景》Python的datetime模块是标准库中用于处理日期和时间的核心模块,本文给大家介绍Pythondatetime模块概述及应用场景,感兴趣的朋... 目录一、python datetime 模块概述二、datetime 模块核心类解析三、日期时间格式化与

使用Python开发Markdown兼容公式格式转换工具

《使用Python开发Markdown兼容公式格式转换工具》在技术写作中我们经常遇到公式格式问题,例如MathML无法显示,LaTeX格式错乱等,所以本文我们将使用Python开发Markdown兼容... 目录一、工具背景二、环境配置(Windows 10/11)1. 创建conda环境2. 获取XSLT

SpringBoot中四种AOP实战应用场景及代码实现

《SpringBoot中四种AOP实战应用场景及代码实现》面向切面编程(AOP)是Spring框架的核心功能之一,它通过预编译和运行期动态代理实现程序功能的统一维护,在SpringBoot应用中,AO... 目录引言场景一:日志记录与性能监控业务需求实现方案使用示例扩展:MDC实现请求跟踪场景二:权限控制与

Android开发环境配置避坑指南

《Android开发环境配置避坑指南》本文主要介绍了Android开发环境配置过程中遇到的问题及解决方案,包括VPN注意事项、工具版本统一、Gerrit邮箱配置、Git拉取和提交代码、MergevsR... 目录网络环境:VPN 注意事项工具版本统一:android Studio & JDKGerrit的邮

Python开发文字版随机事件游戏的项目实例

《Python开发文字版随机事件游戏的项目实例》随机事件游戏是一种通过生成不可预测的事件来增强游戏体验的类型,在这篇博文中,我们将使用Python开发一款文字版随机事件游戏,通过这个项目,读者不仅能够... 目录项目概述2.1 游戏概念2.2 游戏特色2.3 目标玩家群体技术选择与环境准备3.1 开发环境3

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

C语言中位操作的实际应用举例

《C语言中位操作的实际应用举例》:本文主要介绍C语言中位操作的实际应用,总结了位操作的使用场景,并指出了需要注意的问题,如可读性、平台依赖性和溢出风险,文中通过代码介绍的非常详细,需要的朋友可以参... 目录1. 嵌入式系统与硬件寄存器操作2. 网络协议解析3. 图像处理与颜色编码4. 高效处理布尔标志集合

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类