10 KiB
eco startup modes
eco has four front-ends, selected with --ui (see eco_cli.py). The
default remains shell (the traditional terminal session) and nothing
about it changes — /sf/bernina/bin/eco, the script everyone actually
runs today, is untouched and keeps working exactly as before. Everything
below is additive and opt-in.
--ui |
What it is | Status |
|---|---|---|
shell |
Traditional interactive IPython session (default) | live, unchanged |
lab |
JupyterLab on the packaged notebook (eco/voila_app.ipynb) |
existing, not yet wired into /sf/bernina/bin/eco |
voila |
That notebook served as a standalone Voila dashboard | existing, not yet wired |
desktop |
A Spyder/MATLAB-like Qt workbench window | new (this round), not yet wired |
None of lab/voila/desktop are reachable from /sf/bernina/bin/eco
yet — eco_cli.py (the module that implements all four) isn't installed
as the live eco command in bpy312. Try them directly for now:
source /sf/bernina/bin/bpy-env
cd /sf/bernina/code/gac-bernina/eco
python3 eco_cli.py --ui desktop # the new Qt workbench
python3 eco_cli.py --ui desktop --theme dark --lazy
python3 eco_cli.py --ui lab # existing JupyterLab notebook
python3 eco_cli.py --ui voila # existing Voila dashboard
Wiring eco_cli.py up as the actual eco command (so eco --ui desktop
just works) is a separate, deliberately-not-yet-taken step — it touches
the live launcher script everyone uses, so it wants its own explicit
go-ahead rather than happening as a side effect of this work.
desktop — the Qt workbench
eco/widgets/desktop_app.py. One window:
- Center: a live IPython console (
qtconsole) withnamespacealready available. Its kernel is in-process (same Python process as the window itself, so it can literally share the calling terminal's own namespace dict) whenever that's safe — but not when this process already has its own running IPython shell (the normal case for callingeco.start_desktop()from anipythonterminal session): a second in-process kernel can't coexist with one already running (IPython'sInteractiveShellis a process-wide singleton), so the console falls back to a real subprocess kernel with its own, freshly-rebuiltnamespaceinstead. Seeeco.widgets.console_kernelfor the full explanation — the banner shown when the console opens says which case you're in. Passwith_console=Falseto skip this entirely: no console, no kernel, no MultipleInstanceError/PYTHONPATH subtleties to worry about — the calling terminal stays the one and only "master" session (see the "New independent console(s)" section below for the opposite: a genuinely separate second session). - Left dock, "Namespace": every registered name in the namespace, filterable. Names already initialized are opened immediately on click. Lazy (not-yet-built) names are shown grayed out; clicking one triggers initialization in the background (a spinner shows on that row while it runs) and opens it automatically once ready. Failed names are shown in red and are not retried automatically.
- "Opening" a name calls
<name>.widget()directly (not through the console — seeEcoDesktopApp._open_widget's docstring for why calling it straight from the launcher's own Qt code already gets a real QWidget back, the same as typing it at the calling terminal's own prompt), so this works identically whether or notwith_consoleis on. Known v1 limitation: each opened widget is still its own top-level window, not its own dock panel — trueQDockWidgetembedding is a reasonable next step, not attempted in this first pass. - A per-class hook lets a device supply a purpose-built view instead of
the generic property grid: set
_default_widget = "some_method_name"on theAssemblysubclass (seeeco.elements.assembly.Assembly.widget).AxisPTZalready does this —_default_widget = "viewer"opens its live-video viewer instead of a plain slider grid. - A modern theme is available: pass
theme="dark"/"light"— uses the realqt-materialpackage (now installed) if present, else a hand-built approximation of its default teal-on-charcoal look; seeeco.widgets.qt_theme. Same switch used byeco.widgets.camserver_panel_qt/camserver_stream_qt. - Workspace menu: "Save Workspace Now" writes the dock layout plus
which namespace entries are currently open to
~/.eco/desktop_workspace.json(also happens automatically when the window closes); "Reload Last Workspace" restores both — reopening each widget the same way clicking it would (lazy entries get re-initialized first). A fresh window always starts empty; reloading is only ever an explicit action, never automatic on startup. "New (Empty) Workspace" just forgets the tracked open-widget list for the current session.
Testing any of this from a running terminal session, without leaving it
Once eco has started (namespace exists in your session — the normal
case), three functions are available directly as eco.X():
eco.start_desktop(theme="dark") # opens the Qt workbench, non-blocking;
# its console shares *this* session's own
# namespace when that's possible (see above)
eco.start_desktop(with_console=False) # no embedded console/kernel at all --
# this terminal stays the only "master"
# session; the Namespace panel still
# works fully either way (see below)
eco.start_console() # a brand-new, INDEPENDENT console/kernel --
# a second working session, not another view
# onto this one. kind="qt" (default) or
# kind="jupyterlab"
eco.start_jupyterlab() # attaches to a running JupyterLab server if
# there is one, else starts a background one
eco.start_webapp() # starts (or reuses) a background Voila server
None of these block your terminal — the whole point is to try any of the
other front-ends side by side with the one you're already in. See
eco/widgets/app_launchers.py for the full signatures.
Every kernel is tracked, with its full input/output history
Whether opened via start_desktop() or start_console(), every kernel's
activity — everything typed or run, everything that came back (results,
stdout/stderr, errors) — is logged as it happens, tagged with which kernel
it belongs to, so a sequence of commands across several
consoles/widgets can be reconstructed after the fact instead of only being
visible in whichever window happened to be open at the time:
from eco.widgets import kernel_registry
kernel_registry.sessions_summary() # what's running right now, and where its log is
kernel_registry.find_all_logs() # every session's log on disk, including past ones
session.tail(20) # last 20 events for one KernelSession
Each session's log is a plain JSONL file under ~/.eco/kernel_logs/, one
line per event (session_start, input, result, stream, error),
timestamped. See eco.widgets.kernel_registry and
eco.widgets.console_kernel.LoggingJupyterWidget (the console widget both
start_desktop() and start_console() build on, which is what actually
does the logging).
Try it from Python directly instead of the CLI:
from eco.widgets.desktop_app import EcoDesktopApp, build_namespace
namespace = build_namespace(scope="bernina", lazy=True)
app = EcoDesktopApp(namespace, theme="dark") # blocks until the window closes
lab/voila — the browser/ipywidgets side
Already built (a prior round, not this one) in eco/widgets/widget_tray.py
(WidgetTray, make_namespace_dashboard) and eco/voila_app.ipynb. This
is the Voila-compatible answer to the same "browse the namespace, open
and close widgets without leaking EPICS polling" problem — it works in
Voila (no JupyterLab shell needed) as well as JupyterLab/Notebook, which
sidecar (below) cannot.
New this round: eco/widgets/jupyter_sidecar.py's open_in_sidecar(obj, anchor=...) — a thin wrapper around the sidecar package
(pip install sidecar) for JupyterLab specifically: it opens
obj.widget() in a real Lumino dock panel that you can then freely
drag/tab/tile via JupyterLab's own UI, the closest browser-side analogue
to the Qt side's dockable panels. It does not work in Voila or
classic Notebook (no Lumino shell to dock into there) — use
widget_tray/voila_app.ipynb for those. Degrades to a clear
RuntimeError (not a traceback) if sidecar isn't installed.
JupyterLab's own Workspaces feature (jupyter lab workspaces export/ import, also in the command palette) is the closest browser-side
equivalent of the Qt side's window-layout persistence — not eco code,
just worth knowing it exists if a sidecar-heavy layout is worth saving.
What's genuinely new vs. what already existed
New in this round: eco/widgets/desktop_app.py (the whole Qt workbench),
eco/widgets/jupyter_sidecar.py, the _default_widget hook on
Assembly.widget() (eco/elements/assembly.py) plus AxisPTZ using it,
the desktop choice wired into eco_cli.py, and this file. Everything
under lab/voila (widget_tray.py, voila_app.ipynb, eco_cli.py
itself) already existed going into this round and is described here only
for the full picture.
Newer still: eco/widgets/console_kernel.py (the in-process/subprocess
kernel-building + input/output logging shared by every console widget),
eco/widgets/kernel_registry.py (the cross-kernel activity log described
above), eco/widgets/console_window_qt.py and eco.start_console() (a
genuinely independent second console/kernel). These replaced
desktop_app.py's original always-in-process kernel, which crashed
(ipykernel's MultipleInstanceError) the moment eco.start_desktop()
was called from an already-running IPython terminal session — the single
most common real way to actually use it. See
eco.widgets.console_kernel's module docstring for the full explanation.