业务中需要用opencv进行图像处理,视觉算法部分由其他团队处理,由于算法部分是用python编写的,我们需要将python集成到我们的Qt程序中。这里,记录下Qt调用Python踩坑的过程。
1. 在Qt中调用python文件
找到本机下python的安装路径,将include,libs,python3.dll,python310.dll拷贝到工程下


将头文件和lib加入到cmakelist中
include_directories("${PROJECT_SOURCE_DIR}/include/python" )if(WIN32)link_directories("${PROJECT_SOURCE_DIR}/dependencies/windows-x86_64/libs")endif()set(PY_LIBS _tkinter python3 python310)add_executable(MaterialSizeDetectionEdge ${PROJECT_SOURCES})target_link_libraries(MaterialSizeDetectionEdgeQt::CoreQt::GuiQt::Widgets${PY_LIBS}ws2_32)
编写一个简单的python脚本,mytest.py
注意,这里的文件一定不能命名为test.py 会与python自带的test模块冲突
def hello():print("hello word")
qt调用python
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow){ui->setupUi(this);qDebug() << QCoreApplication::applicationDirPath();Py_Initialize();if (!Py_IsInitialized()) {qDebug()<<" py init faild ";}PyRun_SimpleString("import sys");PyRun_SimpleString("sys.path.append('./py')");PyObject* pModule = PyImport_ImportModule("mytest");PyObject* pFunhello= PyObject_GetAttrString(pModule,"hello");PyObject_CallFunction(pFunhello,NULL);Py_Finalize();}
注意,这里需要先取消Qt 中对slots的宏定义
运行结果如下

2. 处理pyhon脚本中对第三方库的引用
大多数时候,我们不只是用python本身的函数,还需要用到很多第三方库,这个时候,我们可以把第三方库直接copy到工程下。
这里,我们用numpy为例,展示,在Qt中调用py脚本中依赖三方库
import numpy as npdef hello():l = np.ones([3, 3])print(l)
numpy库,我们就简单粗暴的用pycharm 下载,下载完成之后,把numpy文件夹复制到 工程中

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow){ui->setupUi(this);qDebug() << QCoreApplication::applicationDirPath();Py_Initialize();if (!Py_IsInitialized()) {qDebug()<<" py init faild ";}PyRun_SimpleString("import sys");PyRun_SimpleString("sys.path.append('./')");QString pyPackagesPath = "sys.path.append('"+QCoreApplication::applicationDirPath()+"/site-packages')";PyRun_SimpleString(pyPackagesPath.toLatin1().data());PyObject* pModule = PyImport_ImportModule("mytest");PyObject* pFunhello= PyObject_GetAttrString(pModule,"hello");PyObject_CallFunction(pFunhello,NULL);Py_Finalize();}
这里需要把 site-packages文件夹复制到cmake路径下

运行结果如下

执行成功
3. 打包
我们期望的效果是,直接把应用解压就可以运行,而不需要再去安装其他的依赖。这样就需要把python环境一起打包到应用中。
在执行打包的时候,很多资料都说是需要把python目录下的dll 还有 libs打包成一个zip文件 ,其实这个是不对的。无论怎么尝试各种结构都会报如下错误
"D:/develop/workspace/xxx/material-size-detection/material-size-detection-edge/cmake-build-debug-visual-studio-2022"Python path configuration:PYTHONHOME = (not set)PYTHONPATH = (not set)program name = 'python'isolated = 0environment = 1user site = 1import site = 1sys._base_executable = 'D:\\develop\\workspace\\xxx\\material-size-detection\\material-size-detection-edge\\cmake-build-debug-visual-studio-2022\\MaterialSizeDetectionEdge.exe'sys.base_prefix = ''sys.base_exec_prefix = ''sys.platlibdir = 'lib'sys.executable = 'D:\\develop\\workspace\\jscoe\\material-size-detection\\material-size-detection-edge\\cmake-build-debug-visual-studio-2022\\MaterialSizeDetectionEdge.exe'sys.prefix = ''sys.exec_prefix = ''sys.path = ['D:\\develop\\workspace\\xxx\\material-size-detection\\material-size-detection-edge\\cmake-build-debug-visual-studio-2022\\python310.zip','.\\DLLs','.\\lib','D:\\develop\\workspace\\xxx\\material-size-detection\\material-size-detection-edge\\cmake-build-debug-visual-studio-2022',]Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encodingPython runtime state: core initializedModuleNotFoundError: No module named 'encodings'Current thread 0x0000bdac (most recent call first):<no Python frame>
正确的做法是,找到官网对应的embeddable版本,也就是可嵌入版本
https://www.python.org/downloads/windows/

将这个3个文件复制到执行目录下即可

4. 优化
以上是把相关的功能实现了,下面需要把一些构建进行优化,打包的时候 ,可以不用手动去拷贝
cmake_minimum_required(VERSION 3.22)project(MaterialSizeDetectionEdge)set(CMAKE_CXX_STANDARD 17)set(CMAKE_AUTOMOC ON)set(CMAKE_AUTORCC ON)set(CMAKE_AUTOUIC ON)#set(CMAKE_PREFIX_PATH "D:/develop/program/Qt/6.3.1/mingw_64")set(CMAKE_PREFIX_PATH "D:/develop/program/Qt/6.3.1/msvc2019_64")set(CONF_FILES config.xml)set(TS_FILES material-size-detection-edge_zh_CN.ts)set(QRC_FILES resource.qrc)set(PROJECT_SOURCES${TS_FILES}src/main.cppsrc/mainwindow.cppsrc/mainwindow.hsrc/mainwindow.ui)find_package(Qt6 COMPONENTSCoreGuiWidgetsXmlREQUIRED)include_directories("${PROJECT_SOURCE_DIR}/include/python" )if(WIN32)link_directories("${PROJECT_SOURCE_DIR}/dependencies/windows-x86_64/libs")endif()set(PY_LIBS _tkinter python3 python310)add_executable(MaterialSizeDetectionEdge ${PROJECT_SOURCES})target_link_libraries(MaterialSizeDetectionEdgeQt::CoreQt::GuiQt::WidgetsQt6::Xml${PY_LIBS}ws2_32)if (WIN32)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_BUILDCOMMAND ${CMAKE_COMMAND} -E make_directory"$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/")add_custom_command(TARGET ${PROJECT_NAME} POST_BUILDCOMMAND ${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 Xml )add_custom_command(TARGET ${PROJECT_NAME} POST_BUILDCOMMAND ${CMAKE_COMMAND} -E copy"${QT_INSTALL_PATH}/bin/Qt6${QT_LIB}${DEBUG_SUFFIX}.dll""$<TARGET_FILE_DIR:${PROJECT_NAME}>")endforeach (QT_LIB)file(GLOB DLL_LIST ${PROJECT_SOURCE_DIR}/dependencies/windows-x86_64/dlls/*.dll)foreach(DLL_FILE ${DLL_LIST})add_custom_command(TARGET ${PROJECT_NAME} POST_BUILDCOMMAND ${CMAKE_COMMAND} -E copy"${DLL_FILE}""$<TARGET_FILE_DIR:${PROJECT_NAME}>")endforeach()add_custom_command(TARGET ${PROJECT_NAME} POST_BUILDCOMMAND ${CMAKE_COMMAND} -E copy_directory"${PROJECT_SOURCE_DIR}/dependencies/windows-x86_64/site-packages""$<TARGET_FILE_DIR:${PROJECT_NAME}>/site-packages")add_custom_command(TARGET ${PROJECT_NAME} POST_BUILDCOMMAND ${CMAKE_COMMAND} -E copy_directory"${PROJECT_SOURCE_DIR}/py""$<TARGET_FILE_DIR:${PROJECT_NAME}>/py")add_custom_command(TARGET ${PROJECT_NAME} POST_BUILDCOMMAND ${CMAKE_COMMAND} -E copy"${PROJECT_SOURCE_DIR}/dependencies/windows-x86_64/python310.zip""$<TARGET_FILE_DIR:${PROJECT_NAME}>")endif ()
参考文档
https://blog.csdn.net/yulinxx/article/details/90231955
https://cloud.tencent.com/developer/ask/sof/367090
文章评论