Files
ktouchV2/opt/ktouch/src/kdisplay.py
T
2026-06-12 16:54:32 +08:00

818 lines
32 KiB
Python

#!/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("<Configure>", 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('<<ComboboxSelected>>',
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('<<ComboboxSelected>>',
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('<<ComboboxSelected>>',
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('<Configure>', app.on_resize)
# sv_ttk.use_light_theme()
root.mainloop()
if __name__ == "__main__":
main()