情况
我在Windows上使用Pyinstaller来制作我的项目的EXE文件。
我想使用--onefile
选项具有干净的结果和易于分发文件/程序。
我的程序使用config.ini
文件来存储配置选项。该文件可以由用户自定义。
问题
使用 --onefile
选项,pyinstaller将所有声明为" data-file&quot s in the单个.exe
文件中"。
我已经看到了此请求,但是它提供了在一个file 内添加捆绑文件的建筑物而不是外部,在.exe
的相同级别和同一dist
目录中。
在某个时候,我想使用shutil.copy命令.pecec ins.pec文件复制此文件...但是我认为是错误的。
我该如何解决此问题?
github上的存储库帮助我找到了我的问题的解决方案。
我已经使用了shutil
模块和.spec
文件来使用Pyinstaller --onefile
选项添加额外的数据文件(在我的情况下,是config-sample.ini
文件)。
为Pyinstaller制作.spec文件
首先,我创建了一个使用我需要的选项的MakePec文件:
pyi-makespec --onefile --windowed --name exefilename scriptname.py
此命令创建一个exefilename.spec
文件,用于与Pyinstaller一起使用。
修改exefilename.spec,添加shutil.copyfile
现在我已经编辑了exefilename.spec
,在文件末尾添加以下代码。
import shutil
shutil.copyfile('config-sample.ini', '{0}/config-sample.ini'.format(DISTPATH))
shutil.copyfile('whateveryouwant.ext', '{0}/whateveryouwant.ext'.format(DISTPATH))
此代码在编译过程结束时复制所需的数据文件。您可以使用shutil
软件包中可用的所有方法。
运行pyinstaller
最后一步是运行编译过程
pyinstaller --clean exefilename.spec
结果是,在DIST文件夹中,您应该将编译的.EXE文件与复制的数据文件一起使用。
。考虑
在Pyinstaller的正式文档中,我找不到获得此结果的选项。我认为它可以被视为解决方法...
这是如何访问与输出文件相同级别的文件。诀窍是Sys.ecutable是单文件.EXE所在的位置。因此,简单地做到了:
import sys
import os.path
CWD = os.path.abspath(os.path.dirname(sys.executable))
使用它,例如用
with open(os.path.join(CWD, "config.ini")) as config_file:
print(config_file.read())
os.getcwd()
/相对路径不起作用的原因
可执行文件只是一个可执行的存档,在执行时提取到临时目录,在该目录中执行 .pyc 文件。因此,当您调用os.getcwd()
而不是通往可执行文件的路径时,您可以获得通往临时文件夹的路径。
我的解决方案与Stefano-Giraldi的优秀人物相似解决方案。当将目录传递给shutil.copyfile
时,我被拒绝了。
我最终使用shutil.copytree
:
import sys, os, shutil
site_packages = os.path.join(os.path.dirname(sys.executable), "Lib", "site-packages")
added_files = [
(os.path.join(site_packages, 'dash_html_components'), 'dash_html_components'),
(os.path.join(site_packages, 'dash_core_components'), 'dash_core_components'),
(os.path.join(site_packages, 'plotly'), 'plotly'),
(os.path.join(site_packages, 'scipy', '.libs', '*.dll'), '.')
]
working_dir_files = [
('assets', 'assets'),
('csv', 'csv')
]
print('ADDED FILES: (will show up in sys._MEIPASS)')
print(added_files)
print('Copying files to the dist folder')
print(os.getcwd())
for tup in working_dir_files:
print(tup)
to_path = os.path.join(DISTPATH, tup[1])
if os.path.exists(to_path):
if os.path.isdir(to_path):
shutil.rmtree(to_path)
else:
os.remove(to_path)
if os.path.isdir(tup[0]):
shutil.copytree(tup[0], to_path)
else:
shutil.copyfile(tup[0], to_path)
#### ... The rest of the spec file
a = Analysis(['myapp.py'],
pathex=['.', os.path.join(site_packages, 'scipy', '.libs')],
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='myapp',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True)
这避免了_mei文件夹,并防止它复制您在DIST文件夹中而不是在临时文件夹中所需的配置文件。
我尝试过许多方法,这对我有用:
-
不要为配置文件使用 .py 扩展名。改用JSON。JSON不好,因为您不能在其中写评论,但是如果您想制作一个EXE文件,则不幸的是必须使用它。
-
在您的脚本中,加载以这种方式设置文件:
filename = "settings.json" contents = open(filename).read() config = eval(contents) setting1 = config['setting1']
-
运行pyinstaller或auto-py-to-exe(我尝试过,所有作品)
-
将您的 settings.json 将您的 .exe .exe 文件的文件放在同一文件夹中。
-
运行它,它将从该文件中进行设置。
如果您的任何Python脚本都使用外部文件(JSON,TEXT或任何配置文件),并且您希望将这些文件包含在可执行文件中,请在a Windows 系统。
考虑到有一个Python脚本app.py
并且它读取JSON文件config.json
,我们的目标是将config.json
文件添加到某个合适的目录中,该文件可以在应用程序运行时访问该文件(作为.exe
)。P>
即使
app.py
未直接读取config.json
,此答案也适用。config.json
可以由app.py
使用的任何模块读取,答案仍然会有所帮助。
app.py
config.json
步骤1:在应用程序运行时解决JSON文件的路径
应用程序正在运行时,将文件复制到Windows系统C:Users<You>AppDataLocalTempMEIxxx
上的临时位置。因此,app.py
需要从此临时位置读取JSON文件。但是,该应用在运行时如何知道它必须在哪个目录中搜索文件?从这个出色的答案中,在app.py
中,我们将有一个功能
def resolve_path(path):
if getattr(sys, "frozen", False):
# If the 'frozen' flag is set, we are in bundled-app mode!
resolved_path = os.path.abspath(os.path.join(sys._MEIPASS, path))
else:
# Normal development mode. Use os.getcwd() or __file__ as appropriate in your case...
resolved_path = os.path.abspath(os.path.join(os.getcwd(), path))
return resolved_path
# While reading the JSON file
with open(resolve_path("config.json"), "r") as jsonfile:
# Access the contents here
现在app.py
知道在哪里查找文件。在此之前,我们需要指示Pyinstaller将config.json
复制到该临时文件目录中。
注意,您需要在python脚本中使用相对路径的任何地方使用resolve_path
。
步骤2:制作和编辑.spec
文件以复制config.json
当我们希望创建app.py
的可执行文件时,我们首先为其创建一个.spec
文件(参考@stefano Giraldi的答案)
pyi-makespec --onefile --windowed --name appexe app.py
打开生成的appexe.spec
文件,您会注意到这些内容,
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
...
datas=[],
...
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
...
)
创建一个新列表files
并将其传递到datas
参数,
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
files = [
( 'config.json' , '.' )
]
a = Analysis(
...
datas=files,
...
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
...
)
元组('config.json', '.')
表示文件的源和目标路径。目标路径相对于临时文件目录。
步骤3:构建可执行文件
最后,我们可以运行.spec
文件来构建安装程序,
pyinstaller --clean appexe.spec
最终的安装程序现在应在没有任何FileNotFoundError
s。
i通过删除config file( config.py <<em> config.py 对我而言)从项目文件夹中运行Pyinstaller。
config_str = """
some configuration code string
"""
with open('path_to_somewhere\config.ini', 'w', encoding="gbk") as writer:
writer.write(config_str)