Add screen resolution scaling and move tab buttons to bottom

This commit is contained in:
2025-11-30 16:44:14 +13:00
parent c5a76c24a7
commit fb4944e475
2 changed files with 135 additions and 122 deletions

View File

@@ -1,46 +1,4 @@
{
"tab_password": "qwerty1234",
"current_profile": "tt",
"patterns": {
"on": {
"min_delay": 10,
"max_delay": 10000
},
"off": {
"min_delay": 10,
"max_delay": 10000
},
"rainbow": {
"Step Rate": "n1",
"min_delay": 10,
"max_delay": 10000
},
"transition": {
"min_delay": 10,
"max_delay": 10000
},
"chase": {
"Colour 1 Length": "n1",
"Colour 2 Length": "n2",
"Step 1": "n3",
"Step 2": "n4",
"min_delay": 10,
"max_delay": 10000
},
"pulse": {
"Attack": "n1",
"Hold": "n2",
"Decay": "n3",
"min_delay": 10,
"max_delay": 10000
},
"circle": {
"Head Rate": "n1",
"Max Length": "n2",
"Tail Rate": "n3",
"Min Length": "n4",
"min_delay": 10,
"max_delay": 10000
}
}
"current_profile": "tt"
}

View File

@@ -55,6 +55,27 @@ class App:
self.root = tk.Tk()
self.root.attributes("-fullscreen", True)
self.root.configure(bg=bg_color)
# Calculate scale factor based on screen resolution
# Reference resolution: 1920x1080
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
ref_width = 1920
ref_height = 1080
# Use the smaller scale factor to ensure everything fits
self.scale_factor = min(screen_width / ref_width, screen_height / ref_height)
# Clamp scale factor between 0.5 and 2.0 for reasonable scaling
self.scale_factor = max(0.5, min(2.0, self.scale_factor))
# Helper methods for scaling
def scale_font(size):
return int(size * self.scale_factor)
def scale_size(size):
return int(size * self.scale_factor)
self.scale_font = scale_font
self.scale_size = scale_size
# Debouncing variables (remain the same)
self.last_rgb_update_time = 0
@@ -77,24 +98,24 @@ class App:
self.websocket_client = WebSocketClient("ws://192.168.4.1:80/ws")
self.root.after(100, async_handler(self.websocket_client.connect))
# Configure ttk style (unchanged)
# Configure ttk style (scaled)
style = ttk.Style()
style.theme_use("alt")
style.configure(".", background=bg_color, foreground=fg_color, font=("Arial", 14))
style.configure(".", background=bg_color, foreground=fg_color, font=("Arial", self.scale_font(14)))
style.configure("TNotebook", background=bg_color, borderwidth=0)
style.configure(
"TNotebook.Tab", background=bg_color, foreground=fg_color, font=("Arial", 30), padding=[10, 5]
"TNotebook.Tab", background=bg_color, foreground=fg_color, font=("Arial", self.scale_font(30)), padding=[self.scale_size(10), self.scale_size(5)]
)
style.map("TNotebook.Tab", background=[("selected", active_bg_color)], foreground=[("selected", fg_color)])
style.configure("TFrame", background=bg_color)
# Create Notebook for tabs (unchanged)
# Create Notebook for tabs (packed first so it takes remaining space)
self.notebook = ttk.Notebook(self.root)
self.notebook.pack(expand=1, fill="both")
# Tab management buttons frame
# Tab management buttons frame (packed at bottom so it stays visible)
tab_management_frame = tk.Frame(self.root, bg=bg_color)
tab_management_frame.pack(side=tk.TOP, fill=tk.X, padx=10, pady=5)
tab_management_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=self.scale_size(10), pady=self.scale_size(5))
add_tab_btn = tk.Button(
tab_management_frame,
@@ -102,11 +123,11 @@ class App:
command=self.add_tab_dialog,
bg=active_bg_color,
fg=fg_color,
font=("Arial", 14),
padx=10,
pady=5
font=("Arial", self.scale_font(14)),
padx=self.scale_size(10),
pady=self.scale_size(5)
)
add_tab_btn.pack(side=tk.LEFT, padx=5)
add_tab_btn.pack(side=tk.LEFT, padx=self.scale_size(5))
edit_tab_btn = tk.Button(
tab_management_frame,
@@ -114,11 +135,11 @@ class App:
command=self.edit_tab_dialog,
bg=active_bg_color,
fg=fg_color,
font=("Arial", 14),
padx=10,
pady=5
font=("Arial", self.scale_font(14)),
padx=self.scale_size(10),
pady=self.scale_size(5)
)
edit_tab_btn.pack(side=tk.LEFT, padx=5)
edit_tab_btn.pack(side=tk.LEFT, padx=self.scale_size(5))
delete_tab_btn = tk.Button(
tab_management_frame,
@@ -126,11 +147,11 @@ class App:
command=self.delete_tab_dialog,
bg=active_bg_color,
fg=fg_color,
font=("Arial", 14),
padx=10,
pady=5
font=("Arial", self.scale_font(14)),
padx=self.scale_size(10),
pady=self.scale_size(5)
)
delete_tab_btn.pack(side=tk.LEFT, padx=5)
delete_tab_btn.pack(side=tk.LEFT, padx=self.scale_size(5))
# Tab reorder buttons
move_left_btn = tk.Button(
@@ -139,11 +160,11 @@ class App:
command=self.move_tab_left,
bg=active_bg_color,
fg=fg_color,
font=("Arial", 14),
padx=10,
pady=5
font=("Arial", self.scale_font(14)),
padx=self.scale_size(10),
pady=self.scale_size(5)
)
move_left_btn.pack(side=tk.LEFT, padx=5)
move_left_btn.pack(side=tk.LEFT, padx=self.scale_size(5))
move_right_btn = tk.Button(
tab_management_frame,
@@ -151,24 +172,24 @@ class App:
command=self.move_tab_right,
bg=active_bg_color,
fg=fg_color,
font=("Arial", 14),
padx=10,
pady=5
font=("Arial", self.scale_font(14)),
padx=self.scale_size(10),
pady=self.scale_size(5)
)
move_right_btn.pack(side=tk.LEFT, padx=5)
move_right_btn.pack(side=tk.LEFT, padx=self.scale_size(5))
# Profile dropdown
tk.Label(tab_management_frame, text="Profile:", bg=bg_color, fg=fg_color, font=("Arial", 14)).pack(side=tk.LEFT, padx=(20, 5))
tk.Label(tab_management_frame, text="Profile:", bg=bg_color, fg=fg_color, font=("Arial", self.scale_font(14))).pack(side=tk.LEFT, padx=(self.scale_size(20), self.scale_size(5)))
self.profile_var = tk.StringVar()
self.profile_dropdown = ttk.Combobox(
tab_management_frame,
textvariable=self.profile_var,
font=("Arial", 14),
width=20,
font=("Arial", self.scale_font(14)),
width=self.scale_size(20),
state="readonly"
)
self.profile_dropdown.pack(side=tk.LEFT, padx=5)
self.profile_dropdown.pack(side=tk.LEFT, padx=self.scale_size(5))
self.profile_dropdown.bind("<<ComboboxSelected>>", self.on_profile_selected)
# New profile button
@@ -178,11 +199,11 @@ class App:
command=self.new_profile_dialog,
bg=active_bg_color,
fg=fg_color,
font=("Arial", 14),
padx=10,
pady=5
font=("Arial", self.scale_font(14)),
padx=self.scale_size(10),
pady=self.scale_size(5)
)
new_profile_btn.pack(side=tk.LEFT, padx=5)
new_profile_btn.pack(side=tk.LEFT, padx=self.scale_size(5))
# Save profile button
save_profile_btn = tk.Button(
@@ -191,11 +212,11 @@ class App:
command=self.save_profile_dialog,
bg=active_bg_color,
fg=fg_color,
font=("Arial", 14),
padx=10,
pady=5
font=("Arial", self.scale_font(14)),
padx=self.scale_size(10),
pady=self.scale_size(5)
)
save_profile_btn.pack(side=tk.LEFT, padx=5)
save_profile_btn.pack(side=tk.LEFT, padx=self.scale_size(5))
# Load profiles
self.profiles_dir = "profiles"
@@ -647,8 +668,8 @@ class App:
self.tabs[key] = tab
def create_light_control_widgets(self, tab, tab_name, ids, initial_settings):
slider_length = 600
slider_width = 50
slider_length = self.scale_size(600)
slider_width = self.scale_size(50)
# Get initial pattern and load pattern-specific settings
initial_pattern = initial_settings.get("pattern", "on")
@@ -666,9 +687,43 @@ class App:
initial_r, initial_g, initial_b = color_utils.hex_to_rgb(initial_hex_color)
# Create a scrollable frame for the tab content
canvas = tk.Canvas(tab, bg=bg_color, highlightthickness=0)
scrollbar = tk.Scrollbar(tab, orient="vertical", command=canvas.yview)
scrollable_frame = tk.Frame(canvas, bg=bg_color)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)
canvas_window = canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
# Make canvas window resize with canvas
def configure_scroll_region(event):
canvas.configure(scrollregion=canvas.bbox("all"))
# Update canvas window width to match canvas
canvas_width = event.width
canvas.itemconfig(canvas_window, width=canvas_width)
canvas.bind('<Configure>', configure_scroll_region)
canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
# Bind mousewheel to canvas (cross-platform)
def _on_mousewheel(event):
if event.num == 4 or event.delta > 0:
canvas.yview_scroll(-1, "units")
elif event.num == 5 or event.delta < 0:
canvas.yview_scroll(1, "units")
canvas.bind_all("<MouseWheel>", _on_mousewheel)
canvas.bind_all("<Button-4>", _on_mousewheel)
canvas.bind_all("<Button-5>", _on_mousewheel)
# Main frame to hold everything within the tab
main_tab_frame = tk.Frame(tab, bg=bg_color)
main_tab_frame.pack(expand=True, fill="both", padx=10, pady=10)
main_tab_frame = scrollable_frame
# Left panel container for sliders and n inputs
left_panel_container = tk.Frame(main_tab_frame, bg=bg_color)
@@ -750,8 +805,8 @@ class App:
delay_slider.set(initial_slider_pos)
# Create a custom label to show the actual delay value, positioned like the default Scale value
delay_value_label = tk.Label(delay_container, text=f"{initial_delay}", font=("Arial", 12), bg=bg_color, fg=fg_color, width=5, anchor="e")
delay_value_label.pack(side=tk.LEFT, padx=(0, 5))
delay_value_label = tk.Label(delay_container, text=f"{initial_delay}", font=("Arial", self.scale_font(12)), bg=bg_color, fg=fg_color, width=self.scale_size(5), anchor="e")
delay_value_label.pack(side=tk.LEFT, padx=(0, self.scale_size(5)))
# Store min/max delay in tab widget for later use
tab.min_delay = min_delay
@@ -784,10 +839,10 @@ class App:
"from_": 0,
"to": 255,
"increment": 1,
"width": 12,
"width": self.scale_size(12),
"bg": bg_color,
"fg": fg_color,
"font": ("Arial", 24),
"font": ("Arial", self.scale_font(24)),
"buttonbackground": active_bg_color,
}
@@ -798,8 +853,8 @@ class App:
n_frame.grid(row=(i-1)//2, column=(i-1)%2, padx=10, pady=10)
n_inputs[f"n{i}_frame"] = n_frame # Store frame reference for hiding/showing
n_label = tk.Label(n_frame, text=f"n{i}", font=("Arial", 20), bg=bg_color, fg=fg_color)
n_label.pack(pady=(0, 5))
n_label = tk.Label(n_frame, text=f"n{i}", font=("Arial", self.scale_font(20)), bg=bg_color, fg=fg_color)
n_label.pack(pady=(0, self.scale_size(5)))
n_inputs[f"n{i}_label"] = n_label # Store label reference
# Create a frame for the input with arrows on both sides
@@ -828,29 +883,29 @@ class App:
left_arrow = tk.Button(
input_container,
text="",
font=("Arial", 32, "bold"),
font=("Arial", self.scale_font(32), "bold"),
bg=active_bg_color,
fg=fg_color,
relief=tk.FLAT,
command=decrease_value,
width=3,
height=1,
width=self.scale_size(3),
height=self.scale_size(1),
)
left_arrow.pack(side=tk.LEFT, padx=2)
left_arrow.pack(side=tk.LEFT, padx=self.scale_size(2))
# Entry in the middle
n_entry = tk.Entry(
input_container,
textvariable=n_var,
font=("Arial", 24),
font=("Arial", self.scale_font(24)),
bg=bg_color,
fg=fg_color,
width=8,
width=self.scale_size(8),
justify=tk.CENTER,
relief=tk.SUNKEN,
bd=2,
)
n_entry.pack(side=tk.LEFT, padx=2, ipady=8)
n_entry.pack(side=tk.LEFT, padx=self.scale_size(2), ipady=self.scale_size(8))
n_entry.bind("<KeyRelease>", lambda event: self.schedule_update_n_params(tab))
n_entry.bind("<FocusOut>", lambda event: self.schedule_update_n_params(tab, force_send=True))
@@ -864,15 +919,15 @@ class App:
right_arrow = tk.Button(
input_container,
text="+",
font=("Arial", 32, "bold"),
font=("Arial", self.scale_font(32), "bold"),
bg=active_bg_color,
fg=fg_color,
relief=tk.FLAT,
command=increase_value,
width=3,
height=1,
width=self.scale_size(3),
height=self.scale_size(1),
)
right_arrow.pack(side=tk.LEFT, padx=2)
right_arrow.pack(side=tk.LEFT, padx=self.scale_size(2))
n_inputs[f"n{i}"] = n_entry
n_inputs[f"n{i}_var"] = n_var # Store the variable for later updates
@@ -913,15 +968,15 @@ class App:
# IDs section - MODIFIED TO BE SIDE-BY-SIDE
ids_frame = tk.Frame(right_panel_frame, bg=bg_color)
ids_frame.pack(pady=10, fill=tk.X)
tk.Label(ids_frame, text="Associated Names:", font=("Arial", 20), bg=bg_color, fg=fg_color).pack(pady=10)
tk.Label(ids_frame, text="Associated Names:", font=("Arial", self.scale_font(20)), bg=bg_color, fg=fg_color).pack(pady=self.scale_size(10))
# New inner frame for the IDs to be displayed horizontally
ids_inner_frame = tk.Frame(ids_frame, bg=bg_color)
ids_inner_frame.pack(fill=tk.X, expand=True) # Pack this frame to fill available width
for light_id in ids:
tk.Label(ids_inner_frame, text=str(light_id), font=("Arial", 18), bg=bg_color, fg=fg_color).pack(
side=tk.LEFT, padx=5, pady=2
tk.Label(ids_inner_frame, text=str(light_id), font=("Arial", self.scale_font(18)), bg=bg_color, fg=fg_color).pack(
side=tk.LEFT, padx=self.scale_size(5), pady=self.scale_size(2)
) # Pack labels horizontally
# --- New Frame to hold Patterns and Color Palette side-by-side ---
@@ -931,7 +986,7 @@ class App:
# Patterns section
patterns_frame = tk.Frame(patterns_and_palette_frame, bg=bg_color, bd=2, relief=tk.GROOVE)
patterns_frame.pack(side=tk.LEFT, padx=10, pady=5, fill=tk.BOTH, expand=True) # Pack to the left
tk.Label(patterns_frame, text="Patterns:", font=("Arial", 20), bg=bg_color, fg=fg_color).pack(pady=10)
tk.Label(patterns_frame, text="Patterns:", font=("Arial", self.scale_font(20)), bg=bg_color, fg=fg_color).pack(pady=self.scale_size(10))
tab.pattern_buttons = {}
patterns = list(self.patterns.keys())
@@ -942,9 +997,9 @@ class App:
command=lambda p=pattern_name: self.send_pattern(tab_name, p),
bg=active_bg_color,
fg=fg_color,
font=("Arial", 18),
padx=15,
pady=5,
font=("Arial", self.scale_font(18)),
padx=self.scale_size(15),
pady=self.scale_size(5),
relief=tk.FLAT,
)
button.pack(pady=5, fill=tk.X)
@@ -957,8 +1012,8 @@ class App:
color_palette_editor_frame.pack(side=tk.LEFT, padx=10, pady=5, fill=tk.BOTH, expand=True) # Pack to the left
tab.color_palette_editor_frame = color_palette_editor_frame # Store reference for update_ui_for_pattern
tk.Label(color_palette_editor_frame, text="Color Palette:", font=("Arial", 20), bg=bg_color, fg=fg_color).pack(
pady=10
tk.Label(color_palette_editor_frame, text="Color Palette:", font=("Arial", self.scale_font(20)), bg=bg_color, fg=fg_color).pack(
pady=self.scale_size(10)
)
# Frame to hold color swatches (will be dynamic)
@@ -975,9 +1030,9 @@ class App:
command=lambda t=tab: self.add_color_to_palette(t),
bg=active_bg_color,
fg=fg_color,
font=("Arial", 16),
padx=10,
pady=5,
font=("Arial", self.scale_font(16)),
padx=self.scale_size(10),
pady=self.scale_size(5),
relief=tk.FLAT,
)
add_color_button.pack(side=tk.LEFT, expand=True, padx=5)
@@ -988,9 +1043,9 @@ class App:
command=lambda t=tab: self.remove_selected_color_from_palette(t),
bg=active_bg_color,
fg=fg_color,
font=("Arial", 16),
padx=10,
pady=5,
font=("Arial", self.scale_font(16)),
padx=self.scale_size(10),
pady=self.scale_size(5),
relief=tk.FLAT,
)
remove_color_button.pack(side=tk.RIGHT, expand=True, padx=5)
@@ -1011,9 +1066,9 @@ class App:
for i, hex_color in enumerate(tab.colors_in_palette):
swatch_frame = tk.Frame(
tab.color_swatches_container, bg=hex_color, width=100, height=50, bd=2, relief=tk.SOLID
tab.color_swatches_container, bg=hex_color, width=self.scale_size(100), height=self.scale_size(50), bd=2, relief=tk.SOLID
)
swatch_frame.pack(pady=3, padx=5, fill=tk.X)
swatch_frame.pack(pady=self.scale_size(3), padx=self.scale_size(5), fill=tk.X)
# Bind click to select this color for editing
swatch_frame.bind("<Button-1>", lambda event, idx=i, t=tab: self.select_color_in_palette(t, idx))
@@ -1023,9 +1078,9 @@ class App:
text=f"Color {i+1}",
bg=hex_color,
fg=color_utils.get_contrast_text_color(hex_color),
font=("Arial", 14),
width=5,
height=3,
font=("Arial", self.scale_font(14)),
width=self.scale_size(5),
height=self.scale_size(3),
)
swatch_label.pack(expand=True, fill=tk.BOTH)
swatch_label.bind("<Button-1>", lambda event, idx=i, t=tab: self.select_color_in_palette(t, idx))