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 复制代码
# 生成可执行文件
add_executable(${PROJECT_NAME} main.cpp
        strutils.cpp strutils.h
        dmutil.cpp dmutil.h
        mymainwindow.cpp mymainwindow.h mymainwindow.ui
        MyMainWorker.cpp MyMainWorker.h
)

代码

c 复制代码
#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_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<MyWindow> &windowVec);


private:
    Ui::MyMainWindow *ui;

    Idmsoft *pCommonDm = nullptr;
};


#endif //QT_DM_DEMO_X_MYMAINWINDOW_H
  • mymainwindow.cpp
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 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<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
cpp 复制代码
#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
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();
}
相关推荐
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer3 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
青花瓷5 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode
幺零九零零6 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
捕鲸叉6 小时前
MVC(Model-View-Controller)模式概述
开发语言·c++·设计模式
徒步僧7 小时前
ThingsBoard规则链节点:RPC Call Reply节点详解
qt·microsoft·rpc
Dola_Pan7 小时前
C++算法和竞赛:哈希算法、动态规划DP算法、贪心算法、博弈算法
c++·算法·哈希算法
yanlou2337 小时前
KMP算法,next数组详解(c++)
开发语言·c++·kmp算法
小林熬夜学编程7 小时前
【Linux系统编程】第四十一弹---线程深度解析:从地址空间到多线程实践
linux·c语言·开发语言·c++·算法