Пользователь
0,3
рейтинг
5 августа 2015 в 19:38

Разработка → Сложности сборки Python3 + Qt5 приложений под Windows

Недавно потребовалось мне сделать небольшую прогу под Windows. Раньше мне не доводилось разрабатывать под нее.
Сама программа несложная, написалась относительно быстро. Намного больше времени отъела сборка ее под винду. Понятно, что выбранные инструменты (Python3 + Qt5) не родные, а универстальные, но что потребуется столько времени затратить на сборку, я не предполагал.
Соответственно, хочется поделиться практикой, может кому еще придется стучаться лбом в эту стену.
Под катом выстраданная инструкция как легко собирать PyQt5 приложения в single-file.exe не требующий инсталлятора.

Наиболее известные решения для моей задачи — сборки python приложений в .exe это: py2exe, cx_freeze и pyinstaller. Про каждый написано много всего. Но очень часто авторы грешат халтурой — легко и быстро собирается программа и запускается… на том же самом компьютере. Надо ли говорить, что это две большие разницы — запуск на дев-среде и у пользователя? У меня-то и так заработает безо всяких сборок и танцев с бубном. Кроме того — кто из пользователей будет ставить себе Qt?

Для каждого инструмента есть туториал. Но у каждого были свои проблемы: один не запускался (pyinstaller до сих пор поддерживает 3-й питон в экспериментальном режиме), другой собрал то, что не смог запустить, третий вообще долгое время ничего не собирал, ругая все вокруг.
После долгой борьбы был выбран py2exe.

Секрет успеха в правильной установке компонент из правильных источников (я не даю прямые ссылки, т.к. они все равно устареют, пишу что где искать):
  • Win XP 32-bit (или другая, которую вы возьмете за базу — я специально взял самую старую систему)
  • Python 3.4 — устанавливаем с www.python.ord/download Windows x86 msi installer. Можно сразу добавить C:\Python34\ в %PATH%
  • PyWin32 — ставим с sf.net файл pywin32-219.win32.py3.4.exe
  • Qt5.5 — ставим с официального сайта qt.io. Одновременно с ним ставится компилятор mingw4.9.2
  • После установки добавляем C:\Qt\Tools\mingw492_32\bin в %PATH%.
  • Git for Windows — он нам нужен в любом случае, ставим с git-scm.com
  • PyQt5 — качаем и ставим riverbankcomputing.co.uk win32 x86 installer
  • SIP — я так и не понял почему, но тот же самый riverbankcomputing.co.uk не удосужился собрать служебный модулек, поэтому качаем win сорцы, распаковываем, дальше удобно продолжить работу в консоли Git Bash:
  • python configure.py -p win32-g++
  • mingw32-make.exe
  • mingw32-make.exe install.
  • Само собой, инсталляция не сработает (. К счастью, нам нужно поставить всего 3 файла. Идем руками в подпапку sipgen/ folder и копируем sip.exe в C:\Python34.
  • Затем иде в ../siplib и
  • copy sip.pyd c:\python34\Lib\site-packages
  • strip /c/Python34/Lib/site-packages/sip.pyd
  • наконец, копируем .h-файл:
  • cp sip.h /c/Python34/include/
  • py2exe — ставим родным способом: python -m pip install py2exe
  • pyreadline — так же: python -m pip install pyinstaller
  • 7-Zip — c сайта 7-zip.org качаем x86 exe
  • 7-zip extra — полезные дополнения качаем оттуда же, распаковываем в папку с установленным 7-zip folder. На конфликты txt-файлов можно забить.
  • Resource Hacker — качаем angusj.com — впрочем, если вы не собираетесь заменять дефолтную пиктограмму программы на свою, то она вам не потребуется. Либо ее можно заменить на windres, которая идет в комплекте с mingw — но у меня уже было сделано с RH.


Делюсь своим setup.py:

from distutils.core import setup
import os, sys
import py2exe
from glob import glob
import PyQt5

NAME="Proga"

qt_platform_plugins = [("platforms", glob(PyQt5.__path__[0] + r'\plugins\platforms\*.*'))]
data_files.extend(qt_platform_plugins)
msvc_dlls = [('.', glob(r'C:\Windows\System32\msvc?100.dll'))]
data_files.extend(msvc_dlls)
# print(data_files)

sys.argv.append('py2exe')

setup(
	data_files=data_files,
	windows=[
		{
			"script": "pyftp1.py",
			"icon_resources": [(0, "resources/favicon.ico")]
		}
	],
	# zipfile=None,
	options={
		"py2exe": {
			"includes":["sip", "atexit",],  # даже если мы нигде не используем atexit его надо добавить, sip указываем явно - сборщики часто про него забывают 
			# "packages": ['PyQt5'],
			"compressed": True,
			"dist_dir": "dist/" + NAME,
			# "bundle_files": 0, # не сработало ( с этой опцией на выходе прога не может найти msvc*.dll 
			# "zipfile": None, # тоже лишнее
			"optimize": 2,
		}
	}
)


Это программа-минимум. Теперь можно собирать само приложение:
python setup.py py2exe


На выходе получаем папку с готовой прогой в dist.
Это хорошо, но не всегда удобно для пользователей. На эту папку можно натравить создаватель инсталляторов, типа Inno Setup и получить msi. В моем случае, надо было обеспечить работоспособность программы с минимальными правами пользователей — точно без права установки либ.

Поэтому я пошел дальше и запаковал в 1 файл с помощью 7-zip. Схема такая: создаем 7z-архив и к нему пристыковывается специальная SFX-голова и конфиг. Голова распаковывает архив во временную папку, смотрит в конфиг и запускает нужный exe-файл. Собирается примерно так:
cat 7zS.sfx resources/config.txt Proga.7z > Proga.exe

В файле конфига можно указать разные опции, я задал минимум:
;!@Install@!UTF-8!
Title="Proga"
RunProgram="Proga\\pyftp1.exe"
;!@InstallEnd@!


Единственный недостаток — некрасивая картинка у exe файла — стандартный распаковщик. Тут-то и подменяем ему картинку на нужную нам:
RESHACKER.exe -addoverwrite Proga.exe, Proga.exe, resources/favicon.ico, ICONGROUP, MAINICON, 0

Чтобы не мучаться с этим всем в консоли, я сделал небольшой Makefile:
# start settings
DIST=dist
# change NAME also in setup.py and resource/config.txt
NAME=Proga
EXT=exe

# final name and location of the built program
FINAL=$(DIST)/$(NAME).$(EXT)

# external programs
7ZIPDIR="C:\Program Files\7-Zip"
RESHACKER="/c/Program\ Files/Resource\ Hacker/ResourceHacker.exe"

# intermediate steps

# no icon version of program 
NOICON=$(DIST)/$(NAME)_no_icon.$(EXT)

# name of .7z archive
7Z_BASENAME=$(NAME).7z
7Z=$(DIST)/$(7Z_BASENAME)

# folder with ready .exe
PROGDIR=$(DIST)/$(NAME)

all: $(FINAL)


$(FINAL): $(NOICON)
	# change icon
	"$(RESHACKER)" -addoverwrite $(NOICON), $(FINAL), resources/favicon.ico, ICONGROUP, MAINICON, 0


$(NOICON): $(7Z)
	#build autorunning sfx with default icon
	cat $(7ZIPDIR)/7zS.sfx resources/config.txt $(7Z) > $(NOICON)


$(7Z): exe
	# compress program folder to .7z
	cd $(DIST);  $(7ZIPDIR)\\\7z.exe a  $(7Z_BASENAME)  $(NAME)


exe:  $(DIST)
	# build program itself
	python setup.py py2exe


$(DIST):
 	# create dist directory
 	# echo $(DIST)/


clean:
	rm -rf $(DIST)/*



Теперь можно очень легко пересобирать прогу:
mingw492-make.exe clean all


Успехов в сборке!
Расскажите про свой опыт!
Евгений Лисицкий @el777
карма
229,5
рейтинг 0,3
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (18)

  • +4
    Мне было влом есть кактус.
    Пишу на 2.7 и PyQt4.
    • +2
      Тоже вариант. С ним действительно проще собирается и тулзы нормально работают.
      Но как-то больше понравилось как сама прога на Qt5 работает — быстрее запускается GUI.
      А когда я начал собирать под Винду, все уже было написано — переписывать назад не хотелось :)
      • 0
        Я как-то пытался PyQt5 собрать, так и не получилось его ни к MinGW, ни к MS VS прикрутить(просил 2010, а под рукой были только 2012 и 2013).
        А Python 3 не использую, потому что пару раз нарывался на то, что нужных библиотек под него нет.
        Я ничего серьезного пока не пишу, но все равно неприятно.

        Если важна скорость попробуйте PyPy
        http://habrahabr.ru/post/87364/
        • +1
          > Я как-то пытался PyQt5 собрать…
          Такая же картина, причем в pip есть целых два пакета (PyQt5 и python-qt5), которые либо не ставятся либо не собираются. Поэтому приходится качать готовый с сайта. До сих пор не понимаю, почему на том же самом сайте не захотели выложить SIP, который приходится руками собирать?

          И вот эта магия «возьми SIP собранный в полнолуние с помощью MingW и плясок, добавь к нему PyQt5 с речного берега, потом щепотку нативного pyreadline, тщательно перемешай в ступе автоконфигом и т.п.» немного удручает.

          PyPy надо посмотреть. Спасибо за наводку.
          Пробовали его под виндой и в таких приложениях?
          • 0
            Я из-за этих причин, стараюсь вообще не касаться изучения С++ и использования Open Source на нем.
            Есть более интересные способы прожить жизнь, чем неделями собирать софт из исходников.
            Как исключение системы портов и портежей, сами понимаете в каких операционках, они на удивление стабильны.

            Не помню уже, что собирал. Там была ошибка в либах mysql. О баге уже знали, но когда выйдет исправление, черт его знает. Бывают хорошие люди, которые явно указывают какую версию либ использовать, а самые продуманные прикладывают к своему коду.

            ИМХО, исходный код должен быть в таком состоянии, чтобы человек мог его скачать, скачать необходимые библиотеки с дефолтных сайтов и собрать без головных болей и красных глаз. Да чтобы еще запустилось.

            Ситуация напоминает середину 2000-х, когда на коне была Delphi 7. Выпускалась масса компонентов с намеренными ошибками, не зная языка подключить их не представлялось возможным. Только тут ошибки ненамеренные и поиск их не имея боевого опыта программирования не имеет смысла.

            PyPy пока не знаю куда воткнуть. Думаю сайтик поднять в 2 экземплярах и покрутить его одновременно на оригинальном интерпретаторе и PyPy, посмотреть самому на скорость и стабильность. Но не знаю дойдут ли руки.
  • +1
    Мой опыт: приложение на Py3.4 и PyQt4 собирается cx_Freeze и запускается нормально. Правда там был какой-то баг в cx_Freeze, который исправили, но почему-то в официальный релиз на тот момент этот патч не попал. При этом исправленная версия была доступна на всем известном сайте. С PyQt5 собирать standalone-сборку честно не пробовал, надо будет проверить.
    • +2
      У меня тоже cx_freeze шел в лидерах — раньше остальных смог собрать прогу, но не рабочую. Видимо, я на этот баг и напоролся.
      Я пробовал самостоятельно компилить патченую версию, но это превратилось в такую мороку.
      Самое смешное, что cx_freeze единственный из тройки не поддерживает сборку в single-executable, т.к. автору не нравятся те хаки, которые для этого применяют py2exe и pyinstaller. Из-за этого и появилась вторая часть моего рецепта — превращение в single exe.
      • +2
        Да, я когда этот вопрос изучал, выделил cx_freeze как самый адекватный проект, он на тот момент умел собирать сложные штуки, типа pyqt/pyside+numpy+scipy+matplotlib и т.п. Это мне и было нужно. Помню, пробовал pyinstaller, переплевался и выкинул в мусорку.

        cx_freeze не умеет собирать single exe, это да, но я в своё время проникся идеей не делать так, хотя бы потому что мой проект зависел от разных dll, и я их периодически обновлял, не пересобирая само приложение, что оказалось очень удобно.
      • 0
        Самое смешное, что cx_freeze единственный из тройки не поддерживает сборку в single-executable, т.к. автору не нравятся те хаки, которые для этого применяют py2exe и pyinstaller.
        В свое время нам почему-то не подошел cx_Freeze, очень возможно, что как раз поэтому: требовался единственный экзешник.

        Посмотрел в том проекте сейчас: для деплоймента в GNU/Linux я таки-писал спек-файл для PyInstaller, а для венды/вайна — setup.py для py2exe, хотя он и не понимал .egg-bundles («does not eat eggs for breakfast»), т.е. пришлось писать unpack_egg(), а потом еще самостоятельно сжимать бинарник UPX'ом.

        Не помню точно, почему не унифицировал: в комментарии написано, что Windows is currently not supported, and would require changing «excludes» list below (в обоих случаях я старался ужаться по минимуму, поэтому скрупулезно выкидывал ненужные либы). Возможно, py2exe, несмотря на недостатки, позволял получить файл меньшего размера.
  • +1
    Используйте Nuitka. Сейчас это лучшая из альтернатив (ИМХО, конечно). Я пробовал собирать небольшое приложение на PyQt5, всё прошло просто и без проблем.

    nuitka.net
    • +1
      Была такая мысль, тоже его попробовал, но не задалось.
      Может есть хороший мануал или какие нюансы его использования?
      • 0
        Так нет никаких нюансов :) просто запускаете из консоли с нужными параметрами и всё. Я своё приложение собирал так:

        nuitka --standalone --recurse-all --recurse-stdlib --windows-disable-console --windows-icon=ICON_FILE --recurse-directory=PROJECT_DIR

        ICON_FILE и PROJECT_DIR надо заменить соответствующими путями.

        Если будете использовать какие-то очень сложные модули, есть вероятность, что их dll'ки придётся вручную в каталог с exe'шником положить, чтобы всё работало. В случае с PyQt5, повторюсь, работает без этого.
        • 0
          Вы со вторым питоном использовали?
          У меня не взлетело (:
          Error, need to find Python2 executable under C:\Python26 or C:\Python27 to execute scons which is not Python3 compatible.
          
          • 0
            Ну так установите соответствующую версию Nuitka.
            • 0
              Лично мне помогла установка соотв. версии Python параллельно с тройкой, для которой и собиралось.
            • 0
              Я ставил через pip:
              python -m pip install nuitka
              Питон в системе только 3, чтобы не мешать либы.
  • +1
    На эту папку можно натравить создаватель инсталляторов, типа Inno Setup и получить msi. В моем случае, надо было обеспечить работоспособность программы с минимальными правами пользователей — точно без права установки либ.

    MSI не обязательно требуют права администратора, но при этом удобней (и солидней :) ) для пользователя чем просто самораспаковывающийся архив. Просто при создании инсталляционного пакета, выберается режим, в котором оговаривается что установка будет по-умолчанию производится не в Program Files/MyProgramName, а в AppData/Local/MyProgramName (как и делают всякие Дропбоксы, Хромы, и т.д). Ну и ярлыки чтобы не в ProgramData закидывались, а в локальную для пользователя папку.

    Главное понимать, что такая локальная инсталляция всегда менее безопасна чем обычная: так исполняемые файлы установленного софта доступны для редактирования кем и чем угодно — в конторы с требованиям по безопасности такой софт лучше не ставить (то же и к самораспаковывающимся архивам относится; особенно если не запихивать их потом руками в Програм Файлс).

    Не знаю как это делается при использовании Inno Setup, но вот при помощи WiX Installer (набор опенсорсных утилит от Microsoft) я такие инсталляторы свободно создавал когда требовалось — мануалы на эту тему в сети найти не сложно. Да и может еще какие новые графические конфигураторы для WiX за это время появились в дополнение к тому что был несколько лет назад (не проверял). В любом случае правильный MSI создать не сложно — более-менее на уровне с другими системами инсталляции. А преимуществах — и удобство юзера, и автоматизация процессов инсталляции/деинсталляции и гибкость.
    • 0
      Спасибо. Интересный вариант.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.