📄 sipref.txt
字号:
// Define the SIP wrapper to the word library. %Module word 0 class Word { %TypeHeaderCode #include <word.h> %End public: Word(const char *w); char *reverse() const; };Obviously a SIP specification file looks very much like a C++ (or C) headerfile, but SIP does not include a full C++ parser. Let's look at thedifferences between the two files. - The `%Module`_ directive has been added [#]_. This is used to name the Python module that is being created and to give it a *generation* number. In this example these are ``word`` and ``0`` respectively. The generation number is effectively the version number of the module. - The `%TypeHeaderCode`_ directive has been added. The text between this and the following `%End`_ directive is included literally in the code that SIP generates. Normally it is used, as in this case, to ``#include`` the corresponding C++ (or C) header file [#]_. - The declaration of the private variable ``this_word`` has been removed. SIP does not support access to either private or protected instance variables.If we want to we can now generate the C++ code in the current directory byrunning the following command:: sip -c . word.sipHowever, that still leaves us with the task of compiling the generated code andlinking it against all the necessary libraries. It's much easier to use theSIP build system to do the whole thing.Using the SIP build system is simply a matter of writing a small Python script.In this simple example we will assume that the ``word`` library we are wrappingand it's header file are installed in standard system locations and will befound by the compiler and linker without having to specify any additionalflags. In a more realistic example your Python script may take command lineoptions, or search a set of directories to deal with different configurationsand installations.This is the simplest script (conventionally called ``configure.py``):: import os import sipconfig # The name of the SIP build file generated by SIP and used by the build # system. build_file = "word.sbf" # Get the SIP configuration information. config = sipconfig.Configuration() # Run SIP to generate the code. os.system(" ".join([config.sip_bin, "-c", ".", "-b", build_file, "word.sip"])) # Create the Makefile. makefile = sipconfig.SIPModuleMakefile(config, build_file) # Add the library we are wrapping. The name doesn't include any platform # specific prefixes or extensions (e.g. the "lib" prefix on UNIX, or the # ".dll" extension on Windows). makefile.extra_libs = ["word"] # Generate the Makefile itself. makefile.generate()Hopefully this script is self-documenting. The key parts are the``Configuration`` and ``SIPModuleMakefile`` classes. The build system containsother Makefile classes, for example to build programs or to call otherMakefiles in sub-directories.After running the script (using the Python interpreter the extension module isbeing created for) the generated C++ code and ``Makefile`` will be in thecurrent directory.To compile and install the extension module, just run the followingcommands [#]_:: make make installThat's all there is to it.See `Building Your Extension with distutils`_ for an example of how to buildthis example using distutils... [#] All SIP directives start with a ``%`` as the first non-whitespace character of a line... [#] SIP includes many code directives like this. They differ in where the supplied code is placed by SIP in the generated code... [#] On Windows you might run ``nmake`` or ``mingw32-make`` instead.A Simple C Example------------------Let's now look at a very similar example of wrapping a fictional C library:: /* Define the interface to the word library. */ struct Word { const char *the_word; }; struct Word *create_word(const char *w); char *reverse(struct Word *word);The corresponding SIP specification file would then look something like this:: /* Define the SIP wrapper to the word library. */ %CModule word 0 struct Word { %TypeHeaderCode #include <word.h> %End const char *the_word; }; struct Word *create_word(const char *w) /Factory/; char *reverse(struct Word *word);Again, let's look at the differences between the two files. - The `%CModule`_ directive has been added. This has the same syntax as the `%Module`_ directive used in the previous example but tells SIP that the library being wrapped is implemented in C rather than C++. - The `%TypeHeaderCode`_ directive has been added. - The Factory_ annotation has been added to the ``create_word()`` function. This tells SIP that a newly created structure is being returned and it is owned by Python.The ``configure.py`` build system script described in the previous example canbe used for this example without change.A More Complex C++ Example--------------------------In this last example we will wrap a fictional C++ library that contains a classthat is derived from a Qt class. This will demonstrate how SIP allows a classhierarchy to be split across multiple Python extension modules, and willintroduce SIP's versioning system.The library contains a single C++ class called ``Hello`` which is derived fromQt's ``QLabel`` class. It behaves just like ``QLabel`` except that the textin the label is hard coded to be ``Hello World``. To make the example moreinteresting we'll also say that the library only supports Qt v3.0 and later,and also includes a function called ``setDefault()`` that is not implementedin the Windows version of the library.The ``hello.h`` header file looks something like this:: // Define the interface to the hello library. #include <qlabel.h> #include <qwidget.h> #include <qstring.h> class Hello : public QLabel { // This is needed by the Qt Meta-Object Compiler. Q_OBJECT public: Hello(QWidget *parent, const char *name = 0, WFlags f = 0); private: // Prevent instances from being copied. Hello(const Hello &); Hello &operator=(const Hello &); }; #if !defined(Q_OS_WIN) void setDefault(const QString &def); #endifThe corresponding SIP specification file would then look something like this:: // Define the SIP wrapper to the hello library. %Module hello 0 %Import qt/qtmod.sip %If (Qt_3_0_0 -) class Hello : QLabel { %TypeHeaderCode #include <hello.h> %End public: Hello(QWidget *parent /TransferThis/, const char *name = 0, WFlags f = 0); private: Hello(const Hello &); }; %If (!WS_WIN) void setDefault(const QString &def); %End %EndAgain we look at the differences, but we'll skip those that we've looked at inprevious examples. - The `%Import`_ directive has been added to specify that we are extending the class hierarchy defined in the file ``qt/qtmod.sip``. This file is part of PyQt. The build system will take care of finding the file's exact location. - The `%If`_ directive has been added to specify that everything [#]_ up to the matching `%End`_ directive only applies to Qt v3.0 and later. ``Qt_3_0_0`` is a *tag* defined in ``qtmod.sip`` [#]_ using the `%Timeline`_ directive. `%Timeline`_ is used to define a tag for each version of a library's API you are wrapping allowing you to maintain all the different versions in a single SIP specification. The build system provides support to ``configure.py`` scripts for working out the correct tags to use according to which version of the library is actually installed. - The ``public`` keyword used in defining the super-classes has been removed. This is not supported by SIP. - The TransferThis_ annotation has been added to the first argument of the constructor. It specifies that if the argument is not 0 (i.e. the ``Hello`` instance being constructed has a parent) then ownership of the instance is transferred from Python to C++. It is needed because Qt maintains objects (i.e. instances derived from the ``QObject`` class) in a hierachy. When an object is destroyed all of its children are also automatically destroyed. It is important, therefore, that the Python garbage collector doesn't also try and destroy them. This is covered in more detail in `Ownership of Objects`_. SIP provides many other annotations that can be applied to arguments, functions and classes. Multiple annotations are separated by commas. Annotations may have values. - The ``=`` operator has been removed. This operator is not supported by SIP. - The `%If`_ directive has been added to specify that everything up to the matching `%End`_ directive does not apply to Windows. ``WS_WIN`` is another tag defined by PyQt, this time using the `%Platforms`_ directive. Tags defined by the `%Platforms`_ directive are mutually exclusive, i.e. only one may be valid at a time [#]_.One question you might have at this point is why bother to define the privatecopy constructor when it can never be called from Python? The answer is toprevent the automatic generation of a public copy constructor.We now look at the ``configure.py`` script. This is a little different to thescript in the previous examples for two related reasons.Firstly, PyQt includes a pure Python module called ``pyqtconfig`` that extendsthe SIP build system for modules, like our example, that build on top of PyQt.It deals with the details of which version of Qt is being used (i.e. itdetermines what the correct tags are) and where it is installed. This iscalled a module's configuration module.Secondly, we generate a configuration module (called ``helloconfig``) for ourown ``hello`` module. There is no need to do this, but if there is a chancethat somebody else might want to extend your C++ library then it would makelife easier for them.Now we have two scripts. First the ``configure.py`` script:: import os import sipconfig import pyqtconfig # The name of the SIP build file generated by SIP and used by the build # system. build_file = "hello.sbf" # Get the PyQt configuration information. config = pyqtconfig.Configuration() # Get the extra SIP flags needed by the imported qt module. Note that # this normally only includes those flags (-x and -t) that relate to SIP's # versioning system. qt_sip_flags = config.pyqt_qt_sip_flags # Run SIP to generate the code. Note that we tell SIP where to find the qt # module's specification files using the -I flag. os.system(" ".join([config.sip_bin, "-c", ".", "-b", build_file, "-I", config.pyqt_sip_dir, qt_sip_flags, "hello.sip"])) # We are going to install the SIP specification file for this module and # its configuration module. installs = [] installs.append(["hello.sip", os.path.join(config.default_sip_dir, "hello")]) installs.append(["helloconfig.py", config.default_mod_dir]) # Create the Makefile. The QtModuleMakefile class provided by the # pyqtconfig module takes care of all the extra preprocessor, compiler and # linker flags needed by the Qt library. makefile = pyqtconfig.QtModuleMakefile( configuration=config, build_file=build_file, installs=installs ) # Add the library we are wrapping. The name doesn't include any platform # specific prefixes or extensions (e.g. the "lib" prefix on UNIX, or the # ".dll" extension on Windows). makefile.extra_libs = ["hello"] # Generate the Makefile itself. makefile.generate() # Now we create the configuration module. This is done by merging a Python # dictionary (whose values are normally determined dynamically) with a # (static) template. content = { # Publish where the SIP specifications for this module will be # installed. "hello_sip_dir": config.default_sip_dir, # Publish the set of SIP flags needed by this module. As these are the # same flags needed by the qt module we could leave it out, but this # allows us to change the flags at a later date without breaking # scripts that import the configuration module. "hello_sip_flags": qt_sip_flags } # This creates the helloconfig.py module from the helloconfig.py.in # template and the dictionary. sipconfig.create_config_module("helloconfig.py", "helloconfig.py.in", content)Next we have the ``helloconfig.py.in`` template script:: import pyqtconfig # These are installation specific values created when Hello was configured. # The following line will be replaced when this template is used to create # the final configuration module. # @SIP_CONFIGURATION@ class Configuration(pyqtconfig.Configuration): """The class that represents Hello configuration values. """ def __init__(self, sub_cfg=None): """Initialise an instance of the class.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -