Files
cimtechniques-service-suite/docs/EXPORT.md
andy 2b85def003 feat(core): add add_export_menu_actions - the export pair on a QMenu
Extract the shared _export_actions builder from add_export_actions so the same
dialog/file-naming/collection logic can target either a toolbar (DA-12, DA-07)
or a QMenu (for toolbars on a width budget). The builder's docstring documents
that the handlers must stay closures: a closure slot is stored strongly by the
connection, so the actions anchor the parent window's Python wrapper for the
life of the C++ action - bound-method slots are stored weakly, and rewiring
them as window methods lets an otherwise-unreferenced window be GC'd mid-test,
cascading C++ deletion into child dialogs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 14:32:46 -04:00

2.9 KiB

Spreadsheet Export

Any data screen can be exported to a real Excel .xlsx workbook: a metadata block (station context), then the grid exactly as shown, including alarm colors. Two toolbar actions — Export This Tab… (one sheet) and Export All Tabs… (one sheet per tab).

How it works

  • cim_suite/core/export/ — Qt-free engine: Sheet/Cell data types and write_xlsx(path, sheets).
  • cim_suite/core/ui/table_tab.pyTableTab.export_sheet() snapshots a tab's grid (text + per-cell colors) into a Sheet.
  • cim_suite/core/ui/export_action.pyadd_export_actions(...) adds the two toolbar actions; add_export_menu_actions(...) adds the same pair to a QMenu instead (for toolbars on a width budget — IOModbus's "Export ▾" menu button uses it); both share one action builder. collect_sheet / collect_all turn tabs into sheets and stamp metadata.

Export adoption checklist (per module)

  • Existing TableTab-based tabs export for free. Adding a column or field to a tab needs no export changeexport_sheet() reads whatever columns the table has.
  • A new module must, once, call add_export_actions(toolbar, tabs, window, metadata_provider, file_prefix=..., last_dir=..., remember_dir=...) in its toolbar (or add_export_menu_actions(menu, ...) to put the pair in a menu) and supply a metadata_provider returning the device-context dict. Without this the module has no export.
  • A non-TableTab screen (a form of custom widgets, a chart, a side panel) has no grid to walk. It must implement its own export_sheet() -> Sheet | None, or it is intentionally excluded (return None / omit the method, and it's skipped). DA-12 History dialog (BL-D5, 2026-06-05): the per-sensor History dialog is a custom chart + table screen. It exports the data table to .xlsx via the shared save_sheets_dialog helper and the chart to PNG — both available from the dialog's own toolbar. This covers the "every data screen exports" requirement for this screen.
  • A tab that needs to emit several worksheets (not just one) may implement the optional export_sheets() -> list[Sheet] (plural) hook instead — each returned Sheet is self-titled and becomes its own worksheet in Export All Tabs. When present it takes precedence over export_sheet() (collect_sheets in export_action.py). The DA-07 ChannelsTab uses it to emit one sheet per configured device, so Export-All covers every device's channels regardless of which one the tab is currently showing.
  • Data shown outside the table (e.g. a value only in a header label) is not captured by the default grid-walker. Surface it in the grid or override export_sheet().

What is intentionally NOT captured

The stylesheet-driven alternating-row banding lives in QSS, not on the cells, so it is correctly ignored. Only colors set directly on items (alarm fills) are exported.