- Как из файла Python3 создать .exe на Windows
- Способ 1
- Способ 2
- How to create an executable (.exe) from a Python script in Windows using pyinstaller
- Requirements
- Implementation
- pyqtdeploy, или упаковываем Python-программу в exe’шник… the hard way
- Установка pyqtdeploy
- Структура программы
- Обзор плагинов sysroot (документация)
- Собираем sysroot
- Создаем «проектный» файл (документация)
- Собираем exe’шник (документация)
- Лирическое отступление #1 — меняем поведение программы в зависимости от того, «заморожено» оно или нет
- Лирическое отступление #2 — использование ресурсов (изображения, иконки и пр.)
- Итоги
Как из файла Python3 создать .exe на Windows
Способ 1
Мы рассмотрим создание .exe с помощью библиотеки модуля py2exe. Для этого необходим Python 3.4 и ниже.
Если у вас установлена более высокая версия Python, попробуйте использовать Способ 2 (ниже)
В этом примере мы рассмотрим создание .exe на примере Python3.4.
Прежде всего на нужно создать виртуальное окружение для Python3.4. В этом примере мы назовем myenv, Вы можете выбрать любое другое имя, но не забывайте сделать соответствующие изменения.
На терминале наберите следующие команды:
В командной строке появится префикс myenv, а это значит, что виртуальное окружение с именем myenv загружено. Все команды Python теперь будет использовать новое виртуальное окружение.
Теперь давайте установим py2exe (https://pypi.python.org/pypi/py2exe
HEAD=dobj) в нашем виртуальном окружении:
И, наконец, чтобы создать единый EXE-файл, в нашем виртуальном окружении выполняем команду:
(замените hello.py на имя вашего скрипта. Если скрипт находится в другой папке, то нужно использовать полный путь к вашему сценарию, например, C:\Projects\Python\ hello.py). Это создаст папку DIST, которая содержит исполняемый файл. Для быстрого доступа к нему, наберите в терминале:
Вы увидите путь к папке, где находится EXE-файл.
Примечание: При выполнении, откроется окно и исчезают так же быстро, как и появилось.
Это происходит потому, что операционная система автоматически закрывает терминал, в котором консольная программа закончена.
Для того, чтобы изменить эту ситуацию, можно добавить строчку
в конце файла Python. Интерпретатор будет ждать ввода пользователя, а окно будет оставаться открытым, пока пользователь не нажимает клавишу ввода.
Вы можете подробно изучить использование py2exe в документации на странице модуля: https://pypi.python.org/pypi/py2exe
Выход из виртуального окружения производится командой
Способ 2
Через командную строку Windows устанавливаем pyinstaller:
В командной строке переходим в папку, где находится файл
Затем в командной строке набираем команду
Вместо exapmle.py используем имя файла, из которого нужно создать exe файл.
Через пару минут все готово! Скоркее всего, exe файл будет находится во созданной подпапке dist
How to create an executable (.exe) from a Python script in Windows using pyinstaller
Read this article in other language
Are you one of those developers that create scripts to make their own life easier? coincidentally, do you like Python? and you have Windows?. Instead of work repetitively with the console executing your scripts manually in the console, you need to know that there’s an easy way to execute them and even create little console applications with them in Windows. We are talking about creating .exe (yeah, application files) files with python scripts, thanks to pyinstaller. PyInstaller is a program that freezes (packages) Python programs into stand-alone executables, under Windows, Linux, Mac OS X, FreeBSD, Solaris and AIX.
In this article, you’ll learn how to create an executable from a Python console script easily using Pyinstaller in windows.
Requirements
To create our executable, we are going to use the pyinstaller package. To download pyinstaller, execute the following command in your command prompt (cmd.exe):
The installation will proceed and you’ll have available pyinstaller in your environment:
You can read more about this package in the official website. To check if pyinstaller was correctly installed, you can check if it’s available in the console as an environment variable executing pyinstaller —h .
Implementation
The creation of an executable using pyinstaller is very straightforward, customizable and very easy to do. In our example, we are going to create an executable of the following script. The filename of the script is file-creator.py and contains the following code (it just prompt the user for the name of a new file and the content):
As you can see, it is a simple console Python application. Now to create the executable, navigate with the console (cmd.exe) to the folder where the script of python is located (in this case Desktop\pythonScripts):
Now, proceed with the creation of the executable using the following command:
That’s the most simple command to create an executable of your script, so that should be enough to create the executable in the folder where the script is located. Note that our application is based only in the console (if you want an executable with the manifest in the same folder and other dependencies, you can add the —console parameter to the command , if you use GUI with libraries like wxPython, then instead of the —console parameter use —windowed .).
Finally, you can test the created executable in the dist folder that will be created where the script is located:
pyqtdeploy, или упаковываем Python-программу в exe’шник… the hard way
Наверняка, каждый, кто хоть раз писал что-то на Python, задумывался о том, как распространять свою программу (или, пусть даже, простой скрипт) без лишней головной боли: без необходимости устанавливать сам интерпретатор, различные зависимости, кроссплатформенно, чтобы одним файлом-exe’шником (на крайний случай, архивом) и минимально возможного размера.
Для этой цели существует немало инструментов: PyInstaller, cx_Freeze, py2exe, py2app, Nuitka и многие другие… Но что, если вы используете в своей программе PyQt? Несмотря на то, что многие (если не все) из выше перечисленных инструментов умеют упаковывать программы, использующие PyQt, существует другой инструмент от разработчиков самого PyQt под названием pyqtdeploy. К моему несчастью, я не смог найти ни одного вменяемого гайда по симу чуду, ни на русском, ни на английском. На хабре и вовсе, если верить поиску, есть всего одно упоминание, и то — в комментариях (из него я и узнал про эту утилиту). К сожалению, официальная документация написана довольно поверхностно: не указан ряд опций, которые можно использовать во время сборки, для выяснения которых мне пришлось лезть в исходники, не описан ряд тонкостей, с которыми мне пришлось столкнуться.
Данная статья не претендует на всеобъемлющее описание pyqtdeploy и работы с ним, но, в конце концов, всегда приятно иметь все в одном месте, не так ли?
Замечание. В статье исполняемый файл собирается под linux. Несмотря на это, в качестве синонима используется слово «exe’шник» для экономии букв и уменьшения числа повторений.
Все началось с того, что мне захотелось один мой проект запихнуть в исполняемый файл со всеми зависимостями (вы и сами уже догадались). Сначала я решил попробовать провернуть эту операцию с помощью 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:
Сборка нашего exe’шника состоит из нескольких этапов:
- Разработка нашей Python-программы, как обычно (сюрприз!);
- Сборка так называемого sysroot для нашей платформы, где будут лежать собранные из исходников нужные зависимости;
- Создание «проектного» файла с расширением .pdy, где будет вся необходимая информация для сборки нашего exe’шника (пути к собранным Qt, PyQt, Python, прочим библиотекам и модулям и другие опции);
- Собственно сборка exe’шника с помощью qmake.
Структура программы
Возьмем в качестве примера проект со следующей структурой: main.py — «точка входа» для нашей программы, она вызывает mainwindow.py — допустим, отрисовывает окошечко с виджетами и берет из resources иконку icon.png и mainwindow.ui, сгенерированный нами с помощью Qt Designer. Имеющиеся зависимости, версии библиотек и прочие необходимые вещи будут всплывать по ходу повествования:
Обзор плагинов sysroot (документация)
Как уже было сказано ранее, на этом этапе мы собираем все необходимые части, которые затем будут использоваться при генерации исполняемого файла. Данный процесс осуществляется с использованием конфигурационного файла sysroot.json (в принципе, вы можете назвать его как хотите и указать затем путь к нему). Он состоит из блоков, каждый из которых описывает сборку отдельного компонента (Python, Qt и т.д.). В pyqtdeploy реализован API, позволяющий вам написать свой плагин, управляющий сборкой необходимой вам библиотеки/модуля/whatever, если он еще не реализован разработчиками pyqtdeploy. Давайте пробежимся по стандартным плагинам и их параметрам (примеры из документации):
openssl (не обязательный) — позволяет собирать из исходников или использовать установленную в системе библиотеку (подробности). Компонент, описывающий данный плагин в sysroot.json, выглядит следующим образом:
Первое, на что следует обратить внимание, это синтаксис: arch1|arch2|. #plugin-name . То есть мы можем выбрать, на какой платформе использовать этот плагин (ios, android, macos, win, linux), а на какой — нет. Более того, этот синтаксис применим и к параметрам внутри блока.
- source (обязательный) — имя архива с исходниками;
- no_asm (не обязательный) — выключаем ассемблерные оптимизации. Если включен, в PATH должен быть установлен nasm;
- python_source (не обязательный) — имя архива, содержащего патчи, необходимые для сборки OpenSSL под macOS для Python v3.6.4 и более ранних версий;
zlib (не обязательный) — используется при сборке других компонентов (если не указан, по идее, будет использоваться тот, что установлен в системе) (подробности):
- source (обязательный) — очевидно, имя архива с исходниками;
- static_msvc_runtime (не обязательный) — статически привязать MSVC библиотеки (Windows);
qt5 (обязательный) — тут понятно (подробности):
- 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 (обязательный) — тут тоже понятно (подробности):
- 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++ библиотек (подробности тут и тут):
- module_name (обязательный) — имя sip-модуля;
- source (обязательный) — имя архива с исходниками sip;
pyqt5 (обязательный) — тут тоже понятно (подробности):
- disabled_features (не обязательный) — позволяет выключить конкретный функционал. Если не указан, выключаемые фичи определяются автоматически на основе фич, выключенных в собранном нами Qt (подробности);
- modules (обязательный) — перечисляем модули, которые мы хотим собрать (подробности);
- source (обязательный) — имя архива с исходниками PyQt;
pyqt3D, pyqtchart, pyqtdatavisualization, pyqtpurchasing, qscintilla (не обязательные) — дополнительные модули, не входящие в состав PyQt. Имеют единственный параметр source — имя архива с исходниками.
Стоит заметить, что некоторые значения параметров могут не работать друг с другом. В таких случаях вы получите ошибку при сборке sysroot с информацией, что не так. Я постарался здесь описать такие случаи, по крайней мере, для обязательных компонентов.
Собираем sysroot
Давайте взглянем на итоговый sysroot.json для нашей программы:
Что интересного мы тут видим? Во-первых, не используется ряд компонентов(например, 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 (можно и любую другую, указав потом путь к ней). Запускаем сборку:
Данная команда имеет еще несколько опций, которые можно посмотреть здесь.
Крайне рекомендую также использовать опцию —verbose . Будьте готовы к тому, что вы получите целую кучу ошибок, прежде чем все удачно соберется. Многие из них будут связаны с тем, что у вас не установлены dev-пакеты. Я их здесь не перечисляю, ибо они зависят от вашей конфигурации и платформы. Наверняка, вам нужен будет python3-dev, также смотрим тут (особенно, разделы Requirements). Правда, вам никто не запрещает использовать для тех же Qt и Python уже установленные версии (я не пробовал, возможны свои подводные камни).
Ну и запаситесь попкорном, ибо, в зависимости от мощности вашего калькулятора компьютера, это может занять немалое время.
Создаем «проектный» файл (документация)
Как только у нас все удачно собралось, приступаем к выбору модулей, которые мы хотим запаковать в exe’шник. Для этого в pyqtdeploy есть удобная утилита с GUI. Запускаем (имя .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’шник (документация)
Наконец-таки собираем наш исполняемый файл:
Гипотетически, все должно собраться, на деле — доки и гугл вам в помощь.
Лирическое отступление #1 — меняем поведение программы в зависимости от того, «заморожено» оно или нет
Если вам нужно определить, запущена ваша программа как есть или из собранного exe’шника, используется тот же подход, что и в PyInstaller:
Лирическое отступление #2 — использование ресурсов (изображения, иконки и пр.)
У Qt имеется специальная «система ресурсов», которая позволяет с помощью утилиты rcc упаковать любые бинарные файлы в exe’шник. Далее с помощью пути специального формата вы можете получить доступ к необходимому ресурсу. В нашем проекте файл с иконкой icon.png расположен в src/resources/images, тогда путь в «системе ресурсов» будет выглядеть так — :/src/resources/images/icon.png. Как видите, ничего хитрого. Однако с таким путем есть одна засада — его понимают только Qt’шные функции. Т.е. если вы напишите у себя в программе что-нибудь в духе:
Все будет в порядке. Но если, например, так:
Ничего не выйдет, ибо open будет пытаться найти такой путь в вашей файловой системе и, естественно, ничего не найдет.
Если вам нужно читать запакованные ресурсы не только средствами Qt (например, вы, как и я, создавали GUI с помощью Qt Designer и получили файл .ui, который потом надо прочитать с помощью loadUi ), нужно будет сделать как-то так:
Итоги
Стоит ли так сильно заморачиваться, если вам нужен exe’шник, и старые добрые дедовские способы распространения программы вам по каким-то причинам не подходят? Если вы не используете PyQt, то, на мой взгляд, точно не стоит. Используйте что-нибудь более дружелюбное (тот же PyInstaller). Если хотите выжать максимум соков из вашего файла — дерзайте. В конечном счете мне таки удалось уменьшить размер файла до
40 МБ (c -optimize-size
35 МБ), что все-равно больше, чем хотелось бы.
Когда у нас собрана минимально необходимая Qt и PyQt, было бы неплохо попробовать сделать на их основе exe’шник с помощью PyInstaller или cx_Freeze и посмотреть на размер, но это, как говорится, уже другая история.