为什么按住顶层中的“X”按钮会停止执行 tkinter 中的主窗口?

问题描述

我有一个程序需要在 tkinter 中打开除主 Toplevel 窗口之外的 Tk() 窗口。 在主窗口中,我有一个 Scale 小部件,它通过 after 调用每 100 毫秒更新一次。但是,在 Toplevel 窗口打开且比例更新的状态下,当我按下 Toplevel 窗口中的“X”按钮时,Scale 停止移动。

enter image description here

这是我的代码

from tkinter import Tk,Toplevel,Scale

root = Tk()

slider = Scale(root,orient='horizontal')
slider.pack()
num = 0


def main():
    global num
    slider.set(num)
    num += 1
    slider.after(500,main)


def toplevel():
    win = Toplevel()


root.bind('<space>',lambda x: [main(),toplevel()])

root.mainloop()

当我停止按下“X”按钮时,比例会跳到它应该的位置

enter image description here

即使按住“X”按钮,如何保持滑块/刻度正常流动?
还有为什么会发生这种情况?

提前致谢!

解决方法

此问题会在 Windows 上发生。你的代码在 Linux 上运行良好。(我已经测试过了)

A possible reason 在这里:

这里发生的事情(简化了很多)是,一旦 Windows 在非客户区检测到按钮按下事件,它就会停止发送更新消息,获取窗口的快照并准备开始绘制所有这些窗口移动、调整大小等效果很好。然后窗口保持冻结状态,直到相应的鼠标松开结束僵局。

这篇文章还提到了另一种解决方案:使用线程。

由于 tkinter 是单线程的,并且这些功能是打包的,所以在 tkinter 中使用线程似乎不起作用。

原因是操作系统如何处理标题栏上的那些“按住”事件。

一个简单的解决方案就是隐藏你的标题栏,并自己自定义这些按钮。(避免操作系统处理这些事件。)喜欢:

from tkinter import Tk,Toplevel,Scale
import tkinter as tk


class CustomToplevel(Toplevel):
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)

        self.__offset_x = 100
        self.__offset_y = 100
        self.window_width = 100
        self.window_height = 100

        self.overrideredirect(True)
        self.title_bar_frame = tk.Frame(self,bg="grey")
        self.title_bar_frame.pack(fill="x")

        self.title_bar_frame.bind('<Button-1>',self.__click)
        self.title_bar_frame.bind('<B1-Motion>',self.__drag)

        self.close_button = tk.Button(self.title_bar_frame,text="X",bg="red",font=("",15),command=self.destroy)
        self.close_button.pack(side="right",fill="y")

        self.geometry(f"{self.window_width}x{self.window_height}+{self.winfo_pointerx() - self.__offset_x}+{self.winfo_pointery() - self.__offset_y}")

    def __click(self,event):
        self.__offset_x = event.x
        self.__offset_y = event.y

    def __drag(self,event):
        self.geometry(f"{self.window_width}x{self.window_height}+{self.winfo_pointerx() - self.__offset_x}+{self.winfo_pointery() - self.__offset_y}")

root = Tk()

slider = Scale(root,orient='horizontal')
slider.pack()
num = 0


def main():
    global num
    slider.set(num)
    num += 1
    slider.after(500,main)


def toplevel():
    win = CustomToplevel()


root.bind('<space>',lambda x: [main(),toplevel()])

root.mainloop()

绑定一些事件或使用一些漂亮的颜色使您的 UI 更漂亮。

,

简而言之,这是一个“功能”,至少在windows上,菜单按钮不应该支持按住它的动作。发生这种情况是因为 mainloop 只是要求更新 它自己的实例来自同一个地方,全局 _default_root,一种解决方法是在分离的进程上创建一个新的 Tk。 请注意,这不会在每个 gui 库上发生,例如 wxWidgets 工作正常。

正如您在此示例中看到的,常规按钮不受影响。

import tkinter as tk

class Top_Window(tk.Toplevel):

    @staticmethod
    def button_release(_):
        print('Button released')

    def __init__(self,name,**kwargs):
        tk.Toplevel.__init__(self,**kwargs)
        self.protocol('WM_DELETE_WINDOW',self.quit_button)
        self.geometry('300x200+300+300')
        self.title = name

        self.button = tk.Button(self,text='Button')
        self.button.bind('<ButtonRelease>',self.button_release)
        self.button.pack()

    def quit_button(self):
        print('Top window destroyed')
        self.destroy()


class Main_Window(tk.Tk):

    num = 0

    def after_loop(self):
        self.num += 1
        self.slider.set(self.num)
        self.after(500,self.after_loop)

    def __init__(self):
        tk.Tk.__init__(self)
        self.geometry('300x200+100+100')

        self.slider = tk.Scale(self,orient='horizontal')
        self.slider.pack()

        self.bind('<space>',self.spawn_top_level)
        self.after(500,self.after_loop)

    def spawn_top_level(self,_):
        Top_Window('Top',master=self)

if __name__ == '__main__':
    app = Main_Window()
    app.mainloop()

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...