上海古都建筑设计集团,上海办公室装修设计公司,上海装修公司高质量的内容分享社区,上海装修公司我们不是内容生产者,我们只是上海办公室装修设计公司内容的搬运工平台

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

guduadmin241月前

文章目录

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

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

      简介

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

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

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

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

        CMake+QT+大漠插件的桌面应用开发(QThread),QT主线程与子线程的交互逻辑图,第1张

      • 交互逻辑描述

        • 当点击“注册”选项时,会发出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.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();
                        }
                        

网友评论

搜索
最新文章
热门文章
热门标签
 
 男人梦见狼 吉兆  梦到蛇很害怕预示着什么  一招辨别怀没怀孕看内裤