使用 --onefile 选项在 PyInstaller 上捆绑 CEFpython
Bundle CEFpython on PyInstaller with --onefile option
我有一个应用程序,其中有两个可执行文件:Flask-SocketIO-Server 和 CefPython 浏览器。我将这两个可执行文件与 PyInstaller 捆绑在一起。带有 --onefile 选项的 Flask-Server 和带有 --onedir 选项的 cefpython,因为我无法使用 --onefile。现在我决定只为这两种代码(Flask 和 CEFpython)提供可执行文件,所以我的 Flask 服务器有代码到 运行 CEF 图形用户界面:
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] == 'dev':
print "Running Flask-SocketIO on dev mode"
else:
print "Running Flask-SocketIO on production mode"
path = os.getcwd()
gui_path = path + '\display_react\display_react.exe'
print 'Running Graphical User Interface...'
thread.start_new_thread(display_react.main, ()) # Baterias
print 'Initializing server'
socketio.run(app, debug=False)
代码工作正常,但是当我尝试使用 --onefile 选项将此代码与 PyInstaller 捆绑在一起时,生成的可执行文件无法工作导致某些 CEF 依赖项。
运行ning Pyinstaller 时的错误:
Running Flask-SocketIO on production mode Running Graphical User
Interface... Initializing server [wxpython.py] CEF Python 57.1
[wxpython.py] Python 2.7.14 64bit [wxpython.py] wxPython 4.0.1 msw
(phoenix) [0727/125110.576:ERROR:main_delegate.cc(684)] Could not load
locale pak for en-US [0727/125110.576:ERROR:main_delegate.cc(691)]
Could not load cef.pak [0727/125110.578:ERROR:main_delegate.cc(708)]
Could not load cef_100_percent.pak
[0727/125110.582:ERROR:main_delegate.cc(717)] Could not load
cef_200_percent.pak [0727/125110.582:ERROR:main_delegate.cc(726)]
Could not load cef_extensions.pak
[0727/125110.648:ERROR:content_client.cc(269)] No data resource
available for id 20418 [0727/125110.648:ERROR:content_client.cc(269)]
No data resource available for id 20419
[0727/125110.650:ERROR:content_client.cc(269)] No data resource
available for id 20420 [0727/125110.655:ERROR:content_client.cc(269)]
No data resource available for id 20421
[0727/125110.656:ERROR:content_client.cc(269)] No data resource
available for id 20422 [0727/125110.656:ERROR:content_client.cc(269)]
No data resource available for id 20417
[0727/125110.680:ERROR:extension_system.cc(72)] Failed to parse
extension manifest.
C:\Users\Ricardo\AppData\Local\Temp_MEI95~1\display_react.py:118:
wxPyDeprecationWarning: Call to deprecated item EmptyIcon. Use
:class:Icon
instead
这是我正在使用的 .spec 文件:
# -*- mode: python -*-
block_cipher = None
def get_cefpython_path():
import cefpython3 as cefpython
path = os.path.dirname(cefpython.__file__)
return "%s%s" % (path, os.sep)
cefp = get_cefpython_path()
a = Analysis(['server.py'],
pathex=['C:\Users\Ricardo\addvolt-scanning-tool\backend'],
binaries=[],
datas=[('PCANBasic.dll', '.'), ('o.ico', '.')], #some dlls i need for flask
hiddenimports=['engineio.async_gevent'], #engineio hidden import for Flask usage
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas + [('locales/en-US.pak', '%s/locales/en-US.pak' % cefp, 'DATA')], # my try to fix that missing dependencies
name='server',
debug=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True )
编辑:已解决
多亏了@cztomczak,我才开始工作。问题不在 PyInstaller 上,而是在 wxpython.py 寻找语言环境、资源和子进程的过程中。尽管所有文件都在 'temp/dir/_MEIxxx' 上,但 wxpython 正在可执行文件的目录中查找这些文件。所以我通知代码在临时目录中查找这些文件的方法是:
dir_temp = tempfile.gettempdir()
files = []
for i in os.listdir(dir_temp):
if os.path.isdir(os.path.join(dir_temp,i)) and '_MEI' in i:
files.append(i)
dir_temp = dir_temp + str(files[0])
dir_temp = os.path.join(dir_temp, str(files[0]))
dir_temp_locale = os.path.join(dir_temp, 'locales')
dir_temp_subprocess = os.path.join(dir_temp_subprocess, 'subprocess.exe')
print dir_temp
dir_temp = dir_temp.replace("\", "\\")
print dir_temp
print dir_temp_locale
dir_temp_locale = dir_temp_locale.replace("\", "\\")
print dir_temp_locale
dir_temp_supbprocess = dir_temp_subprocess.replace("\", "\\")
print dir_temp_subprocess
...
settings = {'auto_zooming': '-2.5', 'locales_dir_path': dir_temp_locale, 'resources_dir_path': dir_temp, 'browser_subprocess_path': dir_temp_subprocess}
我必须这样做,因为在临时 (_MEIxxxx) 上创建的文件夹的名称总是在变化。并且我将来可能会遇到问题,因为如果应用程序崩溃,_MEIxx 文件夹将不会被删除,如果我尝试重新 运行 可执行文件,这段代码将有两个 _MEI 文件夹,并且可能不会一直工作,直到有人清理临时目录。
所以,恢复...要将应用程序捆绑在一个文件中:
- 将钩子-cefpython3.py(包装上有)粘贴到Python27/envs/libs/site-package/Pyinstaller/hooks
- 运行 带有 --onefile 选项的 Pyinstaller
- 告诉 cefpython 代码区域设置、资源和子进程在哪里 (locale_dir_path、resource_dir_path、browser_subprocess_path)
我猜你得到的错误是因为你的规范文件没有包含所有必要的 CEF 二进制文件。有一个官方的 pyinstaller 示例,您可以使用和修改以使用 --onefile 选项:https://github.com/cztomczak/cefpython/blob/master/examples/pyinstaller/README-pyinstaller.md
我遇到了类似的问题,发现使用 _MEIPASS 环境变量是一个更优雅的解决方案。
import cefpython
import os
import sys
if hasattr(sys, '_MEIPASS'):
# settings when packaged
settings = {'locales_dir_path': os.path.join(sys._MEIPASS, 'locales'),
'resources_dir_path': sys._MEIPASS,
'browser_subprocess_path': os.path.join(sys._MEIPASS, 'subprocess.exe'),
'log_file': os.path.join(sys._MEIPASS, 'debug.log')}
else:
# settings when unpackaged
settings = {}
cefPython.Initialize(settings=settings)
我有一个应用程序,其中有两个可执行文件:Flask-SocketIO-Server 和 CefPython 浏览器。我将这两个可执行文件与 PyInstaller 捆绑在一起。带有 --onefile 选项的 Flask-Server 和带有 --onedir 选项的 cefpython,因为我无法使用 --onefile。现在我决定只为这两种代码(Flask 和 CEFpython)提供可执行文件,所以我的 Flask 服务器有代码到 运行 CEF 图形用户界面:
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] == 'dev':
print "Running Flask-SocketIO on dev mode"
else:
print "Running Flask-SocketIO on production mode"
path = os.getcwd()
gui_path = path + '\display_react\display_react.exe'
print 'Running Graphical User Interface...'
thread.start_new_thread(display_react.main, ()) # Baterias
print 'Initializing server'
socketio.run(app, debug=False)
代码工作正常,但是当我尝试使用 --onefile 选项将此代码与 PyInstaller 捆绑在一起时,生成的可执行文件无法工作导致某些 CEF 依赖项。 运行ning Pyinstaller 时的错误:
Running Flask-SocketIO on production mode Running Graphical User Interface... Initializing server [wxpython.py] CEF Python 57.1 [wxpython.py] Python 2.7.14 64bit [wxpython.py] wxPython 4.0.1 msw (phoenix) [0727/125110.576:ERROR:main_delegate.cc(684)] Could not load locale pak for en-US [0727/125110.576:ERROR:main_delegate.cc(691)] Could not load cef.pak [0727/125110.578:ERROR:main_delegate.cc(708)] Could not load cef_100_percent.pak [0727/125110.582:ERROR:main_delegate.cc(717)] Could not load cef_200_percent.pak [0727/125110.582:ERROR:main_delegate.cc(726)] Could not load cef_extensions.pak [0727/125110.648:ERROR:content_client.cc(269)] No data resource available for id 20418 [0727/125110.648:ERROR:content_client.cc(269)] No data resource available for id 20419 [0727/125110.650:ERROR:content_client.cc(269)] No data resource available for id 20420 [0727/125110.655:ERROR:content_client.cc(269)] No data resource available for id 20421 [0727/125110.656:ERROR:content_client.cc(269)] No data resource available for id 20422 [0727/125110.656:ERROR:content_client.cc(269)] No data resource available for id 20417 [0727/125110.680:ERROR:extension_system.cc(72)] Failed to parse extension manifest. C:\Users\Ricardo\AppData\Local\Temp_MEI95~1\display_react.py:118: wxPyDeprecationWarning: Call to deprecated item EmptyIcon. Use :class:
Icon
instead
这是我正在使用的 .spec 文件:
# -*- mode: python -*-
block_cipher = None
def get_cefpython_path():
import cefpython3 as cefpython
path = os.path.dirname(cefpython.__file__)
return "%s%s" % (path, os.sep)
cefp = get_cefpython_path()
a = Analysis(['server.py'],
pathex=['C:\Users\Ricardo\addvolt-scanning-tool\backend'],
binaries=[],
datas=[('PCANBasic.dll', '.'), ('o.ico', '.')], #some dlls i need for flask
hiddenimports=['engineio.async_gevent'], #engineio hidden import for Flask usage
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas + [('locales/en-US.pak', '%s/locales/en-US.pak' % cefp, 'DATA')], # my try to fix that missing dependencies
name='server',
debug=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True )
编辑:已解决
多亏了@cztomczak,我才开始工作。问题不在 PyInstaller 上,而是在 wxpython.py 寻找语言环境、资源和子进程的过程中。尽管所有文件都在 'temp/dir/_MEIxxx' 上,但 wxpython 正在可执行文件的目录中查找这些文件。所以我通知代码在临时目录中查找这些文件的方法是:
dir_temp = tempfile.gettempdir()
files = []
for i in os.listdir(dir_temp):
if os.path.isdir(os.path.join(dir_temp,i)) and '_MEI' in i:
files.append(i)
dir_temp = dir_temp + str(files[0])
dir_temp = os.path.join(dir_temp, str(files[0]))
dir_temp_locale = os.path.join(dir_temp, 'locales')
dir_temp_subprocess = os.path.join(dir_temp_subprocess, 'subprocess.exe')
print dir_temp
dir_temp = dir_temp.replace("\", "\\")
print dir_temp
print dir_temp_locale
dir_temp_locale = dir_temp_locale.replace("\", "\\")
print dir_temp_locale
dir_temp_supbprocess = dir_temp_subprocess.replace("\", "\\")
print dir_temp_subprocess
...
settings = {'auto_zooming': '-2.5', 'locales_dir_path': dir_temp_locale, 'resources_dir_path': dir_temp, 'browser_subprocess_path': dir_temp_subprocess}
我必须这样做,因为在临时 (_MEIxxxx) 上创建的文件夹的名称总是在变化。并且我将来可能会遇到问题,因为如果应用程序崩溃,_MEIxx 文件夹将不会被删除,如果我尝试重新 运行 可执行文件,这段代码将有两个 _MEI 文件夹,并且可能不会一直工作,直到有人清理临时目录。
所以,恢复...要将应用程序捆绑在一个文件中: - 将钩子-cefpython3.py(包装上有)粘贴到Python27/envs/libs/site-package/Pyinstaller/hooks - 运行 带有 --onefile 选项的 Pyinstaller - 告诉 cefpython 代码区域设置、资源和子进程在哪里 (locale_dir_path、resource_dir_path、browser_subprocess_path)
我猜你得到的错误是因为你的规范文件没有包含所有必要的 CEF 二进制文件。有一个官方的 pyinstaller 示例,您可以使用和修改以使用 --onefile 选项:https://github.com/cztomczak/cefpython/blob/master/examples/pyinstaller/README-pyinstaller.md
我遇到了类似的问题,发现使用 _MEIPASS 环境变量是一个更优雅的解决方案。
import cefpython
import os
import sys
if hasattr(sys, '_MEIPASS'):
# settings when packaged
settings = {'locales_dir_path': os.path.join(sys._MEIPASS, 'locales'),
'resources_dir_path': sys._MEIPASS,
'browser_subprocess_path': os.path.join(sys._MEIPASS, 'subprocess.exe'),
'log_file': os.path.join(sys._MEIPASS, 'debug.log')}
else:
# settings when unpackaged
settings = {}
cefPython.Initialize(settings=settings)