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
This commit is contained in:
2025-10-04 09:20:14 +13:00
parent 7beca0cf53
commit aaf515d8f4

View File

@@ -180,9 +180,27 @@ class MidiController:
def get_midi_ports(self): def get_midi_ports(self):
"""Get list of available MIDI input ports.""" """Get list of available MIDI input ports."""
try: 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: except Exception as e:
logging.error(f"Error getting MIDI ports: {e}") logging.error(f"Error getting MIDI ports: {e}")
print(f"Error getting MIDI ports: {e}")
return [] return []
def load_midi_preference(self): def load_midi_preference(self):
@@ -333,7 +351,11 @@ class UIClient:
self.root = tk.Tk() self.root = tk.Tk()
self.root.configure(bg=bg_color) self.root.configure(bg=bg_color)
self.root.title("Lighting Controller - UI Client") 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() self.load_window_geometry()
# MIDI controller # MIDI controller
@@ -388,18 +410,23 @@ class UIClient:
# Improve contrast for Combobox (dark theme) # Improve contrast for Combobox (dark theme)
try: try:
style.configure("TCombobox", style.configure("TCombobox",
foreground=fg_color, foreground="#FFFFFF",
fieldbackground=bg_color, fieldbackground="#2C2C2C",
background=bg_color) background="#2C2C2C",
borderwidth=2,
relief="solid")
style.map("TCombobox", style.map("TCombobox",
fieldbackground=[('readonly', bg_color)], fieldbackground=[('readonly', '#2C2C2C')],
foreground=[('readonly', fg_color)], foreground=[('readonly', '#FFFFFF')],
background=[('readonly', bg_color)]) background=[('readonly', '#2C2C2C')],
focuscolor=[('!focus', '#404040')])
# Dropdown list colors # Dropdown list colors
self.root.option_add("*TCombobox*Listbox*background", bg_color) self.root.option_add("*TCombobox*Listbox*background", "#2C2C2C")
self.root.option_add("*TCombobox*Listbox*foreground", fg_color) self.root.option_add("*TCombobox*Listbox*foreground", "#FFFFFF")
self.root.option_add("*TCombobox*Listbox*selectBackground", highlight_pattern_color) self.root.option_add("*TCombobox*Listbox*selectBackground", "#404040")
self.root.option_add("*TCombobox*Listbox*selectForeground", fg_color) 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: except Exception:
pass pass
@@ -413,15 +440,17 @@ class UIClient:
# MIDI port dropdown # MIDI port dropdown
self.midi_port_var = tk.StringVar() 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, midi_frame,
textvariable=self.midi_port_var, textvariable=self.midi_port_var,
values=[], values=[],
state="readonly", state="readonly",
font=("Arial", 11) font=("Arial", 12, "bold"),
width=20
) )
midi_dropdown.pack(padx=8, pady=4, fill="x") self.midi_dropdown.pack(padx=8, pady=4, fill="x")
midi_dropdown.bind("<<ComboboxSelected>>", self.on_midi_port_change) self.midi_dropdown.bind("<<ComboboxSelected>>", self.on_midi_port_change)
# Refresh MIDI ports button # Refresh MIDI ports button
refresh_button = ttk.Button( refresh_button = ttk.Button(
@@ -575,6 +604,9 @@ class UIClient:
self.root.after(200, self.update_status_labels) self.root.after(200, self.update_status_labels)
self.root.protocol("WM_DELETE_WINDOW", self.on_closing) 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): def open_pattern_window(self, pattern_name: str):
"""Create or focus a child window per pattern with sliders for parameters.""" """Create or focus a child window per pattern with sliders for parameters."""
if pattern_name in self.pattern_windows: if pattern_name in self.pattern_windows:
@@ -733,22 +765,28 @@ class UIClient:
def refresh_midi_ports(self): def refresh_midi_ports(self):
"""Refresh MIDI ports list.""" """Refresh MIDI ports list."""
print("Refreshing MIDI ports...")
old_ports = self.midi_controller.available_ports.copy() old_ports = self.midi_controller.available_ports.copy()
self.midi_controller.available_ports = self.midi_controller.get_midi_ports() self.midi_controller.available_ports = self.midi_controller.get_midi_ports()
print(f"Available ports: {self.midi_controller.available_ports}")
# Update dropdown # Update dropdown directly
for child in self.root.winfo_children(): if hasattr(self, 'midi_dropdown'):
if isinstance(child, ttk.LabelFrame) and child.cget("text") == "MIDI Controller": self.midi_dropdown['values'] = self.midi_controller.available_ports
for widget in child.winfo_children(): print(f"Updated dropdown with: {self.midi_controller.available_ports}")
if isinstance(widget, ttk.Combobox):
widget['values'] = self.midi_controller.available_ports # Clear current selection if it's not in the new list
if (self.midi_controller.available_ports and current_selection = self.midi_port_var.get()
self.midi_port_var.get() not in self.midi_controller.available_ports): if current_selection not in self.midi_controller.available_ports:
self.midi_port_var.set(self.midi_controller.available_ports[0]) if self.midi_controller.available_ports:
self.midi_controller.midi_port_index = 0 self.midi_port_var.set(self.midi_controller.available_ports[0])
self.midi_controller.save_midi_preference() self.midi_controller.midi_port_index = 0
break self.midi_controller.save_midi_preference()
break 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): def on_midi_port_change(self, event):
"""Handle MIDI port selection change.""" """Handle MIDI port selection change."""
@@ -1032,7 +1070,7 @@ class UIClient:
def on_closing(self): def on_closing(self):
"""Handle application closing.""" """Handle application closing."""
logging.info("Closing UI client...") logging.info("Closing UI client...")
# Persist window geometry # Save window geometry
self.save_window_geometry() self.save_window_geometry()
if self.midi_controller.midi_task: if self.midi_controller.midi_task:
self.midi_controller.midi_task.cancel() self.midi_controller.midi_task.cancel()