当前位置: 首页 > news >正文

C++ QT 如何生成dll提供給python使用

我通常在開發的過程中不會考慮跨語言調用的問題,最近遇到了:需要在通過python調用C++ QT生成的dll;
以下是基於QT Creator 的一个生成 device.dll(供 C++ 使用)和 testlib.dll(通过 C 接口供 Python 调用)項目的詳細方案:


一、项目结构设计

建议使用 subdirs 模板管理多个子项目:

MyProject/
├── MyProject.pro          # 主项目文件(subdirs 模板)
├── device/                # device.dll 的 C++ 实现
│   ├── device.pro
│   ├── device.h
│   └── device.cpp
└── testlib/               # testlib.dll 的 C 接口包装
    ├── testlib.pro
    ├── testlib.h
    └── testlib.cpp

二、主项目配置 (MyProject.pro)

TEMPLATE = subdirs
SUBDIRS += device testlib   # 定义子项目顺序
CONFIG += ordered           # 确保构建顺序

三、device.dll 的配置 (device/device.pro)

1. 项目文件
TEMPLATE = lib              # 生成动态库
TARGET = device             # 输出名为 device.dll
CONFIG += shared            # 强制生成动态库
DEFINES += DEVICE_LIB       # 定义导出宏

# 输出目录
DESTDIR = $$PWD/../../bin   # 假设输出到项目根目录的 bin 文件夹
!exists($$DESTDIR): QMAKE_MKDIR += $$DESTDIR

# 源文件
SOURCES += device.cpp
HEADERS += device.h
2. 头文件 device.h(符号导出)
#ifndef DEVICE_H
#define DEVICE_H

#include <QtCore/QtGlobal>

#ifdef DEVICE_LIB
    #define DEVICE_EXPORT Q_DECL_EXPORT
#else
    #define DEVICE_EXPORT Q_DECL_IMPORT
#endif

class DEVICE_EXPORT Device {
public:
    Device();
    int getValue() const;
    void setValue(int value);

private:
    int m_value;
};

#endif // DEVICE_H
3. 实现文件 device.cpp
#include "device.h"

Device::Device() : m_value(0) {}

int Device::getValue() const {
    return m_value;
}

void Device::setValue(int value) {
    m_value = value;
}

四、testlib.dll 的配置 (testlib/testlib.pro)

1. 项目文件
TEMPLATE = lib              # 生成动态库
TARGET = testlib            # 输出名为 testlib.dll
CONFIG += shared            # 强制生成动态库
DEFINES += TESTLIB_EXPORT   # 定义导出宏

# 依赖 device 项目
LIBS += -L$$PWD/../../bin -ldevice   # 链接 device.dll 的导入库(device.lib)
INCLUDEPATH += $$PWD/../device       # 包含 device 头文件

# 输出目录
DESTDIR = $$PWD/../../bin
!exists($$DESTDIR): QMAKE_MKDIR += $$DESTDIR

# 源文件
SOURCES += testlib.cpp
HEADERS += testlib.h
2. 头文件 testlib.h(C 接口导出)
#ifndef TESTLIB_H
#define TESTLIB_H

#ifdef _WIN32
    #ifdef TESTLIB_EXPORT
        #define TESTLIB_API __declspec(dllexport)
    #else
        #define TESTLIB_API __declspec(dllimport)
    #endif
#else
    #define TESTLIB_API
#endif

#ifdef __cplusplus
extern "C" {   // 确保 C 语言兼容性
#endif

// 定义 C 接口函数
TESTLIB_API void* create_device();
TESTLIB_API void destroy_device(void* obj);
TESTLIB_API int get_device_value(void* obj);
TESTLIB_API void set_device_value(void* obj, int value);

#ifdef __cplusplus
}
#endif

#endif // TESTLIB_H
3. 实现文件 testlib.cpp
#include "testlib.h"
#include "../device/device.h"

// 创建 Device 对象
TESTLIB_API void* create_device() {
    return new Device();
}

// 销毁 Device 对象
TESTLIB_API void destroy_device(void* obj) {
    delete static_cast<Device*>(obj);
}

// 获取值
TESTLIB_API int get_device_value(void* obj) {
    return static_cast<Device*>(obj)->getValue();
}

// 设置值
TESTLIB_API void set_device_value(void* obj, int value) {
    static_cast<Device*>(obj)->setValue(value);
}

五、Python 调用 testlib.dll 的示例

1. Python 代码 (test.py)
import ctypes

# 加载 DLL
testlib = ctypes.WinDLL("./bin/testlib.dll", winmode=0x8)

# 定义函数原型
testlib.create_device.restype = ctypes.c_void_p
testlib.destroy_device.argtypes = [ctypes.c_void_p]
testlib.get_device_value.argtypes = [ctypes.c_void_p]
testlib.get_device_value.restype = ctypes.c_int
testlib.set_device_value.argtypes = [ctypes.c_void_p, ctypes.c_int]

# 使用 C 接口
device = testlib.create_device()
testlib.set_device_value(device, 42)
print("Value:", testlib.get_device_value(device))  # 输出: 42
testlib.destroy_device(device)

六、关键注意事项

  1. 符号导出

    • C++ 类需要 Q_DECL_EXPORT/Q_DECL_IMPORT__declspec(dllexport/dllimport)
    • C 接口函数必须用 extern "C" 包裹,避免名称修饰(Name Mangling)。
  2. 构建顺序

    • 确保 device.dll 先于 testlib.dll 构建,因为后者依赖前者。
  3. 运行时依赖

    • device.dlltestlib.dll 放在同一目录(如 bin),或添加到系统 PATH
  4. 编译器兼容性

    • 若使用 MinGW,生成的导入库为 .dll.a(需通过 -L-l 链接)。
    • 若使用 MSVC,生成的导入库为 .lib

七、完整项目树和输出

构建完成后,目录结构如下:

MyProject/
├── bin/
│   ├── device.dll
│   ├── device.lib (MSVC) 或 libdevice.dll.a (MinGW)
│   ├── testlib.dll
│   └── testlib.lib (MSVC) 或 libtestlib.dll.a (MinGW)
├── device/
│   └── ...                # 源码
└── testlib/
    └── ...                # 源码

通过以上配置,你可以实现:

  1. device.dll 供 C++ 项目使用。
  2. testlib.dll 通过 C 接口暴露功能,供 Python 调用。

八、注意事項

我嘗試在QT 6.3.1 + python 3.12.7版本實現了以上的功能,需要注意的是以下几点:

  1. 使用的dll和他的依賴項相對位置
  • testlib = ctypes.WinDLL(“./bin/testlib.dll”, winmode=0x8)可以使得testlib.dll在對應的位置和路徑去搜索依賴dll,這樣對於load有幫助
  1. 我曾經嘗試過pybind11 + 生成C++庫的方式,但是QT和python都包含了對slots的定義,這個導致編譯無法通過
  2. python運行環境和編譯器需要注意,我在VS Code的環境能夠運行的代碼,在spyder上並無法運行,即使使用了同一個環境和解釋器
  3. 這種做法的弊端
  • 這種做法需要將C++的函數或者類包裝為C,然後python調用C的接口, 還需要定義python的使用函數的接口參數和返回結果的類型。稍不注意就會出錯,使用體驗很不好。
http://www.dtcms.com/a/106556.html

相关文章:

  • 星途​(小说)
  • 零欧姆电阻的作用、使用场景及注意事项详解
  • PyTorch 核心详解
  • 第六章、Isaacsim中的资产(usd)
  • 【嵌入式系统设计师】知识点:第1章 计算机系统基础知识
  • 方案精读:IPD业务流程体系构建(中)【附全文阅读】
  • 介绍一点metric self-join和复合索引笔记
  • 数组中两个字符串的最小距离
  • 【深度学习量化交易19】行情数据获取方式比测(1)——基于miniQMT的量化交易回测系统开发实记
  • CCF CSP 第34次(2024.06)(2_矩阵重塑(其二)_C++)(二维矩阵 -> 一维矩阵 -> 二维矩阵)
  • 【万字总结】前端全方位性能优化指南(九)——FSP(First Screen Paint)像素级分析、RUM+合成监控、Lighthouse CI
  • 蓝桥杯C++基础算法-最大公约数
  • 论文阅读:基于增强通用深度图像水印的混合篡改定位技术 OmniGuard
  • 电池自动点焊机:智能制造的得力助手|深圳比斯特自动化
  • 普通链式二叉树(习题版)
  • 脑影像分析软件推荐| SimTB
  • WireShark安装
  • BigMusic来了:火山引擎AI音乐模型的技术革新与应用实践
  • MySQL 错误 报错:Table ‘performance_schema.session_variables’ Doesn’t Exist
  • Doris:打破 SQL 方言壁垒,构建统一数据查询生态
  • TensorFlow 字符串操作
  • Uubuntu20.04复现SA-ConvONet步骤
  • 【2025】物联网发展趋势介绍
  • 制造业数字化转型:智能招聘系统破解蓝领用工匹配难题?
  • MySQL学习笔记集--简单介绍以及下载途径
  • EdgeOne Pages 上线「DeepSeek R1 模板」,1分钟快速部署对话型 AI 类网站
  • MySQL表的增删改查(进阶)
  • 应华为 AI 医疗军团之战,各方动态和反应
  • 【C#】.net core 6.0 依赖注入常见问题之一,在构造函数使用的类,都需要注入到容器里,否则会提示如下报错,让DeepSeek找找原因,看看效果
  • 绿盟1面-流量篇