Tkinter生成禁用隐藏文本框时UI元素抖动问题求助
问题分析与解决方案
抖动原因
UI抖动的核心是组件先渲染文本再立刻被清空导致的重复重绘:
- 在
display_words函数中,创建word_widget和diceroll_widget时,会通过insert_text插入文本并禁用组件; - 若当前处于隐藏状态,会立刻调用
hide_passwords遍历所有组件,把刚插入的文本删掉; - 这个"插入→删除"的过程会让组件尺寸瞬间变化,触发两次UI重绘,视觉上就表现为抖动。
另外hide_passwords里还有逻辑错误:它会重新从组件获取文本覆盖widget_text_dict的内容,当组件被清空后,后续显示时可能拿不到正确的原始文本。
修复方案
方案1:创建组件时直接适配隐藏状态(推荐)
修改display_words的逻辑,创建组件时根据当前password_state决定是否显示文本,避免先插再删的无效操作:
def display_words(pair: dict) -> None: text_height = 1 text_padx = 10 if self.number_of_dicerolls < 35: column_to_be_placed_in = (self.number_of_dicerolls % 5) * 2 self.number_of_dicerolls += 1 (diceroll, word), = pair.items() # 处理骰子数文本框 self.diceroll_widget = customtkinter.CTkTextbox(self) self.diceroll_widget.grid(row=2 + 2 * ((self.number_of_dicerolls - 1) // 5), column=column_to_be_placed_in, pady=(5, 0)) # 根据状态决定是否插入文本 if self.password_state == 'shown': insert_text(self.diceroll_widget, str(diceroll)) else: self.diceroll_widget.configure(state='disabled') self.widget_text_dict[self.diceroll_widget] = str(diceroll) self.output_widgets.append(self.diceroll_widget) self.text_widgets.append(self.diceroll_widget) # 处理单词文本框 self.word_widget = customtkinter.CTkTextbox(self, font=self.word_font, height=text_height) self.word_widget.grid(row=3 + 2 * ((self.number_of_dicerolls - 1) // 5), column=column_to_be_placed_in, pady=(0, 5)) if self.password_state == 'shown': insert_text(self.word_widget, word) else: self.word_widget.configure(state='disabled') self.widget_text_dict[self.word_widget] = word self.output_widgets.append(self.word_widget) self.text_widgets.append(self.word_widget) self.checkbox = customtkinter.CTkCheckBox(master=self, text='', checkbox_width=20, width=0, checkbox_height=20, fg_color='gray', hover_color=('grey', 'white')) self.checkbox.grid(row=3 + 2 * ((self.number_of_dicerolls - 1) // 5), column=column_to_be_placed_in + 1, sticky='w') self.output_widgets.append(self.checkbox) # 直接用原始word作为key,避免换行符问题 self.checkboxes_text[word] = self.checkbox else: answer = tk.messagebox.askquestion('Dice roll limit reached', 'You have reached the maximum limit of 35 dice rolls. Do you want ' 'to clear the screen?', parent=self) if answer == 'yes': clear_window()
同时修正hide_passwords里的错误逻辑,不要再从组件获取文本:
def hide_passwords() -> None: """ Called when the user clicks the hide button, this function hides all passwords. """ for widget, text in self.widget_text_dict.items(): widget.configure(state='normal') widget.delete('0.0', 'end') widget.configure(state='disabled') self.password_state = 'hidden' self.hide_show_button.configure(text='PASSWORDS ARE: HIDDEN', command=show_passwords)
方案2:优化hide_passwords,只处理新增组件
如果不想大改创建逻辑,可以在display_words里记录新增的组件,调用hide_passwords时只处理这些新增组件,避免遍历所有组件:
def display_words(pair: dict) -> None: # ... 原有代码 ... new_widgets = [] # 创建diceroll_widget后添加到new_widgets new_widgets.append(self.diceroll_widget) # 创建word_widget后添加到new_widgets new_widgets.append(self.word_widget) # ... 其他代码 ... if self.password_state == 'hidden': # 只清空新增的组件 for widget in new_widgets: widget.configure(state='normal') widget.delete('0.0', 'end') widget.configure(state='disabled')
额外优化点
checkboxes_text里存储的key用原始word而非word_widget.get('1.0', 'end'),避免带上换行符\n,后续复制时无需额外替换;- 确认
insert_text里的show_copy_menu函数不会触发额外的UI变化。
内容的提问来源于stack exchange,提问作者IceTheDev2
相关产品推荐
相关产品推荐

