Work with PySide

    This post participates in the contest “Smart Phones for Smart Posts”.


    There is some information on the PySide project on the web , but not so much in Russian.

    This article is an attempt to fill this gap. Next, the assembly of the project, the launch of applications on the emulator, an example of creating your own binding will be described. Some key features of PySide will be shown with examples.

    Introduction What is PySide?


    PySide is a project for creating Qt bindings to the Python programming language . It aims to implement support for all Qt features , including QtQuick and QtMobility . The following versions of components and platforms are currently supported.
    Qt4.6, 4.7, 4.8 betta
    QtMobility1.2.0
    Python2.5, 2.6, 2.7, 3.2 (experemental)
    OSLinux / X11, Maemo 5, MeeGo, Windows, Mac OS X

    There is no possibility to use PySide on Symbian yet (or I don’t know this possibility), but according to the developers, work in this direction is underway. You can use PyS60 as an alternative, though it has nothing to do with Qt , so we won’t talk about it later.

    The PySide project also contains a set of tools that allow you to create bindings of any libraries written in C / C ++. We’ll talk more about these tools later.

    PySide is licensed under the LGPL, i.e. It can be used in both open and closed commercial projects. PySide source codes are open and available at github.com/PySide. Also, if you find any problems related to PySide , you can report them to the project’s official bug tracker .

    PySide Build, Health Check


    On the download page , instructions for installing PySide on various OSs are available. But it may turn out that for your Linux distribution, such a package is not available. Let's assemble it ourselves, which is not at all difficult, because even here, Qt developers took care of their users and prepared a set of scripts that automate PySide assembly actions as much as possible .

    So, let's clone a Git repository with build scripts to start. We don’t need anything else yet, because by dependency, all necessary projects will be cloned.

    $ git clone git://github.com/PySide/BuildScripts.git buildscripts
    $ cd buildscripts
    $ git submodule init
    $ git submodule update
    

    You can have several versions of Qt installed on your system . To specify which version to use, edit the enviroment.sh file, in which write the path to the Qt home directory and the path where PySide will be installed . The enviroment.sh file is provided with detailed comments, so you should not have any difficulties. I immediately warn you that with the version of Qt installed on the system by default, PySide may not be collected. I recommend using the latest version of QtSdk to build.

    After everything is configured, collect PySide command

    $ ./build_and_install
    

    To facilitate the work, the enviroment.sh file is easily converted into a script with which applications using PySide are launched . All you need to do is make it executable and add python $ @ at the end .

    To test the functionality of the resulting package, we clone the repository with Qt examples in Python , adapted for PySide

    $ git clone git://github.com/PySide/Examples.git pyside-examples
    

    and run any of the examples you like, for example, Hyper UI.



    Now we are ready to build PySide for the host system. But during development, it may be convenient to run applications in the simulator that is included with QtSdk. Let's build PySide for her too. To do this, edit the QT_SDK_HOME variable in the enviroment.sh file, specifying the path to QtSimulator ($ {YOUPATH} / QtSDK / Simulator / Qt / gcc) as the Qt home directory . You also need to edit the build_and_install file: add the -DQ_WS_SIMULATOR = yes option to the cmake start command. This option helps cmake determine the platform under which the compilation will take place.

    If you start the assembly now, then at the moment it unfortunately will fail, because QtWebKit module will not be able to assemble. I created a report on this error, to which I attached a small patch that fixes this problem. But the moment of writing the article, this bug has not yet been fixed. Therefore, use this patch to fully build PySide under QtSimulator (see the attachment to the bug report using the link above).



    Nothing prevents us from building a QtMobility binding for QtSimulator. Its assembly is no different from the assembly of PySide itself . QtMobilityassemble and even run in the simulator, but not one of the modules at the same time, unfortunately, can not be called working. In full, not a single example supplied with QtMobility itself , nor from the pyside-examples package, worked . I’ll also deal with the reasons for this and, perhaps, someday I will write about it.



    Introduction to PySide Programming. Signals and slots, property system


    This section is not an introduction to the Python programming language . It will only show how to use some of the key features of Qt in Python .

    Signal-slot interaction

    from  PySide import QtCore
    def say_hello(name):
        print "Hello,", name
    class Foo(QtCore.QObject):
        @QtCore.Slot(str)
        def say_bye(self, name):
            print "Bye,", name
    class Bar(QtCore.QObject):
        signal = QtCore.Signal(tuple)
    f = Foo()
    b = Bar()
    b.signal.connect(say_hello)
    b.signal.connect(f.say_bye)
    b.signal.emit("User.")
    b.signal.emit(1.25)
    

    Signal is a class through which the connection of a signal and a slot is controlled, as well as the sending of the signal itself. The types of parameters transmitted with the signal are indicated when it is created. It can be any type of C and Python . If we want to send parameters of different types (as in the example above), tuple or list should be specified as the type. As before, the signal must be declared within the class inherited from QObject.

    If you need to create several signals, you can use the following recording form
    class Test(QtCore.QObject):
        signals = QtCore.Signal((int,), (str,), (float, QtCore.QObject))
    

    This code will add three signals with the signatures signals (int), signals (QString) and signals (double, QObject *). All of them are contained in the signals variable, which can conditionally be considered a dictionary (not iterable). The signal parameters are used as a key.
    someone.signals.emit(10) #по умолчанию используется тип объявленный первым, т.е. int
    someone.signals[str].emit("Hello everybody!")  
    someone.signals[float, QtCore.QObject].emit(1.23, obj)
    

    Any method of a class that is not necessarily inherited from QObject, a global function, or even a lambda function can serve as a slot. But such objects are not true slots, there is no information about them in QMetaObject, so use this approach with caution. For example, if you use a global function as a slot, then you cannot get information about the calling object in it.

    In order to create a true slot, you need to use the Slot decorator from the QtCore module. In code, it looks like this:
    @QtCore.Slot(int)
    @QtCore.Slot(str)
    def mySlot(value):
        print value
    

    Overload rules do not apply to slots and we cannot create a slot that accepts any type of parameters. A separate decorator must be added for each type of parameter. The above is an example of a slot that can accept parameters of type int and QString.

    Both the QtCore.Signal class and the QtCore.Slot decorator as additional parameters can take the name under which the generated signals and slots will be stored in the metobject information. By default, if no name is specified, the signal is assigned the name of the class member to which it is assigned, and the name of the method to decorate is assigned to the slot. The slot can also be set to return type. You can use this functionality to communicate Python and QML .

    More on Python Interoperabilityand QML can be read on developer.qt.nokia.com in the Python section. You can also see pyside-qml-examples .

    Property system

    Working with Qt properties is not much different from working with classic Python properties . Just a small example.

    from PySide import QtCore
    class MyObject(QtCore.QObject):
        def __init__(self):
            QtCore.QObject.__init__(self)
            self._x = None
        def getx(self):
            print "get"
            return self._x
        def setx(self, val):
            print "set"
            self._x = val
        prop = QtCore.Property(int, getx, setx)
    obj = MyObject()
    obj.prop = 12345
    print obj.prop
    

    You can read more about working with Qt properties in Python here .

    Creating a GUI Using PySide


    PySide Tools package includes standard Qt tools for working with application resources, developing a "classic" graphical interface and localizing applications. These are pyside-ui, pyside-rcc and pyside-lupdate. Working with them is no different from the same packages for Qt / C ++. Therefore, you still have the opportunity to create a graphical application framework in QtDesigner. The resulting form is compiled using pyside-ui, which needs to specify the form file and the output file as input parameters through the -o option. Pyside-ui also has an additional interesting option -x which adds application launch code to the resulting Python file . Below is a small example of launching an application using the form created in the designer, resources and localization

    from PySide import QtCore, QtGui
    from form_ui import * #подключаем графический интерфейс
    import resources_rc #подключаем ресурсы приложения
    if __name__ == "__main__":
        import sys
        translator = QtCore.QTranslator()
        translator.load(':/i18n/translations/ru_RU') #подключаем файл локализации из ресурсов
        app = QtGui.QApplication(sys.argv)
        app.installTranslator(translator)
        Form = QtGui.QWidget()
        ui = Ui_Form()
        ui.setupUi(Form)
        Form.show()
        sys.exit(app.exec_())
    

    Also in PySide are available many Qt goodies of recent years, including QtQuick. Those. we are not deprived of the opportunity to create hybrid applications using Python and QML (QtQuick.Particles, Qt Desktop Components, etc.).

    Example of creating your own binding


    Knowing all of the above, we can create custom Python applications using Qt , including those using QtQuick. But what if we need to use a library written in C / C ++ with our project or use our previous developments made in the same C / C ++? Do not rewrite everything again in Python ?

    For example, we have a certain project using Qt Components . All the main functionality is written in QML . But to run it, you need the QmlDesktopViewer class already written in C ++. It’s easy for us to rewrite it in Python.but that would not be interesting right now. Let's make our own binding of this class to Python .

    To do this, use the tools from the PySide project to create bindings. These are API Extractor, Shiboken and Generator Runner. We already have all these tools.

    To create a binding, you first need to create an xml file that describes what data we want to export, hide, rename, etc. In general, what we will access in the resulting Python module .


    I specifically described in detail what files we get at the output of the generator, because they are to be compiled to get the Python module . Understanding this will make it easier to write a project file for your build system. For a more detailed look at the rules for compiling this file, see the API Extractor documentation .

    The next step is to create a C ++ language header file that will be used to extract information about the data for which the binding is being done.

    #undef QT_NO_STL
    #undef QT_NO_STL_WCHAR
    #ifndef NULL
    #define NULL    0
    #endif
    #include "pyside_global.h"
    #include 
    #include 

    The pyside_global.h file we received during the PySide build . You can copy it to the project or add a path to its location.

    As a build system, I use CMake. I will not describe the assembly here. The full example code can be taken here . You can use it as an example to create your own binding.

    We compile the project, and we get a library with a wrapper over our class, which we can use in our application in Python . We will use the resulting module to run one of the Qt Desktop Components examples.

    #!/usr/bin/env python
    from PySide.QtCore import *
    from PySide.QtGui import *
    from PySide.QmlDesktopViewer import *
    if __name__ == "__main__":
        import sys
        if len(sys.argv) < 2:
            print "Usage: qmldesktopviewer "
            sys.exit()
        app = QApplication(sys.argv)
        viewer = QmlDesktopViewer()
        viewer.open(sys.argv[1])
        sys.exit(app.exec_())
    



    I also want to note that there are already several third-party library bindings for PySide . One of them is the Qwt binding .

    Materials used


    PySide v1.0.8 documentation
    PySide Mobility v0.2.2 documentation
    New-style signal / slot
    PySide on Developer Network
    Running PySide applications on Qt Simulator
    PySide Binding Generator
    PySide Binding Generation Tutorial

    Also popular now: