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

文章目录

  • CMake+QT+大漠插件的桌面应用开发
    • 说明
    • 环境
    • 项目结构
    • 配置编译环境
    • 代码

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

说明

  • 在CMake+大漠插件的应用开发——处理dm.dll,免注册调用大漠插件中已经说明了如何免注册调用大漠插件,以及做了几个简单的功能调用(查找窗口、截图)
  • 下面来利用QT做一个简单的窗口查找、截图的桌面工具应用,功能点如下
    • 点击“注册”选项完成大漠插件的注册。
    • 用户在文本框输入窗口标题后,点击“查询”按钮,可对包含该标题的窗口进行查询。
    • 提供表格展示查询到的窗口信息。
    • 点击“截图”按钮,对选中的窗口进行截图并保存。
  • 界面如下

image.png

  • 目前主窗口的UI操作和大漠的调用是在一个线程里面的,当大漠调用时间过长时会出现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_01
  • 将下载好的 dm.dll 文件以及处理好的 dm.tlh、dm.tli 文件放置到项目的 external 目录下
    • 注:dm.tlh、dm.tli 文件的生成请参考 CMake+大漠插件的应用开发——处理dm.dll,免注册调用大漠插件
qt_dm_demo_x_01					     # 项目目录
--|cmake-build-debug-visual-studio	 # 工程构建目录,存临时生成的文件
--|--|...
--|external					         # 引入第三方库文件的所在的文件夹
--|--|dm.dll                         # 大漠插件的dll
--|--|dm.tlh
--|--|dm.tli
--CMakeLists.txt		             # CMake脚本文件
--dmutil.cpp                         # 大漠的功能封装工具
--dmutil.h                           # 大漠的功能封装工具
--main.cpp					         # 程序入口
--mymainwindow.cpp                   # 主窗口
--mymainwindow.h                     # 主窗口
--mymainwindow.ui                    # 主窗口的UI文件
--strutils.cpp                       # 字符串工具
--strutils.h                         # 字符串工具

配置编译环境

  • 配置工具链
    • 和CMake+大漠插件的应用开发——处理dm.dll,免注册调用大漠插件中保持一致即可
  • CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.27)
project(qt_dm_demo_x_01)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

set(CMAKE_PREFIX_PATH "C:/Qt/Qt5.12.12/5.12.12/msvc2017")

find_package(Qt5 COMPONENTS
        Core
        Gui
        Widgets
        REQUIRED)

add_executable(${PROJECT_NAME} main.cpp
        strutils.cpp strutils.h
        dmutil.cpp dmutil.h
        mymainwindow.cpp mymainwindow.h mymainwindow.ui
)

target_link_libraries(${PROJECT_NAME}
        Qt5::Core
        Qt5::Gui
        Qt5::Widgets
)

target_compile_definitions(${PROJECT_NAME} PRIVATE
        -DWIN32
        # -D_DEBUG
        -D_WINDOWS
        -D_UNICODE
        -DUNICODE
)

message(STATUS "CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}")

if (WIN32 AND NOT DEFINED CMAKETOOLCHAIN_FILE)
    set(DEBUG_SUFFIX)
    if (MSVC AND CMAKE_BUILD_TYPE MATCHES "Debug")
        set(DEBUG_SUFFIX "d")
    endif ()
    set(QT_INSTALL_PATH "${CMAKE_PREFIX_PATH}")
    if (NOT EXISTS "${QT_INSTALL_PATH}/bin")
        set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")
        if (NOT EXISTS "${QT_INSTALL_PATH}/bin")
            set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")
        endif ()
    endif ()
    if (EXISTS "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll")
        add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
                COMMAND ${CMAKE_COMMAND} -E make_directory
                "$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/")
        add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
                COMMAND ${CMAKE_COMMAND} -E copy
                "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll"
                "$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/")
    endif ()
    foreach (QT_LIB Core Gui Widgets)
        add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
                COMMAND ${CMAKE_COMMAND} -E copy
                "${QT_INSTALL_PATH}/bin/Qt5${QT_LIB}${DEBUG_SUFFIX}.dll"
                "$<TARGET_FILE_DIR:${PROJECT_NAME}>")
    endforeach (QT_LIB)
endif ()

# 拷贝资源文件 dm.dll
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/external DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

代码

  • dmutil.h
#ifndef DM_DEMO_X_DMUTIL_H
#define DM_DEMO_X_DMUTIL_H

#include <string>
#include <vector>


// #import "./external/dm.dll" no_namespace
#include "./external/dm.tlh"

#define DM_LIB_PATH L"./external/dm.dll"

using namespace std;

struct MyWindow {
    long hwnd;
    wstring title;
    long processId;
};

/**
 * 注册dm.dll,获取大漠实例
 * @return 大漠实例
 */
Idmsoft *GetDmObject();

/**
 * 初始化大漠插件,并注册用户VIP
 * @return 大漠实例
 */
Idmsoft *initialDMAndRegVIP();

/**
 * 获取匹配的窗口
 * @param baseVec 保存window的容器
 * @param pDm 大漠插件
 * @param title 窗口标题中的字段(模糊匹配)
 * @param processName 进程名称(精确匹配,不区分大小写),默认不会根据进程名筛选窗口
 */
void getMatchedWindows(vector<MyWindow>& baseVec, Idmsoft *pDm, const wstring& title, const wstring& processName = L"");

#endif //DM_DEMO_X_DMUTIL_H

  • dmutil.cpp(记得填入自己的 注册码附加码
#include <iostream>
#include <sstream>
#include <string_view>
#include <vector>
#include "dmutil.h"
#include "strutils.h"


using namespace std;



Idmsoft *GetDmObject() {
    Idmsoft *m_dm = nullptr;
    bool m_bInit = false;
    typedef HRESULT(_stdcall
    *pfnGCO)(REFCLSID, REFIID, void**);
    pfnGCO fnGCO = nullptr;
    HINSTANCE hdllInst = LoadLibrary(DM_LIB_PATH);
    if (hdllInst == nullptr) {
        cout << "Load library 'dm.dll' failed ! DM_LIB_PATH = " << DM_LIB_PATH << endl;
        return nullptr;
    }
    fnGCO = (pfnGCO) GetProcAddress(hdllInst, "DllGetClassObject");
    if (fnGCO != nullptr) {
        IClassFactory *pcf = nullptr;
        HRESULT hr = (fnGCO)(__uuidof(dmsoft), IID_IClassFactory, (void **) &pcf);
        if (SUCCEEDED(hr) && (pcf != nullptr)) {
            hr = pcf->CreateInstance(nullptr, __uuidof(Idmsoft), (void **) &m_dm);
            if ((SUCCEEDED(hr) && (m_dm != nullptr)) == FALSE) {
                cout << "Create instance 'Idmsoft' failed !" << endl;
                return nullptr;
            }
        }
        pcf->Release();
        m_bInit = true;
    }
    return m_dm;
}

Idmsoft *initialDMAndRegVIP() {
    Idmsoft *pDm = GetDmObject();
    if (pDm == nullptr) {
        cout << "===> dm.dll registration failed !" << endl;
        return nullptr;
    }
    // 注册dm.dll成功,打印版本
    cout << "===> DM version: " << (char *) pDm->Ver() << endl;
    // 注册用户(同一程序下,只需注册一次,后续不用重复注册)
    long regResult = pDm->Reg(L"注册码", L"版本附加信息(附加码)");
    if (regResult != 1) {
        cout << "===> Account registration failed ! code = " << regResult << endl;
        return nullptr;
    }
    cout << "===> Account registration successful ! " << endl;
    // long releaseRes = pDm->ReleaseRef();
    // cout << "===> ReleaseCode = " << releaseRes << endl;

    return pDm;
}

void getMatchedWindows(vector<MyWindow>& baseVec, Idmsoft *pDm, const wstring& title, const wstring& processName) {
    // 获取匹配的窗口句柄
    _bstr_t hwnds;
    if (!processName.empty()) {
        hwnds = pDm->EnumWindowByProcess(processName.c_str(), title.c_str(), L"", 1 + 8 + 16);
    } else {
        hwnds = pDm->EnumWindow(0, title.c_str(), L"", 1 + 4 + 8 + 16);
    }
    // 拆分找到的多个窗口句柄
    string content(hwnds);
    vector<string_view> hwndStrVec = splitSV(content, ",");

    // 装入容器vector
    baseVec.reserve(hwndStrVec.size());
    for (const string_view& element : hwndStrVec) {
        long curHwnd = viewToInt(element);
        // 获取窗口title
        const _bstr_t &curTitle = pDm->GetWindowTitle(curHwnd);
        // const _bstr_t &className = pDm->GetWindowClass(curHwnd);
        long processId = pDm->GetWindowProcessId(curHwnd);
        baseVec.push_back({curHwnd, {curTitle}, processId});
    }
    
}
  • strutils.h
#ifndef DM_DEMO_X_STRUTILS_H
#define DM_DEMO_X_STRUTILS_H

#include <string>
#include <string_view>
#include <iostream>
#include <vector>

using namespace std;

/**
 * 按分隔符拆分字符串内容为vector,string_view更加高效
 * @param content 待分隔的字符串
 * @param delim 分隔符,默认为空格
 * @return vector容器
 */
vector<string_view> splitSV(string_view content, string_view delim = " ");

/**
 * string_view 转 int,失败时抛出异常
 * @param content 字符串内容view
 * @return int值
 */
int viewToInt(string_view content);

#endif //DM_DEMO_X_STRUTILS_H

  • strutils.cpp
#include <sstream>
#include <string>
#include <vector>
#include <charconv>

#include "strutils.h"

vector<string_view> splitSV(string_view content, string_view delim) {
    vector<string_view> output;
    size_t first = 0;

    while (first < content.size()) {
        const auto second = content.find_first_of(delim, first);

        if (first != second)
            output.emplace_back(content.substr(first, second - first));

        if (second == string_view::npos)
            break;

        first = second + 1;
    }

    return output;
}

int viewToInt(string_view content) {
    int num;
    auto result = std::from_chars(content.data(), content.data() + content.size(), num);
    if (result.ec == std::errc::invalid_argument) {
        throw std::runtime_error("Could not convert.");
    }

    return num;
}

  • mymainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MyMainWindow</class>
 <widget class="QMainWindow" name="MyMainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>窗口查询程序</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QLabel" name="label">
        <property name="font">
         <font>
          <weight>75</weight>
          <bold>true</bold>
         </font>
        </property>
        <property name="text">
         <string>窗口标题:</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QLineEdit" name="edtTitle">
        <property name="minimumSize">
         <size>
          <width>200</width>
          <height>0</height>
         </size>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btnQuery">
        <property name="text">
         <string>模糊查询</string>
        </property>
       </widget>
      </item>
      <item>
       <spacer name="horizontalSpacer_3">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QPushButton" name="btnCapture">
        <property name="text">
         <string>截图(选中行)</string>
        </property>
       </widget>
      </item>
      <item>
       <spacer name="horizontalSpacer_2">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
     </layout>
    </item>
    <item>
     <widget class="QTableWidget" name="tableWidget"/>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>400</width>
     <height>21</height>
    </rect>
   </property>
   <widget class="QMenu" name="menuOperation">
    <property name="title">
     <string>菜单</string>
    </property>
    <addaction name="actionReg"/>
   </widget>
   <addaction name="menuOperation"/>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
  <action name="actionReg">
   <property name="text">
    <string>注册DM</string>
   </property>
  </action>
 </widget>
 <resources/>
 <connections/>
</ui>

  • mymainwindow.h
#ifndef QT_DM_DEMO_X_MYMAINWINDOW_H
#define QT_DM_DEMO_X_MYMAINWINDOW_H

#include <QMainWindow>

#include "dmutil.h"


QT_BEGIN_NAMESPACE
namespace Ui { class MyMainWindow; }
QT_END_NAMESPACE

class MyMainWindow : public QMainWindow {
Q_OBJECT
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 = "告警");

    /**
     * 注册大漠
     * @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);

public slots:

    void showMessageBox(bool result, const QString &message);

    void showTableView(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"

using namespace std;

MyMainWindow::MyMainWindow(QWidget *parent) :
        QMainWindow(parent), ui(new Ui::MyMainWindow) {
    ui->setupUi(this);

    // 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
    // 注册大漠
    connect(ui->actionReg, &QAction::triggered, [this]() {
        ui->actionReg->setEnabled(false);
        this->doRegDM(&this->pCommonDm);
        ui->actionReg->setEnabled(true);
    });
    // 查找窗口
    connect(ui->btnQuery, &QPushButton::clicked, [this]() {
        ui->btnQuery->setEnabled(false);
        this->doFindWindow(this->pCommonDm, ui->edtTitle->text());
        ui->btnQuery->setEnabled(true);
    });
    // 截图
    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) {
                this->doCaptureWindow(this->pCommonDm, hwndL);
            } else {
                this->showWarn("选中行的窗口句柄解析异常!");
            }
        } else {
            this->showWarn("请选中列表中的其中一行!");
        }
        ui->btnCapture->setEnabled(true);
    });

}

MyMainWindow::~MyMainWindow() {
    delete ui;
}

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) {
    if (result) {
        this->showInfo(message);
    } else {
        this->showWarn(message);
    }
}

void MyMainWindow::showTableView(bool result, const QString &msg, const vector<MyWindow> &windowVec) {
    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);
    }
}

void MyMainWindow::doRegDM(Idmsoft **pDm) {
    cout << "========== Initial DM ............ ==========" << endl;
    *pDm = initialDMAndRegVIP();
    if (*pDm == nullptr) {
        cout << "========== Initial DM <Failed>     ==========" << endl;
        showMessageBox(false, "DM 注册失败!");
        return;
    }
    cout << "========== Initial DM <Successful> ==========" << endl;
    cout << endl;
    showMessageBox(true, "DM 注册完成!");
}

void MyMainWindow::doFindWindow(Idmsoft *pDm, const QString &title) {
    vector<MyWindow> windowVec;
    if (pDm == nullptr) {
        cout << "this->pCommonDm == nullptr" << endl;
        this->showTableView(false, "请先在菜单中完成注册!", windowVec);
        return;
    }

    // 找一下title包含findStr的窗口,并打印信息

    getMatchedWindows(windowVec, pDm, title.toStdWString());
    if (windowVec.empty()) {
        cout << "can not find such window" << endl;
        this->showTableView(false, "没有找到包含该标题的窗口!", windowVec);
        return;
    }
    this->showTableView(true, "成功!", windowVec);
}

void MyMainWindow::doCaptureWindow(Idmsoft *pDm, long hwnd) {
    if (pDm == nullptr) {
        cout << "this->pCommonDm == nullptr" << endl;
        this->showMessageBox(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;
            this->showMessageBox(false, "截图失败!");
        } else {
            cout << "capture success" << endl;
            this->showMessageBox(true, QString::fromStdWString(L"截图成功,保存地址为: " + filename));
        }
        // 取消置顶窗口
        pDm->SetWindowState(hwnd, 9);
    } else {
        cout << "DM BindWindow failed" << endl;
        this->showMessageBox(false, "绑定窗口异常!");
    }
    pDm->UnBindWindow();
}
  • main.cpp
#include <QApplication>
#include <iostream>
#include "mymainwindow.h"
using namespace std;

int main(int argc, char *argv[]) {
    setlocale(LC_ALL, "chs");

    QApplication a(argc, argv);
    MyMainWindow mainWindow;
    mainWindow.show();

    return QApplication::exec();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/318958.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

金南瓜SECS/GEM发送event、VID

金南瓜SECS/GEM发送事件&#xff08;CEID&#xff09;很简单&#xff0c;只需一步就完成。 最重要是简单易懂&#xff0c;任何人一看就上手。无需懂得内部逻辑&#xff0c;以及一大堆的导入问题。 代码如下 C#的代码&#xff1a; // 扫码成功 private void buttonReadBarco…

【教学类-43-20】20240113 数独(二)4宫格、9宫格 无空行A4模板

作品展示&#xff1a; 4宫格 9宫格 题目连在一起 背景需求&#xff1a; 制作十宫格数独模板&#xff0c;为了凑满20行&#xff0c;删除了每个数独题之间的行列分割线 【教学类-43-18】A4最终版 20240111 数独11.0 十宫格X*YZ套(n10)&#xff0c;套用没有分割行列的A4横版模板…

浅谈对Maven的理解

一、什么是Maven Maven——是Java社区事实标准的项目管理工具&#xff0c;能帮你从琐碎的手工劳动中解脱出来&#xff0c;帮你规范整个组织的构建系统。不仅如此&#xff0c;它还有依赖管理、自动生成项目站点等特性&#xff0c;已经有无数的开源项目使用它来构建项目并促进团队…

将 OpenCV Java 与 Eclipse 结合使用

配置 Eclipse 首先&#xff0c;从下载页面获取 OpenCV 的新版本&#xff0c;并将其解压缩到一个简单的位置&#xff0c;例如 .我使用的是 2.4.6 版&#xff0c;但其他版本的步骤或多或少相同。C:\OpenCV-2.4.6\ 现在&#xff0c;我们将 OpenCV 定义为 Eclipse 中的用户库&…

【数据结构与算法】之数组系列-20240114

这里写目录标题 一、414. 第三大的数二、448. 找到所有数组中消失的数字三、561. 数组拆分四、594. 最长和谐子序列 一、414. 第三大的数 简单 给你一个非空数组&#xff0c;返回此数组中第三大的数。如果不存在&#xff0c;则返回数组中最大的数。 示例 1&#xff1a; 输入&…

个人网站制作 Part 1 创建网站 | Web开发项目

文章目录 &#x1f469;‍&#x1f4bb; 基础Web开发练手项目系列&#xff1a;个人网站制作&#x1f680; 项目概述&#x1f527; 开发工具和环境配置&#x1f6e0; 项目实现步骤步骤 1: 创建HTML文件步骤 2: 添加CSS样式步骤 3: 链接CSS文件步骤 4: 添加JavaScript交互 &#…

【教学类-43-19】20240113 数独(一) 3-5-6-7-8-10宫格 无空行A4模板

作品展示&#xff1a; 3宫格 5宫格 6宫格 7宫格 8宫格 10宫格&#xff0c;题目连在一起 背景需求&#xff1a; 制作十宫格数独模板&#xff0c;为了凑满20行&#xff0c;删除了每个数独题之间的行列分割线 【教学类-43-18】A4最终版 20240111 数独11.0 十宫格X*YZ套(n10)&am…

【WPF.NET开发】流文档

本文内容 什么是流文档&#xff1f;流文档类型创建流内容与流相关的类内容架构自定义文本 流文档旨在优化查看和可读性。 流文档根据运行时变量&#xff08;例如&#xff0c;窗口大小、设备分辨率和可选的用户首选项&#xff09;来动态调整和重新排列内容&#xff0c;而不是设…

综合评价 | 基于EW、EW-BP、EW-ELM的地区发展水平综合评价(Matlab)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 综合评价 | 基于EW、EW-BP、EW-ELM的地区发展水平综合评价&#xff08;Matlab&#xff09; 程序设计 完整程序和数据获取方式&#xff1a;私信博主回复基于EW、EW-BP、EW-ELM的地区发展水平综合评价&#xff08;Matl…

YOLOV8代码本地编译

下载pycharm 在 Linux 操作系统中安装 Pycharm 社区版_linux中安装pycharm社区版-CSDN博客 Pycharm中配置 Conda 虚拟环境 百度安全验证https://baijiahao.baidu.com/s?id1771914506705481878&wfrspider&forpc 源码编译 果您对参与开发感兴趣或希望尝试最新源代码…

昇腾910b部署Chatglm3-6b进行流式输出【pytorch框架】NPU推理

文章目录 准备阶段避坑阶段添加代码结果展示 准备阶段 配套软件包Ascend-cann-toolkit和Ascend-cann-nnae适配昇腾的Pytorch适配昇腾的Torchvision Adapter下载ChatGLM3代码下载chatglm3-6b模型&#xff0c;或在modelscope里下载 避坑阶段 每个人的服务器都不一样&#xff0…

《向量数据库指南》让「引用」为 RAG 机器人回答增加可信度

在之前的文章中&#xff0c;我们已经介绍了如何用 Milvus 向量数据库以及 LlamaIndex 搭建基础的聊天机器人《Chat Towards Data Science &#xff5c;如何用个人数据知识库构建 RAG 聊天机器人&#xff1f;》《书接上回&#xff0c;如何用 LlamaIndex 搭建聊天机器人&#xff…

双指针算法: 有效三角形的个数

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 前言 声明…

【AI】人工智能和水下机器视觉

目录 一、初识水下机器视觉 ——不同点 ——难点 二、AI如何助力水下机器视觉 三、应用场景 四、关键技术 水下机器视觉&#xff0c;非常复杂&#xff0c;今天来简单讨论一下。因为目标识别更难。 水下机器视觉是机器视觉技术在水下环境中的应用&#xff0c;它与普通机器…

读元宇宙改变一切笔记07_硬件与互操作性(上)

1. 元宇宙的头号入口 1.1. 元宇宙最令人兴奋的地方在于&#xff0c;我们可以借此开发用来访问、渲染和操纵它的新设备 1.1.1. App Newton于1993年发布&#xff0c;是世界上第一款掌上电脑 1.2. 功能超强大又轻巧的AR和沉浸式VR头显 1.2.1.…

[小程序]定位功能实现

第一步:首先要认识三个小程序的 api wx.chooseLocation 和 wx.getLocation 和 wx.openLocation (1).wx.chooseLocation 用于在小程序中选择地理位置。当用户点击选择位置按钮时&#xff0c;小程序会调起地图选择界面&#xff0c;用户可以在地图上选择一个位置&#xff0c;并可以…

黑马程序员 Docker笔记

本篇学习笔记文档对应B站视频&#xff1a; 同学们&#xff0c;在前两天我们学习了Linux操作系统的常见命令以及如何在Linux上部署一个单体项目。大家想一想自己最大的感受是什么&#xff1f; 我相信&#xff0c;除了个别天赋异禀的同学以外&#xff0c;大多数同学都会有相同的…

vue文件在<template>中使用多个<el-main>报错(已解决)

目录 1.原理 2. 根据你的需求&#xff0c;自定义每个 组件的内容。你可以在 标签内部插入文本、其他组件、样式等。 3. 根据需要添加样式或其他属性到每个 组件。你可以使用 class、style 或其他属性来自定义每个组件的外观和行为。 4.一个可以运行的总代码如下 5.我的一…

D课堂 | 为什么网站搭建好了却无法访问?(上)

在上一期D课堂中&#xff0c;D妹教大家如何用最简单的方法快速搭建一个网站&#xff0c;相信很多小伙伴已经跃跃欲试&#xff0c;尝试去搭建自己的网站。&#xff08;点击这里可以快速复习&#xff09; 然而&#xff0c;有不少人明明每个步骤都跟着做了&#xff0c;但最后在浏览…

利用Monte Carlo进行数值积分(二)

进步空间很大的算法版本 话说去年6月的一个周六&#xff0c;我很无聊地发了一个帖子&#xff0c;写了一个自己感觉有点无聊的帖子。 Matlab多重积分的两种实现【从六重积分到一百重积分】https://withstand.blog.csdn.net/article/details/127564478 这个帖子居然成了我这种懒…
最新文章