簡體   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