【问题标题】:Tkinter: Window flash when attempting to click awayTkinter:尝试点击离开时窗口闪烁
【发布时间】:2015-02-16 12:23:32
【问题描述】:

我已经尝试了一段时间了,但还没有找到方法。

我有一个 tkinter 脚本,当按下按钮时会创建一个弹出窗口。但是我不希望用户能够从这个窗口点击到任何以前创建的窗口。我已经使用 root.grab_set() 进行了这项工作,但是没有向用户表明他们必须留在那个窗口上。

class popup(object):
    def __init__(self, parent):
        self.root=Toplevel(parent)
        self.root.grab_set() #prevents the user clicking on the parent window
                             #But the window doesnt 'flash' when an attempt to click away is made

例如,当您有一个由 filedialogue 模块创建的窗口时,如果您尝试单击另一个窗口,则 filedialogue 窗口将停留在顶部并具有“闪烁”动画,让用户知道他们无法点击离开。有没有办法重现这种效果?浏览文件对话的来源对我来说并没有什么成果,谷歌搜索也没有。

【问题讨论】:

    标签: python tkinter


    【解决方案1】:

    我能想到的最简单的方法是使用事件和焦点命令,以及 windows bell 命令:

    #!python3
    
    import tkinter as tk
    
    class popup(object):
        def __init__(self, parent):
            self.root=tk.Toplevel(parent)
            self.root.title("Popup")
            self.root.bind("<FocusOut>", self.Alarm)
    
        def Alarm(self, event):
            self.root.focus_force()
            self.root.bell()
    
    main = tk.Tk()
    main.title("Main")
    pop = popup(main)
    main.mainloop()
    

    【讨论】:

    • 嗯,这是我猜的一种方式。窗户不会闪烁,但至少有一些迹象表明他们需要留在窗户上。但是,您仍然可以使用此方法与父窗口进行交互,但子窗口只是很快将焦点抢回
    【解决方案2】:

    这是一个使用来自user32 dll 的FlashWindowEx 的Windows 解决方案。您需要将 FLASHWINFO 对象传递给它。 grab_set 确保弹出窗口保持焦点并禁用主窗口中的任何小部件,使弹出窗口瞬态确保它始终位于主窗口之上。 &lt;Button-1&gt; 事件用于检查鼠标单击,winfo_containing 检查是否单击了弹出窗口以外的其他窗口。然后我将焦点设置回弹出窗口并闪烁焦点窗口(然后始终是弹出窗口)。

    您需要pywin32 才能使用它。

    import Tkinter as tk
    from ctypes import *
    import win32con
    
    class popup(object):
        def __init__(self, parent):
            self.parent = parent
            self.root=tk.Toplevel(self.parent)
            self.root.title("Popup")
            self.root.grab_set()
            self.root.transient(self.parent)
            self.root.bind("<Button-1>", self.flash)
    
        def flash(self, event):
            if self.root.winfo_containing(event.x_root, event.y_root)!=self.root:
                self.root.focus_set()
                number_of_flashes = 5
                flash_time = 80
                info = FLASHWINFO(0,
                                  windll.user32.GetForegroundWindow(),
                                  win32con.FLASHW_ALL,
                                  number_of_flashes,
                                  flash_time)
                info.cbSize = sizeof(info) 
                windll.user32.FlashWindowEx(byref(info))
    
    class FLASHWINFO(Structure): 
        _fields_ = [('cbSize', c_uint), 
                    ('hwnd', c_uint), 
                    ('dwFlags', c_uint), 
                    ('uCount', c_uint), 
                    ('dwTimeout', c_uint)]
    
    main = tk.Tk()
    main.title("Main")
    pop = popup(main)
    main.mainloop()
    

    现在,闪烁只在单击主窗口的主体时发生,因此单击标题栏只会将焦点返回到弹出窗口而不闪烁。要让它在发生这种情况时也触发,您可以尝试使用&lt;FocusOut&gt; 事件,但您必须确保它仅在焦点传递到主窗口时发生,但自从使用grab_set 以来它从未真正发生过。您可能想弄清楚这一点,但现在它工作得很好。所以它并不完美,但我希望它有所帮助。

    【讨论】:

    • 理想情况下,我希望只使用默认的 python 库,因为这需要是一个易于移植的系统。
    • 另外,我以前见过 self.root.transient(self.parent),但是当我尝试使用它时,它只是导致窗口不显示。这可能与创建弹出窗口时父窗口已经可见有关吗?
    • 我刚刚编辑了事件触发器并检查了一下,所以我认为它工作得更好。抱歉,我想不出只有默认 pyhton 的真正解决方案。也许别人可以。我会让这个答案留在这里,让其他尝试做同样事情的人可能会发现这个问题并且不介意使用 pywin32。至于transient 的问题,可能值得再问一个问题。
    • 我发现self.root.focus_set() 并没有真正做到我想要的。但至少在 linux 上,调用 self.root.focus_force() (也许使用 self.root.after(...) 有时间延迟效果很好......
    猜你喜欢
    • 1970-01-01
    • 2013-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-18
    • 1970-01-01
    • 2014-04-29
    • 1970-01-01
    相关资源
    最近更新 更多