[python] PyInstaller (-onefile)로 데이터 파일 번들링

이미지와 아이콘을 포함하는 PyInstaller로 한 파일 EXE를 빌드하려고합니다. 나는 내 삶을 위해 그것을 작동시킬 수 없습니다 --onefile.

내가 할 경우 --onedir모든 것이 잘 작동합니다. 를 사용 --onefile하면 참조 된 추가 파일을 찾을 수 없습니다 (컴파일 된 EXE를 실행할 때). 두 이미지가 아닌 DLL과 다른 모든 것을 찾습니다.

EXE ( \Temp\_MEI95642\예를 들어)를 실행할 때 생성 된 temp-dir을 살펴 보았고 파일이 실제로 거기에 있습니다. 그 임시 디렉토리에 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 업데이트로 인해 최신 답변이 올바른 것으로 표시되었습니다.



답변

최신 버전의 PyInstaller는 env더 이상 변수를 설정하지 않으므로 Shish의 탁월한 답변 이 작동하지 않습니다. 이제 경로는 다음과 같이 설정됩니다 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)


답변

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"


답변

다른 모든 답변 은 응용 프로그램이 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)


답변

아마도 내가 한 단계를 놓쳤거나 뭔가 잘못했지만 위의 방법은 PyInstaller와 함께 데이터 파일을 하나의 exe 파일로 묶지 않았습니다. 내가 한 단계를 공유하겠습니다.

  1. 단계 : sys 및 os 모듈을 가져 와서 위의 방법 중 하나를 py 파일에 작성하십시오. 나는 둘 다 시도했다. 마지막은 다음과 같습니다.

    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)
  2. 단계 : 콘솔에 pyi-makespec file.py 를 작성하여 file.spec 파일을 만듭니다.

  3. 단계 : Open, file.spec with Notepad ++ to add the data files like below :

    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')
  4. 단계 : 위 단계를 따른 다음 사양 파일을 저장했습니다. 마침내 콘솔을 열고 pyinstaller file.spec (제 경우에는 file = Converter-GUI)을 작성합니다.

결론 : dist 폴더에는 여전히 하나 이상의 파일이 있습니다.

참고 : 저는 Python 3.5를 사용하고 있습니다.

편집 : 마지막으로 Jonathan Reinhart의 방법 으로 작동합니다 .

  1. 단계 : 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)
  2. 단계 : 파일 경로를 추가하여 위 함수를 호출합니다.

    image_path = resource_path("Converter-GUI.ico")
  3. 단계 : 코드에 경로가 필요한 위치에 함수를 호출하는 위의 변수를 작성하십시오. 제 경우에는 다음과 같습니다.

        self.window.iconbitmap(image_path)
  4. 단계 : Python 파일의 동일한 디렉토리에서 콘솔을 열고 아래와 같은 코드를 작성합니다.

        pyinstaller --onefile your_file.py
  5. 단계 : python 파일의 .spec 파일을 열고 a.datas 배열을 추가하고 3 단계에서 편집하기 전에 위에 제공된 exe 클래스에 아이콘을 추가합니다.
  6. 단계 : 경로 파일을 저장하고 종료합니다. spec 및 py 파일이 포함 된 폴더로 이동하십시오. 콘솔 창을 다시 열고 아래 명령을 입력하십시오.

        pyinstaller your_file.spec

6. 단계가 끝나면 하나의 파일을 사용할 수 있습니다.


답변

제안 된대로 모든 경로 코드를 다시 작성하는 대신 작업 디렉토리를 변경했습니다.

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

코드 시작 부분에이 두 줄을 추가하기 만하면 나머지는 그대로 둘 수 있습니다.


답변

허용되는 답변에 약간의 수정이 있습니다.

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)


답변

내가 wrt PyInstaller에서 본 가장 일반적인 불만 / 질문은 “내 코드가 번들에 확실히 포함 된 데이터 파일을 찾을 수 없습니다. 어디에 있습니까?”이며, 코드가 무엇인지, 어디에 있는지 쉽게 알 수 없습니다. 추출 된 코드가 임시 위치에 있고 종료시 제거되기 때문에 검색 중입니다. 코드의이 비트를 추가 참조 하여 onefile에 포함하고, @Jonathon 라인 하트의를 사용하여이되는 경우 어떤resource_path()

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