Evgenii Legotckoi
Evgenii Legotckoi18 липня 2018 р. 03:14

Напишіть власні прив'язки до 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++, перевизначення віртуальних методів, створення та знищення об'єктів тощо.

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 01:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 01:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 11:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 14:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 06:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 03:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 09:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Слідкуйте за нами в соціальних мережах