spacr.qt.screens.graph_builder¶
Workflow inputs and outputs¶
Graph Builder¶
Choose variables, groups and plotting settings from the loaded table, then export the figure with its analysis context.
Open: Home → Graph Builder.
Inputs and outputs below include conditional alternatives. The guidance and handoff notes say which route applies.
Inputs
Measured objects — measurements/measurements.db; object tables depend on the enabled cell, nucleus, pathogen and organelle masks. Relevant tables, depending on the route:
cell,nucleus,pathogen,cytoplasm. Relevant columns, depending on the route:plateID,rowID,columnID,fieldID.
Outputs
Figures and table exports — The output location chosen by the tool; exports describe the selected data and filters.
Before this module
Measure: Choose explicit variables, groups and filters.
The Graph Builder screen — a table, a filter, and a chart you drag together.
Assembles three things that already exist into one surface:
spacr.qt.widgets.graph_builder.GraphBuilderPanel— the drop zones and the canvas;spacr.qt.widgets.data_filter_panel.DataFilterPanel— the Local Data Filter, unchanged, because a filter that narrows every view is worth more than a private one that narrows this chart;spacr.qt.linked_selection— so a brush here highlights the same cells in the UMAP and on the plate map, and a lasso there highlights them here.
What it is for. Exploring a measurement table without plotting code, usually after Measure or Classify: drag columns onto the chart and it redraws as each one lands.
What it needs. One table of a measurements.db or a CSV or TSV file,
chosen with Load table. The object tables and png_list are offered first;
every other table in the database stays available.
What it produces. A chart with six drop zones: x, y, colour, size, facet row and facet column. Only x and y decide the chart type – one continuous column gives a histogram, one categorical column a bar chart of counts, two continuous columns a scatter plot, one of each a box plot and two categorical columns a heatmap of counts – and violin and line plots are explicit choices. A brushed rectangle becomes the shared selection, highlighted in the UMAP and on the plate map.
What to do next. Press Open selection in Annotate to see the brushed objects as image crops, narrow every view with the Local Data Filter beside the chart, or move to Gate Editor when a population should become a named gate that can be saved and re-applied.
The screen goes into the app registry through
spacr.qt.app.register_app() rather than through a row in the table
inside app.py, and its styling goes through
spacr.qt.theme.register_widget_qss(). Both seams exist so that a screen
built in parallel with five others is a new file rather than a merge conflict
in two thousand-line ones.
register() is not called at import — read its docstring for why, and
for the one line plus four side-table entries that finish the wiring. The
screen itself is complete: build it with make_graph_builder_screen(),
hand it a frame, and everything below works.
Classes¶
Drag columns onto channels; the chart follows. |
Functions¶
|
Factory handed to |
|
Read a CSV or one table of a SQLite measurement database. |
|
Put the Graph Builder in the app registry. Idempotent. |
|
Every user table in the SQLite file at |
Module Contents¶
- class spacr.qt.screens.graph_builder.GraphBuilderScreen(parent=None, *, link=None, threaded: bool = True)[source]¶
Bases:
PySide6.QtWidgets.QWidgetDrag columns onto channels; the chart follows.
- Parameters:
link – a private
LinkedSelectionfor tests.Nonejoins the process-wide one, which is the point of the screen in normal use.parent – parent widget; ownership only.
threaded –
Falseruns every table read inline instead of on the job runner’s thread. A TEST NEEDS THE RESULT ON THE LINE AFTER THE CALL; a user needs the window to keep painting while a large table loads. The jobs are the same either way – they still register, and a file that cannot be read still comes back through_on_frame_loaded()as a_Loadedcarrying aproblem– so only the waiting differs.
Build the screen: the graph builder beside the shared filter.
The registry key is named here rather than inherited: a screen that builds itself rather than being the generic
AppScreenhas none, and fold installation dispatches on exactly that – so this screen could declare folds and never be handed them.- Parameters:
parent – parent widget, or
None.link – shared selection link, passed to the builder and the filter.
threaded – read the database on a worker thread. Set
Falsein tests so a load finishes before it returns.
- closeEvent(event)[source]¶
Stop background work and unlink before going away.
- Parameters:
event – the Qt close event.
- load_path(path: str, table: str | None = None) None[source]¶
Load a CSV or one table of a SQLite measurement database.
NOTHING HERE TOUCHES THE FILE. The read has always run on a worker thread –
SELECT * FROM cellinto pandas measures 1.5 s for a 200 000-row measurement table on a warm local SSD – but listing the tables was kept inline on the argument that onesqlite_masterquery costs 0.4 ms. That argument holds only for a disk that answers. Measured on one workstation, a singlestatunder/nas_mnt– anautofsmount whose share was asleep – had not returned after TWENTY SECONDS, andsqlite3.connectopens the file before it can read a byte ofsqlite_master. A user picking a measurements.db off a sleeping share, or dropping a project folder that resolves onto one, froze the whole window with no traceback: a stalled event loop is not a crash.So the listing goes to the worker with the read, as one job, and the picker is populated by
_on_frame_loaded()when the answer arrives. Apath_probepre-flight would not have helped: it answers optimistically from cache, and it is theconnectitself that parks.Returns as soon as the job is dispatched;
_on_frame_loaded()finishes on the GUI thread, whether the file read or not – a failure comes back as data in the_Loadedrather than as an exception, so that it is dropped along with everything else when the load it belongs to has been superseded.- Parameters:
path – a
.csv,.tsvor.txtfile, read as delimited text, or any other file, opened read-only as a SQLite database.
- set_frame(frame: pandas.DataFrame, *, label: str = '') None[source]¶
Plot
frame. The one call a host needs.- Parameters:
frame – the table to chart; handed to the graph builder and the filter panel, and its row and column counts label the source unless
labelis given.
- spacr.qt.screens.graph_builder.make_graph_builder_screen(app_key: str | None = None) PySide6.QtWidgets.QWidget[source]¶
Factory handed to
spacr.qt.app.register_app().
- spacr.qt.screens.graph_builder.read_table(path: str, table: str | None = None, limit: int | None = None) pandas.DataFrame[source]¶
Read a CSV or one table of a SQLite measurement database.
- Parameters:
path – CSV, TSV, text, or SQLite database path. Delimited files are read directly; every other suffix is opened as SQLite in read-only mode.
limit – optional row cap, applied in SQL. The chart’s own large-data policy handles size once the frame is in memory; this is only for the case where the file is too big to read at all.
- spacr.qt.screens.graph_builder.register() bool[source]¶
Put the Graph Builder in the app registry. Idempotent.
Called at import from the bottom of
spacr.qt.app— see_SELF_REGISTERING_APPSthere. It is called from there rather than at the top of this module becauseapp.pyimportsspacr.qt.widgetsat its line 41, beforeregister_appexists, so nothing reachable from the top of that file can register during its import; and a registration that happens later is one that some importer’s snapshot of the registry predates.That used to be fatal as well as untidy, because
SECTIONSwas rebound rather than mutated, so a late registration into the previously empty Explore section was invisible to every module that had already imported the name. It is a list mutated in place now, so a late registration is seen everywhere — but registering from one deterministic point is still what keeps the app inventory the same on every import path, and the ledgers that check it honest.The row itself – the key, the name, the blurb, the section, the “no headless run” sentence, the API doc link and the nine translations of the display name – is declared in
spacr.qt.app_catalog.spacr.qt.app.register_app()distributes those into the four tables each used to need a hand-edit in, and this function’s whole job is to name which row. That is what lets the app be registered without importing this module at all: the launch reads the table, and the screen is imported when somebody opens it.- Returns:
Trueif this call is what registered it. Safe to call again: a module imported twice, or a test that re-imports it, must not raise on the duplicate key.
- spacr.qt.screens.graph_builder.table_names(path: str) List[str][source]¶
Every user table in the SQLite file at
path, in a useful order.- Parameters:
path – path to a SQLite measurement database, opened read-only; the preferred tables (
object,cell,nucleus, …) come first, then the rest alphabetically, withsqlite_internals left out.