Я пытаюсь создать однофайловый EXE с PyInstaller, который должен включать изображение и значок. Я не могу, хоть убей, заставить его работать --onefile
.
Если я это сделаю, --onedir
все будет работать очень хорошо. Когда я использую --onefile
, он не может найти дополнительные файлы, на которые есть ссылки (при запуске скомпилированного EXE). Он находит DLL и все остальное в порядке, но не два изображения.
Я просмотрел временный каталог, созданный при запуске EXE ( \Temp\_MEI95642\
например), и файлы действительно там. Когда я помещаю EXE в этот временный каталог, он их находит. Очень непонятно.
Это то, что я добавил в .spec
файл
a.datas += [('images/icon.ico', 'D:\\[workspace]\\App\\src\\images\\icon.ico', 'DATA'),
('images/loaderani.gif','D:\\[workspace]\\App\\src\\images\\loaderani.gif','DATA')]
Я должен добавить, что я также пытался не помещать их во вложенные папки, но это не имело значения.
Изменить: помечен новый ответ как правильный из-за обновления PyInstaller.
источник
a.datas += ...
) действительно помогла мне только что. в документации по pyinstaller говорится об использовании,COLLECT
но при использовании не удается поместить файлы в двоичный файл--onefile
Ответы:
Более новые версии PyInstaller больше не устанавливают
env
переменную, поэтому отличный ответ Шиша не сработает. Теперь путь устанавливается какsys._MEIPASS
:источник
_MEIPASS2
envs, ноsys._MEIPASS
работает хорошо, поэтому +1. Я предлагаю:path = getattr(sys, '_MEIPASS', os.getcwd())
AttributeError
вместо более общегоException
. Я столкнулся с проблемами, когда мне не хватало импорта sys, а путь по умолчанию был установленos.path.abspath
.pyinstaller распаковывает ваши данные во временную папку и сохраняет этот путь к каталогу в
_MEIPASS2
переменной среды. Чтобы получить_MEIPASS2
каталог в упакованном режиме и использовать локальный каталог в распакованном (разработке) режиме, я использую это:Вывод:
источник
resource_path("file_to_be_accessed.mp3")
. Будьте осторожны, используйте max 'answer для текущей версии PyInstaller.--one-file
опции?Все остальные ответы используют текущий рабочий каталог в случае, если приложение не установлено PyInstalled (т.е.
sys._MEIPASS
не установлено). Это неправильно, так как это не позволяет вам запустить приложение из каталога, отличного от того, в котором находится ваш скрипт.Лучшее решение:
источник
os.env['_MEIPASS2']
(с использованием среды ОС) был старый метод получения каталога. AFAIKsys._MEIPASS
- единственный правильный метод сейчас.resource_path()
находится в основном скрипте. Есть ли способ заставить его работать, если вместо этого он написан в модуле? На данный момент он будет пытаться получить ресурсы из папки, в которой находится модуль, а не из основной.__file__
. Если вы хотите, чтобы модуль имел возможность доступа к ресурсам за пределами его собственной области действия, вам необходимо реализовать, чтобы код импорта предоставлял путь к ним для использования вашим модулем, например, с помощью модуля, предоставляющего вызов "set_resource_location ()", который вызовы импортера - не думайте, что это отличается от того, как вы должны работать, когда не используете pyinstaller.Возможно, я пропустил шаг или сделал что-то не так, но приведенные выше методы не объединяли файлы данных с PyInstaller в один exe-файл. Позвольте мне рассказать о том, что я сделал.
Шаг: запишите один из вышеуказанных методов в свой файл py с импортом модулей sys и os. Я пробовал их обоих. Последний:
Шаг: Запишите pyi-makepec file.py в консоль, чтобы создать файл file.spec.
Шаг: Откройте file.spec с помощью Notepad ++, чтобы добавить файлы данных, как показано ниже:
Шаг: Я выполнил вышеуказанные шаги, а затем сохранил файл спецификации. Наконец открыл консоль и напишите pyinstaller file.spec (в моем случае file = Converter-GUI).
Вывод: в папке dist по-прежнему больше одного файла.
Примечание: я использую Python 3.5.
РЕДАКТИРОВАТЬ: Наконец, он работает с методом Джонатана Рейнхарта.
Шаг: Добавьте приведенные ниже коды в свой файл Python с импортом sys и os.
Шаг: вызовите указанную выше функцию, добавив путь к вашему файлу:
Шаг: напишите указанную выше переменную, которая вызывает функцию туда, где вашим кодам нужен путь. В моем случае это:
Шаг: откройте консоль в том же каталоге, что и ваш файл python, напишите коды, как показано ниже:
Шаг: Сохраните и выйдите из файла пути. Перейдите в свою папку, которая включает файл спецификации и py. Снова откройте окно консоли и введите следующую команду:
После шага 6. ваш единственный файл готов к использованию.
источник
a.datas
массив из шага 5 принимает кортежи строк, см. Pythonhosted.org/PyInstaller/spec-files.html#adding-data-files для справки.Вместо того, чтобы переписать весь код пути, как было предложено, я изменил рабочий каталог:
Просто добавьте эти две строки в начало вашего кода, остальные можете оставить как есть.
источник
Небольшое изменение принятого ответа.
источник
Используя отличный ответ от Макса и этот пост о добавлении дополнительных файлов данных, таких как изображения или звук, а также мое собственное исследование / тестирование, я выяснил, что, по моему мнению, является самым простым способом добавления таких файлов.
Если вы хотите увидеть живой пример, мой репозиторий находится здесь, на GitHub.
Примечание: это для компиляции с использованием команды
--onefile
или-F
с pyinstaller.Моя среда выглядит следующим образом.
Решение проблемы в 2 шага
Чтобы решить эту проблему, нам нужно специально сообщить Pyinstaller, что у нас есть дополнительные файлы, которые необходимо «связать» с приложением.
Нам также необходимо использовать «относительный» путь , чтобы приложение могло работать правильно, когда оно выполняется как скрипт Python или замороженный EXE.
При этом нам нужна функция, которая позволяет нам иметь относительные пути. Используя функцию, которую опубликовал Макс, мы можем легко решить относительный путь.
Мы бы использовали указанную выше функцию таким образом, чтобы значок приложения отображался, когда приложение работает либо как сценарий, либо как замороженный EXE.
Следующим шагом является то, что нам нужно указать Pyinstaller, где найти дополнительные файлы при его компиляции, чтобы при запуске приложения они создавались во временном каталоге.
Мы можем решить эту проблему двумя способами, как показано в документации , но я лично предпочитаю управлять своим собственным файлом .spec, так что мы собираемся это сделать.
Во-первых, у вас уже должен быть файл .spec. В моем случае я смог создать то, что мне нужно, запустив
pyinstaller
дополнительные аргументы, вы можете найти дополнительные аргументы здесь . Из-за этого мой файл спецификаций может немного отличаться от вашего, но я публикую его для справки после того, как объясню важные моменты.added_files - это, по сути, список, содержащий кортежи, в моем случае я хочу добавить только ОДНО изображение, но вы можете добавить несколько ico, png или jpg, используя
('app/img/*.ico', 'app/img')
Вы также можете создать другой кортеж, например, чтобыadded_files = [ (), (), ()]
иметь несколько импортовПервая часть кортежа определяет, какой файл или какой тип файла вы хотите добавить, а также где их найти. Думайте об этом как о CTRL + C
Вторая часть кортежа сообщает Pyinstaller, что нужно сделать путь app / img / и поместить файлы в этот каталог ОТНОСИТЕЛЬНО к тому временному каталогу, который создается при запуске .exe. Думайте об этом как о CTRL + V
Под
a = Analysis([main...
, я установилdatas=added_files
, изначально это было,datas=[]
но мы хотим, чтобы список импорта был ... ну, импортирован, поэтому мы передаем наши пользовательские импорты.Вам не нужно этого делать, если вам не нужен конкретный значок для EXE, в нижней части файла спецификации я говорю Pyinstaller, чтобы установить значок моего приложения для exe с опцией
icon='app\\img\\app_icon.ico'
.Компиляция в EXE
Я очень ленив; Я не люблю печатать больше, чем должен. Я создал файл .bat, по которому можно просто щелкнуть. Вам не нужно этого делать, этот код будет отлично работать и без него в командной строке.
Поскольку файл .spec содержит все наши параметры компиляции и аргументы (также известные как параметры), нам просто нужно передать этот файл .spec в Pyinstaller.
источник
Самая распространенная жалоба / вопрос, которые я видел по поводу PyInstaller, - это «мой код не может найти файл данных, который я определенно включил в пакет, где он?», И нелегко увидеть, что / где ваш код выполняет поиск, потому что извлеченный код находится во временном месте и удаляется при выходе. Добавьте этот фрагмент кода, чтобы увидеть, что включено в ваш onefile и где он находится, используя @Jonathon Reinhart's
resource_path()
источник
Давно (ну очень давно) занимаюсь этим вопросом . Я искал почти все источники, но все не складывалось в моей голове.
Наконец, я думаю, что выяснил точные шаги, которым нужно следовать, и хотел бы поделиться.
Обратите внимание, что в моем ответе используется информация об ответах других на этот вопрос.
Как создать автономный исполняемый файл проекта Python.
Предположим, у нас есть project_folder и дерево файлов выглядит следующим образом:
Прежде всего, предположим, что вы определили свои пути
sound/
иimg/
папки в переменныеsound_dir
иimg_dir
следующим образом :Вы должны изменить их следующим образом:
Куда,
resource_path()
определяется в верхней части вашего скрипта как:Активируйте виртуальный env при использовании venv,
Установите pyinstaller, если вы еще этого не сделали:
pip3 install pyinstaller
.Бегать:
pyi-makespec --onefile main.py
создать файл спецификации для процесса компиляции и сборки.Это изменит иерархию файлов на:
Открытый (с редактором)
main.spec
:Вверху вставьте:
Затем измените строку
datas=[],
наdatas=added_files,
Подробнее о выполняемых операциях
main.spec
см. Здесь.Бегать
pyinstaller --onefile main.spec
И это все, вы можете запустить
main
вproject_folder/dist
из любой точки мира, не имея ничего другого в своей папке. Вы можете распространять только этотmain
файл. Теперь это настоящая автономная версия .источник
Другое решение - создать обработчик времени выполнения, который будет копировать (или перемещать) ваши данные (файлы / папки) в каталог, в котором хранится исполняемый файл. Хук - это простой файл Python, который может делать почти все, непосредственно перед запуском вашего приложения. Чтобы установить его, вы должны использовать
--runtime-hook=my_hook.py
опцию pyinstaller. Итак, если ваши данные представляют собой папку с изображениями , вы должны выполнить команду:Cp_images_hook.py может быть примерно таким:
Перед каждой казнью изображения папка перемещается в текущий каталог (из папки _MEIPASS), поэтому исполняемый файл всегда будет иметь к нему доступ. Таким образом, нет необходимости изменять код вашего проекта.
Второе решение
Вы можете воспользоваться механизмом runtime-hook и изменить текущий каталог, что, по мнению некоторых разработчиков, не является хорошей практикой, но работает нормально.
Код перехвата можно найти ниже:
источник
Я нашел существующие ответы запутанными, и мне потребовалось много времени, чтобы понять, в чем проблема. Вот подборка всего, что я нашел.
Когда я запускаю свое приложение, я получаю сообщение об ошибке
Failed to execute script foo
(еслиfoo.py
это основной файл). Чтобы устранить эту проблему, не запускайте PyInstaller с--noconsole
(или отредактируйте,main.spec
чтобы изменитьconsole=False
=>console=True
). При этом запустите исполняемый файл из командной строки, и вы увидите ошибку.Первое, что нужно проверить, это правильно ли упаковывает ваши лишние файлы. Вы должны добавить кортежи, например,
('x', 'x')
если хотите, чтобы папкаx
была включена.После сбоя не нажимайте ОК. Если вы работаете в Windows, вы можете использовать Поиск во всем . Найдите один из ваших файлов (например
sword.png
). Вы должны найти временный путь, по которому были распакованы файлы (напримерC:\Users\ashes999\AppData\Local\Temp\_MEI157682\images\sword.png
). Вы можете просмотреть этот каталог и убедиться, что в нем есть все. Если вы не можете найти его таким образом, поищите что-нибудь вродеmain.exe.manifest
(Windows) илиpython35.dll
(если вы используете Python 3.5).Если установщик включает все, следующая вероятная проблема - файловый ввод-вывод: ваш код Python ищет файлы в каталоге исполняемого файла, а не во временном каталоге.
Чтобы исправить это, любой из ответов на этот вопрос работает. Лично я обнаружил, что все они работают: условно меняйте каталог в основном файле точки входа, а все остальное работает как есть:
if hasattr(sys, '_MEIPASS'): os.chdir(sys._MEIPASS)
источник
Если вы все еще пытаетесь поместить файлы относительно исполняемого файла, а не во временный каталог, вам необходимо скопировать его самостоятельно. Вот как я закончил это.
https://stackoverflow.com/a/59415662/999943
Вы добавляете в файл спецификации шаг, который копирует файловую систему в переменную DISTPATH.
Надеюсь, это поможет.
источник