Tkinter人事排班系统:只读模式Entry控件禁用与着色异常问题
Tkinter Entry控件着色后无法禁用的问题修复方案
问题背景
我开发了一款基于Tkinter的大型人事排班系统,系统会根据所选月份生成对应天数的Entry控件网格,管理人员可在控件中录入员工HR编码。根据用户权限等级,只读权限用户登录后仅能查看排班,需将Entry控件设置为禁用状态,但保留对应HR编码的着色效果。
问题现象
通过外部函数实现着色+禁用逻辑后,Entry控件无法被禁用,且无任何报错信息。
相关代码
ScheduleHrsFrame类的data_loaded方法
def data_loaded(self, data): """ Callback function to handle the loaded data. This method is called by the WorkbookDataLoader thread when the data is loaded. It updates the existing frames with the loaded data or creates new frames if needed. """ # Destroy existing frames before creating new ones self.destroy_frames() if self.schedule_type == "Overtime": frames = [] for i, (name, starting_asking_hours, starting_working_hours, hours_data) in enumerate(data): frame = HrsMatrixFrame(self.inner_frame, name, self.hdr_date_grid, self.ranking_frame, starting_asking_hours, starting_working_hours) frame.pack(pady=5, fill="x") frames.append(frame) for j, (working_hours, asking_hours) in enumerate(hours_data): working_hours_entry = frame.working_hours_entries[j] working_hours_entry.delete(0, tk.END) working_hours_entry.insert(0, working_hours) asking_hours_entry = frame.asking_hours_entries[j] asking_hours_entry.delete(0, tk.END) asking_hours_entry.insert(0, asking_hours) # Update column sums and labels for Overtime frames frame.update_column_sums(None) frame.labels[0].config(text=name) self.frames = frames # TODO: May need error handling here if self.access_level == "read-only": for frame in self.frames: lock_widgets(frame) else: frames = [] for i, (name, role_data) in enumerate(data): frame = WorkScheduleMatrixFrame(self.inner_frame, name, self.hdr_date_grid, self.ranking_frame) frame.pack(pady=5, fill="x") frames.append(frame) for j, role in enumerate(role_data): entry = frame.crew_member_role_entries[j] entry.delete(0, tk.END) entry.insert(0, role) apply_entry_color_specs(entry, role) self.work_schedule_frames = frames # TODO: Entry widgets should disabled but remain appropriately colored # depending on string value. A bug is preventing the widgets from being # disabled after the color function is executed. No errors generated. if self.access_level == "read-only": for frame in self.work_schedule_frames: lock_and_color_entry_widgets(frame) self.display_legend() self.update_scrollbar() self.get_labels() self.adjust_canvas_size()
辅助函数
def lock_and_color_entry_widgets(container): """ Lock (disable) all Entry widgets and apply formatting in the given container. Args: container (tk.BaseWidget): The container widget. """ for child in container.winfo_children(): if isinstance(child, (tk.Entry, ctk.CTkEntry)): entry_text = child.get() bg_color, fg_color = apply_entry_color_specs(child, entry_text) child.configure(disabledbackground=bg_color, disabledforeground=fg_color) child.configure(state=tk.DISABLED)
def assignment_code_formatting(frame, label, code_text, color_specs): """ Format the WSMatrix Legend labels with the appropriate formatting. Args: frame (widget): The passed frame. label (widget): The passed label. code_text (str): The text contained in the label. color_specs (dict): color specifications {code_text:{bg:color, text:color}} """ if code_text in color_specs: frame.configure(fg_color=color_specs[code_text]["frame"]) label.configure(fg_color=color_specs[code_text]["label_bg"], text_color=color_specs[code_text]["label_text"]) else: frame.configure(fg_color="blue") label.configure(fg_color="blue", text_color="white")
颜色常量定义
COLOR_SPECS = { "FC":{"frame":"#e26b0a", "label_bg":"#e26b0a", "label_text":"yellow"}, "HT":{"frame":"#c0504d", "label_bg":"#c0504d", "label_text":"white"}, "FTF":{"frame":"#ddd9c4", "label_bg":"#ddd9c4", "label_text":"black"}, "ST":{"frame":"#d9d9d9", "label_bg":"#d9d9d9", "label_text":"black"}, "BB":{"frame":"#0071c4", "label_bg":"#0071c4", "label_text":"white"}, "G":{"frame":"#c4d79b", "label_bg":"#c4d79b", "label_text":"black"}, "R":{"frame":"#ffff00", "label_bg":"#ffff00", "label_text":"black"}, "SC":{"frame":"#b1a0c7", "label_bg":"#b1a0c7", "label_text":"black"}, "SA":{"frame":"#c5d9eb", "label_bg":"#c5d9eb", "label_text":"black"}, "T":{"frame":"#00b050", "label_bg":"#00b050", "label_text":"white"}, "BH":{"frame":"#0097b9", "label_bg":"#0097b9", "label_text":"black"}, "V":{"frame":"#00b0f0", "label_bg":"#00b0f0", "label_text":"white"}, "PC":{"frame":"#00b0ef", "label_bg":"#00b0ef", "label_text":"black"}, "ERT":{"frame":"#ff0000", "label_bg":"#ff0000", "label_text":"yellow"}, "BT":{"frame":"#00ffff", "label_bg":"#00ffff", "label_text":"black"}, "M":{"frame":"#ffffff", "label_bg":"#ffffff", "label_text":"black"}, "JD":{"frame":"#d8e4bc", "label_bg":"#d8e4bc", "label_text":"black"}, "SK":{"frame":"#da9694", "label_bg":"#da9694", "label_text":"black"}, "SW":{"frame":"#4e3b63", "label_bg":"#4e3b63", "label_text":"white"}, "OC":{"frame":"#ffc000", "label_bg":"#ffc000", "label_text":"black"}, }
问题原因分析
- Tkinter与CustomTkentry参数不兼容:标准
tk.Entry用disabledbackground/disabledforeground配置禁用样式,但ctk.CTkEntry使用fg_color/text_color,原代码统一用Tkinter参数导致CTkEntry的禁用配置失效。 - 控件遍历不彻底:原函数只遍历容器的直接子控件,嵌套在子Frame中的Entry会被遗漏。
- 重复着色干扰:
data_loaded中先调用apply_entry_color_specs,后续lock_and_color_entry_widgets又重复调用,可能覆盖禁用状态配置。
修复方案
1. 修改lock_and_color_entry_widgets函数
兼容两种Entry类型,同时递归遍历所有子控件:
def lock_and_color_entry_widgets(container): """ Lock (disable) all Entry widgets and apply formatting in the given container. Args: container (tk.BaseWidget): The container widget. """ def traverse(widget): for child in widget.winfo_children(): if isinstance(child, (tk.Entry, ctk.CTkEntry)): entry_text = child.get().strip() # 获取对应颜色配置 if entry_text in COLOR_SPECS: bg_color = COLOR_SPECS[entry_text]["label_bg"] fg_color = COLOR_SPECS[entry_text]["label_text"] else: bg_color = "blue" fg_color = "white" # 针对不同Entry类型配置禁用样式 if isinstance(child, tk.Entry): child.configure( disabledbackground=bg_color, disabledforeground=fg_color, state=tk.DISABLED ) elif isinstance(child, ctk.CTkEntry): child.configure( state="disabled", fg_color=bg_color, text_color=fg_color ) # 递归处理子容器 traverse(child) traverse(container)
2. 调整data_loaded中的着色逻辑
移除提前调用的apply_entry_color_specs,统一交给lock_and_color_entry_widgets处理:
# 原代码中注释掉这一行 # apply_entry_color_specs(entry, role)
3. 优化apply_entry_color_specs函数(可选)
确保着色函数同时兼容正常和禁用状态:
def apply_entry_color_specs(entry, text): """ 为Entry应用颜色样式(兼容正常和禁用状态) """ if text in COLOR_SPECS: bg_color = COLOR_SPECS[text]["label_bg"] fg_color = COLOR_SPECS[text]["label_text"] else: bg_color = "blue" fg_color = "white" if isinstance(entry, tk.Entry): entry.configure(bg=bg_color, fg=fg_color) entry.configure(disabledbackground=bg_color, disabledforeground=fg_color) elif isinstance(entry, ctk.CTkEntry): entry.configure(fg_color=bg_color, text_color=fg_color) return bg_color, fg_color
内容的提问来源于stack exchange,提问作者Blue4Life90
相关产品推荐
相关产品推荐

