文章目录
- 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.cpp strutils.cpp strutils.h dmutil.cpp dmutil.h mymainwindow.cpp mymainwindow.h mymainwindow.ui MyMainWorker.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
#include #include #include "dmutil.h" QT_BEGIN_NAMESPACE namespace Ui { class MyMainWindow; } QT_END_NAMESPACE class MyMainWindow : public QMainWindow { Q_OBJECT QThread 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 &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
#include #include #include #include #include #include #include #include #include #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 "); qRegisterMetaType >("vector "); // Init Views setFixedSize(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 Listener auto 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 &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 &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
#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 &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
#include "MyMainWorker.h" using namespace std; void MyMainWorker::doRegDM(Idmsoft **pDm) { cout << "========== Initial DM ............ ==========" << endl; *pDm = initialDMAndRegVIP(); if (*pDm == nullptr) { cout << "========== Initial DM ==========" << endl; emit this->regDMReady(false, "DM 注册失败!"); return; } cout << "========== Initial DM ==========" << endl; cout << endl; emit this->regDMReady(true, "DM 注册完成!"); } void MyMainWorker::doFindWindow(Idmsoft *pDm, const QString &title) { vector 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(); }
- MyMainWorker.cpp
- MyMainWorker.h
- mymainwindow.cpp
- 启动IDE时,记得以管理员模式启动
-
猜你喜欢
网友评论
- 搜索
- 最新文章
- 热门文章