web-dev-qa-db-ja.com

PyInstallerによるデータファイルのバンドル(--onefile)

PyInstallerを使用して、画像とアイコンを含む1ファイルのEXEを作成しようとしています。私は一生、--onefileで動作させることはできません。

--onedirを実行すると、すべて正常に動作します。 --onefileを使用すると、参照された追加ファイルが見つかりません(コンパイル済みEXEの実行時)。 2つのイメージだけではなく、DLLとその他すべてが正常に検出されます。

EXEの実行時に生成されたtemp-dir(\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の更新により、新しい回答が正しいとマークされました。

86
arboreal shark

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)
120
max

pyinstallerはデータを一時フォルダーに解凍し、このディレクトリパスを_MEIPASS2環境変数に保存します。 _MEIPASS2 dirをパックモードで取得し、ローカルディレクトリをアンパック(開発)モードで使用するには、次を使用します。

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"
49
Shish

他のすべての答えは、アプリケーションがPyInstalledでない場合(つまり、sys._MEIPASSが設定されていない場合)にcurrent working directoryを使用します。スクリプトが存在するディレクトリ以外のディレクトリからアプリケーションを実行できないため、これは間違っています。

より良い解決策:

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)
17

おそらく、ステップを逃したか、何か間違ったことをしましたが、上記の方法では、PyInstallerを使用してデータファイルを1つのexeファイルにバンドルしませんでした。私がやったことのステップを共有させてください。

  1. ステップ:sysおよびosモジュールをインポートして、上記のメソッドの1つを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. ステップ: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')
    
  4. ステップ:上記のステップに従い、スペックファイルを保存しました。最後にコンソールを開いて書いて、 pyinstaller file.spec (私の場合、file = Converter-GUI)。

結論:distフォルダーにはまだ複数のファイルがあります。

注:Python 3.5。

編集:最後に Jonathan Reinhartの方法で動作します。

  1. 手順:以下のコードを、sysおよびosをインポートしてpythonファイルに追加します。

    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.の後、1つのファイルを使用する準備ができました。

9
dildeolupbiten

代わりにすべてのパスコードを提案どおりに書き換えるために、作業ディレクトリを変更しました。

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

コードの先頭にこれらの2行を追加するだけで、残りはそのままにすることができます。

6
Bobsleigh

受け入れられた回答のわずかな変更。

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)
2
Krishna Balan

PyInstallerについて私が見た最も一般的な苦情/質問は、「私のコードは、バンドルに間違いなく含まれているデータファイルを見つけることができません。どこにありますか?」です。抽出されたコードは一時的な場所にあり、終了すると削除されるため、検索しています。 @Jonathon Reinhartのresource_path()を使用して、このコードをsee onefileに含まれるものとその場所に追加します。

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

私は既存の答えが紛らわしいことに気付き、問題がどこにあるかを突き止めるのに長い時間を費やしました。ここに私が見つけたすべての編集物があります。

アプリを実行すると、エラー_Failed to execute script foo_が表示されます(_foo.py_がメインファイルの場合)。これをトラブルシューティングするには、PyInstallerを_--noconsole_で実行しないでください(または_main.spec_を編集して_console=False_ => _console=True_を変更しないでください)。これにより、コマンドラインから実行可能ファイルを実行すると、失敗が表示されます。

最初に確認することは、余分なファイルを正しくパッケージ化していることです。フォルダーxを含める場合は、_('x', 'x')_のようなタプルを追加する必要があります。

クラッシュした後、[OK]をクリックしないでください。Windowsを使用している場合は、 Search Everything を使用できます。ファイルの1つを探します(例、_sword.png_)。ファイルを解凍した一時パスを見つける必要があります(例、_C:\Users\ashes999\AppData\Local\Temp\_MEI157682\images\sword.png_)。このディレクトリを参照して、すべてが含まれていることを確認できます。この方法で見つからない場合は、_main.exe.manifest_(Windows)または_python35.dll_(Python 3.5)を使用している場合)などを探してください。

インストーラーにすべてが含まれている場合、次に起こりやすい問題はファイルI/Oです。Pythonコードは、一時ディレクトリではなく、実行可能ファイルのディレクトリでファイルを探しています。

これを修正するには、この質問に対する回答のいずれかが機能します。個人的に、私はそれらのすべてが機能することを発見しました:メインのエントリポイントファイルで条件付きで最初にディレクトリを変更し、他のすべてはそのままで動作します:

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

0
ashes999