API reference¶
The reference below is generated directly from the current Python sources. Names beginning with an underscore are implementation details.
Smart table¶
ms_table
¶
smart_table ─────────── Advanced table component for PySide6 + SQLModel.
Exports
SmartTableView → Main widget. TableConfig → Declarative configuration. ColumnDef → Column definition. FilterSpec → Filter specification. FilterOperator → Comparison operators. SortSpec → Sort specification. AlignHint → Column alignment. QueryBuilder → Direct access to the builder (tests / advanced use). SmartTableModel → Qt model (advanced use / subclassing). PaginationControl → Standalone pagination control.
Modules:
| Name | Description |
|---|---|
action_header |
action_header.py |
cell_delegate |
cell_delegate.py |
column_panel |
column_panel.py |
example_usage |
example_usage.py |
filter_panel |
filter_panel.py |
metric_filter_bar |
Additive numeric filter bar, rendered as chips. |
pagination_control |
pagination_control.py |
query_builder |
query_builder.py |
session_provider |
|
smart_table_view |
smart_table_view.py |
sort_panel |
|
table_config |
table_config.py |
table_model |
table_model.py |
test_external_filters |
External filter-overlay contract for QueryBuilder. |
test_toolbar |
Self-check for the agnostic ms_table toolbar (zones + overflow + collapsible). |
Classes:
| Name | Description |
|---|---|
ActionHeader |
Horizontal header with per-column action buttons. |
ColumnConfigDialog |
Lets the user: |
FilterPanel |
Filter bar with: |
MetricFilterBar |
Chips over numeric fields, plus a |
SortPanel |
Compact bar showing the active sorts as pills, with a combo to add new |
PaginationControl |
Compact, modern pagination. |
QueryBuilder |
Builds and runs paginated queries from a TableConfig. |
SessionProvider |
|
SmartTableView |
Smart table with pagination, filtering, sorting and column configuration. |
ColumnDef |
Define a visible column in the table. |
FilterSpec |
Specification of an active filter. |
SortSpec |
Sort specification. |
TableConfig |
Full configuration of a SmartTableView. |
ToolbarAction |
An action injected into the toolbar. Deliberately dumb: the component |
SmartTableModel |
Qt model for SmartTableView. |
Functions:
| Name | Description |
|---|---|
choices_from_class |
Extract the string values of a constants class (not an Enum). |
ActionHeader
¶
ActionHeader(config: TableConfig, parent: QWidget | None = None)
Bases: QHeaderView
Horizontal header with per-column action buttons.
Signal
actionClicked(logical_index, action, modifiers) action: "sort" | "search" | "filter" modifiers: Qt.KeyboardModifiers (Shift → additive sort)
Source code in src/ms_components/ms_table/action_header.py
66 67 68 69 70 71 72 73 74 75 76 77 | |
ColumnConfigDialog
¶
ColumnConfigDialog(config: TableConfig, parent=None)
Bases: QDialog
Lets the user
- Enable / disable columns (checkbox)
- Reorder them with drag & drop
On accept it emits the reordered ColumnDef list with updated visibility.
Source code in src/ms_components/ms_table/column_panel.py
40 41 42 43 44 45 | |
FilterPanel
¶
FilterPanel(config: TableConfig, parent=None, *, show_search: bool = True)
Bases: QWidget
Signals
search_changed(str): Global search text. filter_added(FilterSpec): New advanced filter. filter_removed(str): Filter removed (field). filters_cleared(): All filters cleared.
Methods:
| Name | Description |
|---|---|
set_filters |
Show chips for a list of filters (e.g. when restoring state). |
Source code in src/ms_components/ms_table/filter_panel.py
189 190 191 192 193 194 195 | |
set_filters
¶
set_filters(specs: list[FilterSpec]) -> None
Show chips for a list of filters (e.g. when restoring state).
Source code in src/ms_components/ms_table/filter_panel.py
306 307 308 309 310 | |
MetricFilterBar
¶
MetricFilterBar(fields: Sequence[tuple[str, str]], parent: QWidget | None = None, *, decimals: int = 3, default_value: float = 0.0, empty_text: str = 'No filters — everything is shown.')
Bases: QWidget
Chips over numeric fields, plus a + that opens the condition dialog.
fields are (key, label) pairs: the key is what shows up in conditions().
Methods:
| Name | Description |
|---|---|
add_condition |
Add (or replace) a condition: the same field+operator is an edit, not another chip. |
set_conditions |
Whole state at once (restoring a saved view): a single signal. |
Source code in src/ms_components/ms_table/metric_filter_bar.py
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | |
add_condition
¶
add_condition(field: str, op: str, value: float, *, notify: bool = True) -> None
Add (or replace) a condition: the same field+operator is an edit, not another chip.
Source code in src/ms_components/ms_table/metric_filter_bar.py
166 167 168 169 170 171 172 173 174 175 176 177 178 179 | |
set_conditions
¶
set_conditions(conditions: Iterable[Condition]) -> None
Whole state at once (restoring a saved view): a single signal.
Source code in src/ms_components/ms_table/metric_filter_bar.py
181 182 183 184 185 186 187 | |
SortPanel
¶
SortPanel(config: TableConfig, parent=None)
Bases: QWidget
Compact bar showing the active sorts as pills, with a combo to add new ones.
Signals
sort_changed(list[SortSpec])
Source code in src/ms_components/ms_table/sort_panel.py
64 65 66 67 68 69 70 | |
PaginationControl
¶
PaginationControl(total_items: int = 0, items_per_page: int = 20, items_per_page_options: list[int] | None = None, parent=None)
Bases: QWidget
Compact, modern pagination.
Signals
page_changed(int) items_per_page_changed(int)
Source code in src/ms_components/ms_table/pagination_control.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
QueryBuilder
¶
QueryBuilder(db: SessionProvider, config: TableConfig)
Builds and runs paginated queries from a TableConfig.
Usage
builder = QueryBuilder(db, config) builder.set_filters([FilterSpec("name", FilterOperator.ILIKE, "%ann%")]) builder.set_sort([SortSpec("id", descending=True)]) builder.set_page(1) rows, total = builder.fetch()
Methods:
| Name | Description |
|---|---|
set_external_clause |
Overlay a raw SQLAlchemy clause under |
set_search |
Enable an ILIKE search ORed across the given fields. |
remove_search |
Disable the quick search and show everything again. |
all_ids |
PKs of every row matching the current filters/search (no pagination). |
count |
Row total under the current filters/search, without fetching any row. |
cursor_of |
Keyset cursor |
fetch_after |
Next page by keyset: |
fetch_window_after |
The page after the cursor, without |
fetch_window_at |
Direct load for a scrollbar jump to a page that has not been walked yet. |
fetch |
Run the query and return (rows_as_dicts, total_count). |
to_query_spec |
The "filtered" selection as QuerySpec kwargs, ready to hand to a job. |
Source code in src/ms_components/ms_table/query_builder.py
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
set_external_clause
¶
set_external_clause(key: str, clause) -> None
Overlay a raw SQLAlchemy clause under key (None removes it).
Escape hatch for what FilterSpec cannot express - subqueries
(~exists(...)), OR across fields, functions. It composes with AND just
like set_external_filters, and because it is an already-built expression
the builder does not interpret it: making sure it references the right
model is the caller's responsibility.
Source code in src/ms_components/ms_table/query_builder.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 | |
set_search
¶
set_search(text: str, fields: list[str]) -> None
Enable an ILIKE search ORed across the given fields.
Source code in src/ms_components/ms_table/query_builder.py
113 114 115 116 117 | |
remove_search
¶
remove_search() -> None
Disable the quick search and show everything again.
Source code in src/ms_components/ms_table/query_builder.py
119 120 121 122 123 | |
all_ids
¶
all_ids() -> list[int]
PKs of every row matching the current filters/search (no pagination).
Lets a caller resolve "everything the table currently shows" into an id set without materializing rows. Honors filters + quick search, ignores paging.
Source code in src/ms_components/ms_table/query_builder.py
139 140 141 142 143 144 145 146 147 148 149 150 | |
count
¶
count() -> int
Row total under the current filters/search, without fetching any row.
For hosts that want to keep the counter live (e.g. while a job inserts records) without reloading the visible rows.
Source code in src/ms_components/ms_table/query_builder.py
152 153 154 155 156 157 158 159 | |
cursor_of
¶
cursor_of(row: dict) -> tuple[Any, Any] | None
Keyset cursor (sort_value, pk) for an already-loaded row.
Source code in src/ms_components/ms_table/query_builder.py
161 162 163 164 165 166 167 168 169 170 171 172 173 | |
fetch_after
¶
fetch_after(cursor: tuple[Any, Any] | None) -> tuple[list[dict], int]
Next page by keyset: WHERE (sort, pk) > cursor, with no OFFSET.
OFFSET makes the engine walk and discard the previous N rows, so page 500 costs 500 times the first one. The cursor does not: it is always an index seek. The cursor is composite because the visible order is rarely unique (two identical scores).
Source code in src/ms_components/ms_table/query_builder.py
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | |
fetch_window_after
¶
fetch_window_after(cursor: tuple[Any, Any] | None) -> tuple[list[dict], bool]
The page after the cursor, without COUNT(*).
Infinite scroll only needs to know whether another page exists. Asking for one extra row answers that without re-counting a potentially huge table on every move.
Source code in src/ms_components/ms_table/query_builder.py
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | |
fetch_window_at
¶
fetch_window_at(page_index: int) -> tuple[list[dict], bool]
Direct load for a scrollbar jump to a page that has not been walked yet.
The normal path is still keyset via :meth:fetch_window_after. OFFSET only
shows up when the user drags the scrollbar to a position with no known
cursor.
Source code in src/ms_components/ms_table/query_builder.py
208 209 210 211 212 213 214 215 216 217 218 219 220 | |
fetch
¶
fetch() -> tuple[list[dict], int]
Run the query and return (rows_as_dicts, total_count).
Source code in src/ms_components/ms_table/query_builder.py
245 246 247 248 249 250 251 252 253 254 255 256 257 | |
to_query_spec
¶
to_query_spec() -> dict
The "filtered" selection as QuerySpec kwargs, ready to hand to a job.
Returns a dict rather than a QuerySpec so the table does not take ms_flow as
a dependency; the host does QuerySpec(**builder.to_query_spec()). This is
what avoids materialising a list of ids: the job receives the query, not its
result.
Raises when any part of the current state is not expressible (expression columns, LIKE/ILIKE filters over joins): staying quiet and sending a wider query would be worse.
Source code in src/ms_components/ms_table/query_builder.py
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 | |
SessionProvider
¶
Bases: Protocol
Methods:
| Name | Description |
|---|---|
get_session |
Return a fresh SQLModel session for a query cycle. |
get_session
¶
get_session() -> Session
Return a fresh SQLModel session for a query cycle.
Source code in src/ms_components/ms_table/session_provider.py
10 11 | |
SmartTableView
¶
SmartTableView(db: SessionProvider, config: TableConfig, parent=None)
Bases: QWidget
Smart table with pagination, filtering, sorting and column configuration.
Signals
row_clicked(object): SQLModel object for the clicked row. row_double_clicked(object): SQLModel object for the double-clicked row. selection_changed(list): List of selected objects. edit_committed(object, str, object): (raw_obj, field, new_value) when an inline edit is committed. data_refreshed(int): Total item count after each load.
Methods:
| Name | Description |
|---|---|
refresh_counts |
Refresh only the record counter (COUNT), without reloading rows. |
ensure_viewport_filled |
One tick of a job's start-up: reload rows only once there are enough to |
patch_loaded_rows |
Patch visible cached rows by a raw-object/key value; no model reset or reselection. |
open_table_menu |
Pop up the full menu for external hosts (a ribbon, for instance). |
set_external_filters |
Set (or replace) an external filter overlay under |
set_external_clause |
Set (or, with None, drop) a raw SQLAlchemy clause under |
set_empty_state |
Change the empty state at runtime: |
set_toolbar_button_style |
Icons / text / both for the toolbar actions. |
set_toolbar_actions |
Put a host's commands on this table's toolbar while it needs them. |
set_config_actions_visible |
Show/hide the actions the config declares (toolbar_left/center/right). |
view_state |
Current user-facing table prefs: visible column fields + active sort. |
apply_view_state |
Apply a previously saved view_state(). Unknown fields are ignored; |
refresh |
Reload data from the DB with the current filter/sort/page state. |
load_next_page |
Load the next page and drop old pages that fell outside the window. |
set_db |
Replace the session provider (useful on project switch or reconnect). |
repaint_rows |
Repaint the visible rows without re-querying (host state changed, not the data). |
get_selected_objects |
Return the list of selected SQLModel objects. |
get_selected_object |
Return the first selected object, or None. |
all_filtered_ids |
PKs of every row matching the current filters/search (across all pages). |
set_filter |
Apply a filter programmatically. |
set_filters |
Replace the whole scope and reload exactly once. |
select_first_row |
Select the first materialised row; never the sentinel row. |
Attributes:
| Name | Type | Description |
|---|---|---|
record_total |
int
|
Row total under the current filters (for the host's statusbar). |
view |
The inner QTableView — for hosts that need to scope focus or shortcuts to it. |
Source code in src/ms_components/ms_table/smart_table_view.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | |
record_total
property
¶
record_total: int
Row total under the current filters (for the host's statusbar).
refresh_counts
¶
refresh_counts() -> bool
Refresh only the record counter (COUNT), without reloading rows.
Meant for running jobs: the user watches the total grow and infinite scroll knows there are more pages, but the visible rows do not shift under the cursor. Returns False when the table is empty (nothing to preserve: the host should do a plain refresh()).
Source code in src/ms_components/ms_table/smart_table_view.py
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | |
ensure_viewport_filled
¶
ensure_viewport_filled(*, force: bool = False) -> bool
One tick of a job's start-up: reload rows only once there are enough to fill the visible viewport (or once the host decides it has waited long enough, force=True).
Returns True when the viewport is full (or there is nothing else to fetch): the host then switches to "counters only" mode. False = still waiting, call again.
Source code in src/ms_components/ms_table/smart_table_view.py
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
patch_loaded_rows
¶
patch_loaded_rows(key: str, patches: dict[Any, dict[str, Any]]) -> bool
Patch visible cached rows by a raw-object/key value; no model reset or reselection.
Source code in src/ms_components/ms_table/smart_table_view.py
234 235 236 237 238 239 240 241 242 243 244 | |
open_table_menu
¶
open_table_menu(anchor: QWidget | None = None) -> None
Pop up the full menu for external hosts (a ribbon, for instance).
The embedded toolbar exposes these options as actions of its own.
anchor lets another host open the same collection under one of its own
controls.
Source code in src/ms_components/ms_table/smart_table_view.py
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 | |
set_external_filters
¶
set_external_filters(key: str, filters: list[FilterSpec]) -> None
Set (or replace) an external filter overlay under key and reload.
It composes with the user's interactive filters using AND, without touching
them; passing an empty list is the same as clear_external_filters(key).
Source code in src/ms_components/ms_table/smart_table_view.py
600 601 602 603 604 605 606 607 | |
set_external_clause
¶
set_external_clause(key: str, clause) -> None
Set (or, with None, drop) a raw SQLAlchemy clause under key and reload.
For conditions FilterSpec cannot express - typically a ~exists(...)
against another table.
Source code in src/ms_components/ms_table/smart_table_view.py
613 614 615 616 617 618 619 620 | |
set_empty_state
¶
set_empty_state(message: str | None = None, *, show_action: bool | None = None)
Change the empty state at runtime: None restores what the config declares.
A host that filters the table from outside knows something the table does not: "there is no data" and "the filter lets nothing through" are different states, and the action that fills the table (import, create) does not fix the second one.
Source code in src/ms_components/ms_table/smart_table_view.py
676 677 678 679 680 681 682 683 684 685 686 687 | |
set_toolbar_button_style
¶
set_toolbar_button_style(style: ToolButtonStyle) -> None
Icons / text / both for the toolbar actions.
Source code in src/ms_components/ms_table/smart_table_view.py
844 845 846 847 | |
set_toolbar_actions
¶
set_toolbar_actions(key: str, actions: list[ToolbarAction]) -> None
Put a host's commands on this table's toolbar while it needs them.
Same shape as set_external_filters: keyed by the host (a tool id), replacing
whatever that key had, and an empty list is clear_toolbar_actions(key). A tool that
narrows a table can now also act on it, and gives both back on the way out.
Source code in src/ms_components/ms_table/smart_table_view.py
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 | |
set_config_actions_visible
¶
set_config_actions_visible(visible: bool) -> None
Show/hide the actions the config declares (toolbar_left/center/right).
The built-in controls (Columns, Export, Reload, Settings) stay: they describe the table. The config's own actions describe the catalog — a table lent to a tool should not still be offering "Import…".
Source code in src/ms_components/ms_table/smart_table_view.py
1010 1011 1012 1013 1014 1015 1016 1017 1018 | |
view_state
¶
view_state() -> dict
Current user-facing table prefs: visible column fields + active sort. Suitable for JSON/TOML persistence by the host app.
Source code in src/ms_components/ms_table/smart_table_view.py
1039 1040 1041 1042 1043 1044 1045 | |
apply_view_state
¶
apply_view_state(state: dict | None) -> None
Apply a previously saved view_state(). Unknown fields are ignored; an empty/missing 'columns' leaves the config-defined visibility intact.
Source code in src/ms_components/ms_table/smart_table_view.py
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 | |
refresh
¶
refresh(reset_page: bool = True) -> None
Reload data from the DB with the current filter/sort/page state.
Source code in src/ms_components/ms_table/smart_table_view.py
1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 | |
load_next_page
¶
load_next_page() -> None
Load the next page and drop old pages that fell outside the window.
Source code in src/ms_components/ms_table/smart_table_view.py
1228 1229 1230 1231 1232 1233 1234 | |
set_db
¶
set_db(db: SessionProvider) -> None
Replace the session provider (useful on project switch or reconnect).
Source code in src/ms_components/ms_table/smart_table_view.py
1301 1302 1303 1304 1305 1306 | |
repaint_rows
¶
repaint_rows() -> None
Repaint the visible rows without re-querying (host state changed, not the data).
Source code in src/ms_components/ms_table/smart_table_view.py
1313 1314 1315 | |
get_selected_objects
¶
get_selected_objects() -> list[Any]
Return the list of selected SQLModel objects.
Source code in src/ms_components/ms_table/smart_table_view.py
1317 1318 1319 1320 1321 1322 1323 1324 1325 | |
get_selected_object
¶
get_selected_object() -> Optional[Any]
Return the first selected object, or None.
Source code in src/ms_components/ms_table/smart_table_view.py
1327 1328 1329 1330 | |
all_filtered_ids
¶
all_filtered_ids() -> list[int]
PKs of every row matching the current filters/search (across all pages).
Source code in src/ms_components/ms_table/smart_table_view.py
1332 1333 1334 | |
set_filter
¶
set_filter(spec: FilterSpec) -> None
Apply a filter programmatically.
Source code in src/ms_components/ms_table/smart_table_view.py
1336 1337 1338 1339 1340 1341 | |
set_filters
¶
set_filters(filters: list[FilterSpec]) -> None
Replace the whole scope and reload exactly once.
This is the right operation for external controls that change receptor, ligand, protocol and metrics as a single state transition.
Source code in src/ms_components/ms_table/smart_table_view.py
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 | |
select_first_row
¶
select_first_row() -> bool
Select the first materialised row; never the sentinel row.
Source code in src/ms_components/ms_table/smart_table_view.py
1354 1355 1356 1357 1358 1359 | |
ColumnDef
dataclass
¶
ColumnDef(field: str, label: Optional[str] = None, join: Any = None, width: Optional[int] = None, min_width: int = 50, max_width: Optional[int] = None, resizable: bool = True, sortable: bool = True, searchable: bool = True, filterable: bool = True, kind: ColumnKind = TEXT, choices: tuple[str, ...] = (), editable: bool = False, visible: bool = True, align: AlignHint = LEFT, formatter: Optional[Callable] = None, delegate: Optional[str] = None, tooltip: Any = None, db_column: Optional[str] = None, expr: Any = None, progress_max: Optional[str] = None, progress_tone: Optional[Callable] = None, paint_factory: Optional[Callable] = None, widget_factory: Optional[Callable] = None, editor_factory: Optional[Callable] = None, editor_signal: Optional[str] = None, editor_getter: Optional[Callable] = None, cell_height: Optional[int] = None)
Define a visible column in the table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
field
|
str
|
Path to the field. Supports dot-notation for joins: "customer.name" → resolves Order.customer.name |
required |
label
|
Optional[str]
|
Visible header. When None, the capitalised field is used. |
None
|
join
|
Any
|
SQLModel class to join in order to resolve this field. May be a list for chained joins. |
None
|
width
|
Optional[int]
|
Initial width in px (None = auto). |
None
|
min_width
|
int
|
Minimum width in px. |
50
|
max_width
|
Optional[int]
|
Maximum width in px. |
None
|
resizable
|
bool
|
Whether the user may resize it. |
True
|
sortable
|
bool
|
Whether the table may be sorted by this column. |
True
|
filterable
|
bool
|
Whether the column may be filtered. |
True
|
editable
|
bool
|
Whether the cell is editable inline. |
False
|
visible
|
bool
|
Visible by default. |
True
|
align
|
AlignHint
|
Content alignment. |
LEFT
|
formatter
|
Optional[Callable]
|
Function (raw_value) → str used for display. |
None
|
delegate
|
Optional[str]
|
Name of a custom Qt delegate (for editors). |
None
|
tooltip
|
Any
|
Static tooltip, or callable(row_data) → str. |
None
|
db_column
|
Optional[str]
|
Override for the real DB column name (when it differs from field). |
None
|
expr
|
Any
|
SQLAlchemy expression producing the column value, for anything
that is not a model attribute (JSON_EXTRACT, CASE, func.*).
When set, |
None
|
progress_max
|
Optional[str]
|
PROGRESS columns only: the row key holding the maximum. Set, the value is a count and the bar reads "value/max (pct%)", as QProgressBar's "%v/%m (%p%)"; unset, the value is a 0..1 fraction. |
None
|
progress_tone
|
Optional[Callable]
|
PROGRESS columns only: callable(row_data) → a theme accent
("green", "red"), the bar's |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
display_key |
str
|
Unique key used internally (dots replaced). |
FilterSpec
dataclass
¶
FilterSpec(field: str, op: FilterOperator = ILIKE, value: Any = None, label: Optional[str] = None)
Specification of an active filter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
field
|
str
|
Field name (same as ColumnDef.field). |
required |
op
|
FilterOperator
|
Comparison operator. |
ILIKE
|
value
|
Any
|
Value to compare against (None for IS NULL / NOT NULL). |
None
|
label
|
Optional[str]
|
Friendly label shown in the UI chips. |
None
|
SortSpec
dataclass
¶
SortSpec(field: str, descending: bool = False)
Sort specification.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
field
|
str
|
Field to sort by. |
required |
descending
|
bool
|
True = DESC, False = ASC. |
False
|
TableConfig
dataclass
¶
TableConfig(model_class: Type[SQLModel], columns: list[ColumnDef] = list(), default_sort: list[SortSpec] = list(), default_filters: list[FilterSpec] = list(), page_size: int = DEFAULT_PAGE_SIZE, page_size_options: list[int] = (lambda: list(DEFAULT_PAGE_SIZES))(), load_mode: TableLoadMode | str = PAGINATED, infinite_scroll_margin: int = 120, infinite_cache_pages: int = 2, max_export_rows: int = DEFAULT_MAX_EXPORT_ROWS, selectable: bool = True, multi_select: bool = True, show_row_numbers: bool = False, show_vertical_header: bool = False, show_checkboxes: bool = False, alternating_rows: bool = True, allow_column_reorder: bool = True, allow_column_resize: bool = True, allow_row_resize: bool = False, context_menu_actions: dict[str, Callable] = dict(), row_height: Optional[int] = None, empty_message: str = 'No data to show', compact_controls: bool = True, show_filters: bool = True, show_search: bool = True, max_loaded_rows: int = 5000, embedded_controls: bool = True, show_record_count: bool = True, toolbar_left: list['ToolbarAction'] = list(), toolbar_center: list['ToolbarAction'] = list(), toolbar_right: list['ToolbarAction'] = list(), toolbar_collapsible: bool = False, empty_action: Optional['ToolbarAction'] = None, row_background: Optional[Callable] = None)
Full configuration of a SmartTableView.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_class
|
Type[SQLModel]
|
Main SQLModel class (base table). |
required |
columns
|
list[ColumnDef]
|
List of ColumnDef. Their order is the column order. |
list()
|
default_sort
|
list[SortSpec]
|
Initial sort. |
list()
|
default_filters
|
list[FilterSpec]
|
Filters active on startup. |
list()
|
page_size
|
int
|
Initial items per page. |
DEFAULT_PAGE_SIZE
|
page_size_options
|
list[int]
|
Options offered by the pagination dropdown. |
(lambda: list(DEFAULT_PAGE_SIZES))()
|
load_mode
|
TableLoadMode | str
|
"paginated" uses PaginationControl; "infinite" loads the next page as the end comes into view. |
PAGINATED
|
infinite_scroll_margin
|
int
|
Distance in px from the end at which more rows are requested. |
120
|
max_export_rows
|
int
|
Row cap for exports. |
DEFAULT_MAX_EXPORT_ROWS
|
selectable
|
bool
|
Allow row selection. |
True
|
multi_select
|
bool
|
Allow multi-selection. |
True
|
show_row_numbers
|
bool
|
Show the row-number column. |
False
|
show_vertical_header
|
bool
|
Show Qt's native vertical header. |
False
|
show_checkboxes
|
bool
|
Show the selection checkbox column. |
False
|
alternating_rows
|
bool
|
Alternate row colours. |
True
|
allow_column_reorder
|
bool
|
The user may reorder columns by dragging. |
True
|
allow_column_resize
|
bool
|
The user may resize columns. |
True
|
allow_row_resize
|
bool
|
The user may resize row heights. |
False
|
context_menu_actions
|
dict[str, Callable]
|
Extra context-menu actions. dict[label, callable(selected_rows)] |
dict()
|
row_height
|
Optional[int]
|
Row height in px (None = auto). |
None
|
empty_message
|
str
|
Text shown when there are no results. |
'No data to show'
|
compact_controls
|
bool
|
Use a compact toolbar with collapsible panels for filtering and sorting. |
True
|
show_filters
|
bool
|
False = "simple" table: no column offers a filter. |
True
|
show_search
|
bool
|
False = "simple" table: no column offers a search. |
True
|
infinite_cache_pages
|
int
|
Contiguous pages kept in INFINITE mode. Old pages are replaced, so memory does not grow with scroll. |
2
|
max_loaded_rows
|
int
|
Compatibility: extra absolute ceiling for the window. |
5000
|
ToolbarAction
dataclass
¶
ToolbarAction(label: str = '', icon: Any = None, on_click: Optional[Callable[[list], None]] = None, tooltip: str = '')
An action injected into the toolbar. Deliberately dumb: the component
neither interprets on_click nor the objects it passes along - it just
forwards them. All the domain semantics live in the host's closure, which is
what keeps the table agnostic.
on_click: callable(selected_objects) - same signature as context_menu_actions.
icon: QIcon (opaque to this layer) or None.
SmartTableModel
¶
SmartTableModel(config: TableConfig, parent=None)
Bases: QAbstractTableModel
Qt model for SmartTableView.
Signals
data_loaded(int, int): (total_items, current_page) after a refresh. edit_requested(object, str, object): (raw_obj, field_name, new_value) when the user edits a cell.
Methods:
| Name | Description |
|---|---|
load_data |
Load one page of data. Called by SmartTableView. |
set_total |
Update only the total (counters/pagination), leaving loaded rows alone. |
patch_loaded_row |
Change displayed values in one loaded row without resetting selection or scroll. |
Source code in src/ms_components/ms_table/table_model.py
49 50 51 52 53 54 55 56 57 | |
load_data
¶
load_data(rows: list[dict], total: int, page: int, *, window_start: int = 0, total_is_exact: bool = True) -> None
Load one page of data. Called by SmartTableView.
Source code in src/ms_components/ms_table/table_model.py
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | |
set_total
¶
set_total(total: int) -> None
Update only the total (counters/pagination), leaving loaded rows alone.
Source code in src/ms_components/ms_table/table_model.py
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | |
patch_loaded_row
¶
patch_loaded_row(row: int, values: dict[str, Any]) -> bool
Change displayed values in one loaded row without resetting selection or scroll.
Source code in src/ms_components/ms_table/table_model.py
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | |
choices_from_class
¶
choices_from_class(cls: type) -> tuple[str, ...]
Extract the string values of a constants class (not an Enum).
Useful for patterns such as
class MoleculeType: SMALL_MOLECULE = "small_molecule" PROTEIN = "protein"
Source code in src/ms_components/ms_table/table_config.py
29 30 31 32 33 34 35 36 37 38 39 40 | |
Monitor¶
ms_monitor
¶
The monitor of MolSuite jobs. Its names are loaded when first asked for: config is read
by processes that never open a window, and they must not pay for Qt.
Modules:
| Name | Description |
|---|---|
bridge |
|
cards |
|
config |
Default configuration for the monitor component. |
detail_panel |
|
job_cards |
Card-based job list for the monitor. |
models |
|
palette |
Monitor colors, resolved live from the QPalette and the active theme. |
polling |
|
Hosts and executors¶
ms_executors
¶
The machines that run jobs and who runs them on them.
KeptPage is a Settings page: the entries kept under one name each (hosts or executors)
and the form of the one chosen. ExecutorsPanel is the quick window that starts and stops the
executors. Both go through a ms_flow.Workspace (save_host, remove_host, save_executor,
remove_executor, executors, activate, deactivate), which is who refuses what is not to
be done now, and says why.
Classes:
| Name | Description |
|---|---|
KeptPage |
The entries kept in |
ExecutorsPanel |
Who runs jobs now: a row for each executor kept, started and stopped from here. |
KeptPage
¶
KeptPage(workspace, table: str, parent: QWidget | None = None)
Bases: QWidget
The entries kept in table (hosts or executors) and the form of the chosen one.
"Save" and "Remove" act at once, through the workspace: an entry is whole or it is not kept, so the "Save changes" of the dialog around this page is not for it.
Source code in src/ms_components/ms_executors.py
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
ExecutorsPanel
¶
ExecutorsPanel(workspace, parent: QWidget | None = None)
Bases: QDialog
Who runs jobs now: a row for each executor kept, started and stopped from here.
Starting one waits for its machines, so it is done in a thread, and what is done on a machine that is being prepared is shown line by line. The round mark is the executor a job goes to when nobody is named.
Source code in src/ms_components/ms_executors.py
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | |
Projects¶
ms_projects
¶
Modules:
| Name | Description |
|---|---|
browser |
|
scratch |
Reflex/Vercel-style dark theme demo for PySide6 |
Settings¶
ms_settings
¶
Modules:
| Name | Description |
|---|---|
panel |
|
Classes:
| Name | Description |
|---|---|
AppSettingsPanel |
Tree + page typed editor for one or more layered MolSuite configurations. |
AppSettingsPanel
¶
AppSettingsPanel(*, runtime=None, configurations=None, app_name: str | None = None, icon_provider=None, parent: QWidget | None = None)
Bases: QWidget
Tree + page typed editor for one or more layered MolSuite configurations.
Layout follows the IDE convention: search box and a three-level tree on the left, breadcrumb and the selected page on the right.
Methods:
| Name | Description |
|---|---|
set_close_action |
Show a Close button in the footer wired to |
add_page |
Host a self-managing page (no config rows) as one more top-level tree node. |
Source code in src/ms_components/ms_settings/panel.py
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | |
set_close_action
¶
set_close_action(callback) -> None
Show a Close button in the footer wired to callback (used by the host
dialog so Reload / Save / Close share one row).
Source code in src/ms_components/ms_settings/panel.py
329 330 331 332 333 | |
add_page
¶
add_page(widget: QWidget, title: str) -> None
Host a self-managing page (no config rows) as one more top-level tree node.
The page saves on its own -- "Save changes" and "Reload" ignore it.
Source code in src/ms_components/ms_settings/panel.py
771 772 773 774 775 776 777 778 | |
PyMOL¶
ms_pymol
¶
Modules:
| Name | Description |
|---|---|
plugins |
pymol_plugins.py |
widget |
|
Classes:
| Name | Description |
|---|---|
PymolControlBar |
Compact, reusable controls for an embedded PyMOL scene. |
PymolControlBar
¶
PymolControlBar(cmd, parent: QWidget | None = None)
Bases: QFrame
Compact, reusable controls for an embedded PyMOL scene.
Methods:
| Name | Description |
|---|---|
add_menu_action |
Add an application menu that participates in native toolbar overflow. |
add_action |
Add one application action to the embedded toolbar. |
refresh_context_label |
Keep the compact context label useful without turning it into a second status bar. |
Source code in src/ms_components/ms_pymol/widget.py
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | |
add_menu_action
¶
add_menu_action(text: str, menu: QMenu, *, icon_name: str = 'settings.svg', tooltip: str = '') -> QAction
Add an application menu that participates in native toolbar overflow.
Source code in src/ms_components/ms_pymol/widget.py
298 299 300 301 302 303 304 305 306 307 308 309 310 311 | |
add_action
¶
add_action(text: str, *, icon_name: str, tooltip: str = '', callback: Callable[[], None] | None = None, checkable: bool = False) -> QAction
Add one application action to the embedded toolbar.
Source code in src/ms_components/ms_pymol/widget.py
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | |
refresh_context_label
¶
refresh_context_label() -> None
Keep the compact context label useful without turning it into a second status bar.
Source code in src/ms_components/ms_pymol/widget.py
643 644 645 646 647 648 649 650 651 652 653 654 | |
Comparison scenes provide grid and overlay layouts without reloading structures
or changing the camera. Assign each structure/pose pair the same positive PyMOL
grid_slot, then pass its display name:
dock.set_scene_context(
"comparison",
grid_labels={1: "Complex A", 3: "Complex B"},
)
dock.set_comparison_layout("overlay")
grid_labels is also accepted by PymolControlBar and PymolSceneContext.
Without explicit labels, molecular object titles (or object names when no title
is available) are joined per positive grid_slot within the context's target.
Applications must pass display names if those names are not present in PyMOL.
The Display > Comparison > Grid / Overlay menu appears only in comparison scenes. The chosen layout persists when changing poses or applying presets. Overlay displays the existing coordinates together; alignment remains the caller's responsibility.
Each label is a pseudoatom named panel_text<slot>, initially sized 14.
Grid labels appear at the top-left of their panel; overlay labels form a stacked
legend that follows viewport resizing. Receptor cartoons, carbon atoms and label
text share the slot's COMPARISON_COLORS color. Heteroatom colors are preserved.
The label outline uses PyMOL's background-adaptive front color for readability
on light and dark backgrounds. The palette has ten colors and repeats beyond ten
slots.
Existing labels are updated in place, so individual settings remain editable,
for example cmd.set("label_size", 18, "panel_text3"). Presets restore label
visibility after changing molecular representations.
To replace one pose without rebuilding the scene, pass its bytes directly:
dock.replace_comparison_object(
"pose_a", pdb_bytes, "pdb",
reference="receptor_a", label="Complex A [P2]",
)
The new object inherits the reference's transformation and slot, is shown as sticks, and receives the same comparison color. Camera and auto-zoom settings are preserved. The previous object remains available until the replacement loads successfully. This API neither fetches data nor calls the hosting application. Hosts remain responsible for reading their data and discarding stale requests.
Setting any non-comparison scene context after a comparison context removes
the managed labels and sets grid_mode to zero. Applications should update
the scene context when leaving their off-target view, even without loading a
new structure. Unrelated grid scenes and user-created annotations are not
removed. "offtarget" remains accepted as an alias for "comparison".
Stepper¶
ms_stepper
¶
qstepper.py¶
Reusable stepper component for PySide6, modelled on Quasar's (https://quasar.dev/vue-components/stepper).
It provides
- A header with indicators (number / prefix / icon / check / error) and connectors. HORIZONTAL or VERTICAL orientation (inline content).
- Per-step states: inactive, active, done and error.
alternative-labels(horizontal): title/caption centred under the indicator.inline-label(vertical): title next to the indicator.prefix: short text inside the indicator instead of the number.- Per-state icons: done / active / error, global and per step.
- Per-step colours (active / done / inactive / error) over the base theme.
- Validation hooks: each step may carry a validator that blocks advancing and marks the step as failed.
- Programmatic navigation (next / previous / go_to) and header clicks (configurable per step).
- Optional navigation bar (Back / Continue / Finish).
- QSS-style theming: painted colours exposed as
qproperty-*, and chrome (buttons, connectors, background) styleable byobjectName. Example themes: light, dark, outlined and flat. - Signals: current_changed, step_changed, finished, validation_failed.
Classes:
| Name | Description |
|---|---|
StepperTheme |
Colours and metrics of the painted parts (indicators, connectors). |
QStep |
One stepper step. A QWidget whose layout you fill with the content. |
QStepper |
Signals: |
StepperTheme
¶
StepperTheme()
Colours and metrics of the painted parts (indicators, connectors).
The colours come from the live QPalette (and the theme's semantic accents), so they follow the active theme without anyone pinning them by hand.
Source code in src/ms_components/ms_stepper.py
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | |
QStep
¶
QStep(title: str = '', caption: str = '', icon: Optional[IconLike] = None, *, prefix: str = '', name: Optional[str] = None, disabled: bool = False, header_nav: bool = True, validator: Optional[Validator] = None, done_icon: Optional[IconLike] = None, active_icon: Optional[IconLike] = None, error_icon: Optional[IconLike] = None, active_color: Optional[ColorLike] = None, done_color: Optional[ColorLike] = None, inactive_color: Optional[ColorLike] = None, error_color: Optional[ColorLike] = None, parent: Optional[QWidget] = None)
Bases: QWidget
One stepper step. A QWidget whose layout you fill with the content.
Main parameters
title, caption Header texts. icon QIcon or glyph for the indicator (active / inactive). prefix Short text inside the indicator instead of the number. name Optional identifier (for go_to by name). disabled Not navigable. header_nav Allow navigating to this step by clicking the header. validator Callable[[], bool]; returning False blocks advancing. done_icon/active_icon/error_icon Per-state icons (QIcon or glyph). They override the stepper's global ones. active_color/done_color/inactive_color/error_color Per-step colours that override the theme.
Methods:
| Name | Description |
|---|---|
validate |
True when the step is valid (or has no validator). |
Source code in src/ms_components/ms_stepper.py
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | |
validate
¶
validate() -> bool
True when the step is valid (or has no validator).
Source code in src/ms_components/ms_stepper.py
288 289 290 291 292 293 294 295 | |
QStepper
¶
QStepper(orientation: Orientation = HORIZONTAL, theme: Optional[StepperTheme] = None, *, linear: bool = True, alternative_labels: bool = False, inline_label: bool = False, show_navigation: bool = True, auto_error_on_invalid: bool = True, parent: Optional[QWidget] = None)
Bases: QWidget
Signals
current_changed(int) index of the current step step_changed(QStep) object of the current step finished() "Finish" was pressed (last step valid) validation_failed(QStep) a step blocked advancing during validation
Methods:
| Name | Description |
|---|---|
set_qss |
Append user QSS on top of the stepper's default chrome. |
set_current_index |
Navigate without validating (programmatic use). |
next |
Validate the current step and, if it passes, mark it done and advance. |
Source code in src/ms_components/ms_stepper.py
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 | |
set_qss
¶
set_qss(qss: str) -> None
Append user QSS on top of the stepper's default chrome.
Source code in src/ms_components/ms_stepper.py
573 574 575 576 | |
set_current_index
¶
set_current_index(index: int) -> None
Navigate without validating (programmatic use).
Source code in src/ms_components/ms_stepper.py
656 657 658 659 660 661 662 663 664 665 666 667 668 | |
next
¶
next() -> None
Validate the current step and, if it passes, mark it done and advance.
Source code in src/ms_components/ms_stepper.py
681 682 683 684 685 686 687 688 689 690 691 692 | |
Guided tutorials¶
ms_tutorial
¶
Informative and interactive tours for any Qt Widgets application.
Modules:
| Name | Description |
|---|---|
bindings |
The small Python boundary between tutorial content and a live Qt application. |
controller |
Attach a tour to existing widgets/signals without changing their slots. |
models |
Application-independent tutorial content, authored in Python or TOML. |
overlay |
A window-local spotlight: informative steps block input, interactive steps open a hole. |
Classes:
| Name | Description |
|---|---|
TutorialController |
One active tour, with Python bindings and plain-data content. |
Functions:
| Name | Description |
|---|---|
load_tutorial |
Read data only: target/event names are resolved by the application, never evaluated. |
TutorialController
¶
TutorialController(*, targets: Mapping[str, TutorialTarget], events: Mapping[str, TutorialEvent] | None = None, parent: QObject | None = None)
Bases: QObject
One active tour, with Python bindings and plain-data content.
Targets may belong to different windows or dialogs. prepare only reveals
the relevant UI; the component never submits jobs or invokes business actions.
Methods:
| Name | Description |
|---|---|
start |
Validate bindings before replacing the active tour. Must run in the UI thread. |
stop |
Cancel and disconnect only the tutorial's own signal subscription. |
Attributes:
| Name | Type | Description |
|---|---|---|
overlay |
TutorialOverlay | None
|
The current window's overlay; replaced when the tour moves between windows. |
Source code in src/ms_components/ms_tutorial/controller.py
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | |
overlay
property
¶
overlay: TutorialOverlay | None
The current window's overlay; replaced when the tour moves between windows.
start
¶
start(tutorial: Tutorial)
Validate bindings before replacing the active tour. Must run in the UI thread.
Source code in src/ms_components/ms_tutorial/controller.py
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | |
stop
¶
stop()
Cancel and disconnect only the tutorial's own signal subscription.
Source code in src/ms_components/ms_tutorial/controller.py
110 111 112 113 114 115 | |
load_tutorial
¶
load_tutorial(path: str | Path) -> Tutorial
Read data only: target/event names are resolved by the application, never evaluated.
Source code in src/ms_components/ms_tutorial/models.py
45 46 47 48 49 50 51 52 53 54 55 56 57 58 | |
Tool panel¶
tool_panel
¶
Tool panel: the frame every configure-then-run tool shares.
┌ top bar ──── scope and context; created on first use ─────┐
│ body ─────── sections, free widgets, Advanced (scrolls) │
└ action bar ─ summary or progress · actions; first use ────┘
UI only: it knows nothing about jobs or runtimes. The host maps its job events onto
ActionBar.start / set_progress / finish. The status bar and the job monitor keep the
full detail, so the progress here is deliberately a quiet 3 px line.
Inside a QStepper, put one panel per step (step.add_widget(panel) with the step's
body_layout margins at 0): each step keeps its own bar state while hidden, so there is
nothing to swap when the current step changes.
Dividers and rules are painted with the palette, not styled: the panel follows the theme
without any rule in base.qss.
Classes:
| Name | Description |
|---|---|
ContentBar |
Full-width strip with a divider on one edge. The padding lives inside, so the |
ToolSection |
One flat section: a header (title + rule) over a form body. |
ActionBar |
Bottom strip: a summary while idle, a quiet progress line while running, and the |
ToolPanel |
Frame for a configure-then-run tool; see the module docstring. |
Functions:
| Name | Description |
|---|---|
format_count |
12480 -> '12 480' with a narrow no-break space, so a count never wraps mid-number. |
hint_label |
Secondary text (method notes, section descriptions): caption size, placeholder colour. |
ContentBar
¶
ContentBar(divider: Edge, parent: QWidget | None = None)
Bases: QFrame
Full-width strip with a divider on one edge. The padding lives inside, so the background and the divider reach the panel edges.
Source code in src/ms_components/tool_panel.py
96 97 98 99 100 101 | |
ToolSection
¶
ToolSection(title: str, *, description: str = '', checkable: bool = False, checked: bool = True, collapse_when_unchecked: bool = False, collapsible: bool = False, expanded: bool = False, parent: QWidget | None = None)
Bases: QWidget
One flat section: a header (title + rule) over a form body.
checkable The title is a checkbox. Unchecked disables the body (checkable QGroupBox
semantics), or hides it with collapse_when_unchecked for large
optional bodies (flexible residues).
collapsible The title is a disclosure arrow, for options few people need (Advanced).
isChecked / setChecked / toggled mirror QGroupBox, so code written against a
checkable group box keeps working.
Methods:
| Name | Description |
|---|---|
add_row |
|
Source code in src/ms_components/tool_panel.py
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | |
add_row
¶
add_row(*args) -> None
QFormLayout.addRow overloads: (label, field), (widget) spanning, (layout).
Source code in src/ms_components/tool_panel.py
202 203 204 | |
ActionBar
¶
ActionBar(parent: QWidget | None = None)
Bases: ContentBar
Bottom strip: a summary while idle, a quiet progress line while running, and the tool's actions on the right.
Run-type actions give way to Cancel while running. The actions area keeps its idle width meanwhile, so the text and the progress line on the left never jump.
Methods:
| Name | Description |
|---|---|
add_action |
Any button (plain, split, menu). |
set_summary |
Idle text: what a run would cover (scope counts, estimate). |
start |
Running with unknown progress: submitted, waiting for prerequisites. |
set_progress |
|
finish |
Back to idle showing the outcome (done, failed, cancelled) until the next summary. |
Source code in src/ms_components/tool_panel.py
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | |
add_action
¶
add_action(widget: QWidget, *, hide_while_running: bool = True) -> QWidget
Any button (plain, split, menu). hide_while_running=False for actions that
stay useful during a run, such as Open Results.
Source code in src/ms_components/tool_panel.py
302 303 304 305 306 307 308 | |
set_summary
¶
set_summary(headline: str, detail: str = '') -> None
Idle text: what a run would cover (scope counts, estimate).
Source code in src/ms_components/tool_panel.py
310 311 312 313 | |
start
¶
start(headline: str = 'Running…', detail: str = '') -> None
Running with unknown progress: submitted, waiting for prerequisites.
Source code in src/ms_components/tool_panel.py
315 316 317 318 319 320 | |
set_progress
¶
set_progress(done: int, total: int | None = None, *, stage: str = 'Running', unit: str = 'items', eta_seconds: float | None = None, failures: int = 0, detail: str = '') -> None
eta_seconds=None estimates it from the rate observed across calls.
total=None keeps the line indeterminate and drops the ETA: a streamed total is
only a floor, so a percentage would be invented. detail follows the ETA.
Source code in src/ms_components/tool_panel.py
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 | |
finish
¶
finish(headline: str, detail: str = '', *, failures: int = 0) -> None
Back to idle showing the outcome (done, failed, cancelled) until the next summary.
Source code in src/ms_components/tool_panel.py
364 365 366 367 | |
ToolPanel
¶
ToolPanel(parent: QWidget | None = None, *, scrollable: bool = True)
Bases: QWidget
Frame for a configure-then-run tool; see the module docstring.
scrollable=False for tools whose body is mostly a table that scrolls itself.
Methods:
| Name | Description |
|---|---|
add_section |
Options as in |
add_widget |
A free widget in the body; |
Attributes:
| Name | Type | Description |
|---|---|---|
advanced |
ToolSection
|
Collapsed section kept last: seed, threads, batch size, executor. |
Source code in src/ms_components/tool_panel.py
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | |
advanced
property
¶
advanced: ToolSection
Collapsed section kept last: seed, threads, batch size, executor.
add_section
¶
add_section(title: str, **options) -> ToolSection
Options as in ToolSection; inserted after the previous ones, before Advanced.
Source code in src/ms_components/tool_panel.py
456 457 458 459 460 | |
add_widget
¶
add_widget(widget: QWidget, stretch: int = 0) -> QWidget
A free widget in the body; stretch > 0 lets it take the spare height (tables).
Source code in src/ms_components/tool_panel.py
462 463 464 465 466 467 468 | |
format_count
¶
format_count(value: int) -> str
12480 -> '12 480' with a narrow no-break space, so a count never wraps mid-number.
Source code in src/ms_components/tool_panel.py
51 52 53 | |
hint_label
¶
hint_label(text: str, parent: QWidget | None = None) -> QLabel
Secondary text (method notes, section descriptions): caption size, placeholder colour.
Source code in src/ms_components/tool_panel.py
68 69 70 71 72 73 74 | |
Step dialog¶
step_dialog
¶
Runs a list of ms_env Steps off the GUI thread and shows how far they got.
What is on screen is the list of steps, each marked as it runs, ends or fails. The commands and their output are one button away, and open by themselves on a failure.
Used for anything that touches a remote host or boots a cluster: testing an
executor, preparing its environment, ray up. Modality is the caller's call —
blocking=True for the compute backend the app is about to depend on,
non-modal for everything else so HPC/cluster setup can run in the background.
Functions:
| Name | Description |
|---|---|
run_steps_dialog |
Show the dialog and start the run. Blocking calls exec(), the rest show(). |
run_steps_dialog
¶
run_steps_dialog(parent: QWidget | None, title: str, steps: Iterable[Step], *, blocking: bool = False, on_success: Callable[[], None] | None = None) -> StepRunnerDialog
Show the dialog and start the run. Blocking calls exec(), the rest show().
Source code in src/ms_components/step_dialog.py
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | |
Theme¶
theme
¶
Color themes for every MolSuite Qt app (QPalette-based).
Palette data and the QPalette role mapping are derived from qt-themes by Beat Reichenbach, MIT-licensed:
https://github.com/beatreichenbach/qt-themes
Copyright (c) 2024 Beat Reichenbach
The color values are vendored here (they are data, not code) so no extra runtime
dependency is needed. Full MIT text: licenses/qt-themes-LICENSE.txt; keep this
attribution if you redistribute.
Widgets should not hardcode colors. Two mechanisms cover everything:
palette(role)in QSS /QApplication.palette()in paint code — resolved live, so a theme switch repaints without touching a single widget.color("red"/"green"/...)for semantic accents (status dots, warnings) that have no QPalette role.
Same idea for type: font(role) derives from the app font instead of hardcoding
sizes, so the OS/user font and DPI carry through. Roles are data (FONT_ROLES), not
classes -- a title is a scale factor and a weight, nothing worth an object.
Functions:
| Name | Description |
|---|---|
is_dark |
A theme is dark when its text is brighter than its base background. |
current_theme |
The theme dict currently applied (defaults before any |
color |
A semantic accent from the live theme ( |
contrast_ratio |
WCAG contrast ratio used to keep generated palette states readable. |
font |
A QFont for a named role, derived from the application font. |
set_base_font |
Set the application base font: the root everything else inherits from. |
build_palette |
Build a QPalette from a theme dict. |
resolve |
Resolve auto, retired and invalid names to an available concrete theme. |
tone_qss |
|
base_qss |
The shared geometry stylesheet (colors resolved from the live palette). |
apply_theme |
Apply a theme (or "auto") to the QApplication. |
is_dark
¶
is_dark(theme: dict[str, str] | None = None) -> bool
A theme is dark when its text is brighter than its base background.
Source code in src/ms_components/theme.py
41 42 43 44 | |
current_theme
¶
current_theme() -> dict[str, str]
The theme dict currently applied (defaults before any apply_theme).
Source code in src/ms_components/theme.py
47 48 49 | |
color
¶
color(role: str) -> QColor
A semantic accent from the live theme (red, green, orange, ...).
For anything with a QPalette role (background, text, borders, highlight) use the palette instead — this is only for accents Qt has no role for.
Source code in src/ms_components/theme.py
52 53 54 55 56 57 58 | |
contrast_ratio
¶
contrast_ratio(left: QColor, right: QColor) -> float
WCAG contrast ratio used to keep generated palette states readable.
Source code in src/ms_components/theme.py
71 72 73 74 | |
font
¶
font(role: str = 'body', *, mono: bool = False) -> QFont
A QFont for a named role, derived from the application font.
mono=True swaps in the platform's fixed-width family (scores, ids, paths) and
keeps the role's size/weight.
Source code in src/ms_components/theme.py
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | |
set_base_font
¶
set_base_font(points: float = DEFAULT_FONT_PT, *, family: str | None = None, app: QApplication | None = None) -> QFont
Set the application base font: the root everything else inherits from.
One place instead of sizes scattered through the QSS: font(role) scales off this
one, widgets that do not override it inherit it, and the QSS can go without a single
font-size rule (a stylesheet rule would override this).
Call it before creating windows: widgets already built with their own font keep it.
Source code in src/ms_components/theme.py
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | |
build_palette
¶
build_palette(theme: dict[str, str]) -> QPalette
Build a QPalette from a theme dict.
Port of qt-themes' update_palette (MIT, Beat Reichenbach), adapted to read
from a flat dict instead of a dataclass.
Source code in src/ms_components/theme.py
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | |
resolve
¶
resolve(name: str) -> str
Resolve auto, retired and invalid names to an available concrete theme.
Source code in src/ms_components/theme.py
267 268 269 270 271 272 273 274 275 276 | |
tone_qss
¶
tone_qss(theme: dict[str, str] | None = None) -> str
QProgressBar[tone="green"] and siblings, from the live theme.
QSS has no palette role for the accents, so these rules are written per theme. Translucent, so the bar's text stays readable on dark and light themes alike.
Source code in src/ms_components/theme.py
283 284 285 286 287 288 289 290 291 292 293 294 295 | |
base_qss
¶
base_qss() -> str
The shared geometry stylesheet (colors resolved from the live palette).
Source code in src/ms_components/theme.py
298 299 300 | |
apply_theme
¶
apply_theme(name: str, app: QApplication | None = None, *, base_font_pt: float | None = None) -> None
Apply a theme (or "auto") to the QApplication.
Re-applies live to every existing widget. Non-Qt "islands" (PyMOL, pyqtgraph) do not observe this and must be pushed separately.
Source code in src/ms_components/theme.py
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |