如何使用 SIP 制作两个或多个 C++/Qt 类的 python 模块?
Posted
技术标签:
【中文标题】如何使用 SIP 制作两个或多个 C++/Qt 类的 python 模块?【英文标题】:How can I make a python module of two or more C++/Qt classes using SIP? 【发布时间】:2018-12-05 19:34:24 【问题描述】:我有两个 C++/Qt 类(A.h、A.cpp、B.h、B.cpp):
class A : public QObject
// bla-bla
;
class B : public A
// bla-bla
;
我想在 Python 类 A 和 B 中使用类似这样的东西:
import mymodule
class MyB(mymodule.B):
pass
a = mymodule.A()
我可以用一个类制作一个模块并在 Python 中成功使用它,但我不明白如何处理两个或更多类。
这是我用于构建模块的文件如何查找一个类:
*.pro:
TEMPLATE = lib
CONFIG += qt warn_on release
HEADERS = A.h
SOURCES = A.cpp
TARGET = mymodule
DESTDIR = /home/alex/tmp/lib
*.sip:
%Module A 0
%Import QtCore/QtCoremod.sip
class A : QObject
%TypeHeaderCode
#include "A.h"
%End
public:
A();
// bla-bla
;
配置.py:
import os
import sipconfig
from PyQt4 import pyqtconfig
build_file = "A.sbf"
config = pyqtconfig.Configuration()
qt_sip_flags = config.pyqt_sip_flags
os.system(" ".join([config.sip_bin, "-c", ".", "-b", build_file, "-I",
config.pyqt_sip_dir, qt_sip_flags, "A.sip"]))
installs = []
installs.append(["A.sip", os.path.join(config.default_sip_dir, "A")])
makefile = pyqtconfig.QtCoreModuleMakefile(
configuration=config,
build_file=build_file,
installs=installs)
makefile.LFLAGS.append("-L/home/alex/tmp/lib")
makefile.extra_libs = ["A"]
makefile.generate()
运行 Makefile 后,我有了可以在 Python 中使用的“A”模块。 如何在一个 python 模块中创建 2 个或多个类?
【问题讨论】:
【参考方案1】:在开发模块的时候最好把项目组织起来,在这种情况下我会使用如下:
├── configure.py
├── examples
│ └── main.py
├── sip
│ ├── base.sip
│ ├── derived.sip
│ └── pyfoomodule.sip
└── src
├── base.cpp
├── base.h
├── derived.cpp
├── derived.h
├── foomodule_global.h
└── FooModule.pro
base.sip 和 derived.sip 不创建模块,它只定义类:
base.sip
%Import QtCore/QtCoremod.sip
class Base: public QObject
%TypeHeaderCode
#include "base.h"
%End
public:
Base(QObject *parent=nullptr);
virtual QString doStuff();
;
派生的.sip
%Import QtCore/QtCoremod.sip
class Derived: public Base
%TypeHeaderCode
#include "derived.h"
%End
public:
Derived(QObject *parent=nullptr);
QString doStuff();
;
在 pyfoomodule.sip 中创建项目,包括其他 .sip
pyfoomodule.sip
%Module(name=PyFooModule, call_super_init=True, keyword_arguments="Optional")
%DefaultMetatype PyQt4.QtCore.pyqtWrapperType
%DefaultSupertype sip.simplewrapper
%Include base.sip
%Include derived.sip
我还创建了一个脚本,负责编译项目。
configure.py
from PyQt4.QtCore import PYQT_CONFIGURATION as pyqt_config
from distutils import sysconfig
import os, sipconfig, sys
class HostPythonConfiguration(object):
def __init__(self):
self.platform=sys.platform
self.version=sys.hexversion>>8
self.inc_dir=sysconfig.get_python_inc()
self.venv_inc_dir=sysconfig.get_python_inc(prefix=sys.prefix)
self.module_dir=sysconfig.get_python_lib(plat_specific=1)
if sys.platform=='win32':
self.data_dir=sys.prefix
self.lib_dir=sys.prefix+'\\libs'
else:
self.data_dir=sys.prefix+'/share'
self.lib_dir=sys.prefix+'/lib'
class TargetQtConfiguration(object):
def __init__(self, qmake):
pipe=os.popen(' '.join([qmake, '-query']))
for l in pipe:
l=l.strip()
tokens=l.split(':', 1)
if isinstance(tokens, list):
if len(tokens) != 2:
error("Unexpected output from qmake: '%s'\n" % l)
name,value=tokens
else:
name=tokens
value=None
name=name.replace('/', '_')
setattr(self, name, value)
pipe.close()
if __name__=="__main__":
from argparse import ArgumentParser
parser=ArgumentParser(description="Configure PyAnalogClock module.")
parser.add_argument(
'-q', '--qmake',
dest="qmake",
type=str,
default="qmake-qt4",
help="Path to qmake executable"
)
parser.add_argument(
'-s', '--sip-extras',
dest="sip_extras",
type=str,
default="",
help="Extra arguments to sip"
)
args=parser.parse_args()
qmake_exe=args.qmake
if not qmake_exe.endswith('qmake-qt4'):
qmake_exe=os.path.join(qmake_exe,'qmake')
if os.system(' '.join([qmake_exe, '-v']))!=0:
if sys.platform=='win32':
print("Make sure you have a working Qt qmake on your PATH.")
else:
print(
"Use the --qmake argument to explicitly specify a "
"working Qt qmake."
)
exit(1)
sip_args=args.sip_extras
pyconfig=HostPythonConfiguration()
py_sip_dir=os.path.join(pyconfig.data_dir, 'sip', 'PyQt4')
sip_inc_dir=pyconfig.venv_inc_dir
qtconfig=TargetQtConfiguration(qmake_exe)
inc_dir=os.path.abspath(os.path.join(".","src"))
lib_dir=inc_dir
sip_files_dir=os.path.abspath(os.path.join(".","sip"))
output_dir =os.path.abspath(os.path.join(".", "modules"))
build_file="pyfoomodule.sbf"
build_path = os.path.join(output_dir, build_file)
if not os.path.exists(output_dir): os.mkdir(output_dir)
sip_file = os.path.join(sip_files_dir, "pyfoomodule.sip")
config=sipconfig.Configuration()
cmd=" ".join([
config.sip_bin,
pyqt_config['sip_flags'],
sip_args,
'-I', sip_files_dir,
'-I', py_sip_dir,
'-I', config.sip_inc_dir,
'-I', inc_dir,
"-c", output_dir,
"-b", build_path,
"-w",
"-o",
sip_file,
])
print(cmd)
if os.system(cmd)!=0: sys.exit(1)
installs = []
installs.append([os.path.join(sip_files_dir, "pyfoomodule.sip"),
os.path.join(config.default_sip_dir, "PyFooModule")])
makefile=sipconfig.SIPModuleMakefile(
config,
build_file,
dir=output_dir,
installs=installs
)
makefile.extra_defines+=['MYMODULE_LIBRARY','QT_CORE_LIB', 'QT_GUI_LIB']
makefile.extra_include_dirs+=[os.path.abspath(inc_dir), qtconfig.QT_INSTALL_HEADERS]
makefile.extra_lib_dirs+=[qtconfig.QT_INSTALL_LIBS, os.path.join('..','src')]
makefile.extra_libs+=['FooModule']
if sys.platform=='darwin':
makefile.extra_cxxflags+=['-F'+qtconfig.QT_INSTALL_LIBS]
makefile.extra_include_dirs+=[
os.path.join(qtconfig.QT_INSTALL_LIBS,'QtCore.framework','Headers'),
os.path.join(qtconfig.QT_INSTALL_LIBS,'QtGui.framework','Headers')
]
makefile.extra_lflags+=[
'-F'+qtconfig.QT_INSTALL_LIBS,
"-framework QtGui",
"-framework QtCore",
"-framework DiskArbitration",
"-framework IOKit",
"-framework OpenGL",
"-framework AGL",
]
else:
makefile.extra_include_dirs+=[
os.path.join(qtconfig.QT_INSTALL_HEADERS, "QtCore"),
os.path.join(qtconfig.QT_INSTALL_HEADERS, "QtGui"),
]
makefile.extra_lib_dirs+=[os.path.join('..','src','release')]
# makefile.extra_libs+=['Qt4Core','Qt4Gui']
makefile.generate()
sipconfig.ParentMakefile(
configuration = config,
subdirs = ["src", output_dir],
).generate()
os.chdir("src")
qmake_cmd=qmake_exe
if sys.platform=="win32": qmake_cmd+=" -spec win32-msvc2010"
print()
print(qmake_cmd)
os.system(qmake_cmd)
sys.exit()
然后执行以下命令:
python configure.py --qmake /path/of/qmake
make
sudo make install
最后是如何使用它的示例:
from PyQt4 import QtCore
from PyFooModule import Base, Derived
class PyDerived(Base):
def doStuff(self):
return "PyDerived"
if __name__ == '__main__':
print("==============================")
b = Base()
pd = PyDerived()
d = Derived()
print("b.doStuff(): ", b.doStuff())
print("pd.doStuff(): ", pd.doStuff())
print("d.doStuff(): ", d.doStuff())
print("==============================")
print("Base is subclass of QObject: ", issubclass(Base, QtCore.QObject))
print("PyDerived is subclass of Base: ", issubclass(PyDerived, Base))
print("Derived is subclass of Base: ", issubclass(Derived, Base))
print("==============================")
输出:
==============================
b.doStuff(): Base
pd.doStuff(): PyDerived
d.doStuff(): Derived
==============================
Base is subclass of QObject: True
PyDerived is subclass of Base: True
Derived is subclass of Base: True
==============================
完整的例子可以在here找到。
【讨论】:
我有一个问题.. 'make install' mymodule.so 复制到 /usr/lib/python3/dist-packages/mymodule.so 和 mymodule.sip 复制到 /usr/share/sip /mymodule/mymodule.sip 当我尝试从 Python 脚本导入 mymodule 时出现错误:Traceback(最近一次调用最后一次):文件“以上是关于如何使用 SIP 制作两个或多个 C++/Qt 类的 python 模块?的主要内容,如果未能解决你的问题,请参考以下文章