业务中需要用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
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 np
def 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 = 0
environment = 1
user site = 1
import site = 1
sys._base_executable = 'D:\\develop\\workspace\\xxx\\material-size-detection\\material-size-detection-edge\\cmake-bu
ild-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-de
bug-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-stud
io-2022\\python310.zip',
'.\\DLLs',
'.\\lib',
'D:\\develop\\workspace\\xxx\\material-size-detection\\material-size-detection-edge\\cmake-build-debug-visual-stud
io-2022',
]
Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
Python runtime state: core initialized
ModuleNotFoundError: 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
文章评论