Skip to content

GUI: Config validation errors handler dialog & layout fixes - #3397

Merged
deruyter92 merged 14 commits into
mainfrom
cy/config-gui-error-handler
Jul 21, 2026
Merged

GUI: Config validation errors handler dialog & layout fixes#3397
deruyter92 merged 14 commits into
mainfrom
cy/config-gui-error-handler

Conversation

@C-Achard

@C-Achard C-Achard commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Adds a more user-friendly error handling dialog for project configuration loading in the DeepLabCut GUI.

  • Reporting and recovery options for configuration validation errors, such as invalid files, missing files, or permission issues
  • Adds guidance through resolving configuration problems, including options to edit the configuration, retry loading, or cancel.
  • The changes also improve the handling of recent projects and ensure the UI remains consistent even after failed loads, avoiding partial UI states and rogue widgets
  • Brings several other GUI usability improvements:
    • Made tab layout scrollable to avoid collapsing widgets when the window is resized
    • Fixed the disabled Maximize action by uncapping window max size (which is monitor dependent and could break on multiple displays)
    • Fixed a warning in Analyze videos tab going out of bounds
    • Wrap video selection widget to avoid making fields unreadable

Closes #3393.

TODO

C-Achard added 2 commits July 14, 2026 17:51
Introduce a new `config_errors` dialog helper that builds user-facing configuration error reports via `ConfigErrorReport`. It formats Pydantic validation issues into readable field paths, customizes messages for missing/unsupported settings, includes truncated invalid input values, and adds dedicated handling for missing-file and permission errors with technical details preserved for diagnostics.
Refactors project loading in the main GUI window to consistently validate config files and recover from failures. It adds a structured error dialog with Retry/Edit/Cancel actions, opens the config editor for repair, and reloads after edits. The flow now routes recent-project opens through the same validation path, clears partial tabs before retrying, and updates project state/load methods to return success status so invalid or unreadable configs no longer leave stale UI state.
@C-Achard C-Achard self-assigned this Jul 14, 2026
@C-Achard C-Achard added enhancement New feature or request GUI issues relating to GUI new feature config Related to config.yaml, ruamel, YAML parsing, ... labels Jul 14, 2026
@C-Achard C-Achard changed the title GUI: Config validation errors handler GUI: Config validation errors handler dialog Jul 14, 2026
C-Achard added 5 commits July 15, 2026 09:57
Replaces the in-app config repair editor flow with an "Open configuration" action that launches the file in the OS default application, then guides users to save and reload. The config error dialog now supports repeated open/reload attempts in-place, and both project UI initialization and `load_config` were refactored to consistently handle validation and file-read errors with clearer logging and retry behavior.
Refines `SelectedShuffleDisplay` error rendering when `pose_cfg.yaml` is missing by formatting the message with line breaks and full path visibility. The label is now configured for plain text, word wrapping, expandable sizing, and mouse/keyboard text selection, making long file paths easier to read and copy.
Move window sizing constants to module scope and stop forcing the main window max size to screen dimensions. This allows the maximize button to work properly while preserving the initial resize factor and minimum window size defaults.
Wrap `DefaultTab`’s main content in a `QScrollArea` with a dedicated content widget and zero outer margins so long tab content can be scrolled instead of being clipped. Also updates the `parent` type hint to `QtWidgets.QWidget | None` for consistency with modern typing style.
Update `VideoSelectionWidget` so the selected-videos status label can wrap text and expand horizontally. This improves readability when many videos are selected and prevents the text from being clipped in the GUI layout.
@C-Achard C-Achard changed the title GUI: Config validation errors handler dialog GUI: Config validation errors handler dialog & layout fixes Jul 15, 2026
@C-Achard
C-Achard requested a review from Copilot July 15, 2026 09:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves DeepLabCut’s GUI resilience and usability by introducing a dedicated, user-facing configuration error dialog around project loading/validation, preventing partial UI initialization when configs are invalid, and applying several layout/UX fixes across tabs and displays.

Changes:

  • Adds a config-load error reporting + recovery flow (open config externally, retry, cancel) and ensures partially created project tabs are discarded on failure.
  • Improves recent-project opening behavior and keeps the UI in a consistent “welcome” state after failed loads.
  • Applies GUI layout tweaks (scrollable tab content, better wrapping for long labels/messages, and maximize behavior fix).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
deeplabcut/gui/window.py Adds config error dialog + retry loop, recent-project open handler, and cleanup of partially created tab UI during failed config loads.
deeplabcut/gui/dialogs/config_errors.py Introduces a small formatter module to convert config/validation exceptions into user-presentable dialog content.
deeplabcut/gui/displays/selected_shuffle_display.py Improves error message readability and label behavior when shuffle/model config is missing.
deeplabcut/gui/components.py Makes video-selection text wrap and converts default tabs to a scrollable layout to avoid widget collapse on resize.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread deeplabcut/gui/window.py
Comment thread deeplabcut/gui/window.py Outdated
Comment thread deeplabcut/gui/window.py Outdated
Comment thread deeplabcut/gui/window.py Outdated
Add a shared `absolute_path` helper in `gui.utils` and use it in `MainWindow` when selecting, opening recent, and reloading projects so config paths are consistently expanded and absolute. Also treat closing the config error dialog as a cancel action to avoid falling through without an explicit choice.

@deruyter92 deruyter92 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some general comments / questions. To help implementing the suggestions, I've also created a small top-on PR #3399 with additions to this PR. Please review critically and only take what you need. I can also take care of cherry-picking/merging after you've looked at it.

revisit extra='forbid' (in a new PR)?
extra="forbid" makes config loading in general a hard-fail on an unknown field. I think this is good in principle, but it can become painful for forward compatibility (opening a project touched by a newer DLC). Maybe in a separate PR we could add an opt-in tolerant reader that just warns instead of raises (which can be used for the GUI as well).

**self.cfg **
I've added some additions in the follow-up PR to improve the loading for self.cfg: consistently return a ProjectConfig, not (an empty) dict, use caching when possible instead of loading on every access.

Comment thread deeplabcut/gui/dialogs/config_errors.py
Comment thread deeplabcut/gui/dialogs/config_errors.py Outdated
Comment thread deeplabcut/gui/components.py
Comment thread deeplabcut/gui/window.py Outdated
Comment thread deeplabcut/gui/window.py Outdated
Comment thread deeplabcut/gui/window.py
Comment on lines +976 to +981
def _build_project_ui_from_current_config(self) -> bool:
"""Build project tabs using the current config file on disk.

The configuration is reread for every attempt. No validated
configuration state is cached between attempts.
"""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand why it is necessary to read from disk after the config is adjusted because it was invalid. But is rereading the config on every property access not a bit excessive?

Especially if manipulations (e.g. fixes) are happened in between, different parts of one operation may observe different configs.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deruyter92 Agreed that re-reading on every property access is inefficient and can let different parts of one operation observe different file contents. However, do we really want to change the MainWindow.cfg into a persistent cache ? Because that changes its existing behavior and makes reload dependent on filesystem notifications.

Maybe reading and caching once at explicit operation boundaries, such as UI construction, reload, training, evaluation, and analysis would best, and cleanest overall in regards to all our other efforts (as well as a standard solution). Or we can use a scoped UI-build snapshot that is cleared after add_tabs() completes.

I think the goal really is consistency within each operation while avoiding stale global state and I'm not sure either approach works perfectly to that end right now.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

understand your concern, but I think that is just a matter of invalidating the cache when needed. The problem with your proposed alternative is that properties like all_bodyparts, is_multianimal and project_folder are read in quite some different places, after add_tabs() is completed. Each re-reading the config from disk (with possible ad-hoc config repairs that we inherited from legacy config readers. Worst case: different configs for different GUI components).

With the current cached config solution (introduced via #3399), no re-reads are required when the config was not changed. The cache is always invalidated when any project switch happens or the config_path is re-assigned, when _build_ui_from_current_config is called, and whenever load_config is triggered. I think these are clear boundaries where config re-reading is expected. The file monitor is just a bonus which is unrelated: whenever the user changes the file on disk, offer a GUI-notification + button for reloading the config from disk.

I am leaving this comment open so we can always return to it later, even after merging.

* squashed updates for GUI config validation error handling

* replace getattr guard with explicit attr

- Initialize self._config_monitor = None early in __init__
- Replace getattr(self, "_config_monitor", None) with direct attribute access in config_path setter
- Remove unintentional double @Property decorator on project_folder

* fix: widen worker and evaluate-network error handling

- Catch BaseException in Worker.run() so SystemExit from CLI functions
  is marshalled to the UI instead of silently killing the thread
- Wrap the full evaluate_network method in a single try/except Exception
  so runtime errors (GPU OOM, corrupted models, etc.) are consistently
  reported through show_task_error instead of only catching config errors

* refactor: replace QTimer.singleShot with explicit named reload timers
- Add named _reload_timer members to AnalyzeVideos and ManageProject tabs
  so the timer is inspectable in tests and auto-cancels on tab destruction
- Wire editor.accepted to timer.start instead of a fire-and-forget lambda

* Add `MainWindow.invalidate_config_cache()`

a lightweight hook to force the next cfg access to re-read and validate from disk. Can be called from outside the GUI as well (e.g. after video analysis or training network, in case they manipulate te config)

* test: add coverage for show_task_error, cache invalidation, and reload timer

- Add test_task_error.py: verify generic vs config-error dialog rendering
  and that the 'Open configuration' button only appears for config errors
- Test invalidate_config_cache drops the cached config for the next access
  and is safe to call with no project loaded
- Test that the named _reload_timer (replacing QTimer.singleShot) reliably
  fires the reload callback

* widen train_network error handling, similar to 4472fd3

* deliberately narrow down config errors. (exclude TypeError and ValueError)

* watch config file parent directory to detect atomic replacements

Connect to the directoryChanged signal and add the parent directory
to the watched paths in set_path().  Some editors atomically replace
files (delete + rename), which silently drops a file-only watch in
QFileSystemWatcher.  Watching the parent catches those changes and
re-adds the file path in _check_for_change() as before.

* guard pose_cfg.get("method") string-type

* Add a self._extract_error flag.

The error signal sets it to True. _show_success_message checks it and skips the dialog entirely if there was an error.

* update tests with narrow error types

* Revert narrowing of CONFIG_ERRORS - treat only ValidationError as config-related in show_task_error

CONFIG_LOAD_ERRORS includes FileNotFoundError, PermissionError,
OSError, TypeError, and ValueError — all of which can originate
from non-config sources (e.g. missing checkpoint, disk full,
wrong type from a GUI callback) when raised by worker threads.
Showing a config-themed dialog with an "Open configuration"
button for those errors was misleading.

Now show_task_error only trusts ValidationError (which is always
from config parsing) to decide on the config dialog.  The
config-loading recovery loop continues to use the full
CONFIG_LOAD_ERRORS tuple where the context is known.

Restore TypeError and ValueError to CONFIG_LOAD_ERRORS so the
recovery loop properly catches those as user-repairable config
errors.

* fix MainWindow init _config_monitor

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 25 changed files in this pull request and generated 1 comment.

Comment thread deeplabcut/gui/window.py
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@deruyter92
deruyter92 marked this pull request as ready for review July 20, 2026 16:02
@deruyter92 deruyter92 mentioned this pull request Jul 20, 2026
9 tasks
@deruyter92
deruyter92 merged commit 307c380 into main Jul 21, 2026
32 checks passed
@deruyter92
deruyter92 deleted the cy/config-gui-error-handler branch July 21, 2026 06:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config Related to config.yaml, ruamel, YAML parsing, ... enhancement New feature or request GUI issues relating to GUI new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GUI: Wrap config validation errors in error message and avoid partial UI init

4 participants