Qt facet

Table of Contents

Qt entity UI components live in ores.qt and follow a four-part structure: a Client<Entity>Model (async data fetching, pagination, QAbstractTableModel), a list window (toolbar + proxy model + table view), a detail dialog (form editors, save/cancel), and a history dialog (read-only versioned view). Codegen (--profile qt) is the primary path; all four components should be generated from the JSON model before resorting to manual creation. Return to Knowledge.

ClientModel layout

The model inherits QAbstractTableModel and is final. It owns async loading via QFutureWatcher (refresh() launches QtConcurrent::run that calls the service; onDataLoaded() is connected via watcher_.setFuture). Pagination is exposed through load_page(offset, limit). Failure emits loadError (never throws) so the UI can display a message without crashing.

Recency highlighting: data() returns Qt::BackgroundRole with a configurable highlight colour for rows whose valid_from is within a recency window (e.g., last 24 h). This is implemented in the model, not the view.

List window layout

The list window inherits QWidget and is final. It owns the Client{Entity}Model and a QSortFilterProxyModel proxy. The toolbar provides Reload, Add, Edit, Delete (and optionally History) buttons. Double-click or Edit opens the detail dialog on demand; the window does not own the dialog — it connects accepted() to trigger a refresh.

Detail dialog

The detail dialog inherits QDialog and is final. It exposes set{Entity}(...) to populate for edit and get{Entity}() const to read back the edited domain object. One QLineEdit or QComboBox per editable field. On save: call promptChangeReason() (inherited from DetailDialogBase) to collect audit fields before sending to the server, then read fields into a domain object, set change_reason_code and change_commentary on the entity, and call the service asynchronously (same QtConcurrent + QFutureWatcher pattern as the model), emit accepted() on success or rejected() with an error message on failure. On delete: show a confirmation dialog first, then call promptChangeReason() before the async delete.

History dialog

Read-only dialog using its own Client{Entity}HistoryModel (inherits QAbstractTableModel, loads all versions of one entity by UUID). Opened from the list window via a History toolbar button.

Icon selection

Choose an icon from the icon-guidelines skill. Icons are set on QAction / QPushButton using QIcon::fromTheme() or embedded resource paths.

File locations

Component Header Implementation
ClientModel include/ores.qt/Client{Entity}Model.hpp src/Client{Entity}Model.cpp
List window include/ores.qt/{Entity}ListWindow.hpp src/{Entity}ListWindow.cpp
Detail dialog include/ores.qt/{Entity}Dialog.hpp src/{Entity}Dialog.cpp
History dialog include/ores.qt/{Entity}HistoryDialog.hpp src/{Entity}HistoryDialog.cpp
Controller wiring src/MainWindow.cpp (register in menu + plugin)

Templates

Generated by --profile qt. Read the template for the authoritative implementation — do not reproduce it here.

Template Output path
cpp_qt_client_model.hpp.mustache include/ores.qt/Client{EntityPascal}Model.hpp
cpp_qt_client_model.cpp.mustache src/Client{EntityPascal}Model.cpp
cpp_qt_mdi_window.hpp.mustache include/ores.qt/{EntityPascal}MdiWindow.hpp
cpp_qt_mdi_window.cpp.mustache src/{EntityPascal}MdiWindow.cpp
cpp_qt_detail_dialog.hpp.mustache include/ores.qt/{EntityPascal}DetailDialog.hpp
cpp_qt_detail_dialog.cpp.mustache src/{EntityPascal}DetailDialog.cpp
cpp_qt_history_dialog.hpp.mustache include/ores.qt/{EntityPascal}HistoryDialog.hpp
cpp_qt_history_dialog.cpp.mustache src/{EntityPascal}HistoryDialog.cpp
cpp_qt_controller.hpp.mustache include/ores.qt/{EntityPascal}Controller.hpp
cpp_qt_controller.cpp.mustache src/{EntityPascal}Controller.cpp
qt_detail_dialog_ui.mustache ui/{EntityPascal}DetailDialog.ui
qt_history_dialog_ui.mustache ui/{EntityPascal}HistoryDialog.ui

See also

Emacs 29.1 (Org mode 9.6.6)