Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jsf-2/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/joomla/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用PyInstaller(--onefile)绑定数据文件_Python_Pyinstaller - Fatal编程技术网

Python 使用PyInstaller(--onefile)绑定数据文件

Python 使用PyInstaller(--onefile)绑定数据文件,python,pyinstaller,Python,Pyinstaller,我正在尝试用PyInstaller构建一个包含图像和图标的单文件EXE。我一生都无法让它与--onefile一起工作 如果我做了--onedir,一切都很好。 当我使用--onefile时,它无法找到引用的其他文件(在运行编译的EXE时)。它发现DLL和其他一切都很好,只是没有两个图像 我查看了运行EXE时生成的temp dir(例如\temp\\u MEI95642\),文件确实在那里。当我把EXE放到那个临时目录中时,它会找到它们。非常令人困惑 这是我添加到.spec文件中的内容 a.dat

我正在尝试用PyInstaller构建一个包含图像和图标的单文件EXE。我一生都无法让它与
--onefile
一起工作

如果我做了
--onedir
,一切都很好。 当我使用
--onefile
时,它无法找到引用的其他文件(在运行编译的EXE时)。它发现DLL和其他一切都很好,只是没有两个图像

我查看了运行EXE时生成的temp dir(例如
\temp\\u 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更新,将更新的答案标记为正确。

PyInstaller将数据解压到临时文件夹中,并将此目录路径存储在环境变量
\u MEIPASS2
中。要在打包模式下获取
\u 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"

PyInstaller的较新版本不再设置
env
变量,因此Shish的优秀版本将无法工作。现在路径设置为
sys.\u 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)

对已接受答案的轻微修改

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)

为了按照建议重写所有路径代码,我更改了工作目录:

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

只需在代码开头添加这两行,其余的就可以保持原样。

我发现现有答案令人困惑,花了很长时间才找出问题所在。这是我找到的所有东西的汇编

当我运行我的应用程序时,我得到一个错误
无法执行脚本foo
(如果
foo.py
是主文件)。要解决此问题,请不要使用
--noconsole运行PyInstaller(或编辑
main.spec
以更改
console=False
=>
console=True
)。这样,从命令行运行可执行文件,您将看到失败

首先要检查的是它是否正确打包了额外的文件。如果希望包含文件夹
x
,则应添加类似
('x','x')
的元组

崩溃后,不要单击“确定”。如果您使用的是Windows,则可以使用。查找您的一个文件(例如
swarm.png
)。您应该找到解压文件的临时路径(例如
C:\Users\ashes999\AppData\Local\Temp\\u MEI157682\images\swarm.png
)。您可以浏览此目录并确保它包含所有内容。如果您不能以这种方式找到它,请查找类似于
main.exe.manifest
(Windows)或
python35.dll
(如果您使用的是Python 3.5)的内容

如果安装程序包含所有内容,那么下一个可能的问题是文件I/O:Python代码在可执行文件的目录中查找文件,而不是临时目录

为了解决这个问题,这个问题的任何答案都是有效的。就我个人而言,我发现它们都能起作用:首先在主入口点文件中有条件地更改目录,其他一切都按原样工作:


如果hasattr(sys,“\u MEIPASS”):
os.chdir(系统)

如果应用程序未安装(即
sys.\u 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文件中。让我分享一下我所做的步骤

  • 步骤:通过导入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)
    
  • 步骤:将pyi makespec file.py写入控制台,以创建file.spec文件

  • 步骤:使用Notepad++打开file.spec,添加如下数据文件:

    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')
    
        pyinstaller --onefile your_file.py
    
  • 步骤:我按照上面的步骤,然后保存了spec文件。最后打开控制台并编写pyinstaller file.spec(在我的例子中,file=Converter GUI)

  • 结论:dist文件夹中仍有多个文件

    注意:我使用的是Python 3.5

    编辑:最后它与

  • 步骤:通过导入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)
    
  • 步骤:通过添加文件路径调用上述函数:

    image_path = resource_path("Converter-GUI.ico")
    
  • 步骤:编写上面的变量,将函数调用到代码需要路径的地方。就我而言,它是:

        self.window.iconbitmap(image_path)
    
  • 步骤:在python文件的同一目录中打开控制台,编写如下代码:

    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')
    
        pyinstaller --onefile your_file.py
    
  • 步骤:打开python文件的.spec文件,附加a.datas数组,并将图标添加到exe类中,这是在第3步编辑之前给出的
  • 步骤:保存并退出路径文件。转到包含spec和py文件的文件夹。再次打开console窗口并键入以下命令:

        pyinstaller your_file.spec
    

  • 六点以后。步骤:您的一个文件已准备好使用。

    我所看到的wrt PyInstaller最常见的投诉/问题是“我的代码找不到我明确包含在捆绑包中的数据文件,它在哪里?”,并且很难看到您的代码正在搜索什么/在哪里,因为提取的代码位于临时位置,并且在退出时会被删除。使用@Jonathon Reinhart的
    resource\u path()


    如果您仍试图将文件相对于可执行文件而不是放在临时目录中,则需要自己复制它。我就是这样完成的

    在spec文件中添加一个步骤,该步骤将文件系统复制到DISTPATH variabl
    img_dir = os.path.join(os.path.dirname(__file__), "img")
    sound_dir = os.path.join(os.path.dirname(__file__), "sound")
    
    img_dir = resource_path("img")
    sound_dir = resource_path("sound")
    
    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)
    
    project_folder/
        main.py
        xxx.py # modules
        xxx.py # modules
        sound/ # directory containing the sound files
        img/ # directory containing the image files
        venv/ # if using a venv
        main.spec
    
    added_files = [
    
    ("sound", "sound"),
    ("img", "img")
    
    ]
    
    def img_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)
    
    icon_path = img_resource_path("app/img/app_icon.ico")
    root.wm_iconbitmap(icon_path)
    
    
    added_files = [
        ('app/img/app_icon.ico','app/img/')
    ]
    a = Analysis(['main.py'],
                 pathex=['D:\\Github Repos\\Processes-Killer\\Process Killer'],
                 binaries=[],
                 datas=added_files,
                 hiddenimports=[],
                 hookspath=[],
                 runtime_hooks=[],
                 excludes=[],
                 win_no_prefer_redirects=False,
                 win_private_assemblies=False,
                 cipher=block_cipher,
                 noarchive=False)
    pyz = PYZ(a.pure, a.zipped_data,
                 cipher=block_cipher)
    exe = EXE(pyz,
              a.scripts,
              a.binaries,
              a.zipfiles,
              a.datas,
              [],
              name='Process Killer',
              debug=False,
              bootloader_ignore_signals=False,
              strip=False,
              upx=True,
              upx_exclude=[],
              runtime_tmpdir=None,
              console=True , uac_admin=True, icon='app\\img\\app_icon.ico')
    
    pyinstaller.exe "Process Killer.spec"
    
    pyinstaller.py --onefile -F --add-data=images;images --runtime-hook=cp_images_hook.py main.py
    
    import sys
    import os
    import shutil
    
    path = getattr(sys, '_MEIPASS', os.getcwd())
    
    full_path = path+"\\images"
    try:
        shutil.move(full_path, ".\\images")
    except:
        print("Cannot create 'images' folder. Already exists.")
    
    import sys
    import os
    
    path = getattr(sys, '_MEIPASS', os.getcwd())   
    os.chdir(path)
    
    def getPath(filename):
        import os
        import sys
        from os import chdir
        from os.path import join
        from os.path import dirname
        from os import environ
        
        if hasattr(sys, '_MEIPASS'):
            # PyInstaller >= 1.6
            chdir(sys._MEIPASS)
            filename = join(sys._MEIPASS, filename)
        elif '_MEIPASS2' in environ:
            # PyInstaller < 1.6 (tested on 1.5 only)
            chdir(environ['_MEIPASS2'])
            filename = join(environ['_MEIPASS2'], filename)
        else:
            chdir(dirname(sys.argv[0]))
            filename = join(dirname(sys.argv[0]), filename)
            
        return filename
    
    datas = [("path/to/mypackage/data_file.txt", "path/to/mypackage")]
    
    mypackage
          __init__.py  # This is a MUST in order for the package to be registered
          data_file.txt  # The data file you've added
    
    Hello world!