简体   繁体   中英

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

I have a program that needs to open Toplevel windows except the main Tk() window in tkinter. In the main window I have a Scale widget which is updated every 100 miliseconds with the after call. However in a state where the Toplevel window is open and the scale is updated when I press down the 'X' button in the Toplevel window the Scale stops moving.

在此处输入图像描述

This is my code:

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()

When I stop pressing the 'X' button the Scale jumps to the point it should be在此处输入图像描述

How can I keep the slider/scale flowing normally even when I hold down the 'X' button?
And also why does this happen?

Thanks in advance!

This issue would happen on Windows. Your code works fine on Linux.(I've tested it)

A possible reason is here:

What is happening here (simplyfied a lot) is that as soon as Windows detects a button-down event on the non-client area it stops sending update messages, gets a snapshot of the window and gets ready to start drawing all those nice effects for window-moving, -resizing, etc. The window then stays frozen until the corresponding mouse-up ends the impasse.

This post also mentioned another solution: use thread.

Due to tkinter is single-threaded and those features are packaged, it seems using thread doesn't work in tkinter.

The cause is how operate system handle those "holding down" events on the title bar.

An easy solution is just hiding your title bar, and custom these buttons by yourself.(Avoid OS handling those events.) Like:

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()

Binding some events or using some nice color makes your UI prettier.

I have a program that needs to open Toplevel windows except the main Tk() window in tkinter. In the main window I have a Scale widget which is updated every 100 miliseconds with the after call. However in a state where the Toplevel window is open and the scale is updated when I press down the 'X' button in the Toplevel window the Scale stops moving.

在此处输入图像描述

This is my code:

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()

When I stop pressing the 'X' button the Scale jumps to the point it should be在此处输入图像描述

How can I keep the slider/scale flowing normally even when I hold down the 'X' button?
And also why does this happen?

Thanks in advance!

In short, that is a " feature ", at least on windows, the menu buttons are not expected to support the action of holding it. This happens because mainloop is just asking to update its own instance from the same place, the global _default_root , a workaround would be to create a new Tk on a detached process. Note that this does not happen on every gui library, for example wxWidgets works fine.

As you can see on this example, regular buttons are unaffected.

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()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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