【问题标题】:Tkinter - Checking if application has focusTkinter - 检查应用程序是否有焦点
【发布时间】:2020-06-28 15:46:26
【问题描述】:

我正在尝试将 Tk 子类化,它会暂停音频当且仅当整个应用程序失去焦点(即 Tk 实例失去焦点并且焦点没有传递给 @ 987654324@ 或 messagebox 小部件)。

我设法让它与'hack'一起工作 - 当messagebox 打开时,它是Tk 实例的最后一个孩子,也没有孩子。这是我尝试过的:

class TkWin(Tk): 
   def __init__(self, title):
        super().__init__(className=title, baseName=title)
        self.bind('<FocusOut>', lambda event: self.pause_audio())

    def pause_audio(self):
        if self.has_focus():
            return
        else:
            pass
            # pause the audio

    def has_focus(self):
        children = self.winfo_children()
        if any(isinstance(x, Toplevel) for x in children):
            return True
        if len(children[-1].winfo_children()) == 0:
            return True
        return False


win = TkWin('test')
win.mainloop()

上述解决方案解决了在打开Toplevelmessagebox暂停音频的问题。但是,如果打开 Toplevelmessagebox 小部件并且 然后另一个窗口获得焦点,它会失败。

(我知道如果您打开 messagebox 会失败然后创建了一个包含一些小部件的容器,但它适用于我构建应用程序的方式)

有没有更好的方法来解决这个问题?

尝试@Atlas345 的解决方案,在尝试打开messagebox 时遇到此错误:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1705, in __call__
    return self.func(*args)
  File "/usr/lib/python3.6/tkinter/__init__.py", line 749, in callit
    func(*args)
  File "/home/inkblot/Desktop/win test.py", line 12, in check
    if self.focus_get() is None:
  File "/usr/lib/python3.6/tkinter/__init__.py", line 699, in focus_get
    return self._nametowidget(name)
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1353, in nametowidget
    w = w.children[n]
KeyError: '__tk__messagebox'

编辑:

尝试覆盖 focus_get 方法让我有所收获,但我还有最后一个问题:

from tkinter import messagebox, Tk, Button, Label, Toplevel

class TkWin(Tk): 
    def __init__(self, title):
        super().__init__(className=title, baseName=title)
        self.focus_id = self.after(10, self.has_focus)
        
    def focus_get(self):
        try:
            return super().focus_get() is not None
        except KeyError:
            print('messagebox is open')
            return True

    def has_focus(self):
        print('resume audio') if self.focus_get() else print('pause audio')
        self.focus_id = self.after(50, self.has_focus)

def popupmsg(msg):
    popup = Toplevel(win)
    popup.wm_title("Warning!")
    Label(popup, text=msg).pack(side="top", fill='both', expand=True)
    Button(popup, text="okay", command = popup.destroy).pack()
    
win = TkWin('test')
Button(win, text='top', command=lambda: Toplevel(win)).pack()
Button(win, text='msg', command=lambda: messagebox.showinfo('title', 'msg')).pack()
Button(win, text='popup', command= lambda: popupmsg('I dare you!')).pack()
win.mainloop()
        

这让上述错误静默失败,但我现在注意到如果 topmost 小部件没有焦点,focus_get() 方法会返回 None。这意味着,如果打开了任何类型的弹出窗口,然后单击根窗口(或不是最近创建的任何弹出窗口),它会假定整个应用程序没有焦点,因此暂停音频,这不应该说最少。

【问题讨论】:

标签: python tkinter


【解决方案1】:

我正在尝试将 Tk 子类化,当且仅当 整个应用程序失去焦点(即 Tk 实例失去焦点并且 焦点未传递给 Toplevel 或消息框小部件)。

我认为你可以逃脱惩罚:

import tkinter as tk

root = tk.Tk()
b = tk.Button(root, text='top', command= lambda: tk.Toplevel(root))
b.pack()
def check ():
    condition = root.focus_get()
    root.after(500, check)
    if condition == None:
        print('pause music')
        pass
check()
root.mainloop()

因为如果所有窗口都被最小化或失去焦点,root.focus_get() 只会返回 None。 我尝试过的大多数其他方法都是在根目录最小化后立即返回 None。

此方法也因内置消息框而失败,因为这些与根窗口无关。这就是为什么你需要建立自己的喜欢:

def popupmsg(msg):
    popup = tk.Toplevel()
    popup.wm_title("Warning!")
 
    label = tk.Label(popup, text=msg)
    label.pack(side="top", fill=BOTH, expand=True)
    B1 = tk.Button(popup, text="okay", command = popup.destroy)
    B1.pack()

所以完整的例子是这样的:

import tkinter as tk
from tkinter import messagebox

def popupmsg(msg):
    popup = tk.Toplevel(root)
    popup.wm_title("Warning!")
 
    label = tk.Label(popup, text=msg)
    label.pack(side="top", fill='both', expand=True)
    B1 = tk.Button(popup, text="okay", command = popup.destroy)
    B1.pack()

root = tk.Tk()
b = tk.Button(root, text='top', command= lambda: tk.Toplevel(root))
b.pack()

b1 = tk.Button(root, text='msg', command= lambda: messagebox.showinfo('title', 'msg'))
b1.pack()

b2 = tk.Button(root, text='popup', command= lambda: popupmsg('I dare you!'))
b2.pack()
def check ():
    condition = root.focus_get()
    root.after(500, check)
    if condition == None:
        print('pause music')
        pass
check()
root.mainloop()

另一种方式

我在这里找到了这段代码: Determining what tkinter window is currently on top

并在代码中实现。它具有与消息框一起使用的好处,但仅当与您的根相关的另一个 tk 窗口处于活动状态时。此代码与焦点无关,它只是告诉您当前哪个窗口位于顶部。

所以条件是所有的窗口都需要图标化。

import tkinter as tk
from tkinter import messagebox

def popupmsg(msg):
    popup = tk.Toplevel(root)
    popup.wm_title("Warning!")
 
    label = tk.Label(popup, text=msg)
    label.pack(side="top", fill='both', expand=True)
    B1 = tk.Button(popup, text="okay", command = popup.destroy)
    B1.pack()

root = tk.Tk()
b = tk.Button(root, text='top', command= lambda: tk.Toplevel(root))
b.pack()

b1 = tk.Button(root, text='msg', command= lambda: [root.iconify(), messagebox.showinfo('title', 'msg')])
b1.pack()

b2 = tk.Button(root, text='popup', command= lambda: popupmsg('I dare you!'))
b2.pack()
def check ():
    condition1 = root.tk.eval('wm stackorder '+str(root))
    root.after(500, check)
    if condition1 == "":
        print('pause music')
        pass
    else:
        print('return music')
    
check()
root.mainloop()

【讨论】:

  • 感谢您的解决方案,我在打开 messagebox 时收到 KeyError(添加了对问题的跟踪),这是否意味着我无法在我的应用程序中使用它们?
  • 我使用的是您更新的代码。它对我有用,或者我不明白这个问题。
  • 顺便说一句,你应该看看这个接受的答案。因为您应该避免使用单线器进行配置和布局管理stackoverflow.com/questions/1101750/…
  • 您注意到我明确表示此解决方案不适用于消息框吗?
  • 非常感谢您的帮助,但是,我必须将第二个布尔表达式更改为 condition1 == "0" 才能正常工作。
猜你喜欢
  • 2011-04-12
  • 2013-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多