From 69b7970b87852967e10146de6b5a9a45bebb2bc9 Mon Sep 17 00:00:00 2001 From: KRSHH <136873090+KRSHH@users.noreply.github.com> Date: Mon, 28 Oct 2024 13:50:44 +0530 Subject: [PATCH] Improved Camera Selection Menu --- modules/globals.py | 1 + modules/ui.py | 188 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 165 insertions(+), 24 deletions(-) diff --git a/modules/globals.py b/modules/globals.py index d60a099..f015f57 100644 --- a/modules/globals.py +++ b/modules/globals.py @@ -43,3 +43,4 @@ mask_size = 1.0 mask_feather_ratio = 8 opacity_switch = False face_opacity = 100 +selected_camera = None diff --git a/modules/ui.py b/modules/ui.py index afa92ce..dcdd7d3 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -202,6 +202,141 @@ class TargetLabel(DragDropLabel): target_label.configure(text="") +class ModernOptionMenu(ctk.CTkFrame): + def __init__(self, master, values, command=None, **kwargs): + super().__init__(master, fg_color="transparent") + + self.values = values + self.command = command + + # Set initial value based on saved camera or first available + self.current_value = ( + modules.globals.selected_camera + if modules.globals.selected_camera in values + else (values[0] if values else "No cameras found") + ) + + # Main button + self.main_button = ctk.CTkButton( + self, + text=self.current_value, + command=self.show_dropdown, + width=300, + height=40, + corner_radius=8, + fg_color="#1f538d", + hover_color="#1a4572", + text_color="white", + font=("Roboto", 13, "bold"), + border_width=2, + border_color="#3d7ab8", + ) + self.main_button.pack(expand=True, fill="both") + + # Dropdown frame (initially hidden) + self.dropdown_frame = None + self.is_dropdown_visible = False + self.click_binding = None + + def show_dropdown(self): + if self.is_dropdown_visible: + self.hide_dropdown() + return + + # Calculate position and size + button_width = self.main_button.winfo_width() + dropdown_height = min(len(self.values) * 35, 200) # Limit max height + + # Create and show dropdown with fixed size + self.dropdown_frame = ctk.CTkFrame( + self.winfo_toplevel(), + width=button_width, + height=dropdown_height, + fg_color="#1f538d", + corner_radius=8, + border_width=2, + border_color="#3d7ab8", + ) + + # Get the absolute coordinates of the button relative to the screen + button_x = self.winfo_rootx() + button_y = self.winfo_rooty() + + # Position the dropdown above the button, using relative coordinates + relative_x = button_x - self.winfo_toplevel().winfo_rootx() + relative_y = button_y - self.winfo_toplevel().winfo_rooty() - dropdown_height + + self.dropdown_frame.place(in_=self.winfo_toplevel(), x=relative_x, y=relative_y) + + # Prevent frame from resizing + self.dropdown_frame.pack_propagate(False) + + # Create scrollable frame if needed + if len(self.values) * 35 > 200: + scrollable_frame = ctk.CTkScrollableFrame( + self.dropdown_frame, + width=button_width - 20, + height=dropdown_height - 10, + fg_color="#1f538d", + scrollbar_button_color="#3d7ab8", + scrollbar_button_hover_color="#2b5d8b", + ) + scrollable_frame.pack(expand=True, fill="both", padx=5, pady=5) + + container = scrollable_frame + else: + container = self.dropdown_frame + + # Add options + for value in self.values: + option_button = ctk.CTkButton( + container, + text=value, + fg_color="transparent", + hover_color="#233d54", + text_color="white", + height=35, + corner_radius=4, + font=("Roboto", 13), + command=lambda v=value: self.select_value(v), + ) + option_button.pack(padx=2, pady=1, fill="x") + + self.is_dropdown_visible = True + self.click_binding = self.winfo_toplevel().bind( + "", self.on_click_outside, add="+" + ) + + def on_click_outside(self, event): + if self.is_dropdown_visible: + widget_under_cursor = event.widget.winfo_containing( + event.x_root, event.y_root + ) + if widget_under_cursor not in [self.main_button] + ( + self.dropdown_frame.winfo_children() if self.dropdown_frame else [] + ): + self.hide_dropdown() + + def hide_dropdown(self): + if self.dropdown_frame: + if self.click_binding: + self.winfo_toplevel().unbind("", self.click_binding) + self.click_binding = None + self.dropdown_frame.destroy() + self.dropdown_frame = None + self.is_dropdown_visible = False + + def select_value(self, value): + self.current_value = value + self.main_button.configure(text=value) + self.hide_dropdown() + if self.command: + self.command(value) + + def get(self): + return self.current_value + + def save_switch_states(): switch_states = { "keep_fps": modules.globals.keep_fps, @@ -210,7 +345,7 @@ def save_switch_states(): "many_faces": modules.globals.many_faces, "map_faces": modules.globals.map_faces, "color_correction": modules.globals.color_correction, - "nsfw_filter": modules.globals.nsfw_filter, # Add this line + "nsfw_filter": modules.globals.nsfw_filter, "live_mirror": modules.globals.live_mirror, "live_resizable": modules.globals.live_resizable, "fp_ui": modules.globals.fp_ui, @@ -219,6 +354,7 @@ def save_switch_states(): "show_mouth_mask_box": modules.globals.show_mouth_mask_box, "mask_down_size": modules.globals.mask_down_size, "mask_feather_ratio": modules.globals.mask_feather_ratio, + "selected_camera": modules.globals.selected_camera, } with open("switch_states.json", "w") as f: json.dump(switch_states, f) @@ -245,6 +381,7 @@ def load_switch_states(): ) modules.globals.mask_down_size = switch_states.get("mask_down_size", 0.5) modules.globals.mask_feather_ratio = switch_states.get("mask_feather_ratio", 8) + modules.globals.selected_camera = switch_states.get("selected_camera", None) except FileNotFoundError: # If the file doesn't exist, use default values pass @@ -625,14 +762,12 @@ def create_root( else "No cameras found" ) ) - camera_optionmenu = ctk.CTkOptionMenu( + camera_optionmenu = ModernOptionMenu( camera_frame, - variable=camera_variable, values=available_camera_strings, - width=200, - font=("Roboto", 14), + command=lambda value: print(f"Selected: {value}"), # Add your command here ) - camera_optionmenu.pack(side="left", padx=10, pady=10) + camera_optionmenu.pack(side="left", padx=(10, 20), pady=10, fill="x", expand=True) live_button = ModernButton( camera_frame, @@ -647,7 +782,7 @@ def create_root( webcam_preview( root, available_camera_indices[ - available_camera_strings.index(camera_variable.get()) + available_camera_strings.index(camera_optionmenu.get()) ], ), ], @@ -1360,7 +1495,7 @@ def create_source_target_popup_for_webcam(root: ctk.CTk, map: list) -> None: POPUP_LIVE.destroy() simplify_maps() # Get the selected camera index - selected_camera = camera_variable.get() + selected_camera = camera_optionmenu.get() camera_index = available_camera_indices[ available_camera_strings.index(selected_camera) ] @@ -1373,7 +1508,10 @@ def create_source_target_popup_for_webcam(root: ctk.CTk, map: list) -> None: refresh_data(map) update_pop_live_status("Please provide mapping!") - popup_status_label_live = ctk.CTkLabel(POPUP_LIVE, text=None, justify="center") + # Rest of the popup content + popup_status_label_live = ctk.CTkLabel( + POPUP_LIVE, text=None, justify="center", font=("Roboto", 14) + ) popup_status_label_live.grid(row=1, column=0, pady=15) add_button = ctk.CTkButton( @@ -1396,26 +1534,28 @@ def create_source_target_popup_for_webcam(root: ctk.CTk, map: list) -> None: ) close_button.place(relx=0.6, rely=0.92, relwidth=0.2, relheight=0.05) - # Add camera selection - camera_frame = ctk.CTkFrame(POPUP_LIVE) - camera_frame.grid(row=2, column=0, pady=15) + # Create a better styled camera selection frame + camera_frame = ctk.CTkFrame(POPUP_LIVE, fg_color="#2a2d2e", corner_radius=15) + camera_frame.grid(row=2, column=0, pady=15, padx=20, sticky="ew") + POPUP_LIVE.grid_columnconfigure(0, weight=1) - camera_label = ctk.CTkLabel(camera_frame, text="Select Camera:") - camera_label.pack(side="left", padx=(0, 10)) + camera_label = ctk.CTkLabel( + camera_frame, + text="Select Camera:", + font=("Roboto", 14, "bold"), + text_color="#DCE4EE", + ) + camera_label.pack(side="left", padx=20, pady=10) available_cameras = get_available_cameras() available_camera_indices, available_camera_strings = available_cameras - camera_variable = ctk.StringVar( - value=( - available_camera_strings[0] - if available_camera_strings - else "No cameras found" - ) + + camera_optionmenu = ModernOptionMenu( + camera_frame, + values=available_camera_strings, + command=lambda value: print(f"Selected: {value}"), # Add your command here ) - camera_optionmenu = ctk.CTkOptionMenu( - camera_frame, variable=camera_variable, values=available_camera_strings - ) - camera_optionmenu.pack(side="left") + camera_optionmenu.pack(side="left", padx=(10, 20), pady=10, fill="x", expand=True) refresh_data(map) # Initial data refresh