上一篇写了封装C++给python使用。这篇是封装qt给python使用,突然不知道怎么描述了,就简单的说一下思路上代码。可参考上一个。
同样是分为三部分,封装一个dll库,不管你用什么vs,qt,或者cmake,什么都好,只要生成能用的库就行.这部分注意包含qt所需要的文件夹和库。
第二部分也就是根据利用shiboken生成器生成pythonc++文件。具体参数参看自己需求,但是编译不过的话多注意有没有这个东西和是否包含了。具体参数写法看代码或者参考上一篇。
第三部分,就是根据第一和第二部分生成的库和源码来制作一个Py库。
------------------------------------------MainWindow.cpp----------------------------------------------
#include <QtGui>
#include <QSplitter>
#include <QVBoxLayout>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QGraphicsScene>
#include <QGraphicsView>
#include "MainWindow.h"
MainWindow::MainWindow(QWidget * parent):QMainWindow(parent) {
QSplitter * splitter = new QSplitter;
setCentralWidget(splitter);
QWidget * editorContent = new QWidget;
splitter->addWidget(editorContent);
QVBoxLayout * layout = new QVBoxLayout;
editorContent->setLayout(layout);
editor = new QPlainTextEdit;
layout->addWidget(editor);
pb_commit = new QPushButton(tr("Commit"));
connect(pb_commit, SIGNAL(clicked()),
this, SLOT(runPythonCode()));
layout->addWidget(pb_commit);
scene = new QGraphicsScene(this);
viewer = new QGraphicsView;
viewer->setScene(scene);
splitter->addWidget(viewer);
splitter->setSizes(QList<int>() << 400 << 600);
}
MainWindow::~MainWindow() {;}
void MainWindow::runPythonCode() {
emit runSignal();
emit runSignalInt(12);
emit runPythonCode(editor->toPlainText());
setWindowTitle(editor->toPlainText());
}
---------------------------------------MainWindow.h------------------------------------------------
#ifndef MainWindow_H
#define MainWindow_H
#include <QMainWindow>
#include "macros.h"
class QPushButton;
class QGraphicsView;
class QGraphicsScene;
class QPlainTextEdit;
class BINDINGS_API MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget * parent = 0L);
virtual ~MainWindow();
//signals:
Q_SIGNALS:
void runSignal();
void runSignalInt(int);
void runPythonCode(QString);
//private slots:
public slots://Q_SLOTS:
void runPythonCode();
public:
QGraphicsView * viewer;
QGraphicsScene * scene;
QPlainTextEdit * editor;
QPushButton * pb_commit;
};
#endif
---------------------------------------macros.h-----------------------------------------------
#ifndef MACROS_H
#define MACROS_H
#if defined _WIN32 || defined __CYGWIN__
// Export symbols when creating .dll and .lib, and import them when using .lib.
#if BINDINGS_BUILD
#define BINDINGS_API __declspec(dllexport)
#else
#define BINDINGS_API __declspec(dllimport)
#endif
// Disable warnings about exporting STL types being a bad idea. Don't use this in production
// code.
#pragma warning( disable : 4251 )
#else
#define BINDINGS_API
#endif
#endif // MACROS_H
--------------------------------------bindings.h-------------------------------------------------------
#ifndef BINDINGS_H
#define BINDINGS_H
#include "MainWindow.h"
#endif // BINDINGS_H
------------------------------------bindings.xml-----------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<typesystem package="Universe">
<primitive-type name="Qstring"/>
<load-typesystem name="typesystem_widgets.xml" generate="no"/>
<load-typesystem name="typesystem_core.xml" generate="no"/>
<load-typesystem name="typesystem_gui.xml" generate="no"/>
<object-type name="MainWindow">
</object-type>
</typesystem>
-------------------------------------CMakeLists.txt----------------------------------------
#最低版本要求
cmake_minimum_required(VERSION 3.1)
cmake_policy(VERSION 3.1)
# Enable policy to not use RPATH settings for install_name on macOS.
if(POLICY CMP0071)
cmake_policy(SET CMP0071 NEW)
endif()
# Consider changing the project name to something relevant for you.
project(SampleBinding)
#项目名称
# ================================ General configuration ======================================
# Set CPP standard to C++11 minimum.
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_PREFIX_PATH $ENV{QTDIR5122})
# Find required Qt packages.
find_package(Qt5 5.12 REQUIRED COMPONENTS Core Gui Widgets)
#查找qt包
# The sample library for which we will create bindings. You can change the name to something
# relevant for your project.
set(sample_library "libuniverse")
#设置库的名称已备下面使用
# The name of the generated bindings module (as imported in Python). You can change the name
# to something relevant for your project.
set(bindings_library "Universe")
# The header file with all the types and functions for which bindings will be generated.
# Usually it simply includes other headers of the library you are creating bindings for.
set(wrapped_header ${CMAKE_SOURCE_DIR}/bindings.h)
#设置需要绑定的头文件
# The typesystem xml file which defines the relationships between the C++ types / functions
# and the corresponding Python equivalents.
set(typesystem_file ${CMAKE_SOURCE_DIR}/bindings.xml)
#设置xml文件路径
# Specify which C++ files will be generated by shiboken. This includes the module wrapper
# and a '.cpp' file per C++ type. These are needed for generating the module shared
# library.
set(generated_sources
${CMAKE_CURRENT_BINARY_DIR}/${bindings_library}/universe_module_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/${bindings_library}/MainWindow_wrapper.cpp)
#设置资源enerated_sources
# ================================== Shiboken detection ======================================
# Use provided python interpreter if given.
if(NOT python_interpreter)
find_program(python_interpreter "python") #设置python的exe#####################################################
endif()
message(STATUS "Using python interpreter: ${python_interpreter}")
# Macro to get various pyside / python include / link flags and paths.
# Uses the not entirely supported utils/pyside2_config.py file.
macro(pyside2_config option output_var)
if(${ARGC} GREATER 2)
set(is_list ${ARGV2})
else()
set(is_list "")
endif()
execute_process(
COMMAND ${python_interpreter} "${CMAKE_SOURCE_DIR}/../utils/pyside2_config.py"
${option}
OUTPUT_VARIABLE ${output_var}
OUTPUT_STRIP_TRAILING_WHITESPACE)
if ("${${output_var}}" STREQUAL "")
message(FATAL_ERROR "Error: Calling pyside2_config.py ${option} returned no output.")
endif()
if(is_list)
string (REPLACE " " ";" ${output_var} "${${output_var}}")
endif()
endmacro()
# Query for the shiboken generator path, Python path, include paths and linker flags.
pyside2_config(--shiboken2-module-path shiboken2_module_path)
pyside2_config(--shiboken2-generator-path shiboken2_generator_path)
pyside2_config(--python-include-path python_include_dir)
pyside2_config(--shiboken2-generator-include-path shiboken_include_dir 1)
pyside2_config(--shiboken2-module-shared-libraries-cmake shiboken_shared_libraries 0)
pyside2_config(--python-link-flags-cmake python_linking_data )
###-----------------------------------------------------------------------------------
pyside2_config(--pyside2-path PYSIDE2_PATH)
pyside2_config(--pyside2-include-path PYSIDE2_INCLUDE_DIR 1)
pyside2_config(--pyside2-shared-libraries-cmake PYSIDE2_SHARED_LIBRARIES 0)
set(shiboken_path "${shiboken2_generator_path}/shiboken2${CMAKE_EXECUTABLE_SUFFIX}")
if(NOT EXISTS ${shiboken_path})
message(FATAL_ERROR "Shiboken executable not found at path: ${shiboken_path}")
endif()
#一些路径的配置获取
###-----------------------------------------------------------------------------------
# Get all relevant Qt include dirs, to pass them on to shiboken.
#获取qt的一些头文件库路径
get_property(QT_CORE_INCLUDE_DIRS TARGET Qt5::Core PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
get_property(QT_GUI_INCLUDE_DIRS TARGET Qt5::Gui PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
get_property(QT_WIDGETS_INCLUDE_DIRS TARGET Qt5::Widgets PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
set(QT_INCLUDE_DIRS ${QT_CORE_INCLUDE_DIRS} ${QT_GUI_INCLUDE_DIRS} ${QT_WIDGETS_INCLUDE_DIRS})
set(INCLUDES "")
foreach(INCLUDE_DIR ${QT_INCLUDE_DIRS})
list(APPEND INCLUDES "-I${INCLUDE_DIR}")
endforeach()
#包含qt的文件夹,以便解释qt有关的文件
include_directories (${QT_INCLUDE_DIRS})
###-----------------------------------------------------------------------------------
# We need to include the headers for the module bindings that we use.
set(PYSIDE2_ADDITIONAL_INCLUDES "")
foreach(INCLUDE_DIR ${PYSIDE2_INCLUDE_DIR})
list(APPEND PYSIDE2_ADDITIONAL_INCLUDES "${INCLUDE_DIR}/QtCore")
list(APPEND PYSIDE2_ADDITIONAL_INCLUDES "${INCLUDE_DIR}/QtGui")
list(APPEND PYSIDE2_ADDITIONAL_INCLUDES "${INCLUDE_DIR}/QtWidgets")
endforeach()
# ==================================== RPATH configuration ====================================
# =============================================================================================
# !!! (The section below is deployment related, so in a real world application you will want to
# take care of this properly with some custom script or tool).
# =============================================================================================
# Enable rpaths so that the built shared libraries find their dependencies.
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH ${shiboken2_module_path} ${CMAKE_CURRENT_SOURCE_DIR} ${PYSIDE2_PATH})
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# =============================================================================================
# !!! End of dubious section.
# =============================================================================================
# =============================== CMake target - sample_library ===============================
# =============================== CMake target - sample_library ===============================
#处理object问题
set(mocfile MainWindow.h )#包含qobject的文件
QT5_WRAP_CPP(HEADER_MOCS ${mocfile})
#qt5_generate_moc(MainWindow.h MainWindow.moc)
#生产moc文件来解释qobject
# Define the sample shared library for which we will create bindings.
#库的资源注意需要包含HEADER_MOCS来解决qobject问题
set(${sample_library}_sources MainWindow.cpp ${HEADER_MOCS})#${HEADER_MOCS}
add_library(${sample_library} SHARED ${${sample_library}_sources})
#生成添加一个库,参数1名称 2类型 3资源
message( "wwwwwHEADER_MOCS: ${${sample_library}_sources}")
#因为用到Widgets的相关部件,所有需要链接这个库
target_link_libraries(${sample_library} PRIVATE Qt5::Widgets)
#target_link_libraries(${sample_library} PRIVATE Qt5::Core)
#target_link_libraries(${sample_library} PRIVATE Qt5::Gui)
set_property(TARGET ${sample_library} PROPERTY PREFIX "")
# Needed mostly on Windows to export symbols, and create a .lib file, otherwise the binding
# library can't link to the sample library.
target_compile_definitions(${sample_library} PRIVATE BINDINGS_BUILD)
###-----------------------------------------------------------------------------------
#库需要包含的文件夹和第三方库文件
target_include_directories(${sample_library} PRIVATE ${python_include_dir})
target_include_directories(${sample_library} PRIVATE ${shiboken_include_dir})
target_include_directories(${sample_library} PRIVATE ${CMAKE_SOURCE_DIR})
target_include_directories(${sample_library} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
#target_include_directories(${sample_library} PRIVATE ${PYSIDE2_PATH})
target_include_directories(${sample_library} PRIVATE ${QT_INCLUDE_DIRS})
target_include_directories(${sample_library} PRIVATE ${PYSIDE2_ADDITIONAL_INCLUDES})
#target_link_libraries(${sample_library} PRIVATE ${PYSIDE2_SHARED_LIBRARIES})
#set(CMAKE_AUTOMOC ON)
# ====================== Shiboken target for generating binding C++ files ====================
#shiboken操作,注意包含的路径,例如INCLUDES来解决qt需要的头文件 --enable-pyside-extensions信号槽
# Set up the options to pass to shiboken.
set(shiboken_options --generator-set=shiboken --enable-parent-ctor-heuristic
--enable-return-value-heuristic --use-isnull-as-nb_nonzero --enable-pyside-extensions
--avoid-protected-hack
${INCLUDES}###-----------------------------------------------------------------------------------
-I${CMAKE_SOURCE_DIR}
-T${CMAKE_SOURCE_DIR}
-T${PYSIDE2_PATH}/typesystems
--output-directory=${CMAKE_CURRENT_BINARY_DIR}
)
#设置依赖项
set(generated_sources_dependencies ${wrapped_header} ${typesystem_file} )
# Add custom target to run shiboken to generate the binding cpp files.
add_custom_command(OUTPUT ${generated_sources}
COMMAND ${shiboken_path}
${shiboken_options} ${wrapped_header} ${typesystem_file}
DEPENDS ${generated_sources_dependencies}
IMPLICIT_DEPENDS CXX ${wrapped_header}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Running generator for ${typesystem_file}.")
# =============================== CMake target - bindings_library =============================
# Set the cpp files which will be used for the bindings library.
set(${bindings_library}_sources ${generated_sources} )
# Define and build the bindings library.
add_library(${bindings_library} MODULE ${${bindings_library}_sources})
# Apply relevant include and link flags.
target_include_directories(${bindings_library} PRIVATE ${python_include_dir})
target_include_directories(${bindings_library} PRIVATE ${shiboken_include_dir})
target_include_directories(${bindings_library} PRIVATE ${CMAKE_SOURCE_DIR})
target_link_libraries(${bindings_library} PRIVATE ${shiboken_shared_libraries})
target_link_libraries(${bindings_library} PRIVATE ${sample_library})
###-----------------------------------------------------------------------------------
#set_property(TARGET ${bindings_library} PROPERTY AUTOMOC 1)#为修改信号槽问题增加的
target_compile_definitions(${bindings_library} PRIVATE LIBSHIBOKEN_EXPORTS)##################################################
target_include_directories(${sample_library} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(${bindings_library} PRIVATE ${PYSIDE2_PATH})
target_include_directories(${bindings_library} PRIVATE ${PYSIDE2_INCLUDE_DIR})
target_include_directories(${bindings_library} PRIVATE ${QT_INCLUDE_DIRS})
target_include_directories(${bindings_library} PRIVATE ${PYSIDE2_ADDITIONAL_INCLUDES})
target_link_libraries(${bindings_library} PRIVATE Qt5::Widgets)
#target_link_libraries(${bindings_library} PRIVATE Qt5::Core)
#target_link_libraries(${bindings_library} PRIVATE Qt5::Gui)
#target_link_libraries(${bindings_library} PRIVATE ${PYSIDE2_PATH})
target_link_libraries(${bindings_library} PRIVATE ${PYSIDE2_SHARED_LIBRARIES})#pyside2.abi3.lib
##一些打印信息
message( "PYSIDE2_PATH: ${PYSIDE2_PATH}")
message( "PYSIDE2_SHARED_LIBRARIES: ${PYSIDE2_SHARED_LIBRARIES}")
message( "PYSIDE2_INCLUDE_DIR: ${PYSIDE2_INCLUDE_DIR}")
message( "PYSIDE2_ADDITIONAL_INCLUDES: ${PYSIDE2_ADDITIONAL_INCLUDES}")
message( "lpython_include_dir: ${python_include_dir}")
message( "shiboken_include_dir: ${shiboken_include_dir}")
message( "CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}")
message( "shiboken_shared_libraries: ${shiboken_shared_libraries}")
message( "sample_library: ${sample_library}")
message( "QT_INCLUDE_DIRS: ${QT_INCLUDE_DIRS}")
message( "CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")
##
# Adjust the name of generated module.
set_property(TARGET ${bindings_library} PROPERTY PREFIX "")
set_property(TARGET ${bindings_library} PROPERTY OUTPUT_NAME_DEBUG#OUTPUT_NAME #OUTPUT_NAME_DEBUG
"${bindings_library}${PYTHON_EXTENSION_SUFFIX}")
if(WIN32)
set_property(TARGET ${bindings_library} PROPERTY SUFFIX ".pyd")
endif()
# Find and link to the python import library only on Windows.
# On Linux and macOS, the undefined symbols will get resolved by the dynamic linker
# (the symbols will be picked up in the Python executable).
if (WIN32)
list(GET python_linking_data 0 python_libdir)
list(GET python_linking_data 1 python_lib)
#######注意${python_lib}调试版本debug
find_library(python_link_flags ${python_lib} PATHS ${python_libdir} HINTS ${python_libdir})
target_link_libraries(${bindings_library} PRIVATE ${python_link_flags})
message( "python_link_flags: ${python_link_flags}")
endif()
# Same as CONFIG += no_keywords to avoid syntax errors in object.h due to the usage of the word Slot
#target_compile_definitions(${bindings_library} PRIVATE QT_NO_KEYWORDS)
# ================================= Dubious deployment section ================================
if(WIN32)
# =========================================================================================
# !!! (The section below is deployment related, so in a real world application you will
# want to take care of this properly (this is simply to eliminate errors that users usually
# encounter.
# =========================================================================================
# Circumvent some "#pragma comment(lib)"s in "include/pyconfig.h" which might force to link
# against a wrong python shared library.
set(python_versions_list 3 32 33 34 35 36 37 38)
set(python_additional_link_flags "")
foreach(ver ${python_versions_list})
set(python_additional_link_flags
"${python_additional_link_flags} /NODEFAULTLIB:\"python${ver}_d.lib\"")
set(python_additional_link_flags
"${python_additional_link_flags} /NODEFAULTLIB:\"python${ver}.lib\"")
endforeach()
#message( "python_additional_link_flags: ${python_additional_link_flags}")
set_target_properties(${bindings_library}
PROPERTIES LINK_FLAGS "${python_additional_link_flags}")
# Add custom target to hard-link shiboken shared libraries into the build folder, so that
# the user doesn't have to set the PATH manually to point to the PySide2 package.
set(shared_libraries ${shiboken_shared_libraries} ${PYSIDE2_SHARED_LIBRARIES})
foreach(library_path ${shared_libraries})
string(REGEX REPLACE ".lib$" ".dll" library_path ${library_path})
get_filename_component(base_name ${library_path} NAME)
file(TO_NATIVE_PATH ${library_path} source_path)
file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${base_name}" dest_path)
add_custom_command(OUTPUT "${base_name}"
COMMAND mklink /H "${dest_path}" "${source_path}"
DEPENDS ${library_path}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
#WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Creating hardlink to shiboken shared library ${base_name}")
# Fake target that depends on the previous one, but has special ALL keyword, which means
# it will always be executed.
add_custom_target("fake_${base_name}" ALL DEPENDS ${base_name})
endforeach()
# =========================================================================================
# !!! End of dubious section.
# =========================================================================================
endif()
# =============================================================================================
# !!! (The section below is deployment related, so in a real world application you will want to
# take care of this properly with some custom script or tool).
# =============================================================================================
# Install the library and the bindings module into the source folder near the main.py file, so
# that the Python interpeter successfully imports the used module.
install(TARGETS ${bindings_library} ${sample_library}
LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}
RUNTIME DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}
)
# =============================================================================================
# !!! End of dubious section.
# =============================================================================================
-----------------------------------------------------------------------------------------------------------
怎么执行;把上面的文件放在一个文件夹上,然后在该文件夹上新建一个空的文件夹build,进入到build文件夹下,打开cmd命令行。然后cmake -H.. -B. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release
生成一个makefile,继续执行nmake,就能生成我们需要的库,把生成的库一个.dll,一个.pyd,可能还有其余pyside,shiboken放一起,或者你设置的环境变量都能找到也没问题。
python调用:
main.py:
------------------------------------------------------------------------------------------
import sys
from PySide2.QtGui import *
from PySide2.QtCore import *
from PySide2.QtWidgets import *
from Universe import *
class RunScript(QObject):
def __init__(self, mainWindow):
QObject.__init__(self)
self.mainWindow = mainWindow
def runScript(self, script):
print ("runScript" )
mainWindow = self.mainWindow
exec(str(script))
def SlotTest(self):
print (w.editor.toPlainText())
self.runScript(w.editor.toPlainText())
#QObject.connect(SIGNAL('runPythonCodegg(Qstring)'),self.runScript)
@Slot(int)
def SlotTestInt(num):
print ("SlotTestInt" )
speak_words = Signal(str)
signaltest = Signal()
@Slot(str)
def say_some_words(words):
print(words)
a = QApplication(sys.argv)
print ("Start" )
w = MainWindow()
print ("END" )
r = RunScript(w)
w.setWindowTitle('PyHybrid')
w.resize(1000,800)
w.show()
w.connect(w, SIGNAL('runPythonCode(Qstring)'), r.runScript)
a.exec_()
------------------------------------------------------------------------------------------------------------
在制作过程中可能遇到很多问题:
问题1:遇到一些编译器问题,环境变量设置不对,调用不到自己需要的编译器,这个一般是出现在生成makefile的过程中出现,找出你电脑中安装的vs等编译器中vcvars32.bat,这种批量处理的脚本,在控制台执行就会帮助你配置一些库和exe的调用。
问题2:解析xml或者是.h时候出错。.h问题一般是没有在cmake中设置包含,或者是没有包含相关文件夹。
xml问题可能会找不到shiboken,那就是可能电脑没有安装shiboken2_generator的生成器。
生成过程中需要暴露的东西,需要在xml说明。bindings.h包含相关头文件.
https://download.qt.io/official_releases/QtForPython/shiboken2-generator/
这个链接是生成器的下载路径,选择对于版本。
xml头绑定的一些规则:
http://pyside.github.io/docs/api-extractor/typesystem.html
生成器的一些参数设置:http://pyside.github.io/docs/shiboken/
上面需要安装的东西:最基本的,pyside2,shiboken,cmake,shiboken2_generator,vs或者其他编译器.
上面例子可在你安装的D:\PyThon3.5\Lib\site-packages\PySide2\examples\samplebinding
注意:使用qt特别注意的一个是qt库头文件等的包含。qt信号和槽的处理,也就是object的处理另外一个就是资源文件的处理。这个大概就是C++封装和qt的区别了吧。
上文资源下载地址:https://download.csdn.net/download/dreamsongo/11259681
不足之处请大家指出一起讨论。。这些是已经试验过成功的。。