#!/usr/bin/env python3 import tkinter as tk from tkinter import ttk, messagebox, simpledialog import logging import time import os from display_control import DisplayControl import sys import sv_ttk def get_real_path(relative_path): """获取资源的正确绝对路径""" try: # 打包后的情况 base_path = sys._MEIPASS except AttributeError: # 开发环境的情况 base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) def get_display_info(): # 默认临时文件路径 temp_file = "/tmp/display_selection.txt" # 检查环境变量中是否有自定义路径 if 'DISPLAY_SELECTION_FILE' in os.environ: temp_file = os.environ['DISPLAY_SELECTION_FILE'] display_info = {} if os.path.exists(temp_file): with open(temp_file, 'r') as f: for line in f: if '=' in line: key, value = line.strip().split('=', 1) display_info[key] = value # 转换为整数 for key in ['MONITOR', 'X', 'Y', 'WIDTH', 'HEIGHT']: if key in display_info: try: display_info[key] = int(display_info[key]) except ValueError: # 如果转换失败,使用默认值 if key == 'MONITOR': display_info[key] = 0 elif key in ['X', 'Y']: display_info[key] = 0 elif key == 'WIDTH': display_info[key] = 1920 elif key == 'HEIGHT': display_info[key] = 1080 return display_info class DisplaySettingsGUI: def __init__(self, root): self.root = root self.root.title("显示器设置工具(for UOS)") # """根据/tmp/display_selection.txt设置窗口位置""" display_info = get_display_info() target_x = 100 target_x = 100 window_width = 950 # 增加窗口宽度 window_height = 600 # 增加窗口高度 try: # 读取配置 x = display_info.get('X', 0) y = display_info.get('Y', 0) width = display_info.get('WIDTH', 1280) height = display_info.get('HEIGHT', 720) target_x = int( x + (width - window_width) // 2) target_y = int(y + (height - window_height) // 2) except: pass self.root.geometry(f"{window_width}x{window_height}+{target_x}+{target_y}") self.root.resizable(True, True) # 显示器控制对象 self.display_control = DisplayControl() # 显示器信息 self.monitors = [] self.monitor_vars = [] self.resolution_vars = [] self.rotation_vars = [] self.primary_vars = [] # 旋转方向映射 self.rotation_names = { 1: "正常", 2: "向左90度", 4: "翻转", 8: "向右90度" } # 创建界面 self.create_widgets() # 获取显示器信息 self.refresh_monitor_info() def create_widgets(self): # 主框架 main_frame = ttk.Frame(self.root, padding="10") # 增加内边距 main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 配置网格权重 self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=1) main_frame.columnconfigure(1, weight=1) main_frame.rowconfigure(1, weight=1) # 标题 # title_label = ttk.Label(main_frame, text="显示器设置工具(for UOS)", font=("Arial", 12, "bold")) # title_label.grid(row=0, column=0, columnspan=2, pady=(0, 15)) # 增加下边距 # 左侧设置面板 settings_frame = ttk.LabelFrame(main_frame, text="显示器设置", padding="10") # 增加内边距 settings_frame.grid(row=1, column=0,columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 15)) # 增加右边距 # 右侧示意图面板 diagram_frame = ttk.LabelFrame(main_frame, text="显示器排列示意图", padding="10") # 增加内边距 diagram_frame.grid(row=1, column=2, sticky=(tk.W, tk.E, tk.N, tk.S)) # 配置网格权重 main_frame.rowconfigure(1, weight=1) main_frame.columnconfigure(1, weight=1) settings_frame.columnconfigure(0, weight=2) settings_frame.rowconfigure(0, weight=1) diagram_frame.columnconfigure(0, weight=1) diagram_frame.rowconfigure(0, weight=1) # 画布用于显示示意图 self.canvas = tk.Canvas(diagram_frame, bg="#f0f0f0", relief=tk.SUNKEN, bd=1) self.canvas.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 创建设置区域的滚动框架 self.scrollable_frame = ttk.Frame(settings_frame) self.scrollable_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 创建滚动条 scrollbar = ttk.Scrollbar(settings_frame, orient="vertical") scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S)) # 创建画布用于滚动 self.settings_canvas = tk.Canvas(self.scrollable_frame, yscrollcommand=scrollbar.set) self.settings_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 配置滚动条 scrollbar.config(command=self.settings_canvas.yview) # 创建设置区域的内部框架 self.settings_inner_frame = ttk.Frame(self.settings_canvas, padding="1") # 增加内边距 self.settings_canvas.create_window((0, 0), window=self.settings_inner_frame, anchor="nw") # 绑定配置事件以更新滚动区域 self.settings_inner_frame.bind("", self.on_settings_frame_configure) # 按钮框架 button_frame = ttk.Frame(main_frame) button_frame.grid(row=2, column=0, columnspan=2, pady=(15, 0)) # 增加上边距 # 刷新按钮 refresh_btn = ttk.Button(button_frame, text="刷新显示器信息", command=self.refresh_monitor_info) refresh_btn.pack(side=tk.LEFT, padx=(0, 10)) # 应用并保存按钮 apply_save_btn = ttk.Button(button_frame, text="应用并保存设置", command=self.apply_and_save_settings) apply_save_btn.pack(side=tk.LEFT) def on_settings_frame_configure(self, event): """更新滚动区域""" self.settings_canvas.configure(scrollregion=self.settings_canvas.bbox("all")) def refresh_monitor_info(self): """获取显示器信息并更新界面""" try: self.monitors = self.display_control.get_monitors() self.update_settings_ui() self.update_diagram() except Exception as e: messagebox.showerror("错误", f"获取显示器信息时出错: {str(e)}") def update_settings_ui(self): """更新设置界面""" # 清除现有控件 for widget in self.settings_inner_frame.winfo_children(): widget.destroy() self.monitor_vars = [] self.resolution_vars = [] self.rotation_vars = [] self.primary_vars = [] # 获取所有可用的位置选项 position_options = [str(i+1) for i in range(len(self.monitors))] # 为每个显示器创建设置控件 for i, monitor in enumerate(self.monitors): monitor_frame = ttk.LabelFrame(self.settings_inner_frame, text=monitor['name']) monitor_frame.grid(row=i, column=0, sticky=(tk.W, tk.E), padx=(0,10),pady=(0, 15), ipadx=5, ipady=3) # 增加下边距 monitor_frame.columnconfigure(1, weight=1) # 分辨率设置(放在第一行) ttk.Label(monitor_frame, text="分辨率:").grid(row=0, column=0, sticky=tk.W, padx=(0, 20)) # 增加右边距 res_var = tk.StringVar() resolutions = [mode['label'] for mode in monitor['modes']] # print(resolutions) # 设置分辨率 current_res_index = 0 if monitor["current_mode"][0] == 0: # 自定义分辨率 resolutions = [f"自定义:{monitor['current_mode'][1]}x{monitor['current_mode'][2]}@{monitor['current_mode'][3]}Hz"] + resolutions else: for idx, mode in enumerate(monitor['modes']): if mode['id'] == monitor['current_mode'][0]: current_res_index = idx break res_combo = ttk.Combobox(monitor_frame, textvariable=res_var, values=resolutions, state="readonly", width=25,height=20) # 增加宽度 res_combo.current(current_res_index) res_combo.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5, padx=5) # 绑定分辨率变化事件 res_combo.bind('<>', lambda e, m=monitor, v=res_var: self.on_resolution_change(m, v)) self.resolution_vars.append(res_var) # 第二行:位置、旋转和主显示器设置 options_frame = ttk.Frame(monitor_frame) options_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5) # 位置设置 ttk.Label(options_frame, text="位置:").pack(side=tk.LEFT, padx=(0, 5)) pos_var = tk.StringVar(value=str(monitor['position'])) pos_combo = ttk.Combobox(options_frame, textvariable=pos_var, values=position_options, state="readonly", width=5) pos_combo.pack(side=tk.LEFT, padx=(0, 10)) pos_combo.bind('<>', lambda e, m=monitor, v=pos_var: self.update_monitor_position(m, v)) self.monitor_vars.append(pos_var) # 分隔线 ttk.Separator(options_frame, orient='vertical').pack(side=tk.LEFT, padx=10, fill=tk.Y) # 旋转设置 ttk.Label(options_frame, text="旋转:").pack(side=tk.LEFT, padx=(0, 5)) rotation_var = tk.StringVar(value=str(monitor['rotation'])) rotation_combo = ttk.Combobox(options_frame, textvariable=rotation_var, values=["正常", "向左90度", "翻转", "向右90度"], state="readonly", width=10) rotation_combo.pack(side=tk.LEFT, padx=(0, 10)) # 设置显示文本为旋转方向名称 rotation_name = self.rotation_names.get(monitor['rotation'], "正常") rotation_combo.set(rotation_name) # 绑定旋转变化事件 rotation_combo.bind('<>', lambda e, m=monitor, v=rotation_var: self.on_rotation_change(m, v)) self.rotation_vars.append(rotation_var) # 分隔线 ttk.Separator(options_frame, orient='vertical').pack(side=tk.LEFT, padx=10, fill=tk.Y) # 主屏设置 primary_var = tk.BooleanVar(value=monitor['is_primary']) primary_check = ttk.Checkbutton(options_frame, text="主显示器", variable=primary_var, command=lambda v=primary_var, m=monitor: self.update_primary_monitor(v, m)) primary_check.pack(side=tk.LEFT) self.primary_vars.append(primary_var) def on_resolution_change(self, monitor, var): """当分辨率改变时更新显示器信息""" selected_res = var.get() if selected_res == "自定义分辨率": self.prompt_custom_resolution(monitor, var) else: # 解析分辨率并更新显示器信息 if "自定义" in selected_res: selected_res = selected_res[4:] parts = selected_res.split(' @ ') resolution = parts[0].split('x') width = int(resolution[0]) height = int(resolution[1]) monitor['width'] = width monitor['height'] = height # 更新所有显示器的坐标 self.update_all_monitor_positions() # 更新示意图 self.update_diagram() def update_all_monitor_positions(self): """更新所有显示器的坐标""" # 按位置分组显示器 position_groups = {} for monitor in self.monitors: pos = monitor['position'] if pos not in position_groups: position_groups[pos] = [] position_groups[pos].append(monitor) # 计算每个位置组的X坐标 x_positions = {} x_offset = 0 for pos in sorted(position_groups.keys()): # 使用组中第一个显示器的宽度 monitor = position_groups[pos][0] x_positions[pos] = x_offset x_offset += monitor['width'] # 更新所有显示器的坐标 for monitor in self.monitors: position = monitor['position'] monitor['x'] = x_positions.get(position, 0) monitor['y'] = 0 # 假设所有显示器在垂直方向对齐 def prompt_custom_resolution(self, monitor, res_var): """提示用户输入自定义分辨率""" dialog = CustomResolutionDialog(self.root, monitor) result = dialog.result if result: width, height, refresh_rate, force_custom = result monitor['custom_width'] = width monitor['custom_height'] = height monitor['custom_refresh_rate'] = refresh_rate monitor['force_custom'] = force_custom # 更新显示器信息 monitor['width'] = width monitor['height'] = height # 更新所有显示器的坐标 self.update_all_monitor_positions() # 更新下拉框显示 res_var.set(f"自定义:{width}x{height}@{refresh_rate}Hz") # 更新示意图 self.update_diagram() def on_rotation_change(self, monitor, var): """当旋转方向改变时更新显示器信息""" rotation_str = var.get() rotation_map = { "正常": 1, "向左90度": 2, "翻转": 4, "向右90度": 8 } rotation = rotation_map.get(rotation_str, 1) old_rotation = monitor["rotation"] if old_rotation in [1, 4]: if rotation in [2,8]: buff = monitor['width'] monitor['width'] = monitor['height'] monitor['height'] = buff else: if rotation in [1,4]: buff = monitor['width'] monitor['width'] = monitor['height'] monitor['height'] = buff monitor['rotation'] = rotation self.update_all_monitor_positions() # 更新示意图 self.update_diagram() def update_monitor_position(self, monitor, var): """更新显示器位置""" try: position = int(var.get()) if 1 <= position <= len(self.monitors): old_position = monitor.get('position', 1) monitor['position'] = position # 更新所有显示器的坐标 self.update_all_monitor_positions() self.update_diagram() except ValueError: pass def update_primary_monitor(self, var, monitor): """更新主显示器设置""" if var.get(): # 取消其他显示器的选中状态 for i, primary_var in enumerate(self.primary_vars): if primary_var != var: primary_var.set(False) for m in self.monitors: m["is_primary"] = False # 设置主显示器 monitor['is_primary'] = True # 更新示意图 self.update_diagram() else: # 不允许取消主显示器,至少需要有一个主显示器 var.set(True) # messagebox.showwarning("警告", "至少需要有一个主显示器") def update_diagram(self): """更新显示器排列示意图,根据实际坐标进行渲染""" if not self.monitors: return self.canvas.delete("all") # 计算所有显示器的边界 min_x = min(monitor.get('x', 0) for monitor in self.monitors) max_x = max(monitor.get('x', 0) + monitor['width'] for monitor in self.monitors) min_y = min(monitor.get('y', 0) for monitor in self.monitors) max_y = max(monitor.get('y', 0) + monitor['height'] for monitor in self.monitors) # 计算画布大小和缩放比例 canvas_width = self.canvas.winfo_width() canvas_height = self.canvas.winfo_height() if canvas_width <= 1 or canvas_height <= 1: # 画布尚未渲染,使用默认大小 canvas_width = 400 canvas_height = 300 # 计算总宽度和高度 total_width = max_x - min_x total_height = max_y - min_y # 计算缩放比例,保留边距 scale_x = 0.8 * canvas_width / total_width if total_width > 0 else 1 scale_y = 0.8 * canvas_height / total_height if total_height > 0 else 1 scale = min(scale_x, scale_y) # 计算偏移量,使所有显示器居中显示 offset_x = (canvas_width - total_width * scale) / 2 - min_x * scale offset_y = (canvas_height - total_height * scale) / 2 - min_y * scale # 按坐标和分辨率分组显示器 coord_groups = {} for monitor in self.monitors: x = monitor.get('x', 0) y = monitor.get('y', 0) width = monitor['width'] height = monitor['height'] # 创建分组键 group_key = (x, y, width, height) if group_key not in coord_groups: coord_groups[group_key] = [] coord_groups[group_key].append(monitor) # 绘制每个组的显示器 for group_key, monitors in coord_groups.items(): x, y, width, height = group_key # 计算在画布上的位置 canvas_x = offset_x + x * scale canvas_y = offset_y + y * scale canvas_width_scaled = width * scale canvas_height_scaled = height * scale # 绘制组的背景矩形(仅在多个显示器共享相同坐标和分辨率时) if len(monitors) > 1: self.canvas.create_rectangle( canvas_x, canvas_y, canvas_x + canvas_width_scaled, canvas_y + canvas_height_scaled, fill="#e0e0e0", outline="#a0a0a0", width=1, dash=(5, 5) ) # 收集所有显示器的名称和旋转信息 all_names = [] for monitor in monitors: rotation_text = self.rotation_names.get(monitor['rotation'], "未知") name_text = f"{monitor['name']} " if monitor['is_primary']: name_text += " [主]" all_names.append(name_text) # 在组上方显示所有显示器名称 if all_names: name_text = "\n".join(all_names) self.canvas.create_text( canvas_x + canvas_width_scaled / 2, canvas_y - 20, text=name_text, font=("Arial", 9, "bold"), justify=tk.CENTER, fill="blue" ) # 绘制每个显示器(按面积从大到小排序,小的覆盖大的) sorted_monitors = sorted(monitors, key=lambda m: m['width'] * m['height'], reverse=True) # 计算重叠显示器的偏移量 offset_step = min(10 * scale, 10) # 最大偏移10像素 max_offset = offset_step * (len(sorted_monitors) - 1) for i, monitor in enumerate(sorted_monitors): # 计算显示器的偏移位置 monitor_x = canvas_x + min(i * offset_step, max_offset) monitor_y = canvas_y + min(i * offset_step, max_offset) # 绘制矩形 fill_color = "lightblue" if monitor['is_primary'] else "white" rect_id = self.canvas.create_rectangle( monitor_x, monitor_y, monitor_x + canvas_width_scaled, monitor_y + canvas_height_scaled, fill=fill_color, outline="black", width=2 ) # 添加显示器分辨率和旋转信息 rotation_text = self.rotation_names.get(monitor['rotation'], "未知") text_lines = [ f"{monitor['width']}x{monitor['height']}", f"旋转: {rotation_text}" ] text_content = "\n".join(text_lines) text_id = self.canvas.create_text( monitor_x + canvas_width_scaled/2, monitor_y + canvas_height_scaled/2, text=text_content, font=("Arial", 8), justify=tk.CENTER ) # 添加坐标标签 coord_text = f"({x}, {y})" self.canvas.create_text( canvas_x + canvas_width_scaled / 2, canvas_y + canvas_height_scaled + 15, text=coord_text, font=("Arial", 8), fill="green" ) def apply_settings(self): """应用显示器设置""" try: # 检查位置冲突 position_groups = {} for monitor in self.monitors: pos = monitor['position'] if pos not in position_groups: position_groups[pos] = [] position_groups[pos].append(monitor) # 检查是否有重叠的显示器 has_overlap = any(len(monitors) > 1 for monitors in position_groups.values()) if has_overlap: overlap_info = [] for pos, monitors in position_groups.items(): if len(monitors) > 1: monitor_names = ", ".join([m['name'] for m in monitors]) overlap_info.append(f"位置 {pos}: {monitor_names}") warning_msg = "以下位置的显示器将重叠显示:\n" + "\n".join(overlap_info) if not messagebox.askyesno("位置重叠警告", f"{warning_msg}\n\n是否继续?"): return # 计算每个位置组的X坐标 x_positions = {} x_offset = 0 for pos in sorted(position_groups.keys()): # 使用组中第一个显示器的宽度 monitor = position_groups[pos][0] x_positions[pos] = x_offset x_offset += monitor['width'] # 在设置主显示器之前调用SwitchMode self.display_control.switch_mode() # 为每个显示器应用设置 for i, monitor in enumerate(self.monitors): # 设置分辨率 selected_res = self.resolution_vars[i].get() # print(selected_res) if selected_res: if "自定义" in selected_res: config="" # 使用自定义分辨率 # 设置旋转 rotation_str = self.rotation_vars[i].get() rotation_map = { "正常": 1, "向左90度": 2, "翻转": 4, "向右90度": 8 } rotation = rotation_map.get(rotation_str, 1) # 设置位置 position = monitor['position'] x = x_positions.get(position, 0) y = 0 # 设置主显示器 isPrimary="N" if self.primary_vars[i].get(): isPrimary="P" # 添加强制标志 force_flag = "F" if monitor.get('force_custom', False) else "A" config=f'{selected_res[4:]}|{x},{y}|{rotation}|{isPrimary}|{force_flag}' # print(config) self.display_control.save_custom_resolution(monitor['name'], config) continue else: self.display_control.remove_custom_resolution(monitor['name']) # 解析分辨率和刷新率 parts = selected_res.split(' @ ') resolution = parts[0].split('x') width = int(resolution[0]) height = int(resolution[1]) refresh_rate = float(parts[1].replace('Hz', '')) # 优先使用Mode接口 mode_id = None for mode in monitor['modes']: if mode['label'] == selected_res: mode_id = mode['id'] break if mode_id and mode_id != 'custom': self.display_control.set_mode(monitor['path'], mode_id) else: # 备用方案:使用SetModeBySize和SetRefreshRate接口 self.display_control.set_mode_by_size(monitor['path'], width, height) self.display_control.set_refresh_rate(monitor['path'], refresh_rate) # 设置旋转 rotation_str = self.rotation_vars[i].get() rotation_map = { "正常": 1, "向左90度": 2, "翻转": 4, "向右90度": 8 } rotation = rotation_map.get(rotation_str, 1) self.display_control.set_rotation(monitor['path'], rotation) # 设置位置 position = monitor['position'] x = x_positions.get(position, 0) y = 0 # 假设所有显示器在垂直方向对齐 self.display_control.set_position(monitor['path'], x, y) # 设置主显示器 if self.primary_vars[i].get(): self.display_control.set_primary(monitor['name']) # 应用更改 self.display_control.apply_changes() # 等待1秒后刷新显示器信息 self.root.after(1000, self.refresh_monitor_info) # messagebox.showinfo("成功", "显示器设置已应用") except Exception as e: messagebox.showerror("错误", f"应用设置时出错: {str(e)}") def apply_and_save_settings(self): """应用并保存设置""" try: # 先应用设置 self.apply_settings() # 3秒后保存设置 self.root.after(3000, self.save_settings_with_delay) except Exception as e: messagebox.showerror("错误", f"应用设置时出错: {str(e)}") def save_settings_with_delay(self): """延迟保存设置""" try: file_path = "/tmp/ktouch/display_update.txt" dir_path = "/tmp/ktouch" # 检查目录是否存在 if os.path.exists(dir_path): # 写入文件(不存在则创建) with open(file_path, 'w') as f: f.write("1") self.display_control.save_settings() # messagebox.showinfo("成功", "显示器设置已保存") except Exception as e: messagebox.showerror("错误", f"保存设置时出错: {str(e)}") def on_resize(self, event): """处理窗口大小变化事件""" if event.widget == self.root: self.update_diagram() class CustomResolutionDialog: """自定义分辨率对话框""" def __init__(self, parent, monitor): self.parent = parent self.monitor = monitor self.result = None self.dialog = tk.Toplevel(parent) self.dialog.title("自定义分辨率") self.dialog.geometry("350x250") # 增加高度以容纳新控件 self.dialog.transient(parent) self.dialog.grab_set() # 加载之前保存的自定义分辨率 custom_width = monitor.get('custom_width', 1920) custom_height = monitor.get('custom_height', 1080) custom_refresh = monitor.get('custom_refresh_rate', 60.0) force_custom = monitor.get('force_custom', False) # 宽度设置 ttk.Label(self.dialog, text="宽度:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W) self.width_var = tk.StringVar(value=str(custom_width)) ttk.Entry(self.dialog, textvariable=self.width_var).grid(row=0, column=1, padx=5, pady=5, sticky=(tk.W, tk.E)) # 高度设置 ttk.Label(self.dialog, text="高度:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W) self.height_var = tk.StringVar(value=str(custom_height)) ttk.Entry(self.dialog, textvariable=self.height_var).grid(row=1, column=1, padx=5, pady=5, sticky=(tk.W, tk.E)) # 刷新率设置 ttk.Label(self.dialog, text="刷新率 (Hz):").grid(row=2, column=0, padx=5, pady=5, sticky=tk.W) self.refresh_var = tk.StringVar(value=str(custom_refresh)) ttk.Entry(self.dialog, textvariable=self.refresh_var).grid(row=2, column=1, padx=5, pady=5, sticky=(tk.W, tk.E)) # 强制使用自定义分辨率复选框 self.force_var = tk.BooleanVar(value=force_custom) force_check = ttk.Checkbutton( self.dialog, text="强制使用自定义分辨率而不是选取驱动分辨率", variable=self.force_var ) force_check.grid(row=3, column=0, columnspan=2, padx=5, pady=10, sticky=tk.W) # 按钮框架 button_frame = ttk.Frame(self.dialog) button_frame.grid(row=4, column=0, columnspan=2, pady=10) ttk.Button(button_frame, text="确定", command=self.on_ok).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="取消", command=self.on_cancel).pack(side=tk.LEFT, padx=5) # 配置网格权重 self.dialog.columnconfigure(1, weight=1) self.dialog.wait_window() def on_ok(self): """确定按钮处理""" try: width = int(self.width_var.get()) height = int(self.height_var.get()) refresh_rate = float(self.refresh_var.get()) force_custom = self.force_var.get() if width <= 0 or height <= 0 or refresh_rate <= 0: raise ValueError("值必须为正数") self.result = (width, height, refresh_rate, force_custom) self.dialog.destroy() except ValueError as e: messagebox.showerror("错误", f"请输入有效的数值: {str(e)}") def on_cancel(self): """取消按钮处理""" self.dialog.destroy() def main(): root = tk.Tk() # Import the tcl file root.tk.call('source', '/opt/ktouch/forest-light.tcl') # Set the theme with the theme_use method ttk.Style().theme_use('forest-light') app = DisplaySettingsGUI(root) root.bind('', app.on_resize) # sv_ttk.use_light_theme() root.mainloop() if __name__ == "__main__": main()