Наверняка, каждый, кто хоть раз писал что-то на Python, задумывался о том, как распространять свою программу (или, пусть даже, простой скрипт) без лишней головной боли: без необходимости устанавливать сам интерпретатор, различные зависимости, кроссплатформенно, чтобы одним файлом-exe’шником (на крайний случай, архивом) и минимально возможного размера.
Для этой цели существует немало инструментов: PyInstaller, cx_Freeze, py2exe, py2app, Nuitka и многие другие… Но что, если вы используете в своей программе PyQt? Несмотря на то, что многие (если не все) из выше перечисленных инструментов умеют упаковывать программы, использующие PyQt, существует другой инструмент от разработчиков самого PyQt под названием pyqtdeploy. К моему несчастью, я не смог найти ни одного вменяемого гайда по симу чуду, ни на русском, ни на английском. На хабре и вовсе, если верить поиску, есть всего одно упоминание, и то — в комментариях (из него я и узнал про эту утилиту). К сожалению, официальная документация написана довольно поверхностно: не указан ряд опций, которые можно использовать во время сборки, для выяснения которых мне пришлось лезть в исходники, не описан ряд тонкостей, с которыми мне пришлось столкнуться.
Данная статья не претендует на всеобъемлющее описание pyqtdeploy и работы с ним, но, в конце концов, всегда приятно иметь все в одном месте, не так ли?
Все началось с того, что мне захотелось один мой проект запихнуть в исполняемый файл со всеми зависимостями (вы и сами уже догадались). Сначала я решил попробовать провернуть эту операцию с помощью PyInstaller — шикарный инструмент, простой, хорошо документированный. Но на выходе я получил папку размером 170 МБ (для сравнения, весь PyQt5 весил около 180 МБ). Поковырявшись в собранных либах, я понял, что используемые мной модули — QtCore, QtGui, QtWidgets — тащат с собой почти весь пакет. Попытки поиграться с опцией --exclude-module
не увенчались успехом. Справедливости ради, если использовать опцию --onefile
и включить сжатие, то получится файл размером 60 МБ, что все равно много. К тому же, во время запуска происходит разархивирование программы во временную папку, что увеличивает время старта и все равно (пусть и где-то там) отжирает все те же 170 МБ.
Тут мне подвернулся pyqtdeploy. «Утилита от самих разработчиков PyQt… Ну уж они-то должны знать, как по-максимуму отвязаться от лишних зависимостей внутри PyQt и Qt?» — подумал я и взялся плотненько за сей агрегат.
Так что же такое pyqtdeploy? В первом приближении, то же самое, что и выше перечисленные программы. Все ваши модули (стандартная библиотека, PyQt, все прочие модули) упаковываются средствами Qt (используется утилита rcc) в так называемый файл ресурсов, генерируется обертка вокруг питоновского интерпретатора на C++, позволяющая получать доступ ко все вашим модулям, и потом все это пакуется/компилируется/… в исполняемый файл. Для работы самого pyqtdeploy нужны Python 3.5+ и PyQt5. Перечислим несколько особенностей (за подробностями сюда и сюда):
- может собирать exe’шники на основе PyQt4 и PyQt5, Python 2.7 и Python 3.3+ (максимальная поддерживаемая версия на данный момент Python 3.7.2);
- позволяет статически (все пихаем в exe’шник) и динамически привязывать зависимости (использовать уже установленные в системе библиотеки, пакеты — с рядом ограничений);
- поддерживаемые платформы:
- android-32;
- android-64;
- ios-64;
- linux-64;
- macos-64;
- win-32;
- win-64;
- также позволяет собирать несвязанные с PyQt и Qt программы, но из-за тесной интеграции с QtCore, будет тянуть оттуда кое-что в качестве зависимостей.
Установка pyqtdeploy
Как уже было сказано выше, у нас должен быть установлен Python 3.5+ и PyQt5:
pip install PyQt5 pyqtdeploy
Сборка нашего exe’шника состоит из нескольких этапов:
- Разработка нашей Python-программы, как обычно (сюрприз!);
- Сборка так называемого sysroot для нашей платформы, где будут лежать собранные из исходников нужные зависимости;
- Создание «проектного» файла с расширением .pdy, где будет вся необходимая информация для сборки нашего exe’шника (пути к собранным Qt, PyQt, Python, прочим библиотекам и модулям и другие опции);
- Собственно сборка exe’шника с помощью qmake.
Структура программы
Возьмем в качестве примера проект со следующей структурой: main.py — «точка входа» для нашей программы, она вызывает mainwindow.py — допустим, отрисовывает окошечко с виджетами и берет из resources иконку icon.png и mainwindow.ui, сгенерированный нами с помощью Qt Designer. Имеющиеся зависимости, версии библиотек и прочие необходимые вещи будут всплывать по ходу повествования:
main.py
src/
|---__init__.py
|---gui/
|---mainwindow.py
|---__init__.py
|---resources/
|---__init__.py
|---images/
|---icon.png
|---__init__.py
|---ui/
|---mainwindow.ui
|---__init__.py
Обзор плагинов sysroot (документация)
Как уже было сказано ранее, на этом этапе мы собираем все необходимые части, которые затем будут использоваться при генерации исполняемого файла. Данный процесс осуществляется с использованием конфигурационного файла sysroot.json (в принципе, вы можете назвать его как хотите и указать затем путь к нему). Он состоит из блоков, каждый из которых описывает сборку отдельного компонента (Python, Qt и т.д.). В pyqtdeploy реализован API, позволяющий вам написать свой плагин, управляющий сборкой необходимой вам библиотеки/модуля/whatever, если он еще не реализован разработчиками pyqtdeploy. Давайте пробежимся по стандартным плагинам и их параметрам (примеры из документации):
openssl (не обязательный) — позволяет собирать из исходников или использовать установленную в системе библиотеку (подробности). Компонент, описывающий данный плагин в sysroot.json, выглядит следующим образом:
"android|macos|win#openssl": {
"android#source": "openssl-1.0.2r.tar.gz",
"macos|win#source": "openssl-1.1.0j.tar.gz",
"win#no_asm": true
}
Первое, на что следует обратить внимание, это синтаксис: arch1|arch2|...#plugin-name
. То есть мы можем выбрать, на какой платформе использовать этот плагин (ios, android, macos, win, linux), а на какой — нет. Более того, этот синтаксис применим и к параметрам внутри блока.
Параметры:
- source (обязательный) — имя архива с исходниками;
- no_asm (не обязательный) — выключаем ассемблерные оптимизации. Если включен, в PATH должен быть установлен nasm;
- python_source (не обязательный) — имя архива, содержащего патчи, необходимые для сборки OpenSSL под macOS для Python v3.6.4 и более ранних версий;
zlib (не обязательный) — используется при сборке других компонентов (если не указан, по идее, будет использоваться тот, что установлен в системе) (подробности):
"ios|linux|macos|win#zlib": {
"source": "zlib-1.2.11.tar.gz",
"static_msvc_runtime": true
}
Параметры:
- source (обязательный) — очевидно, имя архива с исходниками;
- static_msvc_runtime (не обязательный) — статически привязать MSVC библиотеки (Windows);
qt5 (обязательный) — тут понятно (подробности):
"qt5": {
"android-32#qt_dir": "android_armv7",
"android-64#qt_dir": "android_arm64_v8a",
"ios#qt_dir": "ios",
"linux|macos|win#source": "qt-everywhere-src-5.12.2.tar.xz",
"edition": "opensource",
"android|linux#ssl": "openssl-runtime",
"ios#ssl": "securetransport",
"macos|win#ssl": "openssl-linked",
"configure_options": [
"-opengl", "desktop", "-no-dbus", "-qt-pcre"
],
"skip": [
"qtactiveqt", "qtconnectivity", "qtdoc", "qtgamepad",
...
],
"static_msvc_runtime": true
}
Параметры:
- qt_dir (не обязательный, если указан source) — путь к папке с установленным Qt;
- source (не обязательный, если указан qt_dir) — имя архива с исходниками Qt;
- edition (обязательный, если указан source) — один из 2 вариантов:
- commercial;
- opensource;
- ssl — 3 возможных варианта:
- openssl-linked — будет собран из исходников (подробности должны быть указаны в описании компонента openssl);
- securetransport — используется SSL, реализованный в Qt (который, в свою очередь, будет использовать Apple’s Secure Transport);
- openssl-runtime — используется версия OpenSSL, установленная в системе;
- configure_options — дополнительные опции, используемые при сборке Qt. Существует их целая прорва, смотрим тут;
- skip — позволяет исключить из сборки ненужные модули (точнее говоря, top-level директории, содержащие модули). Открываем архив с исходниками Qt и видим папки, начинающиеся с qt — это и есть top-level директории. Имейте в виду, что эти папки могут содержать и те модули, что вам нужны. К сожалению, можно скипнуть только top-level директорию целиком (подробности);
- disabled_features — позволяет исключить выбранный функционал. Для просмотра всех возможных фич можно воспользоваться командой
configure -list-features
(подробности) - static_msvc_runtime (не обязательный) — статически привязать MSVC библиотеки (Windows);
python (обязательный) — тут тоже понятно (подробности):
"python": {
"build_host_from_source": false,
"build_target_from_source": true,
"source": "Python-3.7.2.tar.xz"
}
Параметры:
- build_host_from_source (обязательный) — true — собираем Python для хоста из исходников, false — используем установленный Python (не поддерживается для win32);
- build_target_from_source (обязательный) — true — собираем Python для целевой платформы из исходников, false — используем установленный Python (использование установленного Python поддерживается только на win32);
- source (обязательный, если Python собирается из исходников) — имя архива с исходниками Python;
- version (обязательный, если используется установленный Python) — версия установленного Python;
- dynamic_loading (не обязательный) — true — включить поддержку динамической загрузки модулей расширения (тех, что на C);
- host_installation_bin_dir (не обязательный) — путь к установленному Python, если не собирается из исходников (если не указан, на win ищется в реестре автоматически, на других платформах — в PATH);
sip (обязательный) — компонент, отвечающий за автоматическое генерирование Python-bindings для C/C++ библиотек (подробности тут и тут):
"sip": {
"module_name": "PyQt5.sip",
"source": "sip-4.19.15.tar.gz"
}
Параметры:
- module_name (обязательный) — имя sip-модуля;
- source (обязательный) — имя архива с исходниками sip;
pyqt5 (обязательный) — тут тоже понятно (подробности):
"pyqt5": {
"android#disabled_features": [
"PyQt_Desktop_OpenGL", "PyQt_Printer", "PyQt_PrintDialog",
"PyQt_PrintPreviewDialog", "PyQt_PrintPreviewWidget"
],
"android#modules": [
"QtCore", "QtGui", "QtNetwork", "QtPrintSupport", "QtWidgets",
"QtAndroidExtras"
],
"ios#disabled_features": [
"PyQt_Desktop_OpenGL", "PyQt_MacOSXOnly",
...
],
"ios|macos#modules": [
"QtCore", "QtGui", "QtNetwork", "QtPrintSupport", "QtWidgets",
"QtMacExtras"
],
"linux#modules": [
"QtCore", "QtGui", "QtNetwork", "QtPrintSupport", "QtWidgets",
"QtX11Extras"
],
"win#disabled_features": ["PyQt_Desktop_OpenGL"],
"win#modules": [
"QtCore", "QtGui", "QtNetwork", "QtPrintSupport", "QtWidgets",
"QtWinExtras"
],
"source": "PyQt5_*-5.12.1.tar.gz"
}
Параметры:
- disabled_features (не обязательный) — позволяет выключить конкретный функционал. Если не указан, выключаемые фичи определяются автоматически на основе фич, выключенных в собранном нами Qt (подробности);
- modules (обязательный) — перечисляем модули, которые мы хотим собрать (подробности);
- source (обязательный) — имя архива с исходниками PyQt;
pyqt3D, pyqtchart, pyqtdatavisualization, pyqtpurchasing, qscintilla (не обязательные) — дополнительные модули, не входящие в состав PyQt. Имеют единственный параметр source
— имя архива с исходниками.
Стоит заметить, что некоторые значения параметров могут не работать друг с другом. В таких случаях вы получите ошибку при сборке sysroot с информацией, что не так. Я постарался здесь описать такие случаи, по крайней мере, для обязательных компонентов.
Собираем sysroot
Давайте взглянем на итоговый sysroot.json для нашей программы:
{
"linux#zlib": {
"source": "zlib-1.2.11.tar.gz"
},
"linux#qt5": {
"source": "qt-everywhere-src-5.12.2.tar",
"edition": "opensource",
"configure_options": [
"-no-dbus", "-no-system-proxies", "-no-cups", "-no-sql-db2",
"-no-sql-ibase", "-no-sql-mysql", "-no-sql-sqlite",
"-no-sql-sqlite2", "-no-sql-oci", "-no-sql-odbc",
"-no-sql-psql", "-no-sql-tds", "-no-sqlite", "-ccache",
"-optimize-size"
],
"skip": [
"qt3d", "qtactiveqt", "qtandroidextras", "qtcanvas3d",
"qtcharts", "qtconnectivity", "qtdatavis3d", "qtdeclarative",
"qtdoc", "qtgamepad", "qtgraphicaleffects", "qtlocation",
"qtmacextras", "qtmultimedia", "qtnetworkauth", "qtpurchasing",
"qtquickcontrols", "qtquickcontrols2", "qtremoteobjects",
"qtscript", "qtscxml", "qtsensors", "qtserialbus",
"qtserialport", "qtspeech", "qtsvg", "qttools",
"qttranslations", "qtvirtualkeyboard", "qtwayland",
"qtwebchannel", "qtwebengine", "qtwebglplugin",
"qtwebsockets", "qtwebview", "qtwinextras", "qtx11extras",
"qtxmlpatterns"
],
"disabled_features": [
"network", "bearermanagement", "dnslookup", "dtls", "ftp",
"http", "localserver", "networkdiskcache", "networkinterface",
"networkproxy", "socks5", "udpsocket", "concurrent", "future",
"cups", "printer", "printdialog", "printpreviewdialog",
"printpreviewwidget", "sql", "sqlmodel", "testlib", "xml"
]
},
"linux#python": {
"build_host_from_source": false,
"build_target_from_source": true,
"source": "Python-3.7.2.tgz",
"dynamic_loading": true
},
"linux#sip": {
"module_name": "PyQt5.sip",
"source": "sip-4.19.15.tar.gz"
},
"linux#pyqt5": {
"modules": ["QtCore", "QtGui", "QtWidgets"],
"source": "PyQt5_*-5.12.2.tar.gz"
}
}
Что интересного мы тут видим? Во-первых, не используется ряд компонентов(например, ssl, pyqt3D и прочие). Во-вторых, собирать наш exe’шник мы будет под linux (а точнее, linux-64; в нашем случае, можно не указывать перед каждым компонентом платформу).
Далее, в qt5 по-максимуму выключены модули и функции, которые не будут использоваться (те, о назначении которых у меня было хотя бы минимальное представление). Среди top-level директорий собирается только QtBase. Особо упомяну опции -optimize-size
и -ccache
. Первая позволяет уменьшить размер собранного Qt и, соответственно, итогового файла (у меня получилось минус 5 МБ), но увеличится время компиляции, вторая — использовать ccache (по крайней мере, на linux), что при повторных компиляциях СУЩЕСТВЕННО уменьшает время (у меня уменьшилось раз в 5). Никакой настройки не требует, просто ставим командой apt install ccache
.
В pyqt5 собираем только модули QtCore, QtGui, QtWidgets.
В python включен dynamic_loading
, так как мы хотим позднее динамически прилинковать C-extension.
Прежде чем приступить к сборке sysroot, не забываем скачать все необходимые исходники: zlib, Qt5, Python, sip, PyQt5 и кладем их в папочку с sysroot.json (можно и любую другую, указав потом путь к ней). Запускаем сборку:
pyqtdeploy-sysroot sysroot.json
Данная команда имеет еще несколько опций, которые можно посмотреть здесь.
Крайне рекомендую также использовать опцию --verbose
. Будьте готовы к тому, что вы получите целую кучу ошибок, прежде чем все удачно соберется. Многие из них будут связаны с тем, что у вас не установлены dev-пакеты. Я их здесь не перечисляю, ибо они зависят от вашей конфигурации и платформы. Наверняка, вам нужен будет python3-dev, также смотрим тут (особенно, разделы Requirements). Правда, вам никто не запрещает использовать для тех же Qt и Python уже установленные версии (я не пробовал, возможны свои подводные камни).
Ну и запаситесь попкорном, ибо, в зависимости от мощности вашего калькулятора компьютера, это может занять немалое время.
Создаем «проектный» файл (документация)
Как только у нас все удачно собралось, приступаем к выбору модулей, которые мы хотим запаковать в exe’шник. Для этого в pyqtdeploy есть удобная утилита с GUI. Запускаем (имя .pdy файла может быть любым):
pyqtdeploy main.pdy
Application Source. В первой вкладке мы видим следующие настройки:
- Name — имя вашего будущего exe’шника;
- Main script file (не указывается, если используется Entry Point) — скрипт, используемый для запуска программы (в нашем случае, main.py);
- Entry Point (не указывается, если используется Main script file) — точка входа для программы, основанной на setuptools;
- sys.path — используется для указания дополнительных директорий, zip-файлов и яиц (тех, что Python egg), которые будут добавлены в sys.path (я не использовал, смотрим доки, там подробно описана эта опция);
- Target Python version — версия Python;
- Target PyQt version — PyQt4 или PyQt5 (игнорируется, если вы мазохист и решили собрать программу, не использующую PyQt, этим монстром);
- Use console — выбрать, если приложение должно использовать консоль (только Windows). Может быть полезно для дебага;
- Application bundle — выбрать, если приложение должно быть собрано как bundle (только MacOS);
- Application Package Directory — содержит все файлы, составляющие вашу программу. Для добавления жмем кнопку Scan… У нас папка со всеми «кишками» (src) отделена от main.py, так что выбираем эту папку и галочками выделяем все файлы, которые мы хотим включить в итоговый файл. Если же у вас нет такого разделения (т.е. main.py находится внутри src), то напротив main.py галочку нужно снять (или напротив вашего аналога, указанного в Main script file).
Еще один момент: любой файл с расширением .py будет «заморожен» (будет сгенерирован байт-код) — в ряде случаев это может быть нежелательным.
Кнопки справа:
- Scan… — добавляем файлы в Application Package Directory;
- Remove all — очищаем Application Package Directory;
- Include all — выделяем все файлы в Application Package Directory;
- Exclude all — снимаем выделение со всех файлов в Application Package Directory;
- Exclusions — паттерны, позволяющие исключить файлы из Application Package Directory. Дважды кликаем на пустой строке для добавления;
qmake. Так как в сборке участвует qmake, здесь можно добавить дополнительные параметры для него (я не использовал);
PyQt Modules. На этой вкладке выделяем все PyQt-модули, которые мы явно импортируем в нашей программе. Если они зависят от других модулей, те выделятся автоматически. В нашем случае использовались QtCore, QtGui, QtWidgets, uic; sip подхватился автоматом.
Если планируется использовать уже установленный PyQt, а не привязывать статически его к нашему исполняемому файлу, ничего не выделяем (такой сценарий не тестировался).
Standard Library. Здесь тот же подход, что и в предыдущем пункте, только для стандартной библиотеки. Если у вас в программе явно импортируется какой-то модуль, ставим галку. Если выделенным нами модулям (или самому интерпретатору) нужны другие модули, они выделятся автоматом (квадратики).
Правда это не всегда работает. Если поставили какой-то пакет со стороны (через тот же pip), и он импортирует что-то из стандартной библиотеки (еще не выделенное), вы получите при запуске ImportError
. Так что вам придется вернуться сюда и поставить галочку. Например, я использую библиотеку PIL, и одному из модулей нужны была библиотека fractions.
Python использует ряд модулей/пакетов (например, ssl), которым для работы нужны внешние библиотеки. Если мы хотим их статически привязать, то мы настраиваем это дело справа. В INCLUDEPATH указываем путь к заголовочным файлам (headers), в LIBS — путь к этой либе (мной не использовались, так что подробности смотрим в доках).
Other Packages. На этой вкладке выбираем необходимые нам сторонние пакеты (например, установленные из pypi). Подход тот же, что и в Application source: кликаем дважды на пустой строке, выбираем папку (в нашем случае, site-packages используемого при разработке virtual environment), жмем Scan и выбираем нужные пакеты/модули (у нас это PIL).
Other Extension Modules. Тут мы настраиваем модули расширения на C, которые хотим СТАТИЧЕСКИ привязать к exe’шнику (сторонние; те, что в стандартной библиотеке, привязываются сами).
Мы может настроить как компиляцию с нуля этих самых расширений, так и привязку уже скомпилированных. Второе делается довольно просто. Допустим у нас есть пакет Package со статической либой Lib.a, то в поле Name указываем полное имя расширения, используемое во время импорта — Package.Lib (без расширения .a); затем в поле LIBS указываем путь к этому расширению, например, -L/home/user1/venv/programme1/lib/python3.7/site-packages/Package -lLib (это специальный формат, также можно указать путь «по старинке», /home/user1/venv/programme1/lib/python3.7/site-packages/Package/Lib.a).
С компиляцией я не разбирался, но советую почитать, во-первых, про эту вкладку в доках, во-вторых, про qmake (там гораздо подробнее описаны опции, чем в pyqt’шных доках).
А что, если у нас динамическая либа, например, Lib.so? Еще проще — переименовываем ее в Package.Lib.so (т.е. все то же полное имя расширения, используемое во время импорта + расширение) и кладем его рядом с нашим exe’шником. Все должно подхватится, если это простое расширение без всяких зависимостей. В противном случае, ждите опять кучу ImportError
. Мне, например, так и не удалось прикрутить _imaging.so, используемый PIL’ом.
Locations. Тут тоже подробно не останавливаемся, за описанием отдельных путей сюда. Если вы действовали в соответствии с этой статьей (собранный sysroot лежит тут же, рядом с main.pdy), тут менять ничего не надо.
Собираем exe’шник (документация)
Наконец-таки собираем наш исполняемый файл:
pyqtdeploy-build main.pdy
cd build-linux-64
qmake
make #nmake для win
Гипотетически, все должно собраться, на деле — доки и гугл вам в помощь.
Лирическое отступление #1 — меняем поведение программы в зависимости от того, «заморожено» оно или нет
Если вам нужно определить, запущена ваша программа как есть или из собранного exe’шника, используется тот же подход, что и в PyInstaller:
if getattr(sys, 'frozen', False):
# запустили из exe'шника
else:
# запустили не из exe'шника
Лирическое отступление #2 — использование ресурсов (изображения, иконки и пр.)
У Qt имеется специальная «система ресурсов», которая позволяет с помощью утилиты rcc упаковать любые бинарные файлы в exe’шник. Далее с помощью пути специального формата вы можете получить доступ к необходимому ресурсу. В нашем проекте файл с иконкой icon.png расположен в src/resources/images, тогда путь в «системе ресурсов» будет выглядеть так — :/src/resources/images/icon.png. Как видите, ничего хитрого. Однако с таким путем есть одна засада — его понимают только Qt’шные функции. Т.е. если вы напишите у себя в программе что-нибудь в духе:
icon = QIcon(':/src/resources/images/icon.png')
Все будет в порядке. Но если, например, так:
icon_file = open(':/src/resources/images/icon.png', 'rb')
icon = icon_file.read()
Ничего не выйдет, ибо open
будет пытаться найти такой путь в вашей файловой системе и, естественно, ничего не найдет.
Если вам нужно читать запакованные ресурсы не только средствами Qt (например, вы, как и я, создавали GUI с помощью Qt Designer и получили файл .ui, который потом надо прочитать с помощью loadUi
), нужно будет сделать как-то так:
ui_file = QtCore.QFile(':/src/resources/images/icon.png')
ui_file.open(QtCore.QIODevice.ReadOnly)
data = ui_file.readAll()
ui_file.close()
ui_file = BytesIO(bytes(data))
Итоги
Стоит ли так сильно заморачиваться, если вам нужен exe’шник, и старые добрые дедовские способы распространения программы вам по каким-то причинам не подходят? Если вы не используете PyQt, то, на мой взгляд, точно не стоит. Используйте что-нибудь более дружелюбное (тот же PyInstaller). Если хотите выжать максимум соков из вашего файла — дерзайте. В конечном счете мне таки удалось уменьшить размер файла до ~40 МБ (c -optimize-size
~35 МБ), что все-равно больше, чем хотелось бы.
Когда у нас собрана минимально необходимая Qt и PyQt, было бы неплохо попробовать сделать на их основе exe’шник с помощью PyInstaller или cx_Freeze и посмотреть на размер, но это, как говорится, уже другая история…
-
- Jul 21, 2017
-
- 262
-
- 5
-
- 4,815
- 7
-
#1
So I made a pyqt script that I want to deploy to android using pyqtdeploy. Problem is that when I run:
It returns:
pyqtdeploy-sysroot: Unable to detect MSVC2015 or MSVC2017
I already downloaded Visual Basic x64 redistributable 2015-2019.
I don’t know what to do. Does pyqtdeploy not work on windows? Do I need to use Linux?
Thanks,
Salm2s
-
- Dec 24, 2011
-
- 8,090
-
- 1,032
-
- 53,590
- 1,539
-
#2
-
- Jul 21, 2017
-
- 262
-
- 5
-
- 4,815
- 7
-
#3
u will need compiler (visual studio 2015 or 2017) not redistributables
Is there a link as to where I can get them?
-
- Dec 24, 2011
-
- 8,090
-
- 1,032
-
- 53,590
-
- 1,539
-
- Jul 21, 2017
-
- 262
-
- 5
-
- 4,815
- 7
-
#5
Do I need to choose anything? Or can I just install?
-
- Jan 13, 2011
-
- 14,023
-
- 434
-
- 76,290
- 3,208
-
#6
-
- Jul 21, 2017
-
- 262
-
- 5
-
- 4,815
-
- 7
-
#7
You need to select «Python development», I suppose.
Thanks. I installed and tried command again in the developer console:
python build-demo.py --target android
But now I get this error:
pyqtdeploy-sysroot: win-32 is not a supported android-32 development host
I am pretty sure I am running Windows 64 bit. Any reason why it says this?
-
- Aug 24, 2012
-
- 15,250
-
- 1,087
-
- 69,540
- 1,656
-
#8
Thread starter | Similar threads | Forum | Replies | Date |
---|---|---|---|---|
|
Question EXCEL. Data Validations don’t work when workbook is SHARED — For ONE USER only. | Apps and Software | 3 | Jan 25, 2023 |
|
Question Realtek drivers don’t detect headset mic and won’t work with the Steelseries app ? | Apps and Software | 1 | Jan 21, 2023 |
|
Question Alexa For PC : Installed And Working Fine. Now, Not At All. | Apps and Software | 1 | Jan 19, 2023 |
H
|
[SOLVED] How to check the code for errors so the app will work properly ? | Apps and Software | 5 | Nov 23, 2022 |
|
[SOLVED] Realtec Audio Utility Link not working | Apps and Software | 1 | Nov 22, 2022 |
C
|
Question Google Dictionary Browser Extension for Chrome not working ? | Apps and Software | 0 | Sep 17, 2022 |
S
|
[SOLVED] Nvidia Control Panel Settings Backup — Does importing only nvdrsdb0.bin work and not nvdrsdb1.bin? | Apps and Software | 2 | Jun 4, 2022 |
S
|
Question What is meant by «streamline the code’s inner workings» ? | Apps and Software | 2 | May 29, 2022 |
|
Question Drop Shadow Effect has stopped working in Davinci Resolve & Paint.NET ? | Apps and Software | 2 | May 16, 2022 |
- Advertising
- Cookies Policies
- Privacy
- Term & Conditions
- Topics
This topic has been deleted. Only users with topic management privileges can see it.
Hi.
I want to deploy my Python 3.4 PyQt 5.3 application to android via pyqtdeploy and Qt5.3 for Android. I’ve already install all prereqs (SDK, NDK, Ant, etc.) and successfuly deploy testing Qt project with button and label to my android device, but it’s just simple Qt c++ project.
I made Qt project from PyQt application (also testing and simple) with pyqtdeploy and trying to deploy it as before, but linker told me, that there is no QtCore, QtWidgets libraries. As I see, it’s mean, that I must build PyQt (and Python?) as static library for arm(?) and say to linker where they are. But how can I do this? Is it possible to make such libraries from sources in Qt, or I need another enviroment.
Or everything is bad, and it’s wrong way?
pyqtdeploy intro page said, that pyqt application can be deployed to android, but documentation only tells about desktop variant, and tells: «For mobile platforms this will be considerably more complicated».
Why there is no prebuilted arm static libraries on official sites? And is it a good idea or waste of time?
Hi and welcome to devnet,
Sounds like you want to do PyQt on Android ?
Yeah.
History part:
I tried kivy with buildozer, but buildozer still not Python 3 ready. I’ve spend hours to fix sources of buildozer and finally I made apk and deploy application, but it’s really annoying. Couple days ago I read about pyqtdeploy on riverbank site. «God news!», — i thought. «Deploying my favorite pyqt on android? Are you kidding me?! Awesome!»
So, just now I tried to build console application (print(«hello!»)) with pyqtdeploy and Qt for Android. There is two perspectives: android and desktop. First I tried desktop, and it builds just fine, but there was some runtime errors, missing libraries, I think. Then I tried andoid, and it didn’t build. Cause python.h have #include <io.h>, which, probably, missing in android qt enviroment. I don’t know really know should I do it this or that way, sadly.
Tomorrow I will write some mail to pyqt support, it’s really unfair to write «It supports deployment to desktop platforms (Linux, Windows and OS/X) and to mobile platforms (iOS, Android and Windows RT).» and don’t write how to do it. Huh…
Probably someone tried and have success here or have any ideas?
pyqtdeploy v0.5 has more support for cross compiling to android and ios. It does require cross compiling several packages, static. The change from the previous version 0.4 is that pyqtdeploy (invoked on a command line) now generates the .pro projects and qmake files to accomplish that static cross compiling of packages (as well as what it did in the previous version, which is let you use a GUI to build your PyQt app.)
pyqtdeploy is still in active development, so it is not foolproof.
«instructions for cross-compiling static packages»:http://pyqt.sourceforge.net/Docs/pyqtdeploy/static_builds.html
Yeah, I’ve tryed it out couple days ago. Nohting new, I’ve tryed to build python as static library for android, but qmake doesn’t work with generated .pro file. It’s telling me that something «includes» in python source, that can’t be found in arm gcc library. Anyway I hope that pyqt guys will do downloads for static arm build of current versions of python, pyqt and all other prereqs. God damn! Make a VM with linux, where everything already installed and ready to use pyqtdeploy. It’s really easiest way to deploy.
PyQt5 applications with pyqtdeploy on Android
This a small guide that explains how to deploy PyQt5 application to Android using pyqtdeploy.
Keep in mind that this is not a Python/Qt/PyQt5 tutorial.
Reading list
Before doing anything read these docs, they get you up to speed how PyQt/Qt/Android works.
-
Qt
- https://doc.qt.io/qt-5/android-getting-started.html
- https://doc.qt.io/qt-5/deployment-android.html
-
PyQt
- https://www.riverbankcomputing.com/static/Docs/pyqtdeploy/ (this is the most useful one)
- https://www.riverbankcomputing.com/static/Docs/PyQt5/index.html
Setting up the environment
This guide was written using Ubuntu 20.04
however there is nothing platform specific here
so it should work on almost any Linux-based system.
Install required dependencies
These are the required dependencies to build and deploy the example application to Android.
The following package versions are used in the guide
- Python 3.7.7
- Qt 5.13.2
- PyQt 5.15.1
- sip 4.9.24
- pyqtdeploy 2.5.1
- Android NDK r20b
Note: it’s recommended to always use the latest PyQt5 source, it’s compatible with older version.
System packages
sudo apt-get install clang make zlib1g zlib1g-dev libbz2-dev libssl-dev openjdk-8-jdk build-essential git
Qt
Download the online installer from https://www.qt.io/download-qt-installer and install it under ~/Qt
.
After the installation is completed there should be a MaintenanceTool
binary in the Qt folder, run it.
Here you should install Qt 5.13.2
and only the Android related packages.
Note: a Qt account might be required.
Android SDK/NDK
Unfortunatelly things don’t seem to work with the command line tool only so we need to download Android Studio from https://developer.android.com/studio and intall it to ~/Android
.
After the installation is done run the following commands to update the Android SDK list and install the Android 29 platform.
~/Android/tools/tools/bin/sdkmanager --update
~/Android/tools/tools/bin/sdkmanager "platforms;android-28"
We also need to install an Android NDK, to do this go to https://developer.android.com/ndk/downloads/older_releases and download android-ndk-r20b
and exract it to ~/Android
.
The command cat ~/Android/android-ndk-r20b/source.properties
should run successfully and show some data.
pip/pyqtdeploy
We need to have the latest pip and pyqtdeploy installed.
You can install them in a virtualenv or not, up to you.
pip install -U pip
pip install pyqtdeploy==2.5.1 pyqt5
Note: the latest pyqtdeploy (3 and above) had some major changes, this guide will be updated to follow up with those.
Download source packages
For building the Android sysroot we will need the source packages of the libraries.
Download the following files and put them in example/sources/
(the tar.gz/tgz versions)
- sip 4.19.24 https://www.riverbankcomputing.com/software/sip/download
- PyQt5 5.15.1 https://www.riverbankcomputing.com/software/pyqt/download5 or https://pypi.org/project/PyQt5/5.15.1/#files
- Python 3.7.7 https://www.python.org/downloads/source/
- openssl 1.0.2r https://www.openssl.org/source/old/1.0.2/
Set environment variables
For pyqtdeploy to work properly we need to have some environment variables set
# required for pyqtdeploy
export ANDROID_SDK_ROOT=$HOME/Android/tools
export ANDROID_NDK_ROOT=$HOME/Android/android-ndk-r20b
export ANDROID_NDK_PLATFORM=android-29
# useful later, need to specify Qt dir while building sysroot
export QT_DIR=$HOME/Qt/5.13.2
# change this to point to the git repo
# useful for pyqtdeploy to know the example app's directory
export APP_DIR=$HOME/path-to/example/app
# for adb and androiddeployqt
export PATH=$HOME/Android/tools/platform-tools:$PATH
export PATH=$HOME/Qt/5.13.2/android_arm64_v8a/bin:$PATH
Building Android sysroot
Go to example/
directory and run the following command
pyqtdeploy-sysroot --target android-64 --source-dir sources/ --source-dir $QT_DIR --verbose sysroot.json
Building the application
pyqtdeploy-build --target android-64 --verbose --no-clean app.pdy
cd build-android-64
qmake
make -j2
make install INSTALL_ROOT=app
# 29 as in android-29, libmain comes from the entrypoint name in the .pdy file
androiddeployqt --gradle --android-platform 29 --input android-libmain.so-deployment-settings.json --output app
To install it to your device run (might need to enable developer mode and USB debugging on your phone, after that it works via USB or over TCP too)
adb devices
adb install app/build/outputs/apk/debug/app-debug.apk
Adding Android specific things
In order to customize our application (name, logo, custom permissions) we need to create our
own AndroidManifest.xml
and also in the long term it’s better to have our own custom
Activity inheried from QtActivity.
To create these custom files we need to specifiy ANDROID_PACKAGE_SOURCE_DIR
(https://doc.qt.io/qt-5/deployment-android.html#android-specific-qmake-variables) in qmake variables (the .pdy file has a separate tab for it).
First copy $QT_DIR/android_arm64_v8a/src/android/templates/AndroidManifest.xml
to example/android_source/
.
Then create ExampleActivity.java
in example/android_source/src/org/kviktor/example/
with the following content
package org.kviktor.example; public class ExampleActivity extends org.qtproject.qt5.android.bindings.QtActivity { private static ExampleActivity m_instance; public ExampleActivity() { m_instance = this; } }
This one just extends the original QtActivity
class and saves a reference to itself in a static variable (this will come in handy later).
In AndroidManifest.xml
change org.qtproject.qt5.android.bindings.QtActivity
to
org.kviktor.example.ExampleActivity
.
What is pyqtdeploy?
pyqtdeploy is a tool that, in conjunction with other tools provided with Qt,
enables the deployment of PyQt applications written with Python v3.5 or later.
It supports deployment to desktop platforms (Linux, Windows and macOS) and to
mobile platforms (iOS and Android). Its design is heavily influenced by the
need to support mobile platforms and cross-compilation.
An application being deployed is built from the application’s source code and a
number of external parts. A part is a pure Python module or an extension
module and is provided by a component. A component is implemented as a
plugin written in Python. pyqtdeploy includes plugins for common components
including, for example, PyQt, OpenSSL and the Python standard library.
All external parts are installed in a system root directory or sysroot.
The contents of a sysroot, ie. the components and how each is configured, is
defined by a sysroot.toml file. This file is created and maintained by
hand. While each application being deployed must have an associated sysroot, a
single sysroot may provide the parts for multiple applications. The first
of deploying an application is to specify an appropriate sysroot.toml file
(but it is not necessary to build the corresponding target-specific sysroot
directory at this point).
pyqtdeploy comprises three different executables: pyqtdeploy-sysroot,
pyqtdeploy-build and pyqtdeploy itself.
pyqtdeploy-sysroot is a command line tool that creates a target-specific
sysroot directory using a sysroot.toml file. It will download, configure
and build the parts specified by each component.
pyqtdeploy is a GUI tool that is used to specify the type and structure of
the application being deployed and the sysroot parts that it is dependent on.
This information is stored in an application-specific project file (that has a
.pdt extension).
pyqtdeploy-build is a command line tool that takes the project file and
creates all the necessary files to build the deployed application. It works by
taking the individual modules of an application, freezing them, and then
placing them in a Qt resource file that is converted to C++ code by Qt’s
rcc tool. Python’s standard library is handled in the same way. It also
generates a simple C++ wrapper around the Python interpreter library that uses
the Python import mechanism to enable access to the embedded frozen modules in
a similar way that Python supports the packaging of modules in zip files.
Finally it generates a Qt .pro file that describes all the generated C++
code. From this Qt’s qmake tool is used to generate a platform-specific
Makefile which will then generate a single executable. Further Qt and/or
platform specific tools can then be used to convert the executable to a
platform specific deployable package.
Author
pyqtdeploy is copyright (c) Riverbank Computing Limited. Its homepage is
https://www.riverbankcomputing.com/software/pyqtdeploy/.
Support may be obtained from the PyQt mailing list at
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
License
pyqtdeploy is released under the BSD license.
Installation
pyqtdeploy can be dowloaded an installed from PyPI:
pip install pyqtdeploy
Documentation
The documentation for the latest release can be found
here.