问题描述
如果我单击 Tkinter 窗口并四处移动(即使由于单击鼠标而仅移动 1px),则窗口在创建包时无法正确调整大小。它还会将窗口跳转回拖动开始时的位置,而不是创建事件发生时的位置。
例如,下面是一个正常运行行为的示例,我使用 after
函数将框架的内容从按住绿色按钮更改为按住灰色按钮:
但是,如果我按住鼠标并四处移动窗口,则会发生以下情况:
我不知道你能不能说出来,但窗口未能调整其大小,导致底部的“停止实验”按钮被切断,“点击我”按钮比应有的小。
如何确保窗口正确调整大小,即使当时用户正在拖动窗口?我试过 root.update()
但没有用。
这是我的代码的稍微精简版:
import tkinter as tk
class MyGui:
def __init__(self,font=None):
self.root = tk.Tk()
self.font = "Arial 12"
self.font_bold = self.font + " bold"
def makeLabel(self,text,parent = None):
label = tk.Label(parent,text=text,font=self.font)
label.pack(fill=tk.X)
return label
def makeButton(self,parent,command = None,**style_arguments):
button = tk.Button(parent,command=command,**style_arguments)
button.pack(fill=tk.X)
return button
def makeFrame(self,**style_arguments):
frame = tk.Frame(parent,**style_arguments)
frame.pack(fill="both")
return frame
def start(self):
self.root.mainloop()
def stop(self):
self.root.quit()
self.root.destroy()
和
def main():
gui = MyGui()
gui.makeLabel("Experiment is running",parent = gui.root)
frame_container = gui.makeFrame(parent = gui.root)
gui.makeButton('TIME LEFT: 0',fg = "white",bg = "green",font = gui.font_bold,parent = frame_container)
frame_container.after(4000,lambda: switchIt(gui,frame_container))
gui.makeButton('STOP EXPERIMENT',bg = "red",parent = gui.root,command = gui.stop)
gui.start()
def switchIt(gui,frame_container):
# Remove placeholder widgets (if any)
for child in frame_container.winfo_children():
child.destroy()
gui.makeButton(
"CLICK ME",parent = frame_container,height=5,width = 40)
gui.root.update()
main()
其中 main()
是被执行的内容。
(注意:为了最低限度的可重复性,我取出了对文本时间进行倒计时的代码,因此它只会显示 0,即使 after
函数仍然适用并且会在 appx 4s 内切换屏幕)。
解决方法
防止窗口在小部件创建或销毁时拒绝调整大小的一种解决方案是暂时禁用标题栏,然后在销毁/创建事件后重新启用它。
例如,更改 switchIt()
函数如下:
def switchIt(gui,frame_container):
self.gui.root.overrideredirect(True) # <========= ADDED
# Remove placeholder widgets (if any)
for child in frame_container.winfo_children():
child.destroy()
gui.makeButton( "CLICK ME",parent = frame_container,height=5,width = 40)
self.gui.root.overrideredirect(False) # <========= ADDED
如果设置为 overrideredirect()
,True
函数将删除标题栏,如果设置为 False
,则返回标题栏。 只有在启用了标题栏的情况下才能进行拖动。因此,您实际上所做的是在短时间内无法进行拖动。作为奖励,在该点之前完成的拖动会被保留(窗口不会像以前那样跳回到其运动的开始处)。
这个过程足够快,以至于用户不会注意到它,因为任何窗口调整都包含在销毁/创建的更大调整中。
一个缺点是鼠标失去了与标题栏的联系,因此当创建/销毁事件发生时(即调用 overrideredirect(True)
时)窗口停止拖动。但这并不感觉不自然,因为它似乎是对窗口内容更改的有效响应,并且如果需要,重新启动拖动很容易,因为标题栏几乎会立即重新出现。