用PyInstaller打包数据文件(–onefile)
我试图用PyInstaller构建一个包含图像和图标的单文件EXE。 我不能为了我的生活让它与--onefile
一起工作。
如果我这样做--onedir
它很适合所有的作品。 当我使用--onefile
,它找不到引用的附加文件(运行编译的EXE时)。 它发现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更新。
较新版本的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 deployment >>> resource_path("app_icon.ico") "/tmp/_MEI34121/app_icon.ico"
相反,按照建议重写所有的路径代码,我改变了工作目录:
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)
所有其他的答案使用当前的工作目录的情况下,应用程序没有PyInstalled(即sys._MEIPASS
没有设置)。 这是错误的,因为它阻止您从脚本以外的目录运行应用程序。
更好的解决方案:
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')
-
步骤:我按照上面的步骤,然后保存spec文件。 最后打开控制台,并写入, pyinstaller file.spec (在我的情况下,文件=转换器 – 图形用户界面)。
结论:dist文件夹中仍然有多个文件。
注意:我正在使用Python 3.5。
编辑:最后它与Jonathan Reinhart的方法一起工作。
-
步骤:将下面的代码添加到您的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
- 步骤:打开python文件的.spec文件,并添加a.datas数组,并将该图标添加到上面在第3步编辑之前给出的exe类。
-
步骤:保存并退出路径文件。 转到包含spec和py文件的文件夹。 再次打开控制台窗口并键入以下命令:
pyinstaller your_file.spec
6.步之后你的一个文件就可以使用了。
我发现现有的答案令人困惑,花了很长时间才找出问题所在。 这是我找到的所有东西的汇编。
当我运行我的应用程序,我得到一个错误Failed to execute script foo
(如果foo.py
是主文件)。 要解决这个问题,不要使用--noconsole
运行PyInstaller(或者编辑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)。
如果安装程序包含所有内容,则下一个可能的问题是文件I / O:您的Python代码正在查看可执行文件的目录,而不是临时目录。
为了解决这个问题,在这个问题上的任何答案工作。 就我个人而言,我发现他们的所有工作混合在一起:在主入口点文件中有条件地改变目录,其他所有东西都按原样运行:
if hasattr(sys, '_MEIPASS'): os.chdir(sys._MEIPASS)