繁体   English   中英

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

[英]Why does holding down the 'X' button in a toplevel stop execution of main window in tkinter?

我有一个程序需要打开顶层Toplevel除了 tkinter 中的主要Tk() window 之外。 在主要的 window 中,我有一个Scale小部件,它每 100 毫秒after调用后更新一次。 然而,在 state 中,顶层 window 处于打开状态,当我按下顶层Toplevel中的“X”按钮时,天平会更新, Scale停止移动。

在此处输入图像描述

这是我的代码:

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”按钮时,Scale 会跳到应有的位置在此处输入图像描述

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

提前致谢!

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

一个可能的原因在这里:

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

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

由于 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 更漂亮。

我有一个程序需要打开顶层Toplevel除了 tkinter 中的主要Tk() window 之外。 在主要的 window 中,我有一个Scale小部件,它每 100 毫秒after调用后更新一次。 然而,在 state 中,顶层 window 处于打开状态,当我按下顶层Toplevel中的“X”按钮时,天平会更新, Scale停止移动。

在此处输入图像描述

这是我的代码:

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()
    win.mainloop()


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

root.mainloop()

当我停止按下“X”按钮时,Scale 会跳到应有的位置在此处输入图像描述

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

提前致谢!

简而言之,那是一个“功能”,至少在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()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM