The wxpython application does not work as it is supposed to work after compiling it with pyinstaller. When I run the python code with the python interpreter, it works properly. But when it is bundled into an app.exe, web.html2 does not render the flask web pages properly. Here is the code:
# webview_app.py
import wx
import wx.html2
import threading
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
return """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bible Display Software</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: green;
}
.card {
background-color: yellow;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
text-align: center;
}
h1 {
margin: 0;
}
</style>
</head>
<body>
<div class="card">
<h1>Bible Display Software</h1>
</div>
</body>
</html>
"""
class MainFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title='WxPython Flask App', size=(800, 600))
panel = wx.Panel(self)
self.browser = wx.html2.WebView.New(panel)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.browser, 1, wx.EXPAND)
panel.SetSizer(sizer)
self.browser.LoadURL("http://127.0.0.1:5000")
def run_flask():
app.run(host='127.0.0.1', port=5000)
def run_wx():
wx_app = wx.App()
frame = MainFrame()
frame.Show()
wx_app.MainLoop()
if __name__ == '__main__':
flask_thread = threading.Thread(target=run_flask, daemon=True)
flask_thread.start()
run_wx()
And below is the webview_app.spec file I tried.
#webview_app.spec
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
# Add the templates directory to the datas
added_files = [
('templates', 'templates'), # (source_dir, dest_dir)
]
a = Analysis(
['webview_app.py'],
pathex=[],
binaries=[],
datas=added_files, # Include templates directory
hiddenimports=['wx', 'wx.html2', 'wx._core', 'wx._html2', 'flask', 'werkzeug', 'jinja2'],
hookspath=[],
hooksconfig={},
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='WebViewApp',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
windowed=True
)
Pyinstaller would create a working EXE out of the box (brand-new, Python 3.13.1/PyInstaller 6.11.1), but it looked weird. Found out EXE uses wx.html2.WebViewBackendIE
and not wx.html2.WebViewBackendEdge
.
Reason for this is PyInstaller does not copy the interfacing DLL \Lib\site-packages\wx\WebView2Loader.dll
See this wxPython ticket for details
What I did to get a EXE where Edge is used as backend
rem change WINPYDIR to where your python is
rem set WINPYDIR="C:\Program Files\python"
pyinstaller ./app.py --distpath ./vdist -y --clean --contents-directory . --add-binary "%WINPYDIR%\Lib\site-packages\wx\WebView2Loader.dll:wx"
Alternatively, the following was proposed on the wxPython Forum
The absolute path can be avoided in the .spec file by:
from PyInstaller import HOMEPATH
binaries=[(f’{HOMEPATH}/wx/WebView2Loader.dll’, ‘.’)],