The business needs to use opencv for image processing, and the vision algorithm part is handled by other teams. Since the algorithm part is written in python, we need to integrate python into our Qt program. Here, record the process of Qt calling Python to step on the pit.


1. Call the python file in Qt to
find the python installation path under the machine, and copy include, libs, python3.dll, python310.dll to the project

picture

picture



Add header files and lib to 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 )

Write a simple python script, mytest.py
Note that the file here must not be named test.py, it will conflict with the test module that comes with python

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

qt calls 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();}

Note that you need to cancel the macro definition of slots in Qt first

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

The running result is as follows

picture

2. Handling references to third-party libraries in python scripts
Most of the time, we not only use functions of python itself, but also many third-party libraries. At this time, we can copy the third-party libraries directly to the project.
Here, we use numpy as an example to show that calling py scripts in Qt relies on third-party libraries

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

The numpy library, we simply use pycharm to download it. After the download is complete, copy the numpy folder to the project

picture

#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();}

Here you need to copy the site-packages folder to the cmake path

picture

The running result is as follows

picture

execution succeed

3. The desired effect of packaging
is that the application can be run directly without the need to install other dependencies. This requires the python environment to be packaged into the application.
When performing packaging, many sources say that the dll and libs in the python directory need to be packaged into a zip file, but this is not true. No matter how you try various structures, the following error will be reported

"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>

The correct way is to find the embeddable version corresponding to the official website, that is, the embeddable version
https://www.python.org/downloads/windows/

picture

Copy these 3 files to the execution directory

picture

4. Optimization
The above is to realize the related functions. Next, some constructions need to be optimized. When packaging, you don’t need to copy them manually.

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 ()

Reference documentation
https://blog.csdn.net/yulinxx/article/details/90231955
https://cloud.tencent.com/developer/ask/sof/367090