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
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
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
The running result is as follows
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 np
def 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
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
The running result is as follows
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-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>
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/
Copy these 3 files to the execution directory
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