Skip to content

Pluggable UI backend for DataSet dialogs #103

@PierreRaybaut

Description

@PierreRaybaut

Summary

Introduce a small registry that lets applications plug an alternate UI backend in front of DataSet.edit, DataSet.view and DataSetGroup.edit, without changing the default Qt-based behaviour. This unlocks running guidata-based applications in environments where Qt is unavailable (typically a browser/React UI driven by Pyodide), while keeping every existing Qt application strictly unchanged.

Motivation

guidata.dataset.DataSet is the canonical way to declare parameter sets across the PlotPyStack and downstream projects (DataLab, Sigima, PlotPy, …). Today, DataSet.edit(), DataSet.view() and DataSetGroup.edit() are hard-wired to the Qt widgets shipped in guidata.dataset.qtwidgets. As soon as a downstream project wants to reuse the same parameter declarations from a non-Qt UI — for example DataLab-Web, which renders the UI with React inside Pyodide — there is no extension point: the only options are to monkey-patch the methods or to fork DataSet.

A first-class extension point keeps a single source of truth for parameter declarations across native (Qt) and web (React) frontends, and avoids importing Qt at all on platforms where it is not available.

Proposed change

Add a tiny handler registry under guidata.dataset.backends and consult it from the three dialog entry points before falling back to the existing Qt implementation. When no handler is registered, behaviour is byte-for-byte identical to today.

Recognised slot names:

  • edit_dataset — synchronous edition of a DataSet. Signature: handler(instance, *, parent, apply, wordwrap, size, object_name) -> int.
  • view_dataset — read-only display of a DataSet. Signature: handler(instance, *, parent, wordwrap, size) -> int.
  • edit_dataset_group — synchronous edition of a DataSetGroup. Signature: handler(instance, *, parent, apply, wordwrap, size, mode) -> int.
  • edit_dataset_async — asynchronous edition of a DataSet, returning an awaitable. Used by the new DataSet.edit_async() coroutine, which falls back to the synchronous handler (or to Qt) when no async handler is registered.

Handlers are looked up at call time, so they may be installed after DataSet instances have been created. Return values follow the existing Qt convention (1 = accepted, 0 = rejected) so callers do not have to special-case the backend in use.

The registry exposes a minimal API: set_handler, get_handler, has_handler, clear_handler, clear_all_handlers.

Why an async variant?

Browser event loops cannot block the calling thread to wait for a modal dialog: DataSet.edit() would deadlock the page. Adding DataSet.edit_async() lets non-Qt backends expose a coroutine that resolves once the user closes the React/HTML dialog, while Qt applications are free to keep using the synchronous edit() exactly as before. When no async handler is registered, edit_async() simply delegates to edit() so the new method is always safe to call.

Backwards compatibility

  • No public signature is changed. DataSet.edit, DataSet.view and DataSetGroup.edit keep their current parameters and defaults.
  • The Qt code paths are only entered when no handler is registered, which is the default. Existing Qt applications observe no behavioural difference.
  • Qt remains an optional dependency: importing guidata.dataset.backends does not import Qt.

Test plan

Tests added in guidata/tests/dataset/test_backends.py cover:

  • Registry semantics (set_handler / get_handler / has_handler / clear_handler).
  • DataSet.edit delegating to a registered handler and forwarding the documented keyword arguments.
  • DataSet.view delegating to a registered handler.
  • DataSet.edit_async falling back to the synchronous handler when no async handler is registered.
  • DataSet.edit_async preferring a dedicated async handler when one is registered.
  • DataSetGroup.edit delegating to a registered handler.

A fixture clears the registry around each test to guarantee isolation.

Files changed

  • guidata/dataset/backends.py (new) — handler registry and module documentation.
  • guidata/dataset/datatypes.py — consult the registry from DataSet.edit, DataSet.view, DataSetGroup.edit; add DataSet.edit_async.
  • guidata/tests/dataset/test_backends.py (new) — unit tests for the registry and the delegation logic.

Out of scope

  • Shipping a concrete non-Qt backend in guidata itself. The DataLab-Web React backend will live in its own repository and only depend on this extension point.
  • Async variants of view() and DataSetGroup.edit(). They can be added later following the same pattern if a concrete need arises.
  • Reworking the Qt dialogs.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions