Decouple CTK from UI logic; lay groundwork for Qt6#1445
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
All of this code is written by Claude Code, but with the goal to keep it human-reviewable and maintainable. I have reviewed lots of it. @Koratahiu could you please review this refactor on the parts you have written originally? Optimizer parameters window, Muon Aux window, etc. |
|
Tested the video tool and it looks good, only noticed one minor issue where the "..." button is overlapping the end of the text input field for file selection. That probably just needs a change to the column width or padding. Otherwise all the download and extract functions seem to work the same as before. The statistics and image preview/augmentation stuff all seems good too. May go through the files in more detail later. |
This comment was marked as resolved.
This comment was marked as resolved.
… VideoToolUI Removes abstract _create_browse_dir_button/_create_browse_file_button from BaseVideoToolUIView and uses the combined path_entry component instead, fixing widget alignment issues. Adds allow_video_files flag to path_entry. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fixed by simplifying: there is a path_entry component already that can be used. you don't have to create an entry box and a button that opens file selection dialog. behaviour might have slightly changed, but it should only have improved: for example now there is input validation (a path that doesn't exist is marked red). please re-test anyway. |
|
Okay looks good now, thanks! Confirmed input validation works, invalid files are highlighted red. It looks like there are also a couple fields in the "Generate Captions" and "Generate Masks" windows that could be changed to use path_entry in a similar way, I think one of those was what I used as a reference for this originally. |
This comment was marked as resolved.
This comment was marked as resolved.
Base view concrete methods were accessing self.controller, self.ui_state, and toolkit-specific action methods that were only set by CTK subclass __init__ after the base __init__ call, with no enforcement in the base. - BaseTrainUIView: add controller/ui_state as constructor params; fix sync_cloud_secrets to use controller.train_config; add @AbstractMethod for export_training, generate_debug_package, open_profiling_tool - CtkTrainUIView: reorder __init__ to create deps before base init call; replace self.train_config with self.controller.train_config - BaseCloudTabView: add controller as constructor param - CtkCloudTabView: pass controller to base __init__, drop redundant assignment - BaseCaptionUIView: add ABC + @AbstractMethod for 6 action callbacks - BaseConceptTabView: remove concrete _update_filters() (accessed CTK vars); add ConceptConfig import; add concept: ConceptConfig param to BaseConceptWidgetView.__init__ - CtkConceptTabView: implement _update_filters(); pass concept to base init - BaseConceptWindowView: initialize bucket_ax/text_color/canvas to None - BaseTrainingTabView: replace callbacks dict with 6 @AbstractMethod declarations; restore_optimizer_config(variable: str) matches controller - CtkTrainingTabView: implement all 6 abstract methods directly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The ctypes DPI awareness call is toolkit-specific (fixes CTK transparency on Windows monitor changes). It already exists in CtkTrainUIView.py and has no place in the toolkit-agnostic controller. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
search_var/filter_var/show_disabled_var were stored in the base but never used there after _update_filters() was removed. Subclasses manage them. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mirrors the change from upstream commit e928dda (Remove COFT Nerogar#1447), which removed COFT from LoraTab.py. The merge didn't carry it across the rename to CtkLoraTabView.py. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Brings in upstream changes (remove COFT Nerogar#1447, remove cautious_mask Nerogar#1451, fix aux optimizer defaults Nerogar#1444). Conflicts resolved by keeping the ctk_abstraction refactored versions (self.components, controller delegation). COFT removal applied manually to BaseLoraTabView. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eep ctk_abstraction for UI conflicts - Apply LoKr and Scaled OFT additions to BaseLoraTabView/LoraTabController - Apply ETA session-tracking fix to TrainUIController - Apply local row counter fix to BaseTrainingTabView.__create_text_encoder_frame - Keep both supported_caption_extensions and json_path_modifier in path_util Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Was sitting on the same row at columns 3-4; now on its own row directly below the toggle for clearer visual grouping. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Was sitting on the same row at columns 3-4; now on its own row directly below the toggle for clearer visual grouping. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
O-J1
left a comment
There was a problem hiding this comment.
Reviewed validation and put an ask re: adding tests?
…erge Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
# Conflicts: # modules/ui/BaseConceptWindowView.py # modules/ui/CtkConceptWindowView.py
# Conflicts: # modules/ui/BaseConceptWindowView.py # modules/ui/BaseTimestepDistributionWindowView.py # modules/ui/BaseTrainingTabView.py # modules/ui/CtkConceptWindowView.py # modules/ui/CtkTimestepDistributionWindowView.py # modules/ui/CtkTrainingTabView.py
previous_image/next_image called self.view.switch_image, but switch_image lives on the controller, not the view; arrow keys raised AttributeError. Also convert the _make_svd_frames docstring to a # comment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… split The Base/Ctk/Controller refactor deleted modules/ui/CaptionUI.py, ConvertModelUI.py and VideoToolUI.py, but the three entry scripts still imported them, crashing on launch with ModuleNotFoundError. Wire each script through the new controller + create_window(parent, view_cls) pattern (as TrainUIController already does internally). The new views are CTkToplevel, not CTk roots, so launch them under a hidden CTk root and wait_window on the toplevel to avoid a stray empty "tk" window and to exit cleanly on close. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ctk view refactor moved ConceptWindow.__download_dataset into the new ConceptWindowController.download_dataset from a base predating PR Nerogar#1524, which silently reverted that PR's fix: the method again called huggingface_hub.login(token=..., new_session=False) with no empty-token guard. new_session was removed from login() in huggingface-hub 1.16, so every call raised TypeError, swallowed by the surrounding except, so snapshot_download never ran and dataset download was fully broken. Re-apply Nerogar#1524 at the new location: only login when a token is configured, and drop new_session. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Base/Ctk refactor renamed UIState to BaseUIState (UIState.py) plus CtkUIState (CtkUIState.py) and updated validation.py, but missed the ctk sibling: ctk_validation.py still imported the now-nonexistent UIState under TYPE_CHECKING. Point it at CtkUIState, the concrete state actually passed to these ctk validators (mirroring validation.py -> BaseUIState). Invisible to ruff and the runtime (used in a string annotation, under TYPE_CHECKING, with future annotations), so only a type checker would flag it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BaseConfigListView and BaseAdditionalEmbeddingsTabView imported customtkinter solely for a `-> ctk.CTkToplevel` return annotation on the abstract open_element_window, the lone toolkit reference in the Base layer (the sibling abstract methods return toolkit objects unannotated). Drop the annotation and the import so the Base layer is genuinely toolkit-free; the concrete Ctk subclasses keep their own CTkToplevel annotation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tk view split The ctk view split copied OptimizerParamsWindow into a controller from a base that predated Nerogar#1444, reverting `if not current_state:` back to `if current_state is None:`. Since muon_adam_config defaults to {} (not None), the empty initial state fell through to from_dict({}), so the Muon/aux-Adam window opened with bare default_values() instead of MUON_AUX_ADAM_DEFAULTS / ADAMW_ADV. Restore the upstream one-line guard. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gc to close connect_window_closed bound <Destroy> unconditionally, so the callback fired once per descendant widget as the toplevel tore down. Guard on the event widget being the window itself so it fires exactly once. With that in place, open_sampling_tool defers torch_gc to window close via connect_window_closed instead of running it inline at open time, restoring the original 'free GPU memory after the sample window closes' behaviour. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
code-reviewed as best as I can, but with this PR I'll have to rely on testing and AI partially. It wouldn't be possible otherwise |
…bstraction Nerogar/merge received PR Nerogar#1564 as a squash-merge with tree identical to ctk_copy's tip. ctk_abstraction already contains all of ctk_copy's real commits, so -X ours is safe here: there is no content on Nerogar/merge beyond what's already in ctk_abstraction. # Conflicts: # modules/ui/BaseAdditionalEmbeddingsTabView.py # modules/ui/BaseCaptionUIView.py # modules/ui/BaseCloudTabView.py # modules/ui/BaseConceptTabView.py # modules/ui/BaseConceptWindowView.py # modules/ui/BaseConvertModelUIView.py # modules/ui/BaseGenerateCaptionsWindowView.py # modules/ui/BaseGenerateMasksWindowView.py # modules/ui/BaseLoraTabView.py # modules/ui/BaseModelTabView.py # modules/ui/BaseOffloadingWindowView.py # modules/ui/BaseProfilingWindowView.py # modules/ui/BaseSampleFrameView.py # modules/ui/BaseSampleParamsWindowView.py # modules/ui/BaseSampleWindowView.py # modules/ui/BaseSamplingTabView.py # modules/ui/BaseSchedulerParamsWindowView.py # modules/ui/BaseTimestepDistributionWindowView.py # modules/ui/BaseTopBarView.py # modules/ui/BaseTrainUIView.py # modules/ui/BaseTrainingTabView.py # modules/ui/BaseVideoToolUIView.py # modules/ui/CaptionUI.py # modules/ui/CaptionUIController.py # modules/ui/CtkGenerateCaptionsWindowView.py # modules/ui/CtkGenerateMasksWindowView.py # modules/ui/CtkSchedulerParamsWindowView.py # modules/ui/GenerateCaptionsWindow.py # modules/ui/GenerateMasksWindow.py # modules/ui/SampleWindow.py # modules/ui/SampleWindowController.py # modules/ui/SchedulerParamsWindow.py # modules/ui/VideoToolUI.py # modules/ui/VideoToolUIController.py
…erogar#1445) into pyside Nerogar/merge's tip is a squash-merge of ctk_abstraction, tree-identical to our ctk_abstraction/pyside_copy ancestors already in pyside's history. -X ours plus manual conflict resolution below keeps pyside's tree unchanged.


All ~25 UI files in
modules/ui/mixed CustomTkinter widget construction with business logic, making it impossible to add a second toolkit without a full rewrite.This PR applies a Controller / Base View / CTK View split to every window and tab:
Controller — owns all application logic with no UI toolkit dependency. For
TrainUI,TrainUIControlleris what callsstart_training(),stop_training(), handles progress callbacks, and opens tool windows.Base View — toolkit-neutral widget layout. Receives a
componentsnamespace at construction and creates the toolkit-neutral layout usingself.components.label(...),self.components.options(...)etc.CTK View — inherits from both
ctk.CTkand the base view. Creates CTK frames, and handles everything that cannot be toolkit-neutral.No behavior changes intended. Method bodies moved verbatim where possible. Labels, tooltips, and option values untouched.
The pattern is similar to #1164 proposed by @HashakGik (but without the Model/Controller abstraction).
However, the git diffs are relative to the original files to enable human review and git tracability, hopefully enabling such a large refactor without introducing bugs.