📜 ⬆️ ⬇️

Work with PySide

This post participates in the contest "Smart phones for smart posts"


The web has a certain amount of information on the PySide project, but not a lot of Russian.

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

Introduction What is PySide?


PySide is a project to create a Qt binding to the Python programming language . It aims to implement support for all Qt features, including QtQuick and QtMobility . The following component versions 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 opportunity to use PySide on Symbian yet (or I don’t know this possibility), but according to the developers, work is being done in this direction. Alternatively, you can use PyS60, although it has nothing to do with Qt , so we will not talk further about it later.
')
The PySide project also contains a set of tools that allow you to bind any libraries written in C / C ++. In more detail we will talk about these tools further.

PySide is licensed under the LGPL, i.e. It can be used in both open and closed commercial projects. PySide source code is open and available at the following address 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


The download page provides instructions for installing PySide on various operating systems. But it may happen that for your Linux distribution, such a package is not available. We will assemble it ourselves, which is not at all difficult, since even here, the Qt developers took care of their users, and prepared a set of scripts that maximally automate the build operations of PySide .

So, to begin to clone a Git repository with build scripts. We don't need anything else yet, because dependencies will be cloned all the necessary projects.

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

You may have several versions of Qt installed on your system. To specify which version to use, edit the enviroment.sh file in which you list 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 have no difficulties. Immediately I warn you that PySide may not build up with the default Qt version installed on the system. I recommend using the latest QtSdk build.

Once everything is set up, build PySide with the command

 $ ./build_and_install 

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

To test the performance of the received 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 your favorite examples, 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, which is included in the QtSdk distribution. Let's collect PySide and for it. To do this, edit the QT_SDK_HOME variable in the enviroment.sh file, specifying the path to the QtSimulator ($ {YOUPATH} / QtSDK / Simulator / Qt / gcc) as the Qt home directory. You also need to edit the build_and_install file: you need to add the -DQ_WS_SIMULATOR = yes option to the cmake startup command. This option helps cmake determine the platform under which the compilation will take place.

If you start the build now, then at the moment it unfortunately will end in failure, because QtWebKit module cannot build. I created a report on this error to which I attached a small patch that fixes this problem. But as of this writing, this bug has not yet been fixed. Therefore, use this patch for a complete PySide build under QtSimulator (see the attachment to the bug report at the link above).



Nothing prevents us from collecting the QtMobility binding for the QtSimulator. Its assembly is no different from the assembly of PySide itself. QtMobility will be assembled and will even run in the simulator, but unfortunately, not one of the modules at the same time can be called a worker. None of the examples supplied with QtMobility itself, nor from the pyside-examples package, has earned in full . I will still understand the reasons for this and, perhaps, someday I'll write about it.



Introduction to programming on PySide. Signals and slots, property system


This section is not an introduction to the Python programming language . Here, 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 the class through which the connection of the signal and the slot is controlled, as well as the sending of the signal itself. The types of parameters transmitted with the signal are specified when it is created. It can be any type of C and Python . If we want to send parameters of a different type (as in the example above), the type must be tuple or list. As before, the signal must be declared within the class inherited from QObject.

If you need to create multiple signals, you can use the following 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 variable signals, which can be considered as a dictionary (not iterable). The signal parameters are used as the key.
 someone.signals.emit(10) #     , .. int someone.signals[str].emit("Hello everybody!") someone.signals[float, QtCore.QObject].emit(1.23, obj) 

A slot can be any class method that is not necessarily inherited from QObject, a global function, or even a lambda function. But such objects are not true slots, in QMetaObject there is no information about them, 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 

For slots, overload signal rules do not apply and we cannot create a slot accepting any type of parameters. For each type of parameter, a separate decorator should be added. 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 can take as additional parameters the name under which the generated signals and slots will be stored in object-specific information. By default, if the name is not specified, the signal is assigned the name of the class member to which it is assigned, and the name of the method to be decorated is assigned to the slot. A slot can also specify the type of return value. You can use similar functionality for Python and QML communication.

You can read more about the interaction between Python and QML at developer.qt.nokia.com in the Python section. You can also see examples of pyside-qml-examples .

Property system

Working with Qt properties is not much different from working with classic Python properties. Just give 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 

More details about working with Qt properties in Python can be read here .

Creating a GUI using PySide


The 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 graphic application framework in QtDesigner. The resulting form is compiled using pyside-ui, which as input parameters you need to specify the form file and the output file, through the option -o. 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 a form created in a 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 , many Qt goodies from recent years are available, including QtQuick. Those. we have the ability to create hybrid applications using Python and QML (QtQuick.Particles, Qt Desktop Components, etc.).

An example of creating your own binding


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

For example, we have a certain project using Qt Components . All main functionality is written in QML . But to launch it, you need the QmlDesktopViewer class already written in C ++. It will not be difficult for us to rewrite it in Python , but it 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 are going to export, hide, rename, etc. In general, what we will get access to in the resulting Python module.

 <?xml version="1.0"?> <!--  ,       --> <!--  package  ""         ()  --> <!--          PySide/QmlDesktopViewer --> <!-- c  qmldesktopviewer_module_wrapper.cpp  pyside_qmldesktopviewer_python.h --> <typesystem package="PySide.QmlDesktopViewer"> <load-typesystem name="typesystem_declarative.xml" generate="no" /> <!--      x11Event. --> <!-- ..  x11Event      Python --> <rejection class="*" function-name="x11Event" /> <!--  , ,   ..      --> <!--          --> <!--      PySide/QmlDesktopViewer      --> <!-- logwidget_wraper.cpp logwidget_wraper.h) --> <object-type name="LoggerWidget" /> <!-- qmldesktopviewer_wraper.cpp qmldesktopviewer_wraper.h) --> <object-type name="QmlDesktopViewer" /> </typesystem> 

I specifically described in detail what files we get at the output of the generator, since they are the ones to be compiled to get the Python module. Understanding this will make it easier to write the project file for the build system you are using. For more information on the rules for compiling this file, see the API Extractor documentation.

The next step is to create a header file for the C ++ language, which will extract information about the data for which the binding is made.

 #undef QT_NO_STL #undef QT_NO_STL_WCHAR #ifndef NULL #define NULL 0 #endif #include "pyside_global.h" #include <loggerwidget.h> #include <qmldesktopviewer.h> 

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

I use CMake as a build system. I will not describe the assembly here. The full sample code can be found 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 Python application. We use this module to launch one of the examples of Qt Desktop Components.

 #!/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 <qml file>" sys.exit() app = QApplication(sys.argv) viewer = QmlDesktopViewer() viewer.open(sys.argv[1]) sys.exit(app.exec_()) 



I also want to note that several bindings of third-party libraries already exist 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 the Developer Network
Running PySide applications on Qt Simulator
PySide Binding Generator
PySide Binding Generation Tutorial

Source: https://habr.com/ru/post/134457/


All Articles