我已阅读相关答案,似乎可接受的方法是将回调绑定到Toplevel小部件中的<Map>
和<Unmap>
事件。我试过以下但没有效果:
from Tkinter import *
tk = Tk()
def visible(event):
print 'visible'
def invisible(event):
print 'invisible'
tk.bind('<Map>', visible)
tk.bind('<Unmap>', invisible)
tk.mainloop()
我在Linux上运行python 2.7。这可能与不同操作系统中的窗口管理器代码有关吗?
在tk.iconify()
之前调用tk.mainloop()
也无效。实际上,产生正确行为的唯一命令是tk.withdraw()
,这与最小化窗口肯定不同。此外,如果通过调用<Map>
,<Unmap>
或pack()
触发grid()
和place()
事件,为什么在应用程序窗口时会触发<Map>
在Windows和/或Mac上最小化,this和this回答建议。为什么在Linux上调用withdraw()
和deiconify()
时会触发它们?
答案 0 :(得分:2)
术语Unmap
在Linux上的含义与在Windows上的含义完全不同。在Linux上,取消映射一个窗口意味着它(几乎)无法追踪;它不会出现在应用程序的图标中,也不再列在wmctrl -l
的输出中。我们可以通过命令取消映射/映射窗口:
xdotool windowunmap <window_id>
和
xdotool windowmap <window_id>
为了看看我们是否甚至可以让tkinter检测窗口的状态最小化,我在你的基本窗口中添加了一个线程,每秒打印一次窗口的状态,使用:
root.state()
最小化与否,线程始终打印:
normal
幸运的是,如果你必须能够检测窗口的最小化状态,那么在Linux上我们有其他工具,如xprop
和wmctrl
。虽然它很脏,但在应用程序中可以很好地编写脚本。
根据评论中的要求,下面是一个简化示例,用于创建您自己的外部工具绑定版本。
wmctrl -lp
通过检查名称和 pid 来获取窗口的id
( tkinter
个窗口有pid 0)。window id
后,我们可以检查字符串_NET_WM_STATE_HIDDEN
是否在xprop -id <window_id>
的输出中。如果是这样,窗口就会最小化。然后我们可以轻松使用tkinter
的{{3}}来定期检查。在下面的例子中,评论应该说明一切。
我们需要安装after() method和wmctrl。在基于Dedian的系统上:
sudo apt-get install wmctrl xprop
import subprocess
import time
from Tkinter import *
class TestWindow:
def __init__(self, master):
self.master = master
self.wintitle = "Testwindow"
self.checked = False
self.state = None
button = Button(self.master, text = "Press me")
button.pack()
self.master.after(0, self.get_state)
self.master.title(self.wintitle)
def get_window(self):
"""
get the window by title and pid (tkinter windows have pid 0)
"""
return [w.split() for w in subprocess.check_output(
["wmctrl", "-lp"]
).decode("utf-8").splitlines() if self.wintitle in w][-1][0]
def get_state(self):
"""
get the window state by checking if _NET_WM_STATE_HIDDEN is in the
output of xprop -id <window_id>
"""
try:
"""
checked = False is to prevent repeatedly fetching the window id
(saving fuel in the loop). after window is determined, it passes further checks.
"""
self.match = self.get_window() if self.checked == False else self.match
self.checked = True
except IndexError:
pass
else:
win_data = subprocess.check_output(["xprop", "-id", self.match]).decode("utf-8")
if "_NET_WM_STATE_HIDDEN" in win_data:
newstate = "minimized"
else:
newstate = "normal"
# only take action if state changes
if newstate != self.state:
print newstate
self.state = newstate
# check once per half a second
self.master.after(500, self.get_state)
def main():
root = Tk()
app = TestWindow(root)
root.mainloop()
if __name__ == '__main__':
main()
答案 1 :(得分:1)
我自己实施雅各布建议的hack。
from Tkinter import Tk, Toplevel
import subprocess
class CustomWindow(Toplevel):
class State(object):
NORMAL = 'normal'
MINIMIZED = 'minimized'
def __init__(self, parent, **kwargs):
Toplevel.__init__(self, parent, **kwargs)
self._state = CustomWindow.State.NORMAL
self.protocol('WM_DELETE_WINDOW', self.quit)
self.after(50, self._poll_window_state)
def _poll_window_state(self):
id = self.winfo_id() + 1
winfo = subprocess.check_output(
['xprop', '-id', str(id)]).decode('utf-8')
if '_NET_WM_STATE_HIDDEN' in winfo:
state = CustomWindow.State.MINIMIZED
else:
state = CustomWindow.State.NORMAL
if state != self._state:
sequence = {
CustomWindow.State.NORMAL: '<<Restore>>',
CustomWindow.State.MINIMIZED: '<<Minimize>>'
}[state]
self.event_generate(sequence)
self._state = state
self.after(50, self._poll_window_state)
if __name__ == '__main__':
root = Tk()
root.withdraw()
window = CustomWindow(root)
def on_restore(event):
print 'restore'
def on_minimize(event):
print 'minimize'
window.bind('<<Restore>>', on_restore)
window.bind('<<Minimize>>', on_minimize)
root.mainloop()
答案 2 :(得分:0)
对我来说,在Win10上,你的代码运行得很完美,但需要注意的是,中间框架按钮会产生“可见”,无论它是“最大化”还是“恢复”。因此,最大化,然后恢复结果,2个新的“可见”变得可见。
我没有特别期待这一点,因为this reference表示地图是在
时生成的正在映射窗口小部件,即在应用程序中可见。例如,当您调用窗口小部件的.grid()方法时,就会发生这种情况。
Toplevels不受几何体管理。权威性更强tk doc说
地图,取消地图
只要窗口的映射状态发生变化,就会生成Map和Unmap事件。
Windows以未映射状态创建。顶级窗口在转换到正常状态时会被映射,并在撤回和标志状态下取消映射。
尝试在主循环调用之前添加+
。这应该与最小化按钮相同。如果它不会导致“不可见”,那么Linux上似乎存在tcl / tk错误。