Qt 调用python 脚本并打包

2022年7月26日 367点热度 0人点赞 0条评论

业务中需要用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(MaterialSizeDetectionEdge Qt::Core Qt::Gui Qt::Widgets ${PY_LIBS} ws2_32 )

编写一个简单的python脚本,mytest.py
注意,这里的文件一定不能命名为test.py 会与python自带的test模块冲突

def hello():    print("hello word")

qt调用python

#include "mainwindow.h"#include "./ui_mainwindow.h"#include <QDebug>#include <QCoreApplication>#include <iostream>#pragma push_macro("slots")#undef slots#include <Python.h>#pragma pop_macro("slots")
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的宏定义

#pragma push_macro("slots")#undef slots#include <Python.h>#pragma pop_macro("slots")

运行结果如下

图片

2. 处理pyhon脚本中对第三方库的引用
大多数时候,我们不只是用python本身的函数,还需要用到很多第三方库,这个时候,我们可以把第三方库直接copy到工程下。
这里,我们用numpy为例,展示,在Qt中调用py脚本中依赖三方库

import numpy as npdef hello():    l = np.ones([3, 3])    print(l)

numpy库,我们就简单粗暴的用pycharm 下载,下载完成之后,把numpy文件夹复制到 工程中

图片

#include "mainwindow.h"#include "./ui_mainwindow.h"#include <QDebug>#include <QCoreApplication>#include <iostream>#pragma push_macro("slots")#undef slots#include <Python.h>#pragma pop_macro("slots")
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 = 0  environment = 1  user site = 1  import site = 1  sys._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.cpp src/mainwindow.cpp src/mainwindow.h src/mainwindow.ui)
find_package(Qt6 COMPONENTS Core Gui Widgets Xml REQUIRED)
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(MaterialSizeDetectionEdge Qt::Core Qt::Gui Qt::Widgets Qt6::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_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 Xml ) add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${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_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${DLL_FILE}" "$<TARGET_FILE_DIR:${PROJECT_NAME}>") endforeach()
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${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_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${PROJECT_SOURCE_DIR}/py" "$<TARGET_FILE_DIR:${PROJECT_NAME}>/py")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${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

56950Qt 调用python 脚本并打包

这个人很懒,什么都没留下

文章评论