我正在使用Tkinter UI编写Python程序。我想要一个没有标题栏的小窗口。此窗口必须接收键盘输入。我不挑剔这是以Entry小部件的形式还是只是绑定到KeyPress。 overrideredirect(True)
通常是标题栏的禁用方式。不幸的是,(除了在Windows中),这似乎阻止了许多事件被接收。我写了这段代码来说明问题:
#!/usr/bin/env python
from __future__ import print_function
import Tkinter
class AppWindow(Tkinter.Tk):
def __init__(self, *args, **kwargs):
Tkinter.Tk.__init__(self, *args, **kwargs)
self.overrideredirect(True)
self.geometry("400x25+100+300")
titleBar = Tkinter.Frame(self)
titleBar.pack(expand = 1, fill = Tkinter.BOTH)
closeButton = Tkinter.Label(titleBar, text = "x")
closeButton.pack(side = Tkinter.RIGHT)
closeButton.bind("<Button-1>", lambda event: self.destroy())
self.bind("<KeyPress>", lambda event: print("<KeyPress %s>" % event.char))
self.bind("<Button-1>", lambda event: print("<Button-1>"))
self.bind("<Enter>", lambda event: print("<Enter>"))
self.bind("<Leave>", lambda event: print("<Leave>"))
self.bind("<FocusIn>", lambda event: print("<FocusIn>"))
self.bind("<FocusOut>", lambda event: print("<FocusOut>"))
if __name__ == "__main__":
app = AppWindow()
app.mainloop()
这会创建一个小窗口(没有标题栏),在收到它们时会打印公共事件的名称。我在Windows 7,Mac OSX(El Capitan)和Ubuntu 14.04.1上运行此脚本。我在虚拟机(VMWare)中只运行了Ubuntu。
在Windows中,这似乎按预期工作。可以接收我的代码测试的所有事件。
在Ubuntu中,Tkinter窗口按预期收到<Enter>
,<Leave>
和<Button-1>
个事件,但<KeyPress>
,<FocusIn>
和{永远不会收到{1}}。实际上,即使在单击窗口之后,具有焦点的最后一个窗口仍继续接收按键。
在OSX中,Tkinter窗口按预期接收<FocusOut>
个事件,但从未收到<Button-1>
,<KeyPress>
和<FocusIn>
。具有焦点的最后一个窗口不会像Ubuntu那样继续接收按键操作。 <FocusOut>
和<Enter>
事件的行为有点奇怪。在单击窗口之前,不会收到<Leave>
事件。然后,一旦发生<Enter>
事件,则需要再次单击该窗口以接收另一个<Leave>
事件。
我在<Enter>
函数结束前尝试了self.focus_force()
。这会导致窗口在程序启动时收到__init__
事件,但不会再收到<FocusIn>
,<KeyPress>
或<FocusIn>
事件。
最终,我的问题是:有没有办法隐藏标题栏但是继续在OSX和Linux中接收键盘输入?
我知道处理同样问题的其他一些问题。在这三个问题中:
接受的答案是使用<FocusOut>
,这对我不起作用,因为我想要一个小小的窗口,而不是全屏应用程序。
还有一个问题:Tkinter overrideredirect no longer receiving event bindings。这似乎与我的问题非常接近,但提供的细节较少,没有答案。
更新:我一直在尝试调查问题的基本机制。我知道Tkinter是Tcl / Tk的包装器,所以我想我会尝试在Tcl中重写我的代码。我不太了解Tcl,但我认为我设法(或多或少)翻译我的Python:
self.attributes('-fullscreen', True)
我在Windows和Mac OSX中尝试了生成的程序。在Windows中我收到了#!/usr/bin/env wish
wm overrideredirect . True
wm geometry . "400x25+100+300"
bind . <KeyPress> {puts "<KeyPress %K>"}
bind . <Button-1> {puts "<Button-1>"}
bind . <Enter> {puts "<Enter>"}
bind . <Leave> {puts "<Leave>"}
bind . <FocusIn> {puts "<FocusIn>"}
bind . <FocusOut> {puts "<FocusOut>"}
个事件,但在OSX中我没有收到<KeyPress>
个事件。如果没有wm overrideredirect . True
行,OSX会收到<KeyPress>
个事件。因此看起来这个问题不是Python,而是Tcl / Tk。
答案 0 :(得分:3)
我已经向Tk提交了一份针对此情况的错误报告。
您可以使用devilspie
程序从窗口中删除装饰。使用wm title . myname
命令为您的窗口指定一个特定名称,并在下面的devilspie
配置片段中使用该名称。从程序中删除overrideredirect
命令。
我测试了这个(作为Tk程序),未修饰的窗口仍然会收到按键等。绑定。
请注意,devilspie
被写为守护程序进程并保持活动状态。该守护进程在启动后可以被杀死,并且它所做的窗口更改仍然有效。或者它可以保持运行,只要您的窗口被激活,就会应用devilspie
配置。
(if (is (application_name) "t.tcl")
(begin (undecorate)))