Dialog Provider
Overview
DialogProvider is the central abstraction for all user-facing dialogs
in the WOLF viewer. Instead of calling wx dialog classes directly, every
component that needs to interact with the user does so through a
DialogProvider instance stored as self._dialogs.
Business logic DialogProvider interface wx back-end
───────────────── ──────────────────────── ───────────
PyDraw / any view ──► self._dialogs.ask_yes_no() ──► wx.MessageDialog
self._dialogs.ask_file_open()──► wx.FileDialog
… …
This indirection has two benefits:
Testability — unit tests inject a
MockDialogProviderthat never opens a real window. Responses are scripted in advance, and every call is recorded for later assertion.Portability — the
wxdependency is confined to a single module. Replacing the back-end (e.g. Qt, CLI, web) only requires a new subclass.
Note
The convention self._dialogs.<method>(parent, ...) replaces every direct
wx.<Dialog>(parent, ...) call site. Do not bypass it.
Class reference
DialogProvider
- class wolfhece.dialog_provider.DialogProvider[source]
Wrapper around common
wxdialogs. All methods accept an optional parent window that is passed directly to the underlying wx constructor.File and directory dialogs
- ask_file_open(message, wildcard='all (*.*)|*.*', default_path='', style=None, parent=None) str | None[source]
Open a File → Open dialog. Returns the selected path, or
Noneif the user cancelled.- Parameters:
message – Dialog title / prompt shown to the user.
wildcard – Pipe-separated filter string (wx format), e.g.
"Images (*.png)|*.png|All files (*.*)|*.*".default_path – Directory shown when the dialog opens.
style – wx style flags; defaults to
wx.FD_OPEN.
- ask_file_open_with_filter(message, wildcard='all (*.*)|*.*', default_path='', style=None, parent=None) tuple[str, int] | None[source]
Same as
ask_file_open(), but also returns the index of the selected wildcard filter. Returns(path, filter_index)orNoneon cancel.
Message dialogs
- show_message(message, caption='', style=DialogStyles.OK_INFO, parent=None) int[source]
Show a non-blocking informational (or warning/error) dialog with an OK button. Returns the wx modal return code (
wx.ID_OK).
- ask_yes_no(message, caption='', style=DialogStyles.YES_NO_QUESTION, parent=None) bool[source]
Ask the user a yes/no question. Returns
Trueif the user clicked Yes,Falseotherwise.
- ask_ok_cancel(message, caption='', style=DialogStyles.OK_CANCEL, parent=None) bool[source]
Show an OK / Cancel dialog. Returns
Trueif the user clicked OK.
- ask_confirmation(message, caption='', default='yes', parent=None) bool[source]
Convenience wrapper around
ask_yes_no()that sets the default focused button.- Parameters:
default –
"yes"(default) focuses the Yes button;"no"focuses the No button.
Choice dialogs
- ask_single_choice(message, caption, choices, preselected=None, style=wx.CHOICEDLG_STYLE, parent=None) str | None[source]
Show a list of items and let the user pick exactly one. Returns the selected string, or
Noneon cancel.- Parameters:
choices – Sequence of option strings.
preselected – Index of the item selected by default.
- ask_multi_choice(message, caption, choices, preselected=None, style=wx.CHOICEDLG_STYLE, parent=None) list[int] | None[source]
Show a list of items and let the user pick multiple items. Returns a list of selected indices, or
Noneon cancel.- Parameters:
preselected – Sequence of indices pre-checked when the dialog opens.
Text and number entry
- ask_text(message, caption='', default='', style=DialogStyles.OK_CANCEL, parent=None) str | None[source]
Prompt the user for a text string. Returns the entered string, or
Noneon cancel.
- ask_float(message, caption='', default='', style=DialogStyles.OK_CANCEL, parent=None) float | None[source]
Prompt the user for a floating-point number (built on top of
ask_text()). Returns the parsedfloat, orNoneon cancel or invalid input.
Progress dialogs
- create_progress(title, message, maximum, style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE, parent=None) ProgressHandle[source]
Create and display a determinate progress bar. Returns a
ProgressHandlethat wraps the underlying dialog.Typical usage:
progress = self._dialogs.create_progress("Processing", "Loading…", 100) for i, item in enumerate(items): process(item) if not progress.update(i + 1): break # user clicked Cancel progress.close()
ProgressHandle
- class wolfhece.dialog_provider.ProgressHandle[source]
Thin wrapper returned by
DialogProvider.create_progress().
DialogStyles
- class wolfhece.dialog_provider.DialogStyles[source]
Named constants that map to common
wxstyle flag combinations. Use these instead of rawwxflags for clarity and to decouple call sites from thewxnamespace.Constant
Equivalent wx flags
OKwx.OKCANCELwx.CANCELOK_CANCELwx.OK | wx.CANCELICON_INFORMATIONwx.ICON_INFORMATIONICON_QUESTIONwx.ICON_QUESTIONICON_WARNINGwx.ICON_WARNINGICON_ERRORwx.ICON_ERRORYES_NOwx.YES_NOYES_NO_QUESTIONwx.YES_NO | wx.ICON_QUESTIONYES_NO_DEFAULT_YESwx.YES_NO | wx.YES_DEFAULTYES_NO_DEFAULT_NOwx.YES_NO | wx.NO_DEFAULTYES_NO_QUESTION_DEFAULT_YESwx.YES_NO | wx.ICON_QUESTION | wx.YES_DEFAULTYES_NO_QUESTION_DEFAULT_NOwx.YES_NO | wx.ICON_QUESTION | wx.NO_DEFAULTOK_INFOwx.OK | wx.ICON_INFORMATIONOK_CANCEL_WARNINGwx.OK | wx.CANCEL | wx.ICON_WARNING
MockDialogProvider (testing)
- class tests.mock_dialog_provider.MockDialogProvider
Scriptable subclass of
DialogProviderintended exclusively for unit tests. It never opens any real window.All dialog methods are overridden to pull scripted answers from a per-method FIFO queue. If a method is called but the queue is empty, an
AssertionErroris raised immediately, making test failures explicit.Every call is appended to
callsfor post-call assertion.- calls : list[dict]
Ordered list of every dialog call recorded since the mock was created. Each entry is a
dictwith a"method"key and one key per argument passed to the method.
- push(method_name, *responses)
Queue one or more scripted responses for the named method. Responses are consumed in FIFO order.
- Parameters:
method_name – Name of the
DialogProvidermethod to script (e.g."ask_yes_no").responses – Values to return, consumed one per call.
mock = MockDialogProvider() mock.push("ask_yes_no", True) # first call → True mock.push("ask_yes_no", False, True) # second → False, third → True
show_busyis also overridden: it records the call and yieldsNonewithout displaying anything.
Usage in tests
A typical test:
Creates a
MockDialogProviderand pushes scripted answers.Injects the mock as
component._dialogs.Calls the component method under test.
Asserts on the result and optionally inspects
mock.calls.
from tests.mock_dialog_provider import MockDialogProvider
def test_save_aborts_on_cancel(viewer):
mock = MockDialogProvider()
mock.push("ask_file_save", None) # simulate Cancel
viewer._dialogs = mock
viewer.save_results()
assert mock.calls[0]["method"] == "ask_file_save"
# no file was written
assert not output_path.exists()
def test_overwrite_confirmed(viewer, tmp_path):
mock = MockDialogProvider()
mock.push("ask_file_save", str(tmp_path / "out.bin"))
mock.push("ask_yes_no", True) # confirm overwrite
viewer._dialogs = mock
viewer.save_results()
assert (tmp_path / "out.bin").exists()
Tip
Always call push() for every dialog that the code
path under test will trigger. An uncaught AssertionError from the mock
is a strong signal that the production code opened an unexpected dialog.
Migration guide
When converting a direct wx call to use DialogProvider:
Identify the
wxdialog call and its return value usage.Replace it with the matching
self._dialogs.<method>(...)call.Add a test that pushes a scripted response and verifies behaviour.
Do not change any business logic during the replacement.
Before |
After |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|