Приложение на PySide6#
Пример собирает приложение на Python и Qt6 и доводит его до установленного Flatpak. Программа пишется здесь же, PySide6 берётся из базового приложения, поэтому пример повторяется целиком.
PySide6 нет в репозиториях Astra, поэтому связку Python и Qt6 поставляет
базовое приложение org.astra.PySide6.BaseApp. Оно собрано на среде
org.astra.Qt6 версии 1.8.6 и её SDK.
Что даёт базовое приложение#
В /app базовое приложение кладёт:
модули Python в
/app/lib/python3/dist-packages: PySide6, shiboken6, numpy, PyOpenGL, packaging;инструменты в
/app/bin:pyside6-lrelease,pyside6-designer,pyside6-genpyiи остальные;скрипт очистки
/app/cleanup-BaseApp.sh, который убирает из готовой сборки то, что нужно только при сборке.
Посмотреть состав после установки:
sudo flatpak install astra-extended org.astra.PySide6.BaseApp//1.8.6
ls /var/lib/flatpak/app/org.astra.PySide6.BaseApp/x86_64/1.8.6/active/files/lib/python3/dist-packages
Чем собирать#
Базовое приложение подключается ключом base, который понимает
flatpak-builder. У aft-app такого поля нет, поэтому приложения на PySide6
собираются flatpak-builder. Про сам инструмент - в блоке
Сборка с помощью flatpak-builder.
Подготовка:
sudo flatpak install astra-main org.astra.Qt6//1.8.6
sudo flatpak install astra-extended org.astra.Qt6Sdk//1.8.6
sudo flatpak install astra-extended org.astra.PySide6.BaseApp//1.8.6
Дерево примера#
pyapp/
|-- ru.example.PyApp.yml
|-- pyapp.sh
|-- ru.example.PyApp.desktop
|-- ru.example.PyApp.metainfo.xml
|-- icon.png
`-- pyapp/
|-- __init__.py
`-- __main__.py
Файл icon.png - любой PNG размером 128 на 128. Файл __init__.py
пустой: он нужен только для того, чтобы каталог считался пакетом Python.
Исходный код#
pyapp/__main__.py:
import sys
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QLabel
def main() -> int:
app = QApplication(sys.argv)
label = QLabel("PySide6 внутри Flatpak")
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
label.resize(360, 120)
label.setWindowTitle("PySide6 Hello")
label.show()
return app.exec()
if __name__ == "__main__":
sys.exit(main())
pyapp.sh:
#!/bin/sh
exec python3 -m pyapp "$@"
ru.example.PyApp.desktop:
[Desktop Entry]
Type=Application
Name=PyApp
Name[ru]=Пример на PySide6
Comment=Минимальное приложение на PySide6
Exec=pyapp
Icon=ru.example.PyApp
Terminal=false
Categories=Utility;
ru.example.PyApp.metainfo.xml:
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>ru.example.PyApp</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>MIT</project_license>
<name>PyApp</name>
<summary>Минимальное приложение на PySide6</summary>
<description>
<p>Пример приложения на Python и Qt6 для проверки сборки во Flatpak.</p>
</description>
<launchable type="desktop-id">ru.example.PyApp.desktop</launchable>
</component>
Манифест#
ru.example.PyApp.yml:
id: ru.example.PyApp
base: org.astra.PySide6.BaseApp
base-version: '1.8.6'
runtime: org.astra.Qt6
runtime-version: '1.8.6'
sdk: org.astra.Qt6Sdk
command: pyapp
finish-args:
- --share=ipc
- --socket=wayland
- --socket=fallback-x11
- --device=dri
- --filesystem=xdg-documents
- --env=PYTHONPATH=/app/lib/python3/dist-packages
cleanup-commands:
- /app/cleanup-BaseApp.sh
modules:
- name: pyapp
buildsystem: simple
build-commands:
- install -Dm0644 -t ${FLATPAK_DEST}/lib/python3/dist-packages/pyapp
pyapp/__init__.py pyapp/__main__.py
- install -Dm0755 pyapp.sh ${FLATPAK_DEST}/bin/pyapp
- install -Dm0644 ru.example.PyApp.desktop
${FLATPAK_DEST}/share/applications/${FLATPAK_ID}.desktop
- install -Dm0644 icon.png
${FLATPAK_DEST}/share/icons/hicolor/128x128/apps/${FLATPAK_ID}.png
- install -Dm0644 ru.example.PyApp.metainfo.xml
${FLATPAK_DEST}/share/metainfo/${FLATPAK_ID}.metainfo.xml
sources:
- type: dir
path: ./pyapp
dest: pyapp
- type: file
path: ./pyapp.sh
- type: file
path: ./ru.example.PyApp.desktop
- type: file
path: ./ru.example.PyApp.metainfo.xml
- type: file
path: ./icon.png
Переменная PYTHONPATH обязательна. Интерпретатор из среды выполнения ищет
модули в системных каталогах и про /app/lib/python3/dist-packages сам не
знает, поэтому без неё не найдётся ни PySide6 из базового приложения, ни код
самого приложения.
Код приложения кладётся туда же, где лежат модули базового приложения, поэтому одной переменной хватает на всё.
Сборка и установка#
cd pyapp
flatpak-builder --force-clean --repo=./repo --default-branch=stable \
build-dir ru.example.PyApp.yml
sudo flatpak remote-add --if-not-exists --no-gpg-verify local-repo ./repo
sudo flatpak install -y local-repo ru.example.PyApp
flatpak run ru.example.PyApp
Очистка сборки#
Вызов cleanup-BaseApp.sh в cleanup-commands выбрасывает из готовой
сборки инструменты сборки PySide6, документацию и вспомогательные модули
Python, которые нужны были только при сборке. Без него приложение уносит с
собой десятки мегабайт, которые ему не нужны.
Скрипт понимает переменные окружения сборки. Если приложение не использует numpy или WebEngine, их удаление заметно сокращает результат:
build-options:
env:
BASEAPP_DISABLE_NUMPY: "1"
BASEAPP_REMOVE_WEBENGINE: "1"
Убирать WebEngine можно только тогда, когда приложение не импортирует
PySide6.QtWebEngineWidgets: иначе оно перестанет запускаться.
Тонкости PySide6#
Версия среды и версия базового приложения совпадают. PySide6 собран против конкретного Qt6, подставить другую версию среды не получится.
Свои зависимости Python ставятся в тот же каталог, что и модули базового приложения. Скачивать их во время сборки нельзя: сборочная песочница flatpak-builder работает без доступа в сеть, поэтому пакеты приносятся источниками манифеста и ставятся из локальных файлов.
Трансляции Qt готовятся на этапе сборки. Инструмент pyside6-lrelease есть в
базовом приложении, но после очистки в готовой сборке его не остаётся, поэтому
файлы .qm собираются до вызова очистки, а не при запуске.
Диалоги открытия файлов идут через портал, поэтому доступ ко всему домашнему каталогу приложению не нужен.