plesty.lib.ui.qt.shell

The PLESTY shell: one home window hosting rearrangeable sub-windows.

The shell is a home window with no fixed content of its own. Every Panel becomes a sub-window the operator owns: drag it to another edge, drop it onto another panel to tab the two together, drag the handle between two panels to change how the space is split, drag it out of the window entirely to get a free-floating window on a second screen, and drag it back in when the bench gets crowded. Closed panels come back from the Panels menu; the arrangement is saved on exit and restored next time, so each rig keeps the layout its operator built.

shell = Shell("Demo", interval_ms=500)
shell.add(spectrum_panel, area="top")
shell.add(map_panel, area="top")
shell.add(power_panel, area="top")
shell.start_recording("run.mp4")   # optional: the run as a video
shell.start()

Everything visual comes from Theme — the shell holds no colour of its own.

Attributes

_AREAS

_ORIENTATIONS

LAYOUT_VERSION

Classes

PanelTitleBar

Title bar of a sub-window: state dot, title, live status, controls.

DockPanel

One sub-window of the shell, wrapping a Panel.

Shell

Home window hosting every panel as a rearrangeable sub-window.

Functions

_slug(→ str)

Return a filesystem-safe slug for a shell title.

_repolish(→ None)

Re-apply the stylesheet after a dynamic property changed.

Module Contents

plesty.lib.ui.qt.shell._AREAS
plesty.lib.ui.qt.shell._ORIENTATIONS
plesty.lib.ui.qt.shell.LAYOUT_VERSION = 1
plesty.lib.ui.qt.shell._slug(text: str) str

Return a filesystem-safe slug for a shell title.

Parameters:

text (str)

Return type:

str

plesty.lib.ui.qt.shell._repolish(widget: PySide6.QtWidgets.QWidget) None

Re-apply the stylesheet after a dynamic property changed.

Parameters:

widget (PySide6.QtWidgets.QWidget)

Return type:

None

class plesty.lib.ui.qt.shell.PanelTitleBar(dock: DockPanel, title: str, closable: bool = True)

Bases: PySide6.QtWidgets.QWidget

Title bar of a sub-window: state dot, title, live status, controls.

Replaces Qt’s default dock title bar so a panel can show what it is doing without stealing plot area — the same window-chrome language as the plesty.net hero card. Mouse presses are left unhandled on purpose, so the dock widget beneath still starts a drag: this bar is the drag handle that pulls a panel out of the home window.

Build the bar for dock.

Parameters:
  • dock (DockPanel) – The sub-window this bar belongs to.

  • title (str) – Text shown on the left.

  • closable (bool) – Show the close button.

_dock
dot
label
status
_button(glyph: str, tip: str, handler: Any) PySide6.QtWidgets.QToolButton

Create one title-bar button.

Parameters:
  • glyph (str)

  • tip (str)

  • handler (Any)

Return type:

PySide6.QtWidgets.QToolButton

set_status(text: str, tone: str = 'idle') None

Update the status line and the state dot.

Parameters:
  • text (str) – Status text shown at the right of the bar.

  • tone (str) – State name understood by the theme ("running", "idle", "error", "warning").

Return type:

None

set_floating(floating: bool) None

Mark the bar as belonging to a floating window.

Parameters:

floating (bool) – Whether the sub-window is currently floating.

Return type:

None

mouseDoubleClickEvent(event: Any) None

Toggle floating on a double click, as a title bar conventionally does.

Parameters:

event (Any) – The Qt mouse event.

Return type:

None

class plesty.lib.ui.qt.shell.DockPanel(panel: plesty.lib.ui.panel.Panel, shell: Shell)

Bases: PySide6.QtWidgets.QDockWidget

One sub-window of the shell, wrapping a Panel.

Movable, floatable, and (unless the panel says otherwise) closable — the three features that let an operator rearrange the window at will.

Wrap panel into a sub-window of shell.

Parameters:
panel
title_bar
toggle_floating() None

Pull the panel out of the home window, or drop it back in.

Return type:

None

refresh() None

Tick the panel and mirror its status into the title bar.

Return type:

None

class plesty.lib.ui.qt.shell.Shell(title: str = 'PLESTY', *, theme: plesty.lib.ui.theme.Theme | None = None, interval_ms: int = 500, layout_file: str | pathlib.Path | None = None, remember_layout: bool = True, parent: PySide6.QtWidgets.QWidget | None = None)

Bases: PySide6.QtWidgets.QMainWindow

Home window hosting every panel as a rearrangeable sub-window.

Create the home window.

Parameters:
  • title (str) – Window title, also the key of the saved layout.

  • theme (Optional[plesty.lib.ui.theme.Theme]) – Palette and stylesheet; None loads the packaged PLESTY theme.

  • interval_ms (int) – Refresh interval — how often every panel is ticked. 500 ms keeps a live spectrum fluent without loading the machine that is also running the measurement.

  • layout_file (Optional[str | pathlib.Path]) – Where to store the arrangement; None uses ~/.plesty/ui/<title>.json.

  • remember_layout (bool) – Restore the stored arrangement on open and save it on close.

  • parent (Optional[PySide6.QtWidgets.QWidget]) – Parent widget.

theme
panels: dict[str, DockPanel]
recorder: plesty.lib.ui.qt.recorder.Recorder | None = None
last_recording: pathlib.Path | None = None
remember_layout = True
layout_file
_timer
add(panel: plesty.lib.ui.panel.Panel, *, area: str | None = None, tabify_with: str | None = None, split_with: str | None = None) DockPanel

Add a panel as a sub-window.

A panel joining an area that already holds one is split against the last panel there rather than dropped into the area as a whole. Only a split puts a real handle between two panels: docks that merely share an area — or worse, sit in different areas — cannot be resized against each other, which is how a panel ends up as an unadjustable strip.

Parameters:
  • panel (plesty.lib.ui.panel.Panel) – The panel to host.

  • area (Optional[str]) – Docking area override; defaults to panel.area.

  • tabify_with (Optional[str]) – Name of an already-added panel to tab this one with, instead of giving it its own space.

  • split_with (Optional[str]) – Name of the panel to split against; defaults to the last one added to the same area.

Returns:

The created sub-window.

Raises:

ValueError – If the panel name is already taken, or the panel named in tabify_with / split_with is unknown.

Return type:

DockPanel

_named(name: str, action: str) DockPanel

Return an already-added sub-window by panel name.

Raises:

ValueError – If no panel of that name was added.

Parameters:
  • name (str)

  • action (str)

Return type:

DockPanel

_last_in_area(area: str) DockPanel | None

Return the last docked sub-window of area, if it has one.

Parameters:

area (str)

Return type:

Optional[DockPanel]

apply_weights() None

Size the panels of each area in proportion to their panel weights.

Qt distributes leftover space evenly, which leaves a status strip as tall as a spectrum. Weights say what each panel is worth: a plot 3, a readout 1.

Return type:

None

remove(name: str) None

Remove a panel and release what it holds.

Parameters:

name (str) – Name of the panel to remove; unknown names are ignored.

Return type:

None

start() None

Show the window, restore the layout, and start refreshing panels.

A stored arrangement wins over the panels’ weights — it is what the operator built last time.

Return type:

None

stop() None

Stop refreshing panels.

Return type:

None

set_status(text: str, state: str = 'idle') None

Set the shell-wide status line.

Parameters:
  • text (str) – Message shown in the status bar.

  • state (str) – State name coloring the indicator ("running", "idle", "error", "warning").

Return type:

None

_tick() None

Refresh every panel; a failing panel never stops the others.

Return type:

None

start_recording(path: str | pathlib.Path, *, fps: int = 10, every: int = 1) plesty.lib.ui.qt.recorder.Recorder

Capture the window after every refresh until stop_recording().

Recording belongs to the window rather than to a view, so whatever is docked is captured — including a panel added or floated halfway through a run.

Parameters:
  • path (str | pathlib.Path) – Destination file; the suffix is corrected to what the available encoder can write (MP4, else animated GIF).

  • fps (int) – Frame rate of the encoded video.

  • every (int) – Capture one frame per every refreshes.

Returns:

The recorder, already active.

Return type:

plesty.lib.ui.qt.recorder.Recorder

stop_recording() pathlib.Path | None

Encode what was captured and stop recording.

Returns:

The written file, or None if nothing was being recorded.

Return type:

Optional[pathlib.Path]

_toggle_recording() None

Menu handler: start a recording beside the layout, or stop one.

Return type:

None

save_layout(path: str | pathlib.Path | None = None) pathlib.Path

Store the current arrangement.

Parameters:

path (Optional[str | pathlib.Path]) – Destination file; None uses layout_file.

Returns:

The path written.

Return type:

pathlib.Path

restore_layout(path: str | pathlib.Path | None = None) bool

Restore a stored arrangement, if one exists and still fits.

Panels that were not added this time are simply absent from the restored layout; new ones keep the placement they were added with.

Parameters:

path (Optional[str | pathlib.Path]) – Layout file; None uses layout_file.

Returns:

Whether a layout was restored.

Return type:

bool

reset_layout() None

Return every panel to the area it was added with, undocked from tabs.

Return type:

None

_build_chrome() None

Create the menus and the status bar.

Return type:

None

closeEvent(event: Any) None

Save the arrangement and release the panels before closing.

Parameters:

event (Any) – The Qt close event.

Return type:

None