Создание исполняемого Python файла для Windows
В Windows регистрация расширения .pyz не является обязательной и кроме того, есть определенные моменты, при которых зарегистрированные расширения не распознаются «прозрачно». Самый простой пример команда subprocess.run([‘myapp’]) не найдет приложение и нужно явно указать расширение .pyz . Поэтому в Windows часто предпочтительнее из zipapp создать исполняемый файл. Это относительно просто, хотя для этого нужен компилятор языка Си.
Основной подход основан на том факте, что к zip-файлам могут добавляться произвольные данные и к файлам exe в Windows так же могут добавляться произвольные данные. Поэтому, создав подходящий модуль запуска и прикрепив файл приложения .pyz к его концу, можно получить исполняемый файл, который будет нормально работать в Windows.
Подходящий модуль запуска может быть таким:
#define Py_LIMITED_API 1 #include "Python.h" #define WIN32_LEAN_AND_MEAN #include #ifdef WINDOWS int WINAPI wWinMain( HINSTANCE hInstance, /* Обращение к текущему экземпляру */ HINSTANCE hPrevInstance, /* Обращение к предыдущему экземпляру */ LPWSTR lpCmdLine, /* Указатель на командную строку */ int nCmdShow /* Показать состояние окна */ ) #else int wmain() #endif wchar_t **myargv = _alloca((__argc + 1) * sizeof(wchar_t*)); myargv[0] = __wargv[0]; memcpy(myargv + 1, __wargv, __argc * sizeof(wchar_t *)); return Py_Main(__argc+1, myargv); >
Если определить препроцессор обработки окон WINDOWS, это сгенерирует исполняемый файл GUI, а без него — исполняемый консольный файл.
Чтобы скомпилировать исполняемый файл, можно либо просто использовать стандартные инструменты командной строки MSVC , либо воспользоваться тем, что distutils знает, как скомпилировать исходный код Python:
from distutils.ccompiler import new_compiler import distutils.sysconfig import sys import os from pathlib import Path def compile(src): src = Path(src) cc = new_compiler() exe = src.stem cc.add_include_dir(distutils.sysconfig.get_python_inc()) cc.add_library_dir(os.path.join(sys.base_exec_prefix, 'libs')) # First the CLI executable objs = cc.compile([str(src)]) cc.link_executable(objs, exe) # Now the GUI executable cc.define_macro('WINDOWS') objs = cc.compile([str(src)]) cc.link_executable(objs, exe + 'w') if __name__ == "__main__": compile("zastub.c")
В результате запускается «Limited ABI», поэтому он будет работать без изменений с любой версией Python 3.x. Все, что ему нужно, это чтобы Python python3.dll был в PATH пользователя.
Для полностью автономного дистрибутива вы можете собрать файл инсталятора install.exe , в котором будут находится дистрибутив Python вместе с приложением, что будет работать на любом ПК с соответствующей архитектурой.
- КРАТКИЙ ОБЗОР МАТЕРИАЛА.
- Интерфейс командной строки модуля zipapp
- Функции create_archive() модуля zipapp
- Функция get_interpreter() модуля zipapp
- Тонкости распространения исполняемых приложений Python
- Автономное приложение на Python с помощью zipapp
- Создание исполняемого Python файла для Windows
Как сделать исполняемый файл из скрипта Python
Вы завидуете разработчикам Go, которые создают исполняемый файл и легко передают его своим клиентам? Разве не было бы замечательно, если бы и клиенты питонистов могли запускать их приложение, ничего не устанавливая? Это мечта и PyInstaller — один из способов реализовать её в экосистеме Python.
Существует бесчисленное множество руководств по настройке виртуальных сред, управления зависимостями и публикациям в PyPI, что полезно при создании библиотек Python. Для разработчиков, создающих приложения Python, информации гораздо меньше. Этот урок для те, кто хочет распространять приложения среди пользователей, которые могут быть не только разработчиками Python.
В этом уроке вы узнаете:
- как PyInstaller может упростить распространение приложений;
- как использовать PyInstaller в ваших собственных проектах;
- как отлаживать ошибки PyInstaller;
- что PyInstaller не может.
PyInstaller дает вам возможность создать папку или исполняемый файл, который пользователи могут сразу запустить без дополнительной установки. Чтобы полностью оценить возможности PyInstaller, полезно еще раз вернуться к некоторым проблемам распространения, которых помогает избежать PyInstaller.
Как из файла Python3 создать .exe на Windows

Мы рассмотрим создание .exe с помощью библиотеки модуля py2exe. Для этого необходим Python 3.4 и ниже.
Если у вас установлена более высокая версия Python, попробуйте использовать Способ 2 (ниже)
В этом примере мы рассмотрим создание .exe на примере Python3.4.
Прежде всего на нужно создать виртуальное окружение для Python3.4. В этом примере мы назовем myenv, Вы можете выбрать любое другое имя, но не забывайте сделать соответствующие изменения.
На терминале наберите следующие команды:
>py -3.4 -m venv myenv > myenv\Scripts\activate.bat
В командной строке появится префикс myenv, а это значит, что виртуальное окружение с именем myenv загружено. Все команды Python теперь будет использовать новое виртуальное окружение.
Теперь давайте установим py2exe (https://pypi.python.org/pypi/py2exe~~HEAD=dobj) в нашем виртуальном окружении:
>pip install py2exe
И, наконец, чтобы создать единый EXE-файл, в нашем виртуальном окружении выполняем команду:
>python -m py2exe.build_exe hello.py -c --bundle-files 0
(замените hello.py на имя вашего скрипта. Если скрипт находится в другой папке, то нужно использовать полный путь к вашему сценарию, например, C:\Projects\Python\ hello.py). Это создаст папку DIST, которая содержит исполняемый файл. Для быстрого доступа к нему, наберите в терминале:
> explorer dist
Вы увидите путь к папке, где находится EXE-файл.
Примечание: При выполнении, откроется окно и исчезают так же быстро, как и появилось.
Это происходит потому, что операционная система автоматически закрывает терминал, в котором консольная программа закончена.
Для того, чтобы изменить эту ситуацию, можно добавить строчку
> input (" Нажмите для выхода . ")
в конце файла Python. Интерпретатор будет ждать ввода пользователя, а окно будет оставаться открытым, пока пользователь не нажимает клавишу ввода.
Вы можете подробно изучить использование py2exe в документации на странице модуля: https://pypi.python.org/pypi/py2exe
Выход из виртуального окружения производится командой
>deactivate
Способ 2
Через командную строку Windows устанавливаем pyinstaller:
>pip install pyinstaller
В командной строке переходим в папку, где находится файл
cd c:\.
Затем в командной строке набираем команду
pyinstaller --onefile example.py
Вместо exapmle.py используем имя файла, из которого нужно создать exe файл.
Через пару минут все готово! Скоркее всего, exe файл будет находится во созданной подпапке dist
Автономный исполняемый файл в PYTHON
И снова ночи! И снова нужна помощь, на этот раз не получается создать полностью автономный исполняемый файл приложения. Варант держать все файлы в одной папке не подходит, про него я читал, да и у самого получалсь таким образом запускать приложение на другом ПК. Я нашел ветку в англоязычной части StackOverflow, но разобраться не смог. Попробую изложить то, в чем, как мне кажется, более менее разобрался. Есть скрипт небольшой программы, все работает, все замечательно. В программе использована внешняя картинка и вот её вшить в .exe’шник не выходит. Я использовал такое руководство к действию:
Находится она в этой ветке: https://stackoverflow.com/questions/7674790/bundling-data-files-with-pyinstaller-onefile/63997336#63997336 Из того, что я там понял это: 1 — в скрип, встрайвается сл. код:
def resource_path(relative_path): """ Get absolute path to resource, works for dev and for PyInstaller """ try: # PyInstaller creates a temp folder and stores path in _MEIPASS base_path = sys._MEIPASS except Exception: base_path = os.path.abspath(".") return os.path.join(base_path, relative_path)
2 — нужно задействовать def resource_path в скрипте, связав с картинками:
img_dir = resource_path("img")
Уже на этом этапе я не понимаю как это сделать. В моем скрипте (прикреплю полный листинг в конце), для иконки окна используется сл. конструкция:
icon = tk.PhotoImage(file=r'H:\PYTHON\CATRi\res\CATRi.png') app.iconphoto(False, icon)
Каким образом связать это с тем что я написал выше, что бы задействовать def resource_path, не понимаю. 3 — создать и отредактировать файл .spec, тут вроде бы все понятно. 4 — скомпилировать и радоваться. С этим тоже вопросов не возникает. Прикладываю полный листинг программы:
import tkinter as tk from tkinter import ttk import sys import os def res_path(relative_path): """ Get absolute path to resource, works for dev and for PyInstaller """ try: # PyInstaller creates a temp folder and stores path in _MEIPASS base_path = sys._MEIPASS except Exception: base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) # ----------------------------------------------------------------------------------- MAIN WINDOW app = tk.Tk() app.title('CATRi') icon = tk.PhotoImage(file=r'H:\PYTHON\CATRi\res\CATRi.png') app.iconphoto(False, icon) app.geometry('300x600') app.resizable(width=False, height=False) app.update_idletasks() s = app.geometry() s = s.split('+') s = s[0].split('x') width_app = int(s[0]) height_app = int(s[1]) w = app.winfo_screenwidth() h = app.winfo_screenheight() w = w // 2 h = h // 2 w = w - width_app // 2 h = h - height_app // 2 app.geometry('+<>+<>'.format(w, h)) # ----------------------------------------------------------------------------------- INTERFACE # ---------------------------------------------------------------- Patient Section labelTitle = tk.Label(text='Пациент:', fg='#eee', bg='#333') labelTitle.place(relwidth=1.0, relx=.5, rely=.02, anchor='center') patientName = tk.StringVar() patientNameLabel = tk.Label(text='ФИО:') patientNameLabel.place(x=8, y=45, anchor='w') patientName_entry = tk.Entry(textvariable=patientName) patientName_entry.place(x=45, y=45, relwidth=.8, anchor='w') patientDate = tk.StringVar() patientDateLabel = tk.Label(text='Дата рождения:') patientDateLabel.place(x=8, y=68, anchor='w') patientDate_entry = tk.Entry(textvariable=patientDate) patientDate_entry.place(x=101, y=68, relwidth=.612, anchor='w') patientSexLabel = tk.Label(text='Пол:') patientSexLabel.place(x=68, y=92, anchor='w') patientSelectSex = ttk.Combobox(app, values=['мужской', 'женский']) patientSelectSex.place(x=101, y=92, relwidth=.25, anchor='w') # ----------------------------------------------------------------------------------- PARAMETERS labelTitle = tk.Label(text='Анамнез:', fg='#eee', bg='#333') labelTitle.place(relwidth=1.0, relx=.5, rely=.206, anchor='center') # ---------------------------------------------------------------- Course Select courseLabel = tk.Label(text='1. Течение:') courseLabel.place(x=7, y=150, anchor='w') courseSelect = ttk.Combobox(app, values=['бессимптомное', 'симптомное', '']) courseSelect.place(x=11, y=177, relwidth=.40, anchor='w') courseSelect.current(2) # ---------------------------------------------------------------- Stenosis Select stenosisLabel = tk.Label(text='2. Стеноз ВСА:') stenosisLabel.place(x=162, y=150, anchor='w') stenosisSelect = ttk.Combobox(app, values=['нет', 'менее 50%', 'от 50 до 70%', 'более 70%', 'окклюзия', '']) stenosisSelect.place(x=165, y=177, relwidth=.40, anchor='w') stenosisSelect.current(5) # ---------------------------------------------------------------- Plaque Select plaqueLabel = tk.Label(text='3. Бляшка:') plaqueLabel.place(x=8, y=227, anchor='w') plaqueSelect = ttk.Combobox(app, values=['нет', 'стабильная', 'нестабильная', '']) plaqueSelect.place(x=11, y=254, relwidth=.40, anchor='w') plaqueSelect.current(3) # ---------------------------------------------------------------- Hearth Select hearthLabel = tk.Label(text='4. Очаг ишемии:') hearthLabel.place(x=162, y=227, anchor='w') hearthSelect = ttk.Combobox(app, values=['нет', 'менее 2,5 см', '2,5 см', 'более 2,5 см', '']) hearthSelect.place(x=165, y=254, relwidth=.40, anchor='w') hearthSelect.current(4) # ---------------------------------------------------------------- Transformation Select transformationLabel = '5. Геморрагическая \n трансформация:' transformationLabel = tk.Label(text=transformationLabel, justify=tk.LEFT) transformationLabel.place(x=7, y=300, anchor='w') transformationSelect = ttk.Combobox(app, values=['нет', 'есть', '']) transformationSelect.place(x=11, y=331, relwidth=.40, anchor='w') transformationSelect.current(2) # ---------------------------------------------------------------- Deficit Select deficitLabel = '6. Неврологический \n дефицит:' deficitLabel = tk.Label(text=deficitLabel, justify=tk.LEFT) deficitLabel.place(x=162, y=300, anchor='w') deficitSelect = ttk.Combobox(app, values=['нет', 'Ренкин 1-3', 'Ренкин 4-5', '']) deficitSelect.place(x=165, y=331, relwidth=.40, anchor='w') deficitSelect.current(3) # ----------------------------------------------------------------------------------- CONCLUSION labelTitle = tk.Label(text='Заключение:', fg='#eee', bg='#333') labelTitle.place(relwidth=1.0, relx=.5, rely=.68, anchor='center') # ----------------------------------------------------------------------------------- ENGINE def clearPatientValues(): patientName_entry.delete(0, tk.END) patientDate_entry.delete(0, tk.END) patientSelectSex.set('') def appAnalyse(): courseVar = courseSelect.get() stenosisVar = stenosisSelect.get() plaqueVar = plaqueSelect.get() hearthVar = hearthSelect.get() transformationVar = transformationSelect.get() deficitVar = deficitSelect.get() if courseVar == 'бессимптомное': courseVar = int('0') elif courseVar == 'симптомное': courseVar = int('2') elif courseVar == '': courseVar = str('') if stenosisVar == 'нет': stenosisVar = int('0') elif stenosisVar == 'менее 50%': stenosisVar = int("1") elif stenosisVar == 'от 50 до 70%': stenosisVar = int('5') elif stenosisVar == 'более 70%': stenosisVar = int('10') elif stenosisVar == 'окклюзия': stenosisVar = int('30') elif stenosisVar == '': stenosisVar = str('') if plaqueVar == 'нет': plaqueVar = int('0') elif plaqueVar == 'стабильная': plaqueVar = int('1') elif plaqueVar == 'нестабильная': plaqueVar = int('5') elif plaqueVar == '': plaqueVar = str('') if hearthVar == 'нет': hearthVar = int('0') elif hearthVar == 'менее 2,5 см': hearthVar = int('1') elif hearthVar == '2,5 см': hearthVar = int('2') elif hearthVar == 'более 2,5 см': hearthVar = int('50') elif hearthVar == '': hearthVar = str('') if transformationVar == 'нет': transformationVar = int('0') elif transformationVar == 'есть': transformationVar = int('50') elif transformationVar == '': transformationVar = str('') if deficitVar == 'нет': deficitVar = int('0') elif deficitVar == 'Ренкин 1-3': deficitVar = int('5') elif deficitVar == 'Ренкин 4-5': deficitVar = int('50') elif deficitVar == '': deficitVar = str('') print(courseVar, stenosisVar, plaqueVar, hearthVar, transformationVar, deficitVar) SCORE = courseVar + stenosisVar + plaqueVar + hearthVar + transformationVar + deficitVar print(SCORE) if SCORE == '': conclusion.config(text=f'Выберите значения!') elif SCORE == 0: conclusion.config( text=f'ЗАКЛЮЧЕНИЕ:\nРиск ОНМК отсутствует.\n\nРЕКОМЕНДОВАНО:\nСпециальных назначений не требуется.\n') elif 0 < SCORE = 50: conclusion.config( text=f'ЗАКЛЮЧЕНИЕ:\nОперативное лечение не показано.\n\nРЕКОМЕНДОВАНО:\nКонсультация сосудистого хирурга через 1 мес.\n') elif 25 < SCORE < 50: conclusion.config( text=f'ЗАКЛЮЧЕНИЕ:\nОперативное лечение не показано.\n\nРЕКОМЕНДОВАНО:\nУЗАС сонных артерий + консультация\nсосудистого хирурга 2 раза в год.\n') conclusion = '' conclusion = tk.Label(text=conclusion, anchor='center', height=10, width=41) conclusion.place(relx=.012, rely=.835, anchor='w') # ---------------------------------------------------------------- Clearing def clearComboboxValues(): courseSelect.set('') stenosisSelect.set('') plaqueSelect.set('') hearthSelect.set('') transformationSelect.set('') deficitSelect.set('') conclusion.config(text=f'') # ---------------------------------------------------------------- Buttons message_button = tk.Button(text='X', command=clearPatientValues) message_button.place(x=261, y=92, relheight=.035, relwidth=.08, anchor="w") message_button = tk.Button(text='Анализ', command=appAnalyse) message_button.place(x=165, y=370, relwidth=.40, anchor='w') message_button = tk.Button(text='Сброс', command=clearComboboxValues) message_button.place(x=11, y=370, relwidth=.40, anchor='w') app.mainloop()
Структура папок с программой:
H:/PYTHON/CATRi CATRi.py res/ # directory containing the image files main.spec

Изображение иконки (если потребуется): Сбственно если кто-то может, подскажите как можно реализовать автономность, по средством алгоритма, который я привел выше.