Работа с PySide

    Этот пост участвует в конкурсе «Умные телефоны за умные посты»


    В Сети имеется некоторое количество информации по проекту PySide, но русскоязычной не так много.

    Данная статья является попыткой восполнить этот пробел. Далее будет описана сборка проекта, запуск приложений на эмуляторе, пример создания собственной привязки. Некоторые ключевые особенности PySide будут показаны на примерах.

    Введение. Что такое PySide?


    PySide — это проект создания привязки Qt к языку программирования Python. Он направлен на реализацию поддержки всех возможностей Qt, в том числе QtQuick и QtMobility. На данный момент поддерживаются следующие версии компонент и платформы
    Qt 4.6, 4.7, 4.8 betta
    QtMobility 1.2.0
    Python 2.5, 2.6, 2.7, 3.2 (experemental)
    OS Linux/X11, Maemo 5, MeeGo, Windows, Mac OS X

    Возможности использовать PySide на Symbian пока нет (или мне такая возможность не известна), но по заявлению разработчиков работа в этом направлении ведётся. В качестве альтернативы можете использовать PyS60, правда к Qt он никакого отношения не имеет, поэтому далее мы о нём говорить не будем.

    Проект PySide содержит также набор инструментов, которые позволяют создавать привязки любых библиотек написанных на С/С++. Подробнее об этих инструментах поговорим далее.

    PySide распространяется под лицензией LGPL, т.е. его можно использовать как в открытых, так и в закрытых, коммерческих проектах. Исходные коды PySide открыты и доступны по следующему адресу github.com/PySide. Так же, если Вы обнаружили какие то проблемы, связанные с PySide, можете сообщить о них на официальный баг трекер проекта.

    Сборка PySide, проверка работоспособности


    На странице загрузки доступна инструкция по установке PySide на различные ОС. Но может так оказаться, что для Вашего Linux дистрибутива, такой пакет недоступен. Соберем его самостоятельно, что совсем не сложно, т.к. даже здесь разработчики Qt позаботилась о своих пользователях, и подготовили набор скриптов, максимально автоматизирующих действия по сборке PySide.

    Итак, клонируем для начала Git репозиторий со скриптами сборки. Более нам ничего, пока, не нужно, т.к. по зависимостям будут клонированы все необходимые проекты.

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

    У Вас в системе может быть установлено несколько версий Qt. Чтобы указать какую именно версию использовать, отредактируйте файл enviroment.sh, в котором пропишите путь до домашней директории Qt и путь, куда будет установлен PySide. Файл enviroment.sh снабжен подробными комментариями, поэтому трудностей у Вас не должно возникнуть. Сразу предупреждаю, что с версией Qt, установленной в системе по умолчанию, PySide может не собраться. Я рекомендую использовать для сборки последнюю версию QtSdk.

    После того как всё настроено, собираем PySide командой

    $ ./build_and_install
    

    Для облегчения работы, файл enviroment.sh легко превращается в сценарий, при помощи которого запускаются приложения, использующие PySide. Всё что нужно — это сделать его исполняемым и добавить в конце python $@.

    Что бы проверить работоспособность полученного пакета, клонируем репозиторий с примерами Qt на Python, адаптированных для PySide

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

    и запускаем любой из понравившихся примеров, например Hyper UI



    Сейчас у нас готова сборка PySide под хостовую систему. Но при разработке может оказаться удобным запускать приложения в симуляторе, который входит в поставку QtSdk. Соберем PySide и для нее. Для этого отредактируйте переменную QT_SDK_HOME в файле enviroment.sh, указав в качестве домашней директории Qt путь до QtSimulator (${YOUPATH}/QtSDK/Simulator/Qt/gcc). Также необходимо отредактировать файл build_and_install: к команде запуска cmake нужно добавить опцию -DQ_WS_SIMULATOR=yes. Эта опция помогает cmake определить платформу под которую будет происходить компиляция.

    Если сейчас запустить сборку, то на данный момент она к сожалению закончится неудачей, т.к. не сможет собраться модуль QtWebKit. Я создал отчет об этой ошибке к которому приложил небольшой патч, который эту проблему исправляет. Но момент написания статьи, этот баг еще не исправлен. Поэтому воспользуйтесь этим патчем для полноценной сборки PySide под QtSimulator (см. вложение к баг-репорту по ссылке выше).



    Ничего не мешает нам собрать привязку QtMobility для QtSimulator. Его сборка ничем не отличается от сборки самого PySide. QtMobility соберётся и даже будет запускать в симуляторе, но не один из модулей при этом, к сожалению, нельзя назвать рабочим. В полном объеме не заработал ни один пример поставляемый с самим QtMobility, ни из пакета pyside-examples. С причинами этого еще буду разбираться и, возможно, когда-нибудь об этом напишу.



    Введение в программирование на PySide. Сигналы и слоты, система свойств


    Данный раздел не является введением в язык программирование Python. Здесь лишь будет показано как использовать некоторые ключевые возможности Qt на Python.

    Сигнал-слот взаимодействие

    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 — это класс, через который происходит управление соединением сигнала и слота, а так же отправка самого сигнала. Типы передаваемых с сигналом параметров указываются при его создании. Это может быть любой тип С и Python. Если мы хотим отправлять параметры разного типа (как в примере выше), в качестве типа должен быть указан tuple или list. Как и раньше, сигнал должен быть объявлен в пределах класса унаследованного от QObject.

    Если нужно создать несколько сигналов, можно воспользоваться следующей формой записи
    class Test(QtCore.QObject):
        signals = QtCore.Signal((int,), (str,), (float, QtCore.QObject))
    

    Этот код добавит три сигнала с сигнатурами signals(int), signals(QString) и signals(double,QObject*). Все они содержатся в переменной signals, который условно можно считать словарем (не итерабельным). В качестве ключа используются параметры сигнала.
    someone.signals.emit(10) #по умолчанию используется тип объявленный первым, т.е. int
    someone.signals[str].emit("Hello everybody!")  
    someone.signals[float, QtCore.QObject].emit(1.23, obj)
    

    В качестве слота может выступать любой метод класса, не обязательно унаследованного от QObject, глобальная функция или даже lambda-функция. Но подобные объекты не являются истинными слотами, в QMetaObject о них нет никакой информации, поэтому используйте такой подход с осторожностью. Например, если вы в качестве слота используете глобальную функцию, то вы не сможете получить в ней информацию о вызывающем объекте.

    Для того чтобы создать истинный слот, нужно воспользоваться декоратором Slot из модула QtCore. В коде это выглядит следующим образом:
    @QtCore.Slot(int)
    @QtCore.Slot(str)
    def mySlot(value):
        print value
    

    Для слотов не действуют правила сигналов по перегрузке и мы не можем создать слот принимающий любой тип параметров. Для каждого типа параметра должен быть добавлен отдельный декоратор. Выше приведен пример слота, который может принимать параметры типа int и QString.

    И класс QtCore.Signal и декоратор QtCore.Slot в качестве дополнительных параметров могут принимать имя под которыми создаваемые сигналы и слоты будут храниться в метообъектной информации. По умолчанию, если имя не задано, сигналу назначится имя члена класса которому он присваивается, а слоту назначается имя декорируемого метода. Слоту также можно задать тип возвращаемого значения. Использовать подобный функционал можно для связи Python и QML.

    Подробнее о взаимодействии Python и QML можно почитать на developer.qt.nokia.com в разделе Python. Так же можно посмотреть примеры pyside-qml-examples.

    Система свойств

    Работа со свойствами Qt мало чем отличается от работы с классическими свойствами Python. Просто приведу небольшой пример

    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
    

    Более подробно про работу со свойствами Qt в Python можно почитать здесь.

    Создание графического интерфейса с использованием PySide


    В пакет PySide Tools входят стандартные средства Qt для работы с ресурсами приложений, разработки «классического» графического интерфейса и локализации приложений. Это pyside-ui, pyside-rcc и pyside-lupdate. Работа с ними ничем не отличается от тех же самых пакетов для Qt/C++. Поэтому вы все так же имеете возможность создавать каркас графического приложения в QtDesigner. Полученная форма компилируется при помощи pyside-ui, которому в качестве входных параметров нужно указать файл формы и выходной файл, через опцию -o. У pyside-ui есть также дополнительная интересная опция -x которая добавляет в получаемый файл на Python код запуска приложения. Ниже приведен небольшой пример запуска приложения использующего форму созданную в дизайнере, ресурсы и локализацию

    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_())
    

    Также в PySide доступны многие вкусности Qt последних лет, в том числе QtQuick. Т.е. мы не лишены возможности создавать гибридные приложения с использованием Python и QML (QtQuick.Particles, Qt Desktop Components и т.д.).

    Пример создания собственной привязки


    Зная все выше описанное, мы можем создавать собственные приложения на Python использующие Qt, в том числе использующие QtQuick. Но как быть, если нам понадобится использовать библиотеку написанную на С/С++ с своем проекте или использовать свои предыдущие наработки сделанные на том же С/С++? Не переписывать же все заново на Python?

    К примеру, у нас есть некий проект использующий Qt Components. Весь основной функционал написан на QML. Но для его запуска нужен класс QmlDesktopViewer уже написанный на С++. Нам не составит большого труда переписать его на Python, но это было бы сейчас не интересно. Давайте сделаем собственную привязку это класса к Python.

    Для этого воспользуемся инструментами из проекта PySide для создания привязок. Это API Extractor, Shiboken и Generator Runner. Все эти инструменты у нас уже есть.

    Что бы создать привязку, нужно для начала создать xml файл, в котором описывается, какие данные мы ходим экспортировать, скрыть, переименовать и т.д. В общем то, до чего мы будем получать доступ в полученном модуле Python.

    <?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>
    

    Я специально подробно описал, какие файлы мы получим на выходе генератора, т.к. именно их предстоит скомпилировать, для получения модуля Python. Понимание этого облегчит написание файла проекта для используемой Вами системы сборки. Для более подробного ознакомления с правилами составления этого файла, смотрите документацию API Extractor.

    Следующим шагом мы должны создать заголовочный файл языка С++ по которому будет извлекаться информация о данных, для которых делается привязка.

    #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>
    

    Файл pyside_global.h мы получили во время сборки PySide. Можно скопировать его в проект или добавить путь до места его нахождения.

    В качестве системы сборки я использую CMake. Я не буду здесь описывать саму сборку. Полный код примера можно взять здесь. Можете воспользоваться им как примером для создания собственной привязки.

    Компилируем проект, и получаем библиотеку с оберткой над нашем классом, которую мы можем использовать в своем приложении на Python. Воспользуемся полученным модулем для запуска одного из примеров 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_())
    



    Также хочу отметить, что для PySide уже существует несколько привязок сторонних библиотек. Одна из них — это привязка Qwt.

    Используемые материалы


    PySide v1.0.8 documentation
    PySide Mobility v0.2.2 documentation
    New-style signal/slot
    PySide на Developer Network
    Running PySide applications on Qt Simulator
    PySide Binding Generator
    PySide Binding Generation Tutorial
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 14

      +4
      Думаю можно еще добавить отличия от PyQt4. Их немного, а некоторым все-таки будет полезно.
      P.S. Удачи вам на конкурсе!
        +1
        Спасибо за положительный отзыв.
        Отличий от PyQt4 действительно не много. Я не стал добавлять это в статью. Можно почитать про эти отличия здесь: Differences Between PySide and PyQt
        +4
        Главное отличие — PySide идет под LGPL лицензией.
        Если я не ошибаюсь, то это первопричина создания pyside.
          +1
          Да, Вы абсолютно правы. Об этом упоминают сами разработчики PySide в FAQ.
          О том, что PySide распространяется под лицензией LGPL, я упоминал в этой статье.

          P.S. к сожалению pyside.org на данный момент не доступен, не известно по каким причинам.
          +1
          Скажите а android PySlide не поддерживает?
          +1
          Официальную поддержку со стороны Нокии же хотели прикрыть проекту по известным причинам. Он сейчас силами сообщества пилится или всё-таки нокией?
            +1
            Вы застали меня врасплох, у меня нет никакой информации по этому поводу. И я в первые слышу, что Nokia хотела отказаться от проекта. От Qt Nokia не отказывается. Надеюсь и от PySide не откажется. Хотя еще во время написания статьи меня несколько напряг факт того, что основной рипозиторий проекта находится на github, а не на привычном qt.gitorious.
              +1
              Получил ответ на ваш вопрос. Цитирую: «The PySide is only supported by the INdT team currently.»

                +2
                Кажется я поторопится, опубликовав сюда первый же ответ. Тема получила развитие, и я получил более развернутый ответ. Процитирую и его тоже.
                Hi Igor,

                I better clarify Paulo's quite confused answers. :-)

                The PySide core dev team work is paid by Nokia until the end of this year. After that, INdT has had plans to continue the work, but I don't know the details, and anyway, it's better they speak for themselves.

                I have been working with Qt people to make PySide a Qt add-on to give the project a permanent home after the MeeGo team funding comes to an end. While progress is being made, it's slow, and it might be that the migration won't be finished before the end of the year. But it's happening, anyway.

                Cheers, ma.

                Кому интересно, можете последить за перепиской по ссылке, хотя все ответы уже вроде бы даны.
                +1
                Надо будет в рассылке поинтересоваться.
                  0
                  Ничего не мешает нам собрать привязку QtMobility для QtSimulator. Его сборка ничем не отличается от сборки самого PySide. QtMobility соберётся и даже будет запускать в симуляторе, но не один из модулей при этом, к сожалению, нельзя назвать рабочим. В полном объеме не заработал ни один пример поставляемый с самим QtMobility, ни из пакета pyside-examples. С причинами этого еще буду разбираться и, возможно, когда-нибудь об этом напишу.

                  Немного поразбирался с причинами. Модуль QtMobility вполне работоспособен на QtSimulator. Проблема именно в самих примерах. Их как написали когда то, так больше и не обновляли. Правятся они достаточно легко, достаточно посмотреть traceback.


                    0
                    Начал осваивать pyside. Возник вопрос из любопытства, а есть возможность делать приложение для iOS при помощи python? Насколько я понял, это QtMobility?

                    Only users with full accounts can post comments. Log in, please.