Class Reference

PhysPlot is both a table-first GUI application and an importable backend package. The classes below describe the current runtime components most useful to users and contributors.

Core classes

  • physplot.PhysPlot

    Public backend facade for loading data, assigning roles, transforming columns, plotting with modules, exporting outputs, and running workflows.

  • physplot_gui.app.main_window.MainWindow

    Modern PyQt6 main window. Orchestrates the central table, Simple/Advanced panels, protocol menus, Help/About links, and backend API calls.

  • physplot_gui.widgets.central_table.CentralTable

    Spreadsheet widget with column-role dropdowns, cell editing, copy/paste, rename, row deletion, and column deletion behavior.

  • physplot.steps.WorkflowStep

    Base class for replayable workflow steps such as loading data, setting roles, transforming columns, calculating formulas, deleting rows/columns, renaming columns, editing cells, and generating plot modules.

  • physplot.plotting_modules.PlotterRegistry

    Registry for built-in plotter modules and their available plot types.

Autodoc view

PhysPlot package.

class PhysPlot[source]

Bases: object

Package-first backend facade for loading, transforming, fitting, and exporting data.

calculate(formula: str, output: str, record=True)[source]
delete_columns(column_references)[source]
delete_rows(row_indices)[source]
describe_columns()[source]
export(output)[source]
export_workflow(path)[source]

Write an importable and headless-runnable Python sequence file.

fit(kind='linear')[source]
get_active_dataset()[source]
get_column_metadata(column_reference)[source]
insert_step(index, step)[source]

Insert step into self.workflow before position index.

index may equal len(self.workflow) to append. Negative indices are rejected so positions stay unambiguous for the GUI.

load(path, loader='auto', dataset_name=None)[source]
load_workflow(path)[source]
move_step(old_index, new_index)[source]

Move the step at old_index so it ends up at new_index.

plot(mode='single', output=None)[source]
plot_with_module(plotter_id, plot_type=None, record=True, **config)[source]

Render a plot through the modular plotter registry.

rename_column(old_reference, new_name: str)[source]
rerun_from(index, steps=None, *, allow_column_number_fallback=False, raise_on_error=False) → list[StepResult][source]

Restore the saved state before step index and run from there.

steps defaults to self.workflow. Steps before index must be unchanged since the run that saved the state; editing index or a later step is fine. If the exact resume point was evicted, the latest valid earlier one is used. Raises RuntimeError when no valid saved state exists, in which case run the whole sequence first.

run_bulk(workflow, input_folder, output_folder, **kwargs)[source]
run_workflow(steps, allow_column_number_fallback=False)[source]

Run steps in order and raise the first failure.

Per-step outcomes are still recorded on last_results. Use run_workflow_detailed to get results without raising and to keep resume points for rerun_from.

run_workflow_detailed(steps, *, start_index=0, allow_column_number_fallback=False, raise_on_error=False, keep_snapshots=True) → list[StepResult][source]

Run steps and return one StepResult per step.

Execution stops at the first failing step; that step is failed and every later step is skipped. With raise_on_error the failure is re-raised after last_results is set. With keep_snapshots the state before each executed step is saved so rerun_from can resume without replaying earlier steps.

save_workflow(path)[source]
set_cell_value(row_index: int, column_reference, value)[source]
set_roles(record=True, **roles)[source]

Assign column roles; recorded as a SetRoleStep so replays keep them.

start_recording()[source]
stop_recording()[source]
transform(input_column, function_name, output=None, record=True, **params)[source]
workflow_script() → str[source]

Return the same runnable Python source used by export_workflow.

Build and run the PhysPlot main window: table, mode panels, menus and status bar.

This module defines MainWindow, the main window of the PyQt6 desktop GUI, the helper figureforge_plugin_dirs() and a few module constants. The function run_app in physplot_gui.app.runner (used by python -m physplot_gui and by the physplot-gui / python-physplot-gui entry points) creates the QApplication, builds one MainWindow and shows it.

The window is an orchestration layer only. It owns the widgets, reacts to what the user does and forwards every scientific operation to the backend physplot.PhysPlot object kept in self.state.pp. Loading, transforming, plotting, exporting and sequence replay all happen in the physplot package; this module calls the backend, redraws the table from the backend dataset and records the user’s actions as rows of the protocol, most of them linked to replayable backend steps.

The user documentation pages “GUI Walkthrough” and “UI Reference” in the project wiki (wiki/), and the Read the Docs walkthrough (docs/getting_started.rst), describe the same window from the user’s point of view.

Window layout

MainWindow._build_ui() stacks the parts of the window from top to bottom:

+--------------------------------------------------------------------------------------+
| File   Protocol   View   Plot   Help                               (native menu bar) |
+--------------------------------------------------------------------------------------+
| [LSF] [PhysLab]           [  PhysPlot wide logo  ]         Mode: [Simple] [Advanced] |
+--------------------------------------------------------------------------------------+
|      |  Column 1  |  2: Time   |  3: Volts  | ...   <- headers: number and name      |
|      | [Ignore v] | [X      v] | [Y      v] | ...   <- role dropdown row             |
|    1 |            |    0.0     |    1.2     |                                        |
|    2 |            |    0.5     |    1.9     |       central spreadsheet table        |
|   .. |            |            |            |       (CentralTable, spare height)     |
+--------------------------------------------------------------------------------------+
| Lower mode panel (QStackedWidget owned by ModeManager)                               |
|   Simple:   [1. Data Importer] [2. Mathematical Transformation] [3. Plotter Module]  |
|   Advanced: tabs [Build Protocol] [Run Sequence]                                     |
+--------------------------------------------------------------------------------------+
| (o) Status: <last message>   Rows: N   Columns: N   File: <name>   Workflow   Mode   |
+--------------------------------------------------------------------------------------+
  • Menu bar. Created with QMainWindow.menuBar(); on macOS Qt shows it in the system menu bar at the top of the screen instead of inside the window.

  • Header (MainWindow._header()). The LSF and PhysLab logos sit on the far left, the wide PhysPlot logo is centred and the Simple/Advanced switcher (ModeSwitcher) is on the right. When the wide logo file is missing, a text title “PhysPlot” with the subtitle “Advanced Plotting Made Simple” is shown instead. A missing LSF or PhysLab image is simply left out.

  • Central table (CentralTable). Row 0 holds one role dropdown per column (Ignore, X, Y, X Error, Y Error, Group, Label, Batch Key, Fit Weight). Data rows are numbered from 1 in the row header. Column headers read Column N for default names and N: name otherwise. Double-click a header to rename the column; right-click a header, a row number or a cell for rename, copy, paste, clear and delete. The table always shows at least 100 data rows and 26 columns and grows when it is scrolled or typed into, so it usually shows more (empty) columns than the dataset has.

  • Lower mode panel (ModeManager). Simple Mode shows SimpleModePanel (three panels, sized to exactly their height). Advanced Mode shows AdvancedModePanel with the tabs Build Protocol and Run Sequence (300 to 430 px tall). Simple Mode is shown at start-up.

  • Status bar (PhysPlotStatusBar). Shows Status: <message> (long messages are cut with an ellipsis; hover for the full text), the row and column counts of the backend dataset and the loaded file name (File: - when none). In Advanced Mode it also shows a green dot, Workflow: <sequence file name> (Untitled until a sequence is saved or imported) and Mode: Advanced.

Native menu bar

MainWindow._build_menu_bar() creates five menus. Shortcuts are Qt key sequences, so Ctrl appears as the Command key on macOS.

File

  • Import Data… (Ctrl+O): MainWindow.import_data() with the Auto Loader. Pick a file; it replaces the table and is recorded as a File Loader protocol row.

  • Import Folder…: MainWindow.import_folder(). Pick a folder; the status bar reports Folder selected: <name>. No data is loaded (folders are processed from Advanced Mode > Run Sequence).

  • Export Data… (Ctrl+E): MainWindow.export_data(). Pick a folder; the backend writes data.csv, columns.csv and workflow.py there, plus fit.json and plot.png when a fit result or a figure exists.

  • Open Config Folder: opens the per-user Documents/PhysPlot/config folder (or the config folder under PHYSPLOT_USER_DIR when that variable is set) in the system file manager, creating the folder tree first when it is missing.

  • Reload Config Modules (Ctrl+Shift+R): re-scans the config/ folders without a restart. It rebuilds the Insert Protocol Module submenu, asks every panel that has a refresh_plugins method (Simple Mode) to rebuild its loader, function, plotter, template and fit-style lists, refreshes the window and reports Reloaded config modules from <folder>.

Protocol

  • Import Sequence.py…: MainWindow.open_workflow(). Replaces the protocol with the steps of a sequence file. The steps are not run.

  • Export Sequence.py… (Ctrl+S): MainWindow.save_workflow(). Writes the protocol as a runnable Python file.

  • Apply This Sequence (Ctrl+R): MainWindow.apply_current_sequence(). Replays the whole protocol on the table and fills the Status column.

  • Copy Sequence Code: copies the generated Python of the protocol (the text physplot.PhysPlot.workflow_script() returns) to the clipboard and reports Sequence script copied.

  • Clear Sequence: empties the protocol rows and the backend step list without asking. The table data is left as it is.

  • Insert Protocol Module (submenu): one item per .py file found in config/protocol_modules (label from the file’s DISPLAY_NAME or its file name, tooltip and status tip from DESCRIPTION). A disabled item “No protocol modules in config/protocol_modules” is shown when there are none. Choosing a module appends its steps to the end of the protocol without running them and reports Inserted protocol module <name>; a module that cannot be loaded is reported as “Insert protocol module failed”.

View

Plot

  • Generate Plot (Ctrl+G): MainWindow.generate_plot(). A Basic Plotter scatter plot of the X and Y role columns, styled with the Template selected in Simple Mode and opened in the Figure Editor.

  • Update Integrated Preview: MainWindow.generate_integrated_plot(). Refreshes the panels’ plotter lists and adds a display-only protocol row.

Help

What the panel controls call

ModeManager(self) hands this window to every panel as its actions object. The panels call its public methods directly and read shared data through actions.state:

Edits in the central table arrive as signals connected in MainWindow._build_ui(): role_changed to MainWindow.set_column_role(), column_renamed to MainWindow.rename_column(), cell_value_changed to MainWindow.record_cell_edit(), rows_deleted to MainWindow.record_row_delete(), columns_deleted to MainWindow.record_column_delete() and table_edited to MainWindow.sync_table_to_backend().

How actions reach the backend and the protocol

All shared state lives in self.state (GuiState) and survives mode switches: pp (the backend), timeline (protocol rows), transformations (a display list of applied transformations), mode, current_file and workflow_file. Two lists describe the protocol:

self.state.pp.workflow

The ordered backend step objects (LoadDataStep, SetRoleStep, TransformColumnStep, PlotModuleStep, RenameColumnStep, SetCellValueStep, DeleteRowsStep, DeleteColumnsStep, …). This list is the replayable source of truth: Apply This Sequence, Export Sequence.py, Copy Sequence Code, the Build Protocol Code view and bulk runs all read it.

self.state.timeline

One dict per row of the Build Protocol and Run Sequence tables, with the keys action, details, target, workflow_index, workflow_indices and code. workflow_indices links the row to one or more positions in pp.workflow (a File Loader row covers the LoadDataStep and the SetRoleStep that follows it). A row without indices is display-only and is never replayed.

Every recording goes through MainWindow._append_sequence(), in one of three ways:

  1. The GUI builds the step object and passes it in; _append_sequence appends it to pp.workflow. Used for imports, role changes, column renames, cell edits and row and column deletions.

  2. The backend records the step itself (physplot.PhysPlot.transform() and physplot.PhysPlot.plot_with_module() append to pp.workflow); the GUI passes the new step’s index so the row links to it. Used for transformations and plots.

  3. Nothing replayable is recorded: MainWindow._record() adds a display-only row (used by Update Integrated Preview), and plots from loader-declared callable plotters add a row without a step.

A typical action, Apply in Simple Mode, runs: apply_backend_transform() -> apply_simple_transform() -> _apply_transform() -> sync_table_to_backend() (table contents copied into the backend dataset) -> physplot.PhysPlot.transform() (adds the column and records a TransformColumnStep) -> table redrawn from the backend dataframe -> _append_sequence() -> status Transformation applied -> _refresh_all().

MainWindow._refresh_all() redraws the role dropdowns, the panels’ column lists, both protocol tables (including the Status column), the plotter lists and the status bar counts. Role changes, cell edits and row or column deletions record their rows without calling it, so those rows appear in Build Protocol at the next full refresh.

Apply This Sequence replays pp.workflow from its first step through physplot.PhysPlot.run_workflow_detailed() (a LoadDataStep reads its file from disk again), redraws the table from the backend dataset and fills the Status column from pp.last_results. The run stops at the first failing step; later steps are marked Skipped and the table keeps the state reached before the failure. Rerun from this step resumes from a state snapshot saved by an earlier run. Import Sequence.py, Apply Code to Table and Insert Protocol Module replace or extend the protocol but do not run it.

Dialogs and status messages

  • File dialogs: Import Data (the built-in types, loader-plugin types or any file), Import Folder, Export Data, Export Plot (PNG, PDF or SVG), Save Sequence.py and Load Sequence.py (starting in the writable config/sequences folder), Import Pipeline and Export Pipeline (config/pipelines) and the Run Sequence folder and file pickers. Cancelling a dialog leaves the data and the protocol unchanged.

  • Message boxes: About PhysPlot, Fit Results, Sequence Manager and the error warnings described below.

  • Plot windows: every plot opens in a non-modal PhysPlot dialog titled PhysPlot - <plotter>: <plot type> with the Matplotlib navigation toolbar and an Advanced Styling… button that opens the figure in the Figure Editor (same process).

  • Status messages set by this module include Ready, Transformation applied, Enter or import data before applying a transformation, Plot generated, Plot preview updated, Plot exported, Exported, Fit complete, Pipeline exported, Sequence saved, Sequence loaded, Sequence complete, Sequence updated, Sequence cleared, Sequence code applied, Sequence script copied, Reran from row N, Row N has no replayable step, Row N failed (<StepName>): <error>, Bulk complete: N outputs, Folder selected: <name>, Inserted protocol module <name> and Reloaded config modules from <folder>. After an error the status bar shows the error title (for example Import failed).

Figure Editor (FigureForge)

A plot window’s Advanced Styling… button opens its figure in FigureForge, which the UI calls the “Figure Editor”. PhysPlot ships its own editable copy in physplot_gui/figure_editor (FigureForge 0.3.3, MIT License); the PyPI package is not used. MainWindow._open_figure_editor():

The editor uses the same PyQt6 and Matplotlib as the rest of PhysPlot and runs in the same process, on the same figure object:

  1. Points FigureForge’s preferences.json at the copy’s built-in plugins folder. PhysPlot’s own plugins (the bundled config/figureforge_plugins folder, then the per-user copy, whose files win) are loaded in place from the folders in PHYSPLOT_FIGURE_EDITOR_PLUGIN_DIRS; nothing is copied.

  2. Adds hidden placeholder artists (line, scatter, legend, annotation) to every axes.

  3. Opens the editor window on the figure (one editor per figure; a second click raises it). Each redraw in the editor also redraws the plot window, so edits show there live.

  4. When the editor closes, the plot window takes the figure back with the edits. Nothing is saved to a file and there is no save prompt.

Edits made in the Figure Editor change the figure only; they are not recorded in the protocol.

Bulk runs

Run Bulk Workflow on the Run Sequence tab calls MainWindow.run_bulk_workflow(), which passes the chosen sequence file, or a copy of the current protocol when no file is given, to physplot.PhysPlot.run_bulk() (physplot.bulk.run_folder()). Each matching file of the input folder is processed in name order with a fresh backend and exported to <output folder>/<file stem>/. The run happens in the GUI thread, so the window does not respond until it finishes. The first failing file stops the run; the error names it, and files before it have already been exported.

Error reporting

Nearly every action method catches exceptions and passes them to MainWindow._error(), which puts the error title in the status bar, stores (title, exception) in MainWindow.last_error and then either prints <title>: <message> to stderr (when the Qt platform is offscreen, as in headless tests and CI, where no one can dismiss a dialog) or shows a modal warning box with the title and the exception text. The window stays usable after an error.

Module constants

LOGO_WIDE, LOGO_ICON, LSF_LOGO, PHYSLAB_LOGO

Image files in physplot/inc: the centred header logo (PhysPlotWide1.png), the window icon that is also shown in the About dialog and passed to the Figure Editor (PhysPlot.png), and the two left header logos (lsf.jpeg and physlab.png).

FIGUREFORGE_PLUGIN_DIR

The bundled config/figureforge_plugins folder, named in the error raised when no Figure Editor plugin folder exists.

BUILTIN_FUNCTIONS

Menu label and tooltip of each built-in transformation in the Simple Mode Function list, keyed by the backend function name that sequences record.

Related modules

BUILT_IN_DATA_EXTENSIONS = ['.csv', '.txt', '.dat', '.tsv', '.msa', '.xls', '.xlsx', '.xrdml']

File types the built-in loaders read, listed first in the Import Data dialog.

DATA_ROLES = {'U', 'V', 'W', 'X', 'X Error', 'Y', 'Y Error', 'Y2', 'Z'}

Roles that feed a plot’s axes or error bars; a plot type that does not read one clears it.

FIGURE_EDITOR_PACKAGE_DIR = PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/physplot/checkouts/latest/physplot_gui/figure_editor')

PhysPlot’s own copy of FigureForge (the Figure Editor), on the same PyQt6 as the rest of the GUI.

class MainWindow(*args: Any, **kwargs: Any)[source]

Bases: QMainWindow

Show the PhysPlot window and route every user action to the backend.

MainWindow is the main window of the GUI. See the module documentation above for the layout sketch, the complete menu bar and the flow from a click to a recorded protocol step.

Responsibilities

Attributes

state

The GuiState: the backend pp, the protocol rows timeline, the display list transformations, the current mode, the loaded data file current_file and the saved or imported sequence file workflow_file.

central_table

The CentralTable spreadsheet with the role dropdown row.

mode_manager

The ModeManager. mode_manager.stack is the lower QStackedWidget and mode_manager.panels maps "Simple" and "Advanced" to the panel widgets.

mode_switcher

The ModeSwitcher on the right of the header (created by _header()).

status

The PhysPlotStatusBar at the bottom.

file_menu, protocol_menu, view_menu, plot_menu, help_menu

The QMenu objects of the native menu bar. protocol_modules_menu is the Protocol > Insert Protocol Module submenu.

last_error

(title, exception) of the most recent error passed to _error(), or None. Tests read it because no dialog is shown on the offscreen platform.

_active_loader_entry

The loader-plugin entry used for the last import, or None after a built-in loader or new_table(). Plotters declared by that plugin are added to the Plotter Module list by plotter_entries().

_custom_plotters

Loader-declared callable plotters by their "loader:<id>" identifier, rebuilt by plotter_entries().

Attributes created on first use: _figure_editors (open Figure Editor windows by figure), _module_plot_dialogs (open plot dialogs) and, for the legacy plot windows, plot_config_window, plot_config_ui, plot_window and _main_window.

Signals connected

add_pipeline_step(payload: dict) → None[source]

Apply a pipeline-style transformation; same as apply_pipeline_step().

Parameters:

payload – See apply_pipeline_step().

apply_backend_transform(input_column: str, function_entry: str | dict, multiplier: float, offset: float, output: str) → None[source]

Run the transformation configured in Simple Mode’s Mathematical Transformation.

Called by the panel’s Apply button with the Input column, the selected Function entry, a multiplier of 1.0, the + offset and the Output text. Does nothing when no input column is selected. The function name is read from the entry’s function_name (built-in functions) or display_name (plugins); plugin entries are passed on whole so the plugin module can be used. An empty output defaults to <input>_<function name>. The work is done by apply_simple_transform().

Parameters:
apply_current_sequence() → None[source]

Replay the whole protocol on the table and show each row’s status.

Triggered by Protocol > Apply This Sequence (Ctrl+R) and the Apply This Sequence button of Build Protocol. With an empty protocol, “Apply sequence failed” is reported with “Build or import a protocol sequence first.”. Otherwise _run_current_sequence() replays every step from the first one, redraws the table and fills the Status column (OK, Failed or Skipped), and the status reads Sequence complete.

When a step fails, the replay stops there, later steps are Skipped and the table keeps the state reached before the failure. The failure is reported as “Apply sequence failed” with the text Row N failed (<StepName>): <error> (a dialog, or stderr on the offscreen platform), and the status bar keeps that text. An exception raised by the run itself (outside the per-step results) is also reported as “Apply sequence failed”.

apply_pipeline_step(payload: dict) → None[source]

Apply a transformation described by a pipeline-style payload.

The payload’s params text, such as "factor=2, value=0.5", is parsed by _parse_params() (numbers become floats). The function defaults to multiply and the output to <input>_<function>. The transformation then runs through _apply_transform(), which records it. No control in the bundled panels calls this method.

Parameters:

payload – A dict with the keys input, function, params (text) and output (optional).

apply_sequence_code(source: str) → None[source]
apply_simple_transform(input_column: str, output: str, function_name: str | dict, multiplier: float, offset: float)[source]

Apply a built-in or plugin transformation to a column, as Simple Mode’s Apply does.

Does nothing when input_column is empty. When the input column has no non-blank cell in the table, the status reads Enter or import data before applying a transformation and nothing else happens. An empty output means “overwrite the input column”.

A plugin entry (a dict with a module) is handed to _apply_function_plugin(), which passes multiplier and offset to the plugin. Built-in functions are mapped to backend parameters:

  • identity runs the backend multiply with factor=multiplier (so the protocol shows multiply);

  • multiply uses factor=multiplier;

  • add and subtract use value=offset;

  • divide uses divisor=multiplier (1 when the multiplier is 0);

  • every other function runs without parameters.

Each call goes through _apply_transform(). For any built-in function other than add and subtract, a non-zero offset is then applied by a second add transformation on the output column, which is recorded as a separate protocol row.

Parameters:
  • input_column – Name of the source column.

  • output – Name of the output column; a new name adds a column and an existing name overwrites that column.

  • function_name – A backend function name, or a plugin entry from simple_function_entries().

  • multiplier – Factor for identity, multiply and divide; passed to plugins.

  • offset – Value added to the result; used directly by add and subtract.

apply_step_edits(row_index: int, values: list[dict])[source]

Update a row’s steps with edited field values and replay from it.

values holds one field mapping per step in the row, as returned by StepEditorDialog.validated_values(). All steps are validated before any is changed.

assign_roles_for_plot_type(plotter_id: str, plot_type: str) → list[tuple[str, str]][source]

Give free columns the roles a plot type still needs, in table order.

Called when the user picks a plotter, category or plot type. Data roles the plot type does not read (X, Y, Y2, Z, U, V, W, X Error, Y Error) are first set back to Ignore; Group, Label, Batch Key and Fit Weight are kept. Each required role no column has yet goes to the next column whose role is Ignore: a numeric column for X, Y, Z and the other axis roles, a text column (or any free column) for Label and Group. Each assignment goes through set_column_role(), so it is recorded in the protocol like a role picked by hand. Nothing happens for plotters that read columns by name.

Returns:

The (column, role) pairs assigned.

backend_loader_entries() → list[dict][source]

Return the entries of the Simple Mode Data Loader dropdown.

The list starts with the built-in backend loaders in the order physplot.loaders.list_loaders() gives them (currently Auto Loader, CSV, TXT, Excel, Nanoindentation, XRDML and DataFrame loaders), each as {"display_name", "loader_id", "enabled"}. enabled is False only for the internal dataframe loader, which the panel shows greyed out and import_data() refuses. Loader plugins from config/data_importers follow, as returned by physplot_gui.app.plugin_discovery.discover_fileloaders() ({"display_name", "path", "module"}); the plugin files are imported again on every call.

The panel stores each entry as item data and passes the selected one back to import_data(). The list is also rebuilt after File > Reload Config Modules.

Returns:

The loader entries, built-in loaders first.

backend_transform_entries() → list[str][source]

Return the names of the built-in backend transformations.

A thin wrapper around physplot.core.transformations.list_transforms(). No control in the bundled panels calls it; see simple_function_entries() for the list the user sees.

Returns:

Backend function names such as "normalize_max" or "log10".

browse_input_folder(field: PyQt6.QtWidgets.QLineEdit) → None[source]

Pick the bulk-run input folder (Run Sequence, Input Folder > Browse).

Opens an Input Folder directory dialog in the current working directory and writes the chosen path into field. Cancelling leaves the field unchanged.

Parameters:

field – The Input Folder line edit of the Bulk Run panel.

browse_output_folder(field: PyQt6.QtWidgets.QLineEdit) → None[source]

Pick the bulk-run output folder (Run Sequence, Output Folder > Browse).

Opens an Output Folder directory dialog in the current working directory and writes the chosen path into field. Cancelling leaves the field unchanged.

Parameters:

field – The Output Folder line edit of the Bulk Run panel.

browse_workflow_file(field: PyQt6.QtWidgets.QLineEdit) → None[source]

Pick a sequence file for a bulk run (Run Sequence, Sequence File > Browse).

Opens a Sequence File dialog in the writable config/sequences folder, filtered to Python files, and writes the chosen path into field. Cancelling leaves the field unchanged. When the field stays empty, the bulk run uses the current protocol.

Parameters:

field – The Sequence File line edit of the Bulk Run panel.

can_move_timeline_row(row_index: int, delta: int) → bool[source]

Rows that load data stay first; nothing moves above them.

check_for_updates(manual: bool = True) → None[source]

Ask GitHub for the newest commit on the update channel, without blocking the UI.

Help > Check for Updates… (manual) always reports the result; the automatic check only speaks up when an update is available that was not skipped.

clear_pipeline() → None[source]

Empty the display list of applied transformations.

Only state.transformations is cleared; table data, the backend sequence and the protocol rows are unchanged. No control in the bundled panels calls this method.

clear_recording() → None[source]
copy_workflow_script() → None[source]
delete_pipeline_step(index: int) → None[source]

Remove one entry from the display list of applied transformations.

Only state.transformations changes: the column the transformation created, the backend sequence and the protocol rows stay as they are. Out-of-range indices are ignored. No control in the bundled panels calls this method.

Parameters:

index – Position of the entry in state.transformations.

delete_timeline_step(index: int) → None[source]
discover_function_entries() → list[dict][source]

Return the transformation plugins in config/transformations, or a placeholder.

No control in the bundled panels calls this method; the Simple Mode Function list comes from simple_function_entries().

Returns:

The entries from physplot_gui.app.plugin_discovery.discover_functions() (keys name, display_name, path and module). When no plugin is found, a single placeholder {"display_name": "x", "path": None, "module": None}.

discover_loader_entries() → list[dict][source]

Return the loader plugins in config/data_importers, or an Auto Loader placeholder.

No control in the bundled panels calls this method; the Simple Mode Data Loader list comes from backend_loader_entries().

Returns:

The entries from physplot_gui.app.plugin_discovery.discover_fileloaders() (keys display_name, path and module). When no plugin is found, a single entry {"display_name": "Auto Loader", "path": None, "module": None, "loader_id": "auto"}.

edit_timeline_step(row_index: int) → None[source]

Open the step editor for a row and apply the result.

export_data() → None[source]

Export the current dataset, sequence, fit and plot to a folder.

Triggered by File > Export Data… (Ctrl+E) and Simple Mode’s Export Data button. The table is first synced to the backend (sync_table_to_backend()), then an Export Data directory dialog opens in the current working directory. For the chosen folder physplot.PhysPlot.export() writes data.csv (the table), columns.csv (column metadata) and workflow.py (the protocol as a runnable sequence file), plus fit.json when a fit result exists and plot.png when a figure has been generated. The status then reads Exported; errors are reported as “Export failed”. The export is not recorded in the protocol.

export_module_plot() → None[source]

Save the most recently generated plot to an image file.

Triggered by Simple Mode’s Export Plot button. Uses state.pp.last_figure; when no plot has been generated yet, “Export plot failed” is reported with “Generate a plot before exporting.”. Otherwise an Export Plot save dialog opens with the suggested file physplot_plot.png in the current working directory and the filters PNG, PDF, SVG and All Files, and the figure is saved at 300 dpi. The status then reads Plot exported; errors are reported as “Export plot failed”. Changes made in the Figure Editor are not included, because they live in the editor’s own process. Nothing is recorded in the protocol.

export_pipeline() → None[source]

Save the display list of applied transformations as a pipeline JSON file.

An Export Pipeline save dialog suggests pipeline.json in the writable config/pipelines folder. state.transformations (one {"input", "function", "params", "output"} entry per applied transformation, with params as display text) is written as indented JSON and the status reads Pipeline exported. Write errors are not caught here. No control in the bundled panels calls this method.

fit_model() → None[source]

Fit a straight line to the X and Y role columns in the backend.

Syncs the table, then calls physplot.PhysPlot.fit() with "linear", which stores the slope and intercept in state.pp.fit_result (see show_fit_results()). The status reads Fit complete and the window is refreshed. The fit is not recorded in the protocol. Errors, such as a missing X or Y role, are reported as “Fit failed”. No control in the bundled panels calls this method; Simple Mode fits through the LSQ fit option of generate_module_plot().

fit_style_entries() → list[dict][source]

Return the entries of the Simple Mode Fit Style dropdown.

The first entry, {"name": "Default", "path": None, "style": None}, keeps the Fit Line fields as they are. It is followed by the LSQ fit-style presets from physplot_gui.fit_styles.list_fit_style_presets() (config/figureforge_fit_styles, or the folder in PHYSPLOT_FIT_STYLE_DIR), sorted by name. Choosing a preset fills the fit label, line style, line width and legend fields from its style payload.

Returns:

Entries of the form {"name", "path", "style"}, where style is the preset’s JSON content.

generate_integrated_plot() → None[source]

Refresh the panels’ plot controls and log a display-only protocol row.

Triggered by Plot > Update Integrated Preview. The table is synced to the backend and ModeManager.refresh_plots runs (Simple Mode rebuilds its plotter, plot type and template lists; the Advanced panel’s refresh_plot does nothing). A Generate Plot row with the details Create <mode> integrated plot and the target Plot Preview is added through _record(); it has no backend step, so it is not replayed. The status reads Plot preview updated. No figure is drawn, and because _refresh_all() is not called the new row shows in Build Protocol at the next full refresh. Errors are reported as “Plot failed”.

generate_module_plot(plotter_id: str, plot_type: str, style_module=None, fit_config=None) → None[source]

Draw a plot with the selected plotter module, as Simple Mode’s Generate Plot does.

Does nothing when no plotter is selected. The table is synced to the backend first. A fit_config is passed to the backend as lsq_fit=fit_config so a least-squares fit is overlaid. Then, by plotter:

  • "basic" (Basic Plotter): physplot.PhysPlot.plot_with_module() draws the figure and records a PlotModuleStep (including the fit settings); the Template is applied and the figure is shown in a PhysPlot plot dialog (_show_module_figure()).

  • A loader-declared callable plotter (id "loader:<name>"): run by _run_custom_plotter(); no backend step is recorded, so the protocol row is display-only. Asking for an LSQ fit raises “LSQ fit from Simple Mode is available for backend plotter modules.”. The Template is applied and the figure is shown in a PhysPlot plot dialog (_show_module_figure()).

  • Any other backend plotter: drawn, recorded and shown like the Basic Plotter.

Every dialog is titled PhysPlot - <plotter>: <plot type> and its Advanced Styling… button opens the figure in the Figure Editor.

The Template is applied to the figure after the backend call and is not part of the recorded step. A Generate Plot row with the details Create <plotter> <plot type> and the target Plotter Module is added, the status reads Plot generated and the window is refreshed. Errors are reported as “Plot failed”; if the error happens after the backend call, the PlotModuleStep is already recorded in pp.workflow but has no protocol row.

Parameters:
  • plotter_id – Id from plotter_entries(), for example "basic".

  • plot_type – Plot type from plot_type_entries(), for example "scatter".

  • style_module – Path of the template JSON file, or None for no template.

  • fit_config – The panel’s LSQ fit settings (enabled, expression, parameters, initial, label, line_style, line_width, show_legend), or None when LSQ fit is not ticked.

generate_plot() → None[source]

Plot the X and Y columns as a Basic Plotter scatter plot in a PhysPlot plot window.

Triggered by Plot > Generate Plot (Ctrl+G). The table is synced to the backend, then physplot.PhysPlot.plot_with_module() draws ("basic", "scatter") from the X and Y role columns and records a PlotModuleStep. The Template selected in Simple Mode (_selected_style_module()) is applied to the figure, which then opens in a PhysPlot plot window (_show_module_figure()); its Advanced Styling… button opens the figure in the Figure Editor. A Generate Plot row with the details Create <mode> plot (Single in Simple Mode, Sequence in Advanced Mode; see _current_plot_mode()) and the target X vs Y is linked to the recorded step, the status reads Plot generated and the window is refreshed.

Errors, such as missing X/Y roles or a Figure Editor that is not installed, are reported as “Plot failed”. If the error happens after the backend call, the PlotModuleStep is already in pp.workflow but no protocol row is added for it.

import_data(loader: str | dict = 'auto') → None[source]

Ask for a data file, load it into the table and record a File Loader row.

Triggered by File > Import Data… (Ctrl+O, always the Auto Loader) and by the Import Data button of Simple Mode’s 1. Data Importer (the loader selected in Data Loader). An Import Data file dialog opens in the current working directory with two filters: “Data Files” from data_file_filter() (the built-in types plus every loader plugin’s FILE_EXTENSIONS) and “All Files”. Cancelling does nothing.

Loader plugin (an entry with a module, from config/data_importers): the plugin’s load_data(path) is called. A returned DataFrame keeps its column names unless the plugin defines COLUMN_NAMES (or column_names); any other result is turned into an array and named from those names or Column N (see _plugin_column_names()). A 1-D result becomes one column; anything that is not 2-D raises “Loader must return a 2D table-like array.”. The table is loaded into the backend through the dataframe loader under the file’s stem, the plugin path and name are stored in the dataset metadata (loader_plugin, loader_name), the plugin’s DEFAULT_COLUMN_ROLES are applied (_apply_loader_roles()) and the entry is remembered as the active loader, so its declared plotters appear in the Plotter Module list. The recorded step is LoadDataStep(path, loader="dataframe", dataset_name=<stem>, loader_plugin=<plugin path>).

Built-in loader (a loader id string or an entry with a loader_id): physplot.PhysPlot.load() reads the file ("auto" picks the loader from the file extension), the loader’s suggested roles are applied and the active loader plugin is cleared. The recorded step is LoadDataStep(path, loader=<id>, dataset_name=<stem>).

In both cases the file becomes state.current_file (shown as File: in the status bar) and the table is redrawn from the backend dataset. The load step and, when any column has a role other than Ignore, a SetRoleStep with those roles are appended to the protocol as one File Loader row whose details read "<loader> (column names and role setup)" (or "(column names)" without roles) and whose target is the file name. The status is set to Ready and the window refreshed.

Errors, including choosing a disabled entry such as the DataFrame loader (”… is not available yet.”), are reported as “Import failed” through _error().

Parameters:

loader – A backend loader id such as "auto" or "csv", or an entry from backend_loader_entries().

import_folder() → None[source]

Ask for a folder and report the choice in the status bar.

Triggered by File > Import Folder… and Simple Mode’s Import Folder button. An Import Folder directory dialog opens in the current working directory; after a choice the status reads Folder selected: <folder name>. No files are loaded and nothing is recorded. To process every file of a folder, use Advanced Mode > Run Sequence (run_bulk_workflow()).

import_pipeline() → None[source]

Load the display list of transformations from a pipeline JSON file.

An Import Pipeline dialog opens in the writable config/pipelines folder. The chosen file’s JSON content replaces state.transformations; the transformations are not applied to the data and nothing is recorded in the protocol. Errors are reported as “Import pipeline failed”. No control in the bundled panels calls this method.

insert_protocol_module(path, after_row: int | None = None) → None[source]

Insert a reusable protocol module after after_row (default: append).

insert_step_dialog(after_row: int | None = None) → None[source]

Ask for a step type, edit its fields, and insert it after after_row.

insert_steps(steps: list, after_row: int | None = None, message: str = 'Steps inserted')[source]

Insert steps as new rows after after_row (None appends).

New rows never go above the leading rows that load data.

install_update(channel: str) → None[source]

Start the installer for channel in its own window, then quit PhysPlot.

last_error: tuple[str, Exception] | None
load_file(path, loader: str | dict = 'auto') → bool[source]

Load path into the table and record a File Loader row, without a dialog.

This is import_data() after the file has been chosen; see there for what each kind of loader does. Errors are reported as “Import failed”.

Parameters:
Returns:

True when the file was loaded.

move_timeline_row(row_index: int, delta: int)[source]

Swap a row with its neighbour and replay from the earlier one.

new_table(rows: int, columns: int, delete_old: bool) → None[source]

Replace the data with a blank or resized table named “Untitled”.

CentralTable.resize_table either builds a blank table (delete_old true) or keeps the current values, padding or cutting them to the new size; in both cases the roles are cleared. The result is loaded into the backend as Untitled, the active loader plugin and the current file are forgotten, the status is set to Ready and the window is refreshed. Nothing is recorded in the protocol. No control in the bundled panels or menus calls this method.

Parameters:
  • rows – Number of data rows.

  • columns – Number of columns.

  • delete_old – True to discard the current values.

open_and_plot(path) → None[source]

Load a file with the Auto Loader and plot it, for Open with PhysPlot.

Used for files named on the command line (physplot-gui data.csv) and files the operating system asks PhysPlot to open (right-click > Open With > PhysPlot, or a double-click when PhysPlot is the default app). The Auto Loader picks the loader from the file extension, including loader plugins’ FILE_EXTENSIONS. When the loaded columns have both an X and a Y role, generate_plot() draws them; otherwise the table is left for the user to assign roles.

Parameters:

path – The data file.

open_in_figure_editor

When true, plots open straight in the Figure Editor instead of a plot window (Simple Mode’s Advanced Figure Editor tick box; remembered by run_app).

open_user_config_folder() → None[source]
open_workflow() → None[source]

Replace the protocol with the steps of a Sequence.py file.

Triggered by Protocol > Import Sequence.py… and the Import Sequence.py button of Build Protocol. A Load Sequence.py dialog opens in the writable config/sequences folder. The chosen file is executed as Python by physplot.PhysPlot.load_workflow() and must define WORKFLOW_STEPS or build_workflow(). Its steps replace pp.workflow, the protocol rows are rebuilt from them (_sequence_rows_from_steps()), the file becomes state.workflow_file and the status reads Sequence loaded. The steps are not run and the table is not changed; use Apply This Sequence to replay them. Errors are reported as “Load sequence failed” and leave the protocol as it was. The window is refreshed whenever a file was chosen.

plot_type_categories(plotter_id: str) → list[dict] | None[source]

Return the plotter’s plot types grouped into categories, or None.

The Basic Plotter groups its types like the Matplotlib gallery (physplot.plotting_modules.gallery.PLOT_CATEGORIES). Only the types the plotter offers are kept (a subclass such as the Line Plotter offers fewer), and plot-type presets from config/plot_types go in a last Presets category. Plotters without categories return None and keep the flat Plot Type menu.

Parameters:

plotter_id – Id of the selected plotter.

Returns:

[{"name": str, "types": [{"id", "label", "description"}, ...]}, ...].

plot_type_entries(plotter_id: str) → list[str][source]

Return the Plot Type choices for the selected plotter module.

Called by Simple Mode whenever the Plotter Module selection changes.

Parameters:

plotter_id – Id of the selected plotter, as returned by plotter_entries().

Returns:

For a loader-declared callable plotter, its declared plot_types (or ["publication_ready"] when it declares none). For a backend plotter, the types physplot.plotting_modules.PlotterRegistry.list_plot_types() offers for the current dataset (for the Basic Plotter, for example scatter, line and scatter_line). An empty list when plotter_id is empty.

plot_type_selected(plotter_id: str, plot_type: str) → dict[source]

Limit the table’s role menus to the roles a plot type uses; return its hint.

Called by Simple Mode whenever Plot Type changes (also during refreshes); it never changes a role. For a catalogued plot type the role menus offer Ignore, the roles it uses, and Group, Label and Batch Key; a column keeps a role the plot type does not use (shown greyed as “not used by this plot type”). Other plotters offer every role.

Returns:

{"hint": "Uses: X, Y, Z", "description": ...} (empty for other plotters).

plotter_entries() → list[dict][source]

Return the entries of the Simple Mode Plotter Module dropdown.

The list starts with the backend plotter modules that physplot.plotting_modules.PlotterRegistry.list_plotters() offers for the current dataset, each as {"plotter_id", "name", "category"}.

When the last import used a loader plugin (self._active_loader_entry), the plotters that plugin declares (see physplot_gui.app.plugin_discovery.discover_loader_plotters()) are appended with the category "Loader":

  • An entry backed by a Python callable gets the id "loader:<plotter_id>" and is stored in self._custom_plotters so generate_module_plot() can run it.

  • Any other entry uses its backend_plotter_id and is plotted by the backend.

  • An id already in the list is skipped.

self._custom_plotters is reset on every call. The Simple Mode panel calls this whenever the window refreshes (through ModeManager.refresh_plots).

Returns:

The plotter entries in display order.

record_cell_edit(row_index: int, column: str, value: str, column_number: int) → None[source]

Record one edited cell as a SetCellValueStep.

Connected to CentralTable.cell_value_changed, which fires for each cell the user types into and for each cell filled by a paste. Adds an Edit Cell row with the details Row <row>, <column> and the new value as target, linked to SetCellValueStep(row_index, column, value, column_number=column_number). The new value itself reaches the backend through sync_table_to_backend(), which runs on the table_edited signal. The window is not refreshed here.

Parameters:
  • row_index – 1-based data row number, as shown in the row header.

  • column – Name of the edited column.

  • value – The cell’s new text.

  • column_number – 1-based column position, used on replay when the name is not found.

record_column_delete(columns: list[str], column_numbers: list[int]) → None[source]

Record deleted table columns as a DeleteColumnsStep.

Connected to CentralTable.columns_deleted (Delete Column in the header context menu). Adds a Delete Columns row with the column names as details and the target Table, linked to DeleteColumnsStep(columns, column_numbers=column_numbers). The columns have already been removed from the table; the backend dataset follows through sync_table_to_backend(). The window is not refreshed here.

Parameters:
  • columns – Names of the deleted columns.

  • column_numbers – Their 1-based positions before the deletion.

record_row_delete(row_indices: list[int]) → None[source]

Record deleted table rows as a DeleteRowsStep.

Connected to CentralTable.rows_deleted (Delete Row in the row-number context menu). Adds a Delete Rows row with the details Rows 3, 4 and the target Table, linked to DeleteRowsStep(row_indices). The rows have already been removed from the table; the backend dataset follows through sync_table_to_backend(). The window is not refreshed here.

Parameters:

row_indices – 1-based data row numbers that were deleted, in ascending order.

refresh_style_modules() → None[source]

Refresh the plotter-related dropdowns of the panels.

Calls ModeManager.refresh_plots: Simple Mode rebuilds its Plotter Module, Plot Type and Template lists, and Advanced Mode’s refresh_plot does nothing. No control in the bundled panels calls this method; the Reload button next to Template calls the panel’s own refresh_styles.

reload_config_modules() → None[source]

Re-scan every config/ folder without restarting the application.

rename_column(old_name: str, new_name: str) → None[source]

Rename a column in the backend and record a RenameColumnStep.

Connected to CentralTable.column_renamed, emitted after the user renames a column (double-click on the header, or Rename Column in the header’s context menu) and the table has already changed its header. When the backend dataset has a column called old_name, its 1-based column number is looked up and the column is renamed there with physplot.PhysPlot.rename_column(); otherwise the number is taken from a default name such as Column 4 (or left as None) and the backend is not changed. In both cases a Rename Column row (old -> new, target the new name) is recorded with RenameColumnStep(old_name, new_name, old_column_number=<number>), so a replay can fall back to the column number when no column has the old name. The window is refreshed. Errors are reported as “Column rename failed”.

Parameters:
  • old_name – The column’s previous name.

  • new_name – The column’s new name.

rerun_from_timeline_step(row_index: int) → None[source]

Resume the sequence at a table row without replaying earlier rows.

Triggered by Rerun from this step in the context menu of a Build Protocol row. The backend restores the state it saved before the row’s first step during an earlier run (physplot.PhysPlot.rerun_from(); when that snapshot is no longer kept, the latest valid earlier one is used) and runs from there to the end, so edits to that step or later ones take effect. Steps before the resume point are reported as OK. The results are shown like a full run (_show_sequence_results()) with the status Reran from row N, or the failure text when a step fails.

Out-of-range rows are ignored. A row without backend steps (a display-only row) only sets the status to Row N has no replayable step. When no saved state is usable (for example no full run yet, or earlier steps changed since that run), the backend’s error is reported as “Rerun failed”.

Parameters:

row_index – 0-based row index in the protocol table.

run_bulk_workflow(payload: dict) → None[source]

Run a sequence over every matching file of a folder and export each result.

Triggered by Run Bulk Workflow on the Advanced Mode Run Sequence tab. The sequence is the Sequence File when one is given (the backend loads it), otherwise a copy of the current protocol, including its load step, whose loader plugin is reused for every file; an empty protocol raises “Build or import a protocol sequence before running bulk automation.”. physplot.PhysPlot.run_bulk() (physplot.bulk.run_folder()) then processes each matching file of the input folder in name order with a fresh backend, replays the sequence with allow_column_number_fallback=True and exports the result to <output folder>/<file stem>/ (data.csv, columns.csv, workflow.py and, when present, fit.json and plot.png). Which files match depends on the sequence’s load step: a plugin format limits the run to that file extension, otherwise the built-in table formats are used.

On success the status reads Bulk complete: N outputs. The run happens in the GUI thread, so the window does not respond until it ends. The first failing file stops the run; the error is reported as “Bulk run failed” with the message Bulk run stopped at <file name>: <error> and a note that files before it were exported and later files were not run. Other errors are reported as “Bulk run failed” as they are. The folder texts are passed to the backend without checks here. The table and the protocol are not changed.

Parameters:

payload – The Bulk Run panel’s fields: input_folder, workflow_file (may be empty) and output_folder.

run_workflow() → None[source]

Replay the current protocol; same as apply_current_sequence().

save_workflow() → None[source]

Save the protocol as a runnable Sequence.py file.

Triggered by Protocol > Export Sequence.py… (Ctrl+S) and the Export Sequence.py button of Build Protocol. A Save Sequence.py dialog suggests sequence.py in the writable config/sequences folder. The backend writes the same Python source that Copy Sequence Code produces (physplot.PhysPlot.save_workflow()): a WORKFLOW_STEPS list plus build_workflow() and run() helpers, usable from notebooks, scripts or physplot run-workflow. The file becomes state.workflow_file (shown as Workflow: in the Advanced Mode status bar), the status reads Sequence saved and the window is refreshed. Errors are reported as “Save workflow failed”.

schedule_update_check(delay_ms: int = 5000) → None[source]

Check for updates in the background after start-up (at most once a day).

Called by run_app. Skipped when Check for Updates Automatically is off, when the last check was less than a day ago, and for off-screen runs (tests).

sequence_code_text() → str[source]
set_column_role(column: str, role: str) → None[source]

Apply a role picked in a column’s dropdown and record it.

Connected to CentralTable.role_changed. The role is set on the backend dataset (the backend moves a single-use role such as X or Y away from the column that had it, which then becomes Ignore). For every role except Ignore a row is recorded with the action Set <role>, the details Set column as <role>, the column as target and a SetRoleStep such as SetRoleStep({"x": "Time"}) (role names are converted by _role_key()). Choosing Ignore for a column that had a role records a Clear Role row with SetRoleStep({"ignore": column}) so a replay ends with the same roles; for a column that was already Ignore nothing is recorded. The role dropdowns are then redrawn from the backend roles and the status bar counts updated; _refresh_all() is not called, so the new row shows in Build Protocol at the next full refresh.

Errors, for example a column that the backend dataset does not contain, are reported as “Role update failed”.

Parameters:
  • column – Name of the column whose dropdown changed.

  • role – The chosen role label, for example "X" or "Y Error".

set_open_in_figure_editor(enabled: bool) → None[source]

Open new plots in the Figure Editor (True) or a plot window (False).

Set by Simple Mode’s Advanced Figure Editor tick box and saved in the PhysPlot settings, so the choice is kept between sessions.

set_timeline_row_enabled(row_index: int, enabled: bool)[source]

Enable or disable every step in a row and replay from it.

set_update_channel(channel: str) → None[source]

Follow channel (main, indevelopment or bleedingedge) and check it now.

show_about_dialog() → None[source]

Show the modal About PhysPlot dialog (Help > About PhysPlot).

The dialog shows, top to bottom: the PhysPlot icon (64 px, when the image exists), the title “PhysPlot”, the summary “Table-first scientific plotting and replayable workflow builder.”, three links (Documentation on Read the Docs, GitHub Repository and Report issues or bugs) that open in the default browser, and a Close button. The call blocks until the dialog is closed. Nothing is recorded and the status bar is not changed.

show_fit_results() → None[source]

Show the backend’s last fit result in a Fit Results message box.

Each entry of state.pp.fit_result is shown on its own key: value line (for a linear fit: kind, x, y, slope and intercept; for an LSQ fit from a plot: method, expression and parameters). Without a result the box reads “No fit result yet.”. No control in the bundled panels calls this method.

simple_function_entries() → list[dict][source]

Return the entries of the Simple Mode Function dropdown.

The order is:

  1. identity followed by every built-in backend transformation from physplot.core.transformations.list_transforms(). Each becomes {"display_name", "function_name", "tooltip"}, with the label and tooltip taken from BUILTIN_FUNCTIONS (for example baseline_subtract is shown as “subtract first value”); a name missing from that table is shown as is, without a tooltip.

  2. Every transformation plugin from config/transformations (physplot_gui.app.plugin_discovery.discover_functions()), keeping its name, display_name, path and module keys. Its tooltip is the first line of the plugin module’s docstring followed by the file name in brackets, or just the file name when the module has no docstring.

Labels are made unique: a plugin whose label is already taken is relabelled "<label> (<name>)" so it stays reachable, and any other repeated label is dropped (the first entry wins). The selected entry is passed back to apply_backend_transform() when the user clicks Apply.

Returns:

The function entries in display order.

start_recording() → None[source]

Set the backend’s recording flag and log a display-only Start Tracking row.

Calls physplot.PhysPlot.start_recording() and adds a row reading “Sequence tracking is always on” with the target Advanced; it has no backend step. The GUI records every action regardless of this flag. No control in the bundled panels calls this method.

stop_recording() → None[source]

Log a display-only Tracking row and clear the backend’s recording flag.

Adds a row reading “Sequence tracking stays on” with the target Advanced (no backend step), then calls physplot.PhysPlot.stop_recording(). The GUI keeps recording every action regardless of this flag. No control in the bundled panels calls this method.

style_module_entries() → list[dict][source]

Return the entries of the Simple Mode Template dropdown.

The first entry, {"name": "None", "path": None}, means “no template”. It is followed by every figure template JSON file from physplot_gui.plot_styles.list_style_modules() (the per-user config/templates folder first, then the bundled one, or only the folder in PHYSPLOT_STYLE_DIR when that variable is set), sorted by name. Templates are saved from the Figure Editor with PhysPlot > Save as Template.

Returns:

Entries of the form {"name", "path"}.

sync_table_to_backend() → None[source]

Copy the visible table contents into the backend dataset.

Connected to CentralTable.table_edited (emitted after typing, pasting, clearing, renaming and deleting rows or columns) and called before every action that reads the data: export, plotting, transformations, fitting and sequence replay.

CentralTable.to_dataframe trims the table to its used extent (keeping columns that have a role) and converts columns whose non-empty cells are all numbers to numeric. refresh_dataset_values() then reloads that frame into state.pp while keeping the dataset name, roles, metadata, source path and loader information. The status bar counts are updated.

Nothing is recorded here; the individual edits are recorded by record_cell_edit(), record_row_delete(), record_column_delete() and rename_column().

timeline_row_enabled(row: dict) → bool | None[source]

Return whether a row’s steps run, or None if it has no steps.

timeline_row_status(row: dict) → tuple[str | None, str | None][source]

Return (status, tooltip) for a Build Protocol row.

update_channel() → str[source]

The branch PhysPlot updates from: the saved choice, else the installed branch.

workflow_manager() → None[source]
ROLE_LABELS = {'batch_key': 'Batch Key', 'fit_weight': 'Fit Weight', 'group': 'Group', 'ignore': 'Ignore', 'label': 'Label', 'u': 'U', 'v': 'V', 'w': 'W', 'x': 'X', 'xerr': 'X Error', 'y': 'Y', 'y2': 'Y2', 'yerr': 'Y Error', 'z': 'Z'}

Role labels by the keys SetRoleStep stores (the inverse of MainWindow._role_key).

data_file_extensions() → list[str][source]

Return every extension PhysPlot can open: built-in types, then loader-plugin types.

The installers register these with the operating system so Open With > PhysPlot appears when a data file is right-clicked.

data_file_filter() → str[source]

Return the Import Data dialog filter: built-in types plus loader-plugin types.

Every extension a loader plugin declares in FILE_EXTENSIONS (for example .ras or .jdx) is added, so a file a plugin can read is listed without switching the dialog to All Files.

Returns:

A Qt name filter such as "Data Files (*.csv ... *.ras);;All Files (*)".

figure_editor_version() → str[source]

Return __version__ of PhysPlot’s FigureForge copy without importing it.

figureforge_plugin_dirs() → list[Path][source]

Return the Figure Editor plugin folders in copy order: bundled first, then per-user.

physplot.user_paths.plugin_search_dirs() lists the per-user Documents/PhysPlot/config/figureforge_plugins folder (or the one under PHYSPLOT_USER_DIR) before the bundled config/figureforge_plugins folder. This function reverses that order because the Figure Editor loads the folders one after another: a per-user plugin file with the same name as a bundled one is loaded last and so replaces it. Folders are returned whether or not they exist; the caller skips missing ones.

Returns:

The plugin folders, bundled folder first.

loader_label(step) → str[source]

Return the Data Loader menu label for a recorded LoadDataStep.

A loader plugin is named by its DISPLAY_NAME (matched on the plugin file name, or the file name itself when the plugin is missing); a built-in loader by its menu name, for example "auto" becomes “Auto Loader”. This is the text a live import records, so rows rebuilt from a saved sequence read the same as the rows recorded while working.

Parameters:

step – The LoadDataStep.

Returns:

The loader label.

plot_details(plotter_id: str, plot_type: str | None, fit_config=None) → str[source]

Return the Details text of a Generate Plot row, for example “Create line line”.

Parameters:
  • plotter_id – The plotter module id.

  • plot_type – The plot type, or None for the plotter’s default.

  • fit_config – The step’s lsq_fit settings; an enabled fit adds “ with LSQ fit”.

Returns:

The row text.

transform_label(function_name: str) → str[source]

Return the protocol-row text for a transformation, as a live Apply records it.

Built-in transformations keep their backend name (normalize_max); a plugin from config/transformations is shown by its DISPLAY_NAME (14_xrd_baseline_remove becomes “XRD: Baseline Remove”). An unknown name is returned unchanged.

Parameters:

function_name – The recorded function_name.

Returns:

The row text.