Skip to content

Commit 1c8d0a5

Browse files
committed
add option to run as standalone application
1 parent f5d02e2 commit 1c8d0a5

File tree

3 files changed

+64
-9
lines changed

3 files changed

+64
-9
lines changed

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ dependencies = [
2828
"kaleido", # required for exporting PNG to disk
2929
"asteval",
3030
"muscle3",
31+
"pywebview[qt]",
32+
"pyinstaller",
3133
]
3234
dynamic = ["version"]
3335

waveform_editor/cli.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,13 @@ def parse_linspace(ctx, param, value):
7474
@click.option(
7575
"-p", "--port", type=int, default=0, help="Specify port to host application."
7676
)
77-
def launch_gui(file, port):
77+
@click.option(
78+
"--standalone",
79+
is_flag=True,
80+
default=False,
81+
help="Launch as a standalone desktop window using pywebview instead of a browser.",
82+
)
83+
def launch_gui(file, port, standalone):
7884
"""Launch the Waveform Editor GUI using Panel.
7985
8086
\b
@@ -85,13 +91,20 @@ def launch_gui(file, port):
8591
# cases:
8692
import panel as pn
8793

88-
from waveform_editor.gui.main import WaveformEditorGui
94+
from waveform_editor.gui.main import PanelDesktop, WaveformEditorGui
8995

9096
try:
91-
app = WaveformEditorGui()
92-
if file is not None:
93-
app.load_yaml_from_file(Path(file))
94-
pn.serve(app, port=port, threaded=True)
97+
98+
def create_app():
99+
app = WaveformEditorGui()
100+
if file is not None:
101+
app.load_yaml_from_file(Path(file))
102+
return app.__panel__()
103+
104+
if standalone:
105+
PanelDesktop().serve(create_app, port=port)
106+
else:
107+
pn.serve(create_app, port=port, threaded=True)
95108
except Exception as e:
96109
logger.error(f"Failed to launch GUI: {e}")
97110

waveform_editor/gui/main.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import logging
2+
import socket
3+
import threading
24

35
import imas
46
import panel as pn
57
import param
8+
import webview
69

710
import waveform_editor
811
from waveform_editor.configuration import WaveformConfiguration
@@ -195,6 +198,43 @@ def serve(self):
195198
return self.template.servable()
196199

197200

198-
# Allow serving with `panel serve waveform_editor/gui/main.py`
199-
if "bokeh" in __name__:
200-
WaveformEditorGui().serve()
201+
class PanelDesktop:
202+
def __init__(self, title="Waveform Editor", width: int = 1920, height: int = 1080):
203+
self.title = title
204+
self.width = width
205+
self.height = height
206+
207+
@staticmethod
208+
def _find_free_port():
209+
"""Find a free port on localhost."""
210+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
211+
s.bind(("", 0))
212+
s.listen(1)
213+
port = s.getsockname()[1]
214+
return port
215+
216+
def serve(self, app_factory, port: int = 0):
217+
if not port:
218+
port = self._find_free_port()
219+
220+
server_thread = threading.Thread(
221+
target=lambda: pn.serve(
222+
app_factory,
223+
port=port,
224+
show=False,
225+
autoreload=False,
226+
),
227+
daemon=True,
228+
)
229+
server_thread.start()
230+
231+
webview.create_window(
232+
self.title,
233+
f"http://localhost:{port}",
234+
resizable=True,
235+
fullscreen=False,
236+
width=self.width,
237+
height=self.height,
238+
text_select=True,
239+
)
240+
webview.start()

0 commit comments

Comments
 (0)