Подтвердить что ты не робот

Объединение файлов данных с помощью PyInstaller (--onefile)

Я пытаюсь создать однофазный EXE с PyInstaller, который должен включать изображение и значок. Я не могу за свою жизнь заставить его работать с --onefile.

Если я выполняю --onedir, все работает очень хорошо. Когда я использую --onefile, он не может найти указанные дополнительные файлы (при запуске скомпилированного EXE). Он находит DLL и все остальное прекрасным, а не два изображения.

Я посмотрел в temp-dir, сгенерированный при запуске EXE (например, \Temp\_MEI95642\), и файлы действительно там. Когда я бросаю EXE в этот каталог temp, он находит их. Очень сложно.

Это то, что я добавил в файл .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.

4b9b3361

Ответ 1

Новые версии PyInstaller больше не устанавливают переменную env, поэтому Shish отлично answer не будет работать. Теперь путь устанавливается как sys._MEIPASS:

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

pyinstaller распаковывает ваши данные во временную папку и сохраняет этот путь каталога в переменной среды _MEIPASS2. Чтобы получить директорию _MEIPASS2 в упакованном режиме и использовать локальный каталог в режиме распаковки (разработки), я использую это:

def resource_path(relative):
    return os.path.join(
        os.environ.get(
            "_MEIPASS2",
            os.path.abspath(".")
        ),
        relative
    )

Вывод:

# in development
>>> resource_path("app_icon.ico")
"/home/shish/src/my_app/app_icon.ico"

# in production
>>> resource_path("app_icon.ico")
"/tmp/_MEI34121/app_icon.ico"

Ответ 3

Все остальные ответы используют текущий рабочий каталог в случае, когда приложение не установлено PyInstalled (т.е. sys._MEIPASS не установлен). Это неправильно, так как не позволяет запускать ваше приложение из каталога, отличного от того, в котором находится ваш скрипт.

Лучшее решение:

import sys
import os

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
    return os.path.join(base_path, relative_path)

Ответ 4

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

  • Шаг: напишите один из вышеуказанных методов в файл py с импортом модулей sys и os. Я попробовал их обоих. Последнее:

    def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
        base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
        return os.path.join(base_path, relative_path)
    
  • Шаг: напишите, pyi-makepec file.py, на консоль, чтобы создать файл file.spec.

  • Шаг: Откройте файл file.spec с помощью Notepad ++, чтобы добавить файлы данных, как показано ниже:

    a = Analysis(['C:\\Users\\TCK\\Desktop\\Projeler\\Converter-GUI.py'],
                 pathex=['C:\\Users\\TCK\\Desktop\\Projeler'],
                 binaries=[],
                 datas=[],
                 hiddenimports=[],
                 hookspath=[],
                 runtime_hooks=[],
                 excludes=[],
                 win_no_prefer_redirects=False,
                 win_private_assemblies=False,
                 cipher=block_cipher)
    #Add the file like the below example
    a.datas += [('Converter-GUI.ico', 'C:\\Users\\TCK\\Desktop\\Projeler\\Converter-GUI.ico', 'DATA')]
    pyz = PYZ(a.pure, a.zipped_data,
         cipher=block_cipher)
    exe = EXE(pyz,
              a.scripts,
              exclude_binaries=True,
              name='Converter-GUI',
              debug=False,
              strip=False,
              upx=True,
              #Turn the console option False if you don't want to see the console while executing the program.
              console=False,
              #Add an icon to the program.
              icon='C:\\Users\\TCK\\Desktop\\Projeler\\Converter-GUI.ico')
    
    coll = COLLECT(exe,
                   a.binaries,
                   a.zipfiles,
                   a.datas,
                   strip=False,
                   upx=True,
                   name='Converter-GUI')
    
  • : Я выполнил следующие шаги, а затем сохранил файл spec. Наконец, откройте консоль и напишите pyinstaller file.spec (в моем случае, файл = конвертер-GUI).

Заключение: в папке dist еще несколько файлов.

Примечание. Я использую Python 3.5.

EDIT: Наконец, он работает с методом Джонатана Рейнхарта.

  • Шаг: добавьте приведенные ниже коды в файл python с импортом sys и os.

    def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
        base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
        return os.path.join(base_path, relative_path)
    
  • Шаг: вызовите вышеуказанную функцию с добавлением пути к вашему файлу:

    image_path = resource_path("Converter-GUI.ico")
    
  • Шаг: напишите вышеперечисленную переменную, которая вызывает функцию, где ваши коды должны иметь путь. В моем случае это:

        self.window.iconbitmap(image_path)
    
  • : Откройте консоль в той же папке вашего файла python, напишите коды, как показано ниже:

        pyinstaller --onefile your_file.py
    
  • : Откройте файл .spec файла python и добавьте массив a.datas и добавьте значок в класс exe, который был указан выше перед редактированием на 3-м шаге.
  • : Сохранить и выйти из файла пути. Перейдите в папку, в которую входят файл spec и py. Откройте снова окно консоли и введите следующую команду:

        pyinstaller your_file.spec
    

После 6 шага ваш один файл готов к использованию.

Ответ 5

Вместо того, чтобы переписывать весь мой код пути, как было предложено, я сменил рабочий каталог:

if getattr(sys, 'frozen', False):
    os.chdir(sys._MEIPASS)

Просто добавьте эти две строки в начале кода, вы можете оставить остальные как есть.

Ответ 6

Небольшая модификация принятого ответа.

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)

    return os.path.join(os.path.abspath("."), relative_path)

Ответ 7

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

Когда я запускаю свое приложение, я получаю сообщение об ошибке Failed to execute script foo (если foo.py является основным файлом). Чтобы устранить эту проблему, не запускайте PyInstaller с помощью --noconsole (или отредактируйте main.spec, чтобы изменить console=False = > console=True). При этом запустите исполняемый файл из командной строки, и вы увидите сбой.

Первое, что нужно проверить, - это правильно упаковать ваши лишние файлы. Вы должны добавить кортежи, например ('x', 'x'), если вы хотите включить папку x.

После сбоя не нажимайте OK. Если вы находитесь в Windows, вы можете использовать Искать все. Найдите один из ваших файлов (например, sword.png). Вы должны найти временный путь, где он распаковал файлы (например, C:\Users\ashes999\AppData\Local\Temp\_MEI157682\images\sword.png). Вы можете просмотреть этот каталог и убедиться, что он включает все. Если вы не можете найти его таким образом, найдите что-то вроде main.exe.manifest (Windows) или python35.dll (если вы используете Python 3.5).

Если программа установки включает в себя все, следующая вероятная проблема связана с файлами ввода/вывода: ваш код Python ищет в исполняемом каталоге вместо каталога temp для файлов.

Чтобы исправить это, любой из ответов на этот вопрос работает. Лично я нашел смесь из них всех, чтобы работать: сначала замените каталог в главном файле точки входа, а все остальное работает как есть:

if hasattr(sys, '_MEIPASS'): os.chdir(sys._MEIPASS)

Ответ 8

Самая распространенная жалоба/вопрос, который я видел в PyInstaller: "Мой код не может найти файл данных, который я определенно включил в комплект, где он находится?", И непросто увидеть, что/где ваш код выполняет поиск, потому что извлеченный код находится во временном местоположении и удаляется при выходе. Добавьте этот фрагмент кода, чтобы увидеть, что входит в ваш файл и где он находится, используя @Jonathon Reinhart resource_path()

for root, dirs, files in os.walk(resource_path("")):
    print(root)
    for file in files:
        print( "  ",file)