plesty.server.view.dialogs

The console’s pop-up views: declare a device, run a field test, follow a job.

Each dialog collects an intent and turns it into a model object (DeviceSpec, FieldTestRun); the window hands that to the presenter. JobDialog is the one that renders as it goes: it tails a job log through the presenter until the job ends.

Classes

WrapLabel

A word-wrapped label that holds on to the height its text needs.

DeclareDialog

Add (or edit) a device declaration.

FieldTestDialog

Configure a field-test run for one device.

JobDialog

Follow one job: its log as it grows, its state, a cancel button.

ProblemReportDialog

Help ▸ Report a problem: say what happened, read what goes with it.

UpdateDialog

Help ▸ Check for updates…: the version installed, the newest one, and Update (#29).

DeviceConfigDialog

Edit → Device config…: the declaration and the .env file of one device, tabbed.

AgentDialog

Server ▸ Configure…: what this bench serves remote clients on.

PeersDialog

Server ▸ Benches…: the other benches this master shows and drives.

FleetFileDialog

Edit → Fleet file…: the fleet as YAML, validated before it is written.

CatalogueDialog

Hub → Browse…: the Hub's modules, put on this bench.

Functions

confirm(→ bool)

A yes/no question; True when confirmed.

alert(→ None)

Show an error.

about_text() → str)

The About box's text: what the console is, version, and details as a table.

about() → None)

The About box, with the PLESTY mark: the version, and details about it (#29).

Module Contents

class plesty.server.view.dialogs.WrapLabel(text: str = '')

Bases: PySide6.QtWidgets.QLabel

A word-wrapped label that holds on to the height its text needs.

minimumSizeHint is asked for without a width, so a wrapped label answers with the single line its text would take unwrapped. Every extra line the wrap actually adds is therefore missing from the dialog’s minimum height, and QFormLayout makes up the shortfall out of the rows below — which is how the working fleet checkbox came to be drawn inside the Env editor once the project link wrapped.

Taking the wrapped height as the minimum makes the dialog grow to fit instead of compressing what is under the label. The height is re-taken whenever the width changes or the text does, since either can change how many lines the text needs.

A label showing text, word-wrapped.

Parameters:

text (str)

_take_height() → None

Claim the height this text needs at the current width.

Return type:

None

resizeEvent(event: Any) → None

Re-take the height for the width just assigned.

Parameters:

event (Any)

Return type:

None

setText(text: str) → None

Show text, and re-take the height it needs.

Parameters:

text (str)

Return type:

None

class plesty.server.view.dialogs.DeclareDialog(parent: PySide6.QtWidgets.QWidget | None = None, spec: plesty.server.model.DeviceSpec | None = None, options: plesty.server.presenter.SourceOptions | None = None, packages: tuple[str, Ellipsis] = (), options_for: collections.abc.Callable[[str], plesty.server.presenter.SourceOptions] | None = None, info_for: collections.abc.Callable[[str], Any] | None = None, fetch_for: collections.abc.Callable[[str], plesty.server.presenter.SourceOptions] | None = None, worker: Any = None)

Bases: PySide6.QtWidgets.QDialog

Add (or edit) a device declaration.

The source is one of two, never both: a release (PyPI, pinned to a version or the newest) or a repository (git URL at a ref). The version and ref fields are editable drop-downs filled from what the bench knows (SourceOptions); anything else can be typed.

Build the form.

Parameters:
  • parent (PySide6.QtWidgets.QWidget | None) – Qt parent.

  • spec (plesty.server.model.DeviceSpec | None) – The declaration to edit; None declares a new device.

  • options (plesty.server.presenter.SourceOptions | None) – Drop-down contents for the (initial) package.

  • packages (tuple[str, Ellipsis]) – Installed modules a new device may be declared from — the only choices; the catalogue installs new ones.

  • options_for (collections.abc.Callable[[str], plesty.server.presenter.SourceOptions] | None) – Returns the drop-down contents for a package, called when the user picks another one.

  • info_for (collections.abc.Callable[[str], Any] | None) – Returns the package-level record (repository, latest release) shown read-only under the package.

  • fetch_for (collections.abc.Callable[[str], plesty.server.presenter.SourceOptions] | None) – Like options_for, but asks PyPI and the repository for every release and tag; run on worker when the user picks another package, and the drop-downs refilled from it.

  • worker (Any) – Runs fetch_for off the GUI thread.

_options_for = None
_info_for = None
_fetch_for = None
_worker = None
name
_proposed
_name_before
package
release_radio
repo_radio
_source_group
version
git
ref
args
env
autostart
project
hint
error
_spec: plesty.server.model.DeviceSpec | None = None
property repository_mode: bool

Whether the repository source is selected (else the release).

Return type:

bool

property package_name: str

The package the declaration names.

Return type:

str

show_project(info: Any) → None

Show the package’s project page and latest release (a PackageInfo).

Parameters:

info (Any)

Return type:

None

_name_changed(text: str) → None

Say what the rename will do, while the new name is still being typed.

Parameters:

text (str)

Return type:

None

_package_changed(package: str) → None

Refill the version/ref/url drop-downs (and the project line) for another package.

Parameters:

package (str)

Return type:

None

_fill(options: plesty.server.presenter.SourceOptions, keep: bool = False) → None

Refill the version/ref/url drop-downs; keep what was already entered in them.

Parameters:
Return type:

None

_source_changed(*_args: object) → None
Parameters:

_args (object)

Return type:

None

to_spec() → plesty.server.model.DeviceSpec

The declaration the form describes.

Raises:

FleetError – When a field is malformed.

Return type:

plesty.server.model.DeviceSpec

property spec: plesty.server.model.DeviceSpec | None

The accepted declaration, or None when cancelled.

Return type:

plesty.server.model.DeviceSpec | None

_accept() → None
Return type:

None

class plesty.server.view.dialogs.FieldTestDialog(name: str, parent: PySide6.QtWidgets.QWidget | None = None)

Bases: PySide6.QtWidgets.QDialog

Configure a field-test run for one device.

The gates are the standard plesty-lib field-test gates as check boxes (all checked = the script’s default: run everything); a script’s own --help may narrow the list once the window has fetched it.

Build the form for device name.

Parameters:
  • name (str)

  • parent (PySide6.QtWidgets.QWidget | None)

tier
address
gate_boxes: dict[str, PySide6.QtWidgets.QCheckBox]
gates_box
_gates_layout
repetitions
keep_going
keep_server
extra
options_label
error
_run: plesty.server.model.FieldTestRun | None = None
_set_gates(gates: list[str]) → None

Rebuild the gate check boxes (all checked).

Parameters:

gates (list[str])

Return type:

None

show_options(info: dict[str, list[str]]) → None

Show what the device’s script accepts (fetched by the window off the GUI thread).

Parameters:

info (dict[str, list[str]])

Return type:

None

property selected_gates: tuple[str, Ellipsis] | None

The checked gates in order, or None when all are checked (= run everything).

Return type:

tuple[str, Ellipsis] | None

to_run() → plesty.server.model.FieldTestRun

The request the form describes.

Raises:

FleetError – When a field is malformed (e.g. no gate checked).

Return type:

plesty.server.model.FieldTestRun

property run: plesty.server.model.FieldTestRun | None

The accepted request, or None when cancelled.

Return type:

plesty.server.model.FieldTestRun | None

_accept() → None
Return type:

None

class plesty.server.view.dialogs.JobDialog(bench: plesty.server.presenter.Bench, job: plesty.server.model.Job, parent: PySide6.QtWidgets.QWidget | None = None, poll_ms: int = 400, on_finished: collections.abc.Callable[[plesty.server.model.Job], None] | None = None)

Bases: PySide6.QtWidgets.QDialog

Follow one job: its log as it grows, its state, a cancel button.

Start tailing job through bench.

Parameters:
bench
job
_offset = 0
_on_finished = None
_finished = False
state
log
cancel_button
close_button
_timer
poll() → None

Read new log text and the job state; stop polling when it ended.

Return type:

None

_cancel() → None
Return type:

None

plesty.server.view.dialogs.confirm(parent: PySide6.QtWidgets.QWidget | None, title: str, text: str) → bool

A yes/no question; True when confirmed.

Parameters:
  • parent (PySide6.QtWidgets.QWidget | None)

  • title (str)

  • text (str)

Return type:

bool

plesty.server.view.dialogs.alert(parent: PySide6.QtWidgets.QWidget | None, title: str, text: str) → None

Show an error.

Parameters:
  • parent (PySide6.QtWidgets.QWidget | None)

  • title (str)

  • text (str)

Return type:

None

class plesty.server.view.dialogs.ProblemReportDialog(parent: PySide6.QtWidgets.QWidget | None, bench: plesty.server.presenter.Bench)

Bases: PySide6.QtWidgets.QDialog

Help ▸ Report a problem: say what happened, read what goes with it.

The report is shown in full and stays editable, because “reviews what is sent” is only true if what is shown is what is sent and the operator can take something out of it. Typing in the description rebuilds the report around it, so the two never disagree.

The bench files nothing. It holds no credential that could write to the tracker, and giving it one so it could would put a token on every lab machine for the sake of a convenience. Instead the report goes on the clipboard or into a file, and Open tracker opens the project’s own new-issue page in the operator’s browser, signed in as themselves.

Gather the report and show it beside the description.

Parameters:
PROJECT = 'https://gitlab.com/plesty/core/plesty-bench'
bench
title
what
body
copy_button
save_button
open_button
said
_rebuild() → None

Rebuild the report around the description as it is typed.

Return type:

None

payload() → str

What would be sent: the body as it stands, edits included.

Return type:

str

_issue_title() → str

The title, or a usable stand-in when none was typed.

Return type:

str

_copy() → None

Put the report on the clipboard.

Return type:

None

_save() → None

Write the report to a file the operator picks.

Return type:

None

_open_tracker() → None

Copy the report, then open the tracker’s new-issue page.

Both, and in that order: a report longer than a query string will not fit in the URL, so the clipboard is what actually carries it and the page is opened with the title filled in and a line saying to paste.

Return type:

None

plesty.server.view.dialogs.about_text(version: str, details: collections.abc.Sequence[tuple[str, str]] = ()) → str

The About box’s text: what the console is, version, and details as a table.

The changelog link goes to the one released with version, so it reads as this version’s changes; a version that is not a release (a checkout) links to the changelog of the integration line.

Parameters:
  • version (str)

  • details (collections.abc.Sequence[tuple[str, str]])

Return type:

str

plesty.server.view.dialogs.about(parent: PySide6.QtWidgets.QWidget | None, version: str, details: collections.abc.Sequence[tuple[str, str]] = ()) → None

The About box, with the PLESTY mark: the version, and details about it (#29).

Parameters:
  • parent (PySide6.QtWidgets.QWidget | None)

  • version (str)

  • details (collections.abc.Sequence[tuple[str, str]])

Return type:

None

class plesty.server.view.dialogs.UpdateDialog(parent: PySide6.QtWidgets.QWidget | None, installed: str, can_update: bool, cannot: str = '')

Bases: PySide6.QtWidgets.QDialog

Help ▸ Check for updates…: the version installed, the newest one, and Update (#29).

It opens at once on the installed version and asks PyPI for the newest in the background (show_latest()). Update is enabled once there is a newer release and this console is an install that can replace itself — the Windows installer’s, or a uv tool one; a source checkout is told why not. Accepting the dialog is asking for the update: the window then closes and hands it to the helper.

Build the dialog.

Parameters:
  • parent (PySide6.QtWidgets.QWidget | None) – The window it belongs to.

  • installed (str) – The version running.

  • can_update (bool) – Whether this console can install a newer one itself.

  • cannot (str) – Why it cannot, said when a newer one is found.

installed
can_update
cannot = ''
chosen: str | None = None
latest
note
buttons
update_button
show_latest(latest: str, newer: bool) → None

Say what the newest release is, and offer it when it is newer than the installed one.

Parameters:
  • latest (str)

  • newer (bool)

Return type:

None

show_error(message: str) → None

Say why the newest release could not be read.

Parameters:

message (str)

Return type:

None

class plesty.server.view.dialogs.DeviceConfigDialog(bench: plesty.server.presenter.Bench, name: str, parent: PySide6.QtWidgets.QWidget | None = None)

Bases: PySide6.QtWidgets.QDialog

Edit → Device config…: the declaration and the .env file of one device, tabbed.

Build the dialog for device name on bench.

Parameters:
bench
name
declare
env
env_highlighter
tabs
error
_save() → None
Return type:

None

class plesty.server.view.dialogs.AgentDialog(bench: plesty.server.presenter.Bench, parent: PySide6.QtWidgets.QWidget | None = None)

Bases: PySide6.QtWidgets.QDialog

Server ▸ Configure…: what this bench serves remote clients on.

A bench with the console open looks up and healthy while a plesty-bench client times out against it, because nothing has started the agent — it could only be started from a command line. This is where the endpoint, the token and the worker count are set, and the settings are kept in the bench home so the next start uses them.

The token travels by hand: it is generated here, copied, and pasted into the client machine’s PLESTY_BENCH_TOKEN (or BenchClient(token=…)). An empty token is legal and reads as what it is — no check at all.

Show the agent settings of bench.

Parameters:
bench
bind
token
workers
role
show_token
copy_button
data_share
data_folder
check_updates
error
saved: Any = None
to_settings() → Any

The settings the form describes.

Raises:

FleetError – When they would not bind.

Return type:

Any

_browse_data_share() → None
Return type:

None

_show_token_changed(shown: bool) → None
Parameters:

shown (bool)

Return type:

None

_generate() → None
Return type:

None

_copy() → None
Return type:

None

_save() → None
Return type:

None

class plesty.server.view.dialogs.PeersDialog(bench: plesty.server.presenter.Bench, parent: PySide6.QtWidgets.QWidget | None = None)

Bases: PySide6.QtWidgets.QDialog

Server ▸ Benches…: the other benches this master shows and drives.

A row per bench: what to call it, where its agent listens, and the secret that agent checks. The name is this console’s — it labels the Host column and the control panels — so it is what an operator calls the rig rather than whatever the machine calls itself.

The tokens are the nodes’ own, written to peers.yaml readable by its owner alone. They are shown masked for the same reason the agent’s is: a console is open on a bench in a shared room.

Show the node list of bench.

Parameters:
bench
editor
table
error
saved: Any = None
_add_row() → None
Return type:

None

_remove_row() → None
Return type:

None

to_peers() → Any

The benches the table describes; see BenchesEditor.to_peers().

Return type:

Any

_save() → None
Return type:

None

class plesty.server.view.dialogs.FleetFileDialog(bench: plesty.server.presenter.Bench, parent: PySide6.QtWidgets.QWidget | None = None)

Bases: PySide6.QtWidgets.QDialog

Edit → Fleet file…: the fleet as YAML, validated before it is written.

Show the current fleet text of bench.

Parameters:
bench
editor
error
_save() → None
Return type:

None

class plesty.server.view.dialogs.CatalogueDialog(bench: plesty.server.presenter.Bench, parent: PySide6.QtWidgets.QWidget | None = None, worker: Any = None)

Bases: PySide6.QtWidgets.QDialog

Hub → Browse…: the Hub’s modules, put on this bench.

A market place, and nothing more: installing from it stocks the bench with a module. No device is declared and no fleet file is touched — Edit ▸ Add device… does that, choosing from what the bench holds.

The source is the one the bench can carry out: a release from PyPI, which needs only uv, or the repository at a ref, which needs git. Which of the two a module offers is what PyPI is asked on selection.

Build the dialog; the catalogue is fetched on load().

Parameters:
  • bench (plesty.server.presenter.Bench) – The presenter.

  • parent (PySide6.QtWidgets.QWidget | None) – Qt parent.

  • worker (Any) – Runs the PyPI lookup off the GUI thread; without one it is done in place (tests).

bench
_entries: list[Any] = []
_shown: list[Any] = []
_releases: dict[str, tuple[tuple[str, Ellipsis], tuple[str, Ellipsis]]]
_worker = None
_item_type
query
kind
vendor
refresh_button
table
release_radio
repo_radio
_source_group
version
ref
source_note
install_button
status
error
selected_entry: Any = None
started_job: plesty.server.model.Job | None = None
reload(refresh: bool = False) → None

Fetch the catalogue and show it; refresh asks the hub rather than the cache.

The fetch runs on the worker when the dialog was given one, so the window stays answerable while the hub is slow. A fetch that fails reports and leaves the table as it is: an unreachable hub should not cost the operator the list they are already reading.

Parameters:

refresh (bool)

Return type:

None

load(entries: list[Any]) → None

Show entries (fetched by the caller, off the GUI thread).

Parameters:

entries (list[Any])

Return type:

None

apply_filter() → None

Repaint the table for the current query and vendor.

Return type:

None

_on_bench(package: str) → str

What this bench already holds of package: versions and refs, or —.

Parameters:

package (str)

Return type:

str

_selection_changed() → None
Return type:

None

_ask_pypi(entry: Any) → None

Look up every release and tag of entry, off the GUI thread when it can.

Parameters:

entry (Any)

Return type:

None

_show_source(entry: Any, releases: tuple[str, Ellipsis], tags: tuple[str, Ellipsis]) → None

Offer every release and tag, select the source the bench can carry out, say why.

Parameters:
  • entry (Any)

  • releases (tuple[str, Ellipsis])

  • tags (tuple[str, Ellipsis])

Return type:

None

_source_changed() → None
Return type:

None

_install() → None
Return type:

None