Напишите собственные привязки к Python

C++, Qt5, bind, Python

Сегодня мы взглянем, как вы можете создавать привязки для собственного проекта.

В Qt Company рады сообщить, что Qt для Python будет также включать в себя Shiboken  - ваш основной инструмент создания привязки.

Прочтите материал ниже, и вы получите представление о том, как создавать привязки Python для простой библиотеки C ++.

Будем надеяться, это способствует тому, что вы сделаете тоже самое с собственными пользовательскими библиотеками.

Как и в любом проекте Qt мы рады представить статьи по Shiboken, тем самым улучшая его понимание для всех.

Пример библиотеки

Основными целями этого поста  будет использование нами немного бессмысленной пользовательской библиотеки под названием Universe. Она предусматривает два класса: Icecream (мороженое) и Truck (грузовик).

Icecreams характеризуется вкусом, а Truck служит, как средство доставки Icecream для детей в окрестности. Довольно просто.

Мы хотели бы использовать эти классы внутри Python. Вариант использования – добавление дополнительных вкусов мороженного или проверка успешности доставки мороженного. Говоря простыми словами, мы хотим обеспечить привязки Python для Icecream и Truck, чтобы мы могли использовать их в собственном скрипте Python.

Мы пропустим некоторую часть для краткости, но вы можете посмотреть в репозитории полный исходный код. Ссылка на репозиторий pyside-setup/examples/samplebinding .

C++ библиотека

Во-первых, давайте взглянем на заголовочный файл Icecream

class Icecream
{
public:
    Icecream(const std::string &flavor);
    virtual Icecream *clone();
    virtual ~Icecream();
    virtual const std::string getFlavor();
 
private:
    std::string m_flavor;
};

и заголовочный файл Truck

class Truck {
public:
    Truck(bool leaveOnDestruction = false);
    Truck(const Truck &other);
    Truck& operator=(const Truck &other);
    ~Truck();
 
    void addIcecreamFlavor(Icecream *icecream);
    void printAvailableFlavors() const;
 
    bool deliver() const;
    void arrive() const;
    void leave() const;
 
    void setLeaveOnDestruction(bool value);
    void setArrivalMessage(const std::string &message);
 
private:
    void clearFlavors();
 
    bool m_leaveOnDestruction = false;
    std::string m_arrivalMessage = "A new icecream truck has arrived!\n";
    std::vector m_flavors;
};

Большинство API должны быть достаточно просты для понимания, но мы подведем итоги важнейших частей:

  • Icecream является полиморфным типом и предназначается для переопределения
  • getFlavor() вернет вкус в зависимости от фактического производного типа
  • Truck сохраняет вектор принадлежащих ему объектов Icecream, которые могут быть добавлены через addIcecreamFlavor()
  • Сообщение прибытия «грузовика» можно настроить с помощью setArrivalMessage()
  • deliver() скажет нам, была ли доставка «мороженого» успешной или нет

Система типов Shiboken

Чтобы информировать shiboken об API, мы нуждаемся в привязках, для этого мы делаем файл-заголовок, который включает в себя интересующие нас типы:

#ifndef BINDINGS_H
#define BINDINGS_H
#include "icecream.h"
#include "truck.h"
#endif // BINDINGS_H

Кроме того, shiboken также требуется XML typesystem файл, который определяет отношение между типами C++ и Python:

<?xml version="1.0"?>
<typesystem package="Universe">
    <primitive-type name="bool"/>
    <primitive-type name="std::string"/>
    <object-type name="Icecream">
        <modify-function signature="clone()">
            <modify-argument index="0">
                <define-ownership owner="c++"/>
            </modify-argument>
        </modify-function>
    </object-type>
    <value-type name="Truck">
        <modify-function signature="addIcecreamFlavor(Icecream*)">
            <modify-argument index="1">
                <define-ownership owner="c++"/>
            </modify-argument>
        </modify-function>
    </value-type>
</typesystem>

Первая важная вещь на которую нужно обратить внимание – то, что мы объявляем "bool" и "std::string", как примитивные типы. Некоторые из методов C++ используют их как параметр / возвращаемые типы данных, и поэтому shiboken должен знать о них. Затем он может генерировать соответствующий код преобразования между C ++ и Python.

Большинство примитивных типов C ++ обрабатываются shiboken, не требуя дополнительного кода. Затем мы объявляем два вышеупомянутых класса. Один из них как “object-type”(объект-тип), а другой как “value-type” (значение-объект).

Основное отличие состоит в том, что “object-type” передаются в сгенерированном коде, как указатели, тогда как “value-type” копируются (семантика значений).

Задав имена классов в файле системы типов, shiboken автоматически попытается создать привязки для всех методов, объявленных в классах, поэтому нет необходимости указывать все имена методов вручную...

Если вы не хотите каким-либо образом изменить функцию, то это приводит нас к следующей теме «Правилам владения».

Shiboken не может волшебным образом узнать, кто отвечает за освобождение объектов C++, размещенных в коде Python’а.

Может быть множество случаев: Python должен освободить память C ++, когда количество ссылок на объект Python становится равным нулю или Python никогда не должен удалять объект C++, допуская, что он будет удален в определенный момент внутри библиотеки C ++. Или, возможно, это родительский элемент для другого объекта (например, как в QWidgets). В нашем случае метод clone() вызывается только внутри библиотеки C++, и мы предполагаем, что код C++ позаботится о освобождении памяти клонированного объекта.

Что касается addIcecreamFlavor(), мы знаем, что Truck располагается в объекте Icecream, и будет удален сразу же после уничтожения Truck. Таким образом, вновь, право владения устанавливается на “c++.”

Если бы мы не указали правила владения, то в этом случае объекты C++ будут удалены, когда соответствующие имена Python выйдут за пределы области видимости.

Сборка

Чтобы собрать пользовательскую библиотеку Universe и затем сгенерировать для нее привязки, мы предоставляем хорошо документированный, в основном общий файл CMakeLists.txt, который вы можете повторно использовать для своих собственных библиотек.

В основном это сводится к вызову «cmake.», чтобы настроить проект, а затем построить с помощью цепочки инструментов по вашему выбору (мы рекомендуем генератор «(N) Makefiles»).

В результате создания проекта вы получаете две общие библиотеки: libuniverse. (so/dylib/dll) и Universe. (so/pyd).

Первая – это пользовательская библиотека C++ и последняя – это модуль Python, который может быть импортирован из скрипта (документ, тестовый драйвер) Python.

Конечно, есть также промежуточные файлы, созданные shiboken (файлы .h / .cpp, созданные для создания привязок Python). Не волнуйтесь о них, если вам не нужно устранять неполадки или по какой-либо причине не удается скомпилировать или не ведет себя так, как должно. Тогда вы можете отправить Qt Company отчет об ошибке!

И, наконец, мы добираемся до части Python.

Использование Python модуля

В следующем маленьком скрипте мы будем использовать модуль Universe, наследуемся от Icecream, внедрим виртуальные методы, инстанцируем объекты и многое другое

from Universe import Icecream, Truck
 
class VanillaChocolateIcecream(Icecream):
    def __init__(self, flavor=""):
        super(VanillaChocolateIcecream, self).__init__(flavor)
 
    def clone(self):
        return VanillaChocolateIcecream(self.getFlavor())
 
    def getFlavor(self):
        return "vanilla sprinked with chocolate"
 
class VanillaChocolateCherryIcecream(VanillaChocolateIcecream):
    def __init__(self, flavor=""):
        super(VanillaChocolateIcecream, self).__init__(flavor)
 
    def clone(self):
        return VanillaChocolateCherryIcecream(self.getFlavor())
 
    def getFlavor(self):
        base_flavor = super(VanillaChocolateCherryIcecream, self).getFlavor()
        return base_flavor + " and a cherry"
 
if __name__ == '__main__':
    leave_on_destruction = True
    truck = Truck(leave_on_destruction)
 
    flavors = ["vanilla", "chocolate", "strawberry"]
    for f in flavors:
        icecream = Icecream(f)
        truck.addIcecreamFlavor(icecream)
 
    truck.addIcecreamFlavor(VanillaChocolateIcecream())
    truck.addIcecreamFlavor(VanillaChocolateCherryIcecream())
 
    truck.arrive()
    truck.printAvailableFlavors()
    result = truck.deliver()
 
    if result:
        print("All the kids got some icecream!")
    else:
        print("Aww, someone didn't get the flavor they wanted...")
 
    if not result:
        special_truck = Truck(truck)
        del truck
 
        print("")
        special_truck.setArrivalMessage("A new SPECIAL icecream truck has arrived!\n")
        special_truck.arrive()
        special_truck.addIcecreamFlavor(Icecream("SPECIAL *magical* icecream"))
        special_truck.printAvailableFlavors()
        special_truck.deliver()
        print("Now everyone got the flavor they wanted!")
        special_truck.leave()

После импортирования классов из нашего модуля, мы создаем два производных (вторичных) типа Icecream, которым настроили “вкусы”.

Затем мы создаем truck (грузовик), добавляем к нему некоторые обычные варианты (разновидности) Icecreams (мороженого) и два специальных.

Мы стараемся отправить (доставить) Icecream (мороженое).

Если же доставка не удалась, мы создаем новый truck (грузовик) со старыми вариантами, скопированными, и новый *волшебный* вариант, который, несомненно, удовлетворит всех клиентов.

В приведенном выше скрипте кратко показано использование выводов из типов C++, переопределение виртуальных методов, создание и уничтожение объектов и т. д.

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.
Support the author Donate

Comments

Only authorized users can post comments.
Please, Log in or Sign up
How to become an author?

Contribute to the evolution of the EVILEG community.

Learn how to become a site author.

Learn it
Donate

Good day, Dear Users!!!

I am Evgenii Legotckoi, developer of EVILEG. And it is my hobby project, which helps to learn programming another programmers and developers

If the site helped you, and you want also support the development of the site, than you can donate by following ways

PayPalYandex.Money
Timeweb

Let me recommend you the excellent hosting on which EVILEG is located.

For many years, Timeweb has been proving his stability.

For projects on Django I recommend VDS hosting

View Hosting Timeweb
June 6, 2020, 12:20 a.m.
Aleksej

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:60points,
  • Rating points-1
June 6, 2020, 12:15 a.m.
Aleksej

C++ - Test 001. The first program and data types

  • Result:53points,
  • Rating points-4
V
June 5, 2020, 5:47 p.m.
Vladzo

C++ - Test 005. Structures and Classes

  • Result:83points,
  • Rating points4
Last comments
June 5, 2020, 11:52 a.m.
progammist

Распознавание изображений на Python с помощью TensorFlow и Keras

Огромное спасибо за метериал, по-больше бы подобных статей (с подробным описанием работы и примерами применения) на тему современных технологий. Вопрос поразмышлять. На текущий момент реал…
June 5, 2020, 2:39 a.m.
Evgenij Legotskoj

Qt/C++ - Tutorial 091. How to write a custom delegate controlling the highlighting of a row in a table

По-моему, смысла в этом нет особого. Если делегат будет игнорировать настройки таблицы, то это приведёт ещё к большему непониманию, что вообще происходит, для программиста, который после вас буд…
June 5, 2020, 2:34 a.m.
IscanderChe

Qt/C++ - Tutorial 091. How to write a custom delegate controlling the highlighting of a row in a table

Сижу, размышляю: можно ли переписать делегата так, чтобы независимо от настроек строк выделялись строки?
June 5, 2020, 2:31 a.m.
Evgenij Legotskoj

Qt/C++ - Tutorial 091. How to write a custom delegate controlling the highlighting of a row in a table

Понятно. Я не обратил внимания на то, что там было в старом коде по настройкам строк :)
Now discuss on the forum
s
June 6, 2020, 2:54 a.m.
shuric

Qt/C++ Определение положения курсора над действие(кнопкой) в QToolBar

Доброго дня. Возник вопрос - как можно определить что курсор находится над определенным действием(кнопкой) в qtoolbar ? mainwindow.cpp MainWindow::MainWi…
s
June 6, 2020, 1:45 a.m.
shuric

Qt/C++ особенности QProxyStyle

Да, Вы правы. Код был скопирован с сайта (уже не помню с какого), но решил пойти по пути более легком. Пришлось переписать - кому интересно: использовал stackedWidget для пе…
June 6, 2020, 12:08 a.m.
Aleksej

Посоветуйте новичку (базы данных и Qt, что учить)

Блин, а я недавно купил Шлее Qt 5.10 :( С детства хотел стать программистом, баловался Паскалем, писал простенькие программки на Delphi, создавал движок на php, изучал C (забросил и перешел на п…
June 5, 2020, 2:09 p.m.
IscanderChe

QPlainTextEdit настройка цвета фона

Вечер добрый. Пытаюсь настроить цвет фона QPlainTextEdit следующим образом: CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent){ ... QPalette::ColorRole role = bac…
June 5, 2020, 7:13 a.m.
IscanderChe

Фильтр для QtableView sql

Добрый день. Для такой фильтрации необходимо использовать QSortFilterProxyModel. В оффдоках есть хороший пример.
About
Services
© EVILEG 2015-2020
Recommend hosting TIMEWEB