Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
fe50e8d
feat: split CTK UI into controller/base-view/ctk-view pattern
May 10, 2026
a88f369
refactor: abstract CTK views and migrate logic to controllers
May 10, 2026
ec2ee0a
refactor: replace manual entry+browse-button pairs with path_entry in…
May 11, 2026
c3961ba
fix: declare undeclared self attrs in Base*View classes
May 15, 2026
d039871
refactor: move Windows DPI awareness block to CtkTrainUIView
May 15, 2026
1bb5a56
refactor: remove unused __init__ from BaseConceptTabView
May 15, 2026
9f42590
Merge branch 'upstream' into ctk_copy
May 16, 2026
8dd86c6
fix: apply upstream COFT removal to CtkLoraTabView
May 16, 2026
19b5724
Merge branch 'ctk_copy' into ctk_abstraction
May 16, 2026
3984c3f
Merge branch 'upstream' into ctk_copy
dxqb May 29, 2026
c602458
Sync Ctk* copies with upstream changes merged into Base* files
dxqb May 29, 2026
23ad1a2
Merge ctk_copy: LoKr support, Scaled OFT, ETA fix, row counter fix; k…
dxqb May 29, 2026
01cca79
Move Rolling Backup Count under the Rolling Backup toggle
dxqb May 29, 2026
647afc2
Move Rolling Backup Count under the Rolling Backup toggle
dxqb May 29, 2026
408e7e2
Merge branch 'upstream' into ctk_copy
dxqb Jun 4, 2026
a0bd3e4
Sync CtkConceptWindowView and ConceptWindowController with Base copy
dxqb Jun 4, 2026
3d90a9e
Merge ctk_copy: transformers 5.9, torch 2.12, compile thread-safety
dxqb Jun 4, 2026
51a839e
Merge branch 'upstream' into ctk_copy
dxqb Jun 13, 2026
fdea894
Sync CtkConceptWindowView with BaseConceptWindowView after upstream m…
dxqb Jun 13, 2026
e70bada
Merge branch 'ctk_copy' into ctk_abstraction
dxqb Jun 13, 2026
dd9bdbe
Merge branch 'upstream' into ctk_copy
dxqb Jun 19, 2026
1f73b3a
Sync Ctk* views with Base* after merging upstream
dxqb Jun 19, 2026
aee3609
Merge branch 'ctk_copy' into ctk_abstraction
dxqb Jun 19, 2026
816c17a
Merge upstream: Disable LoRA rank check (false positive on Flux2 LoHA…
dxqb Jun 26, 2026
68b6a86
Merge ctk_copy: Disable LoRA rank check (false positive on Flux2 LoHA…
dxqb Jun 26, 2026
826835e
Fix CaptionUI arrow-key navigation and SVD-frame docstring
dxqb Jun 27, 2026
3f676d7
Fix standalone caption/convert/video UI launch scripts after Ctk view…
dxqb Jun 27, 2026
9706006
Fix HF dataset download in concept window after Ctk view split
dxqb Jun 27, 2026
7377fd7
Fix stale UIState type reference in ctk_validation
dxqb Jun 27, 2026
8f70897
Drop customtkinter dependency from toolkit-agnostic Base views
dxqb Jun 27, 2026
d52b8d2
Re-apply aux-optimizer empty-state default fix (#1444) after Ctk view…
dxqb Jun 27, 2026
0f361c9
Fix sample-window lifecycle: scope <Destroy> binding and defer torch_…
dxqb Jun 29, 2026
57d53bf
Remove PLAN.md from .gitignore
dxqb Jun 29, 2026
c6de3f9
Merge Nerogar/merge (squashed ctk_copy split #1564) into ctk_abstraction
dxqb Jul 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions modules/ui/AdditionalEmbeddingsTabController.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

from modules.util.config.TrainConfig import TrainConfig, TrainEmbeddingConfig


class AdditionalEmbeddingsTabController:
def __init__(self, config: TrainConfig):
self.train_config = config

def create_new_element(self) -> TrainEmbeddingConfig:
return TrainEmbeddingConfig.default_values()

def randomize_uuid(self, embedding_config: TrainEmbeddingConfig) -> TrainEmbeddingConfig:
embedding_config.uuid = TrainEmbeddingConfig.default_values().uuid
return embedding_config
135 changes: 36 additions & 99 deletions modules/ui/BaseAdditionalEmbeddingsTabView.py
Original file line number Diff line number Diff line change
@@ -1,136 +1,73 @@

from modules.ui.ConfigList import ConfigList
from modules.util.config.TrainConfig import TrainConfig, TrainEmbeddingConfig
from modules.util.ui import components
from modules.util.ui.UIState import UIState

import customtkinter as ctk


class AdditionalEmbeddingsTab(ConfigList):

def __init__(self, master, train_config: TrainConfig, ui_state: UIState):
super().__init__(
master,
train_config,
ui_state,
attr_name="additional_embeddings",
enable_key="train",
from_external_file=False,
add_button_text="add embedding",
is_full_width=True,
show_toggle_button=True
)
from modules.ui.BaseConfigListView import BaseConfigListView
from modules.util import path_util


class BaseAdditionalEmbeddingsTabView(BaseConfigListView):

def refresh_ui(self):
if self.element_list is not None:
self.element_list.destroy()
self._destroy_frame(self.element_list)
self.element_list = None
self.widgets_initialized = False
self._create_element_list()

def create_widget(self, master, element, i, open_command, remove_command, clone_command, save_command):
return EmbeddingWidget(master, element, i, open_command, remove_command, clone_command, save_command)

def create_new_element(self) -> dict:
return TrainEmbeddingConfig.default_values()

def open_element_window(self, i, ui_state) -> ctk.CTkToplevel:
def open_element_window(self, i, ui_state):
pass


class EmbeddingWidget(ctk.CTkFrame):
def __init__(self, master, element, i, open_command, remove_command, clone_command, save_command):
super().__init__(
master=master, corner_radius=10, bg_color="transparent"
)
class BaseEmbeddingWidgetView:

def __init__(self, components):
self.components = components

self.element = element
self.ui_state = UIState(self, element)
def build_content(self, top_frame, bottom_frame, ui_state, i, save_command, remove_command, clone_command, controller):
self.ui_state = ui_state
self.i = i
self.save_command = save_command

self.grid_columnconfigure(0, weight=1)

top_frame = ctk.CTkFrame(master=self, corner_radius=0, fg_color="transparent")
top_frame.grid(row=0, column=0, sticky="nsew")
top_frame.grid_columnconfigure(3, weight=1)
top_frame.grid_columnconfigure(5, weight=1)

bottom_frame = ctk.CTkFrame(master=self, corner_radius=0, fg_color="transparent")
bottom_frame.grid(row=1, column=0, sticky="nsew")
bottom_frame.grid_columnconfigure(7, weight=1)

# close button
close_button = ctk.CTkButton(
master=top_frame,
width=20,
height=20,
text="X",
corner_radius=2,
fg_color="#C00000",
command=lambda: remove_command(self.i),
)
close_button.grid(row=0, column=0)
self.components.colored_icon_button(top_frame, 0, 0, "X", "#C00000", lambda: remove_command(self.i))

# clone button
clone_button = ctk.CTkButton(
master=top_frame,
width=20,
height=20,
text="+",
corner_radius=2,
fg_color="#00C000",
command=lambda: clone_command(self.i, self.__randomize_uuid),
)
clone_button.grid(row=0, column=1, padx=5)
self.components.colored_icon_button(top_frame, 0, 1, "+", "#00C000", lambda: clone_command(self.i, controller.randomize_uuid), padx=5)

# embedding model names
components.label(top_frame, 0, 2, "base embedding:",
tooltip="The base embedding to train on. Leave empty to create a new embedding")
components.path_entry(
self.components.label(top_frame, 0, 2, "base embedding:",
tooltip="The base embedding to train on. Leave empty to create a new embedding")
self.components.path_entry(
top_frame, 0, 3, self.ui_state, "model_name",
mode="file", path_modifier=components.json_path_modifier
mode="file", path_modifier=path_util.json_path_modifier
)

# placeholder
components.label(top_frame, 0, 4, "placeholder:",
tooltip="The placeholder used when using the embedding in a prompt")
components.entry(top_frame, 0, 5, self.ui_state, "placeholder")
self.components.label(top_frame, 0, 4, "placeholder:",
tooltip="The placeholder used when using the embedding in a prompt")
self.components.entry(top_frame, 0, 5, self.ui_state, "placeholder")

# token count
components.label(top_frame, 0, 6, "token count:",
tooltip="The token count used when creating a new embedding. Leave empty to auto detect from the initial embedding text.")
token_count_entry = components.entry(top_frame, 0, 7, self.ui_state, "token_count")
token_count_entry.configure(width=40)
self.components.label(top_frame, 0, 6, "token count:",
tooltip="The token count used when creating a new embedding. Leave empty to auto detect from the initial embedding text.")
self.components.entry(top_frame, 0, 7, self.ui_state, "token_count", width=40)

# trainable
components.label(bottom_frame, 0, 0, "train:")
trainable_switch = components.switch(bottom_frame, 0, 1, self.ui_state, "train", command=save_command)
trainable_switch.configure(width=40)
self.components.label(bottom_frame, 0, 0, "train:")
self.components.switch(bottom_frame, 0, 1, self.ui_state, "train", command=save_command, width=40)

# output embedding
components.label(bottom_frame, 0, 2, "output embedding:",
tooltip="Output embeddings are calculated at the output of the text encoder, not the input. This can improve results for larger text encoders and lower VRAM usage.")
output_embedding_switch = components.switch(bottom_frame, 0, 3, self.ui_state, "is_output_embedding")
output_embedding_switch.configure(width=40)
self.components.label(bottom_frame, 0, 2, "output embedding:",
tooltip="Output embeddings are calculated at the output of the text encoder, not the input. This can improve results for larger text encoders and lower VRAM usage.")
self.components.switch(bottom_frame, 0, 3, self.ui_state, "is_output_embedding", width=40)

# stop training after
components.label(bottom_frame, 0, 4, "stop training after:",
tooltip="When to stop training the embedding")
components.time_entry(bottom_frame, 0, 5, self.ui_state, "stop_training_after", "stop_training_after_unit")
self.components.label(bottom_frame, 0, 4, "stop training after:",
tooltip="When to stop training the embedding")
self.components.time_entry(bottom_frame, 0, 5, self.ui_state, "stop_training_after", "stop_training_after_unit")

# initial embedding text
components.label(bottom_frame, 0, 6, "initial embedding text:",
tooltip="The initial embedding text used when creating a new embedding")
components.entry(bottom_frame, 0, 7, self.ui_state, "initial_embedding_text")

def __randomize_uuid(self, embedding_config: TrainEmbeddingConfig):
embedding_config.uuid = TrainEmbeddingConfig.default_values().uuid
return embedding_config
self.components.label(bottom_frame, 0, 6, "initial embedding text:",
tooltip="The initial embedding text used when creating a new embedding")
self.components.entry(bottom_frame, 0, 7, self.ui_state, "initial_embedding_text")

def configure_element(self):
pass

def place_in_list(self):
self.grid(row=self.i, column=0, pady=5, padx=5, sticky="new")
Loading