Статья Пакуем Python в EXE

Всем известно что питон это интерпретируемый, а не компилируемый язык . И в первую очередь рассчитан для работы в командной строке. Тем не менее, существует много вариантов оформления кода в GUI при необходимости. Но сегодня речь пойдёт об упаковке программы в EXE.

Зачем вообще это надо? Да по сути и не надо в большинстве случаев. Но бывает, что вы написали или скопировали какую-нибудь интересную тулзу, и хотите с ней поделиться. Конечно, если у того, кому вы отправляете прогу есть Python, то проблем нет. А бывает, что человек не шарит совсем в кодинге, и как пользователь тоже не имеет установленного питона нужной версии.

Вот тогда и выручает упаковка файла или файлов в EXE. Такой файл будет запускаться и работать по клику мышки, всё очень просто.

Для сборки файлов в экзешник есть разные приложения. Я рассмотрю самый простой, без всяких заморочек, с которым разберётся любой новичок.

Скачиваем прогу
Распаковываем архив. Внутри папки будет конвертер, запускающийся по клику мышки.

Так, нам понадобится подопытный. Для этого я сотряпал простой текстовый редактор, который открывает файл, и после редактирования сохраняет его. Ну или просто в окне можно что-то написать и сохранить.

Код редактора:

Python:
# -*- coding:utf -8 -*-
__version__ = 'Version:1.0'
from tkinter import *
from tkinter import filedialog as fd

root = Tk()
root.title("Textedit  " +str(__version__))
root.resizable(width=False, height=False)
root.geometry("420x300+300+300")
calculated_text = Text(root,height=15, width=50)

def insertText():
    file_name = fd.askopenfilename()
    f = open(file_name)
    s = f.read()
    calculated_text.insert(1.0, s)
    f.close()

def extractText():
    file_name = fd.asksaveasfilename(filetypes=(("TXT files", "*.txt"),
                                        ("HTML files", "*.html;*.htm"),
                                                ("All files", "*.*") ))
    f = open(file_name, 'w')
    s = calculated_text.get(1.0, END)
    f.write(s)
    f.close()  
   
def erase():  
    calculated_text.delete('1.0', END)
   
b1 = Button(text="Открыть",command=insertText)
b1.grid(row=3, column=0, sticky=E, padx=5, pady=8,)
b2 = Button(text="Сохранить", command=extractText)
b2.grid(row=3, column=1, sticky=E, padx=5, pady=8,)
erase_button = Button(text="Очистить", command=erase)
erase_button.grid(row=3, column=2, padx=35, pady=8, sticky="W")

scrollb = Scrollbar(root, command=calculated_text.yview)
scrollb.grid(row=4, column=4, sticky='nsew')
calculated_text.grid(row=4, column=0, sticky='nsew', columnspan=3)
calculated_text.configure(yscrollcommand=scrollb.set)

root.mainloop()

Запускаем прогу, так она выглядит
pyins.png


Выставляем следующие настройки -windowed чтобы прога запускалась без консоли, название выходного файла, onefile чтобы сборка была в один файл, noupx уже стоит по умолчанию ибо сжатие не всегда прокатывает. Ну и собственно выбираете нужный файл питоновский, который будете упаковывать. Также есть возможность выбрать иконку для exe-шника, поддерживается только ICO.

Вот что получилось
pyins2.png


Жмём Build, прога чуток поколдует, выдаст нам сообщение об успешной сборке.
pyins3.png


После этого в папке с конвертором появятся 2 папки
pyins4.png


В папке dist и будет лежать наш готовый дистрибутив в формате EXE. Переместите его куда угодно, а потом обе эти папки можно смело удалять. Теперь проверяем работоспособность выходного файла, кликнув на него.
pyins5.png


Алиллуйя, работает! :)

P.S. Редактор писался второпях, там есть косячок - при сохранении файла, расширение само не ставится, нужно вручную писать. Если кто поправит, буду признателен. Мне уже некогда, уезжаю отдыхать на 2 недели сейчас, так что на форуме меня не будет это время.
 
Последнее редактирование:

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
exe файл уже получился, но открывается и командная строка..можно как то от строки избавиться?

Нужно читать внимательнее - процитирую сам себя:
Выставляем следующие настройки - windowed чтобы прога запускалась без консоли
 

LeMit

Member
20.02.2020
5
0
BIT
0
Всем доброго времени суток! Возникла проблема запуска из скомпилированного exe-файла других скриптов, написанных на питошке) Запускаю на компе, где не установлен питон. Скомпилированного exe-файл был собран через pyinstaller обычной командой: pyinstaller avtotest.py
в коде скомпилированного exe-файла запускаю другой скрипт написанный на питошке так:
Python:
def Adminnewpriglash():
    subprocess.Popen("python C:\Avtotest\Adminnewpriglash.py")
 adminmenu2.add_command(label="Приглашения", command=Adminnewpriglash)

В скомпилированном exe-файле при нажатии на кнопку "Приглашения" получаю ошибку в консоли:

Код:
Exception in Tkinter callback
Traceback (most recent call last):
  File "tkinter\__init__.py", line 1705, in __call__
  File "avtotest.py", line 159, in Adminnewpriglash
  File "subprocess.py", line 800, in __init__
  File "subprocess.py", line 1207, in _execute_child
FileNotFoundError: [WinError 2] Не удается найти указанный файл

Что нужно чтобы скрипты запускались? мб изменить код запуска? Есть вариант перекомпилить все скрипты в exe и уже запускать exe файлы из главного exe, но их достаточно много и отладка станет неудобной, поэтому ищу варианты как сделать действующий код рабочим)
 

LeMit

Member
20.02.2020
5
0
BIT
0
Конечно же он никогда не запустится с таким кодом на другом компе. Откуда там возьмётся файл по адресу C:\Avtotest\Adminnewpriglash.py
Благодарю за ответ) этот файл там есть естественно) он лежит прямо по такому же адресу, который и прописан в коде)
 
20.02.2020
3
0
BIT
0
Конечно же он никогда не запустится с таким кодом на другом компе. Откуда там возьмётся файл по адресу C:\Avtotest\Adminnewpriglash.py
Снимок.PNG

А если вот такое сообщение об ошибке (когда открываешь exe файл на компьютере без питона)? В чем проблема?
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Благодарю за ответ) этот файл там есть естественно) он лежит прямо по такому же адресу, который и прописан в коде)
Это не поможет, так как интерпретатор python работает локально внутри exe. Нужно переписать программу, убрав внешнее подключение.

Посмотреть вложение 38560
А если вот такое сообщение об ошибке (когда открываешь exe файл на компьютере без питона)? В чем проблема?
Некорректная сборка, программа не видит отдельные модули библиотеки Tkinter. Пользуйтесь для сборки cx_Freeze, и прописывайте эти модули вручную, указав к ним путь.
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
А как ее переписать не подскажите?
Если совсем коротко -внешнюю программу нужно положить в одну папку с основной программой, и подключить её как модуль import Adminnewpriglash но в основной программе ещё потребуются правки в коде, чтобы модуль работал. В общем гуглите Создание своего модуля.
 

LeMit

Member
20.02.2020
5
0
BIT
0
Если совсем коротко -внешнюю программу нужно положить в одну папку с основной программой, и подключить её как модуль import Adminnewpriglash но в основной программе ещё потребуются правки в коде, чтобы модуль работал. В общем гуглите Создание своего модуля.
Вызов был сделан так:
Python:
import Adminnewpriglash

def avt3():
    Adminnewpriglash.Admnewpriglash()

adminmenu2.add_command(label="avt3", command=avt3)

только теперь при запуске главной программы сразу выполняется функция avt3, а она должна выполняться при нажатии на кнопку "avt3"
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Вызов был сделан так:
Python:
import Adminnewpriglash

def avt3():
    Adminnewpriglash.Admnewpriglash()

adminmenu2.add_command(label="avt3", command=avt3)

только теперь при запуске главной программы сразу выполняется функция avt3, а она должна выполняться при нажатии на кнопку "avt3"

В вашем коде я никакой кнопки вообще не вижу, по крайней мере этой части кода не выложено. А функция avt3 выполняется, потому что вы её вызываете, не нужно этого делать.
Вот вам простенькая программа с тремя кнопками, посмотрите как я сделал вызов функций кнопками.
Python:
# -*- coding:utf -8 -*-
# !/usr/bin/python3


__version__ = 'Version: 2'
from tkinter import *
import random
from tkinter import filedialog as fd

root = Tk()
root.resizable(width=False, height=False)
root.title("Генератор паролей  " + str(__version__))
root.geometry("450x324+300+300")
calculated_text = Text(root, height=14, width=50)


def erase():
    calculated_text.delete('1.0', END)


chars = '+-/*!&$#?=@<>abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
x = 0


def passw():
    for n in range(int(number_entry.get())):
        password = ''
        global x
        x += 1
        for i in range(int(length_entry.get())):
            password += random.choice(chars)
        if x >= 1:
            calculated_text.insert(END, "Пароль" + '  ' + str(x) + ': ' + password + "\n")
        

def savepass():
    file_name = fd.asksaveasfilename(filetypes=(("TXT files", "*.txt"),
                                                ("All files", "*.*")), defaultextension='')
    try:
        f = open(file_name, 'w')
        s = calculated_text.get(1.0, END)
        f.close()
        f.write(s)
    except FileNotFoundError:
        pass


display_button = Button(text="Сгенерить", width=8, command=passw)
erase_button = Button(text="Очистить", width=8, command=erase)
save = Button(text="Сохранить", width=8, command=savepass)

number_entry = Entry(width=10, justify=CENTER)
length_entry = Entry(width=10, justify=CENTER)
number_entry.insert(0, "8")
length_entry.insert(0, "25")

number_label = Label(text="      Количество паролей")
length_label = Label(text="      Длина пароля")
number_label.grid(row=0, column=0, sticky="w")
length_label.grid(row=1, column=0, sticky="w")
number_entry.grid(row=0, column=1, padx=1, pady=5)
length_entry.grid(row=1, column=1, padx=1, pady=5)

save.grid(row=3, column=2, padx=50, pady=5, sticky="w")
display_button.grid(row=3, column=0, padx=30, pady=5, sticky="e")
erase_button.grid(row=3, column=1, padx=30, pady=5, sticky="e")

scrollb = Scrollbar(root, command=calculated_text.yview)
scrollb.grid(row=4, column=3, sticky='nsew')
calculated_text.grid(row=4, column=0, sticky='nsew', columnspan=3)
calculated_text.configure(yscrollcommand=scrollb.set)

root.mainloop()
 

LeMit

Member
20.02.2020
5
0
BIT
0
В вашем коде я никакой кнопки вообще не вижу, по крайней мере этой части кода не выложено. А функция avt3 выполняется, потому что вы её вызываете, не нужно этого делать.
Вот вам простенькая программа с тремя кнопками, посмотрите как я сделал вызов функций кнопками.
Прощу прощение за заблуждение у меня в меню встроены label, которые я называю кнопками) в общем при нажатии на label должна вызываться функция. но сейчас работает так: если в коде убрать везде упоминание о "Adminnewpriglash", кроме как здесь "import Adminnewpriglash", то при запуске главного приложения сразу же вызывается Adminnewpriglash, что весьма странно. Если же закомментить строку "#import Adminnewpriglash", то главное приложение запускается как нужно. не пойму в чем проблема?
У Вас в коде тоже также запускается как и у меня через command:
Python:
display_button = Button(text="Сгенерить", width=8, command=passw)
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
в общем при нажатии на label должна вызываться функция
Это и есть ответ на ваш вопрос. Я уже писал выше, что у вас функция вызывается. То есть это происходит до того, как нажимаете на меню. По-моему тут всё понятно. А так телепатов нет, чтобы на конкретную строку указать, нужны кода обоих программ.
 

garrip91

New member
11.03.2020
2
0
BIT
0
Здравствуйте!

1. Пытаюсь запустить Ваш этот "Конвертер", используя "python PyInstallerGUI.py", и вижу следующее:
Traceback (most recent call last):
File "PyInstallerGUI.py", line 22, in <module>
from PIL import Image, ImageTk, ImageOps
ModuleNotFoundError: No module named 'PIL'
PS C:\Users\garri\OneDrive\Desktop\ЭДУАРД\Programming\PyInstallerGUI-master>

2. Пытаюсь загрузить "PIL", используя "pip install PIL", и вижу следующее:
ERROR: Could not find a version that satisfies the requirement PIL (from versions: none)
ERROR: No matching distribution found for PIL

НУ И КАКИМ ЖЕ ОБРАЗОМ ВЫ ЗАПУСКАЕТЕ ЭТОТ ТАК НАЗЫВАЕМЫЙ "КОНВЕРТЕР"???
 

atum

Green Team
06.02.2019
13
20
BIT
0
Здравствуйте!

1. Пытаюсь запустить Ваш этот "Конвертер", используя "python PyInstallerGUI.py", и вижу следующее:
Traceback (most recent call last):
File "PyInstallerGUI.py", line 22, in <module>
from PIL import Image, ImageTk, ImageOps
ModuleNotFoundError: No module named 'PIL'
PS C:\Users\garri\OneDrive\Desktop\ЭДУАРД\Programming\PyInstallerGUI-master>

2. Пытаюсь загрузить "PIL", используя "pip install PIL", и вижу следующее:
ERROR: Could not find a version that satisfies the requirement PIL (from versions: none)
ERROR: No matching distribution found for PIL

НУ И КАКИМ ЖЕ ОБРАЗОМ ВЫ ЗАПУСКАЕТЕ ЭТОТ ТАК НАЗЫВАЕМЫЙ "КОНВЕРТЕР"???
Убедись что запускаешь pip из того-же окружения что и pyinstaller.
А так-же, я не рекомендую использовать GUI версию pyinstaller.
Мало ли ее же тоже люди пишут и обновляют. От того и могут возникнуть ошибки.

Вот у меня тоже спортивный интерес возник.
Каким образом можно максимально уменьшить размер получаемого .exe файла.
Если к примеру, в моем .exe не используются сторонние модули, то вес моего .exe получается 6.5мб.
Можно ли, к примеру вырезать из интерпретатора python ненужные "стандартные" модули, такие как tkinter, email, imaplib, и выиграть тем самым еще пару мегабайт?
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Здравствуйте!

1. Пытаюсь запустить Ваш этот "Конвертер", используя "python PyInstallerGUI.py", и вижу следующее:
Traceback (most recent call last):
File "PyInstallerGUI.py", line 22, in <module>
from PIL import Image, ImageTk, ImageOps
ModuleNotFoundError: No module named 'PIL'
PS C:\Users\garri\OneDrive\Desktop\ЭДУАРД\Programming\PyInstallerGUI-master>

2. Пытаюсь загрузить "PIL", используя "pip install PIL", и вижу следующее:
ERROR: Could not find a version that satisfies the requirement PIL (from versions: none)
ERROR: No matching distribution found for PIL

НУ И КАКИМ ЖЕ ОБРАЗОМ ВЫ ЗАПУСКАЕТЕ ЭТОТ ТАК НАЗЫВАЕМЫЙ "КОНВЕРТЕР"???

Уже был ответ, во-первых pyinstaller не поддерживается Python версии более чем 3.7.0 Во-вторых модуль PIL давно обновился и называется pillow, поставить можно так: sudo easy_install pillow

Ну и надстройками пользоваться не обязательно, всё делается одной командой -
pyinstaller -clean -windowed -onefile -noupx main_name_programm.py

Можно ли, к примеру вырезать из ядра python ненужные "стандартные" модули, такие как tkinter, email, imaplib, и выиграть тем самым еще пару мегабайт?

Нет никакого "ядра" python. На скриншоте показал, где находятся файлы питона на винде. В либах стандартные модули, tkinter в папке tcl. Можно экспериментировать как угодно - сначала сохранить оригинальные файлы куда-нибудь, и пробовать удалять "ненужное".

551.png
 

atum

Green Team
06.02.2019
13
20
BIT
0
Нет никакого "ядра" python. На скриншоте показал, где находятся файлы питона на винде. В либах стандартные модули, tkinter в папке tcl. Можно экспериментировать как угодно - сначала сохранить оригинальные файлы куда-нибудь, и пробовать удалять "ненужное".
Ну вы меня правильно поняли, имелось ввиду из интерпретатора.
Проверю. Главное что-бы там небыло никаких проверок на целостность.
 

ff125s

New member
12.03.2020
1
0
BIT
0
Failed to execute script -- такая ошибка, пока не понимаю в чем проблема.
 

Vladej

New member
13.03.2020
1
0
BIT
0
спасибо, товарищ. Как ни странно для меня, но это работает, и что главное - всего 27 мб. А то я с нового года копаю python ради своей идеи/программки, и тут сюрприз - deploy. Это как если написать новый код для новичка (которому 51). Почему элементарное не может быть простым, вот вопрос? Благодаря вам это возможно. А то пытался сделать: pyinstaller --onefile --icon=name.ico --noconsole myscript.py - не работает как надо, консоль включается. Спасибо.
 

garrip91

New member
11.03.2020
2
0
BIT
0
Уже был ответ, во-первых pyinstaller не поддерживается Python версии более чем 3.7.0 Во-вторых модуль PIL давно обновился и называется pillow, поставить можно так: sudo easy_install pillow
Ну что касается пункта "во-первых" - у меня Python версии "3.8.1" и "Pyinstaller" у меня очень даже неплохо работает!
А что касается пункта "во-вторых" - информацию насчёт "pillow" принял к сведению, попробую это, посмотрю что получится и отпишусь, спасибо! 😉
 
15.03.2020
1
0
BIT
0
Ну вот я и вернулся с моря. Очень хорошо что есть баг, это заставило меня провести небольшое исследование по сборке файлов в EXE.
Для этого я прогнал разные ресурсы и выяснил по крайней мере 3 причины, по которым компилл не получается как надо.

Грабли, грабли, грабли...

1. Отсутствуют некоторые библиотеки dll в самой винде.
2. Присутствует скрытый импорт в модуле. В музыкальной открытке есть предупреждение: WARNING: Hidden import "pygame._view" not found!
Соответственно сборщик не может знать путей к нужному модулю.
3. Файл "моя программа.py" изначально не запускается двойным кликом мышки, а лишь через IDLE и т.п.

1 пункт решается просто - из лога копипастим название dll, гуглим, скачиваем нужную библиотеку и добавляем в system32.
2 пункт можно обойти заменой на схожий модуль ( а ведь всегда есть варианты), но не имеющий скрытый импорт.
3 Ну здесь даже если не будет ни одной ошибки или предупреждения, то сборка обречена на провал. И единственным выходом будет переписать код программы.
Наверняка и другие проблемы бывают, это лишь то что я увидел.

Вывод можно сделать следующий - не все программы корректно соберутся, и если какую-то программку очень нужно собрать в экзешник, придётся её перепиливать. И это может занять больше времени, чем изначальное написание.



Так как за 2 недели никого не нашлось, кто поправил бы код, делаю это сам :)
Всё просто - аргумент 'defaultextension' не указал, всего лишь добавить его в функцию extractText нужно, получится так:

Python:
def extractText():
    file_name = fd.asksaveasfilename(filetypes=(("TXT files", "*.txt"),
                                        ("HTML files", "*.html;*.htm"),
                                                ("All files", "*.*")),defaultextension='')
    f = open(file_name, 'w')
    s = calculated_text.get(1.0, END)
    f.write(s)
    f.close()
Теперь выбранное расширение подставляется к имени файла.


Более сложные программы можно собрать через другие тулзы. Правда работать чуток посложнее, больше телодвижений. Одна из программ cx_Freeze.

Сначала нужно установить wheel. В cmd вводим pip install wheel или pip3 install wheel

Посмотреть вложение 22232

Далее выбираем и скачиваем нужную версию cx_Freeze


Посмотреть вложение 22231

Скачанный файлик кидаем в корень диска С и в cmd вводим команду:
pip install С:\cx_Freeze-5.1.1-cp36-cp36m-win32.whl (название своей версии, если оно отличается)

В каталоге с программой которую нужно скомпилировать создайте файл "setup.py" в который разместите код:


Python:
from cx_Freeze import setup, Executable

setup(
    name = "название проги",
    version = "1.0",
    description = "описание - необязательно",
    executables = [Executable("free.py")]
)

Последний этап - переходим в cmd в папку с программой, которую нужно упаковать и вводим команду python.exe setup.py build
При успешной сборке появится папка build, в которой и будет наш EXE.

К сожалению, в отличии от pyinstaller, сборка не будет в один файл, а будет куча библиотек и прочего, без которых экзешник работать не будет. По сути мы получим портабл версию. В этом минус. Плюс заключается в том, что настройки гибкие, и даже очень упрямые программы можно собрать этим инструментом.

Попробовал собрать открытку из темы Ваяем музыкальную открытку на Python со стандартным файлом setup.py ничего не получилось. Пришлось колдовать часа 1,5 чтобы решить все вопросы.

Сначала cx_Freeze ругалась на модуль tkinter, так как не могла найти пути к tcl8.6 и tk8.6. Вручную прописал, на этом трудности не закончились. Дальше захотелось ей dll-ок, тоже добавил. Вдоволь поизвращавшись, прога успешно собралась.

Однако бесячее окно с надписью от модуля pygame резало глаза

Посмотреть вложение 22233


Ещё малость шаманских танцев с бубном и наши победили )))

Вот
Python:
import os
from cx_Freeze import setup, Executable
import sys

os.environ['TCL_LIBRARY'] = 'c:/python36/tcl/tcl8.6'
os.environ['TK_LIBRARY'] = 'c:/python36/tcl/tk8.6'
buildOptions = dict( packages = [], excludes = [], include_files=['c:/python36/DLLs/tcl86t.dll', 'c:/python36/DLLs/tk86t.dll'] )

base = None
if (sys.platform == "win32"):
    base = "Win32GUI"

setup(
    name='codeby',
    version = '1.0',
    description = '',
    options = dict(build_exe = buildOptions),executables =  [Executable("new 1.py", base=base)]
)

Весь этот гемор я победил лишь из-за спортивного интереса :ROFLMAO:

Кстати открытка с переписанным кодом в Pyinstaller тоже успешно собралась без всяких танцев с бубном :)

Посмотреть вложение 22236
Смотри,на моём -всё прекрасно работает. Скопировал всю папку на флешку, воткнул на другом компе, попытался открыть exe-шник и тут получил ошибку ввиде:
-не найден python38.dll
Нашёл его у себя на компе -> копировал во все папки билдера сначала по очереди, потом во все сразу. Всё равно не видит его. Что делать?
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!