From aaf515d8f4f1df6746e112301afe4a3432a07604 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Sat, 4 Oct 2025 09:20:14 +1300 Subject: [PATCH] UI: Fix MIDI dropdown contrast and device detection - Improve MIDI dropdown contrast with better colors (#FFFFFF text on #2C2C2C background) - Add bold font and larger size for better visibility - Fix MIDI device detection to show all available devices - Add port validation to only show accessible MIDI devices - Use direct widget reference instead of searching for dropdown - Add delayed initialization to ensure UI is ready before populating dropdown - Improve debugging output for MIDI port detection - Add placeholder text 'No MIDI device selected' when no devices available - Restore window geometry persistence for proper window positioning --- src/ui_client.py | 98 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 30 deletions(-) diff --git a/src/ui_client.py b/src/ui_client.py index 83e179f..293e15f 100644 --- a/src/ui_client.py +++ b/src/ui_client.py @@ -180,9 +180,27 @@ class MidiController: def get_midi_ports(self): """Get list of available MIDI input ports.""" try: - return mido.get_input_names() + # Get input ports (we only need input for receiving MIDI) + input_ports = mido.get_input_names() + + # Also check if we can actually open each port + valid_ports = [] + for port in input_ports: + try: + # Try to open the port to see if it's accessible + test_port = mido.open_input(port) + test_port.close() + valid_ports.append(port) + print(f"Valid MIDI input port: {port}") + except Exception as e: + print(f"Cannot open MIDI port '{port}': {e}") + + print(f"Found MIDI input ports: {input_ports}") + print(f"Valid MIDI input ports: {valid_ports}") + return valid_ports except Exception as e: logging.error(f"Error getting MIDI ports: {e}") + print(f"Error getting MIDI ports: {e}") return [] def load_midi_preference(self): @@ -333,7 +351,11 @@ class UIClient: self.root = tk.Tk() self.root.configure(bg=bg_color) self.root.title("Lighting Controller - UI Client") - # Restore last window geometry if available + + # Set window size to fit all content properly - 4x4 button grid needs more space + self.root.geometry("1800x1200") + + # Load saved window geometry if available self.load_window_geometry() # MIDI controller @@ -388,18 +410,23 @@ class UIClient: # Improve contrast for Combobox (dark theme) try: style.configure("TCombobox", - foreground=fg_color, - fieldbackground=bg_color, - background=bg_color) + foreground="#FFFFFF", + fieldbackground="#2C2C2C", + background="#2C2C2C", + borderwidth=2, + relief="solid") style.map("TCombobox", - fieldbackground=[('readonly', bg_color)], - foreground=[('readonly', fg_color)], - background=[('readonly', bg_color)]) + fieldbackground=[('readonly', '#2C2C2C')], + foreground=[('readonly', '#FFFFFF')], + background=[('readonly', '#2C2C2C')], + focuscolor=[('!focus', '#404040')]) # Dropdown list colors - self.root.option_add("*TCombobox*Listbox*background", bg_color) - self.root.option_add("*TCombobox*Listbox*foreground", fg_color) - self.root.option_add("*TCombobox*Listbox*selectBackground", highlight_pattern_color) - self.root.option_add("*TCombobox*Listbox*selectForeground", fg_color) + self.root.option_add("*TCombobox*Listbox*background", "#2C2C2C") + self.root.option_add("*TCombobox*Listbox*foreground", "#FFFFFF") + self.root.option_add("*TCombobox*Listbox*selectBackground", "#404040") + self.root.option_add("*TCombobox*Listbox*selectForeground", "#FFFFFF") + self.root.option_add("*TCombobox*Listbox*borderwidth", "1") + self.root.option_add("*TCombobox*Listbox*relief", "solid") except Exception: pass @@ -413,15 +440,17 @@ class UIClient: # MIDI port dropdown self.midi_port_var = tk.StringVar() - midi_dropdown = ttk.Combobox( + self.midi_port_var.set("No MIDI device selected") + self.midi_dropdown = ttk.Combobox( midi_frame, textvariable=self.midi_port_var, values=[], state="readonly", - font=("Arial", 11) + font=("Arial", 12, "bold"), + width=20 ) - midi_dropdown.pack(padx=8, pady=4, fill="x") - midi_dropdown.bind("<>", self.on_midi_port_change) + self.midi_dropdown.pack(padx=8, pady=4, fill="x") + self.midi_dropdown.bind("<>", self.on_midi_port_change) # Refresh MIDI ports button refresh_button = ttk.Button( @@ -574,6 +603,9 @@ class UIClient: # Schedule periodic UI updates self.root.after(200, self.update_status_labels) self.root.protocol("WM_DELETE_WINDOW", self.on_closing) + + # Initialize MIDI ports after a short delay to ensure UI is ready + self.root.after(500, self.refresh_midi_ports) def open_pattern_window(self, pattern_name: str): """Create or focus a child window per pattern with sliders for parameters.""" @@ -733,22 +765,28 @@ class UIClient: def refresh_midi_ports(self): """Refresh MIDI ports list.""" + print("Refreshing MIDI ports...") old_ports = self.midi_controller.available_ports.copy() self.midi_controller.available_ports = self.midi_controller.get_midi_ports() + print(f"Available ports: {self.midi_controller.available_ports}") - # Update dropdown - for child in self.root.winfo_children(): - if isinstance(child, ttk.LabelFrame) and child.cget("text") == "MIDI Controller": - for widget in child.winfo_children(): - if isinstance(widget, ttk.Combobox): - widget['values'] = self.midi_controller.available_ports - if (self.midi_controller.available_ports and - self.midi_port_var.get() not in self.midi_controller.available_ports): - self.midi_port_var.set(self.midi_controller.available_ports[0]) - self.midi_controller.midi_port_index = 0 - self.midi_controller.save_midi_preference() - break - break + # Update dropdown directly + if hasattr(self, 'midi_dropdown'): + self.midi_dropdown['values'] = self.midi_controller.available_ports + print(f"Updated dropdown with: {self.midi_controller.available_ports}") + + # Clear current selection if it's not in the new list + current_selection = self.midi_port_var.get() + if current_selection not in self.midi_controller.available_ports: + if self.midi_controller.available_ports: + self.midi_port_var.set(self.midi_controller.available_ports[0]) + self.midi_controller.midi_port_index = 0 + self.midi_controller.save_midi_preference() + else: + self.midi_port_var.set("No MIDI device selected") + print("Successfully updated MIDI dropdown") + else: + print("ERROR: MIDI dropdown not found!") def on_midi_port_change(self, event): """Handle MIDI port selection change.""" @@ -1032,7 +1070,7 @@ class UIClient: def on_closing(self): """Handle application closing.""" logging.info("Closing UI client...") - # Persist window geometry + # Save window geometry self.save_window_geometry() if self.midi_controller.midi_task: self.midi_controller.midi_task.cancel()