我正在用Tkinter UI编写Python程序。 我想有一个没有标题栏的小窗口。 该窗口必须接收键盘input。 我不挑剔这是否是一个Entry小部件的forms,或者只是绑定到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()
这会创build一个小窗口(没有标题栏),当它接收到这些窗口时,会显示常见事件的名称。 我已经在Windows 7,Mac OSX(El Capitan)和Ubuntu 14.04.1上运行这个脚本。 我只在一台虚拟机(VMWare)上运行Ubuntu。
在Windows中,这似乎按预期工作。 我的代码testing的所有事件都可以被接收。
在Ubuntu中,Tkinter窗口按预期接收<Enter>
, <Leave>
和<Button-1>
事件,但是从不接收<KeyPress>
, <FocusIn>
和<FocusOut>
。 事实上,即使在窗口被点击之后,最后一个有焦点的窗口仍继续接收按键。
在OSX中,Tkinter窗口按预期接收<Button-1>
事件,但始终不会收到<KeyPress>
, <FocusIn>
和<FocusOut>
。 有焦点的最后一个窗口不会像Ubuntu一样继续接收按键。 <Enter>
和<Leave>
事件有点奇怪。 直到窗口被点击, <Enter>
事件才被接收。 然后,一旦<Leave>
事件发生,窗口需要再次点击接收另一个<Enter>
事件。
我也在__init__
函数结束之前尝试了self.focus_force()
。 这会导致窗口在程序启动时收到一个<FocusIn>
事件,但不会再收到<KeyPress>
, <FocusIn>
或<FocusOut>
事件。
最终,我的问题是:是否有任何方法来隐藏标题栏,但继续在OSX和Linux接收键盘input?
我知道还有一些其他问题涉及这个相同的问题。 在这三个问题中:
接受的答案是使用self.attributes('-fullscreen', True)
,这对我不起作用,因为我想要一个小窗口,而不是全屏应用程序。
还有一个问题: Tkinter overrideredirect不再接收事件绑定 。 这似乎与我的问题非常接近,但提供的细节不多,没有答案。
更新:我一直在试图调查我的问题的基本机制。 我知道Tkinter是Tcl / Tk的包装,所以我想我会尝试在Tcl中重写我的代码。 我不是很了解Tcl,但是我想我已经(或多或less地)翻译了我的Python:
#!/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>"}
我尝试了在Windows和Mac OSX中产生的程序。 在Windows中,我收到了<KeyPress>
事件,但在OSX中我没有。 没有wm overrideredirect . True
wm overrideredirect . True
line,OSX确实收到<KeyPress>
事件。 因此,看起来这个问题不是Python,而是Tcl / Tk。
对于这种情况,我已经向Tk提交了一个错误报告。
您可以使用devilspie
程序从您的窗口中删除装饰品。 使用wm title . myname
wm title . myname
命令给你的窗口一个特定的名字,并在下面的devilspie
配置片段中使用这个名字。 从您的程序中删除overrideredirect
命令。
我已经测试了这个(作为一个Tk程序),未修饰的窗口仍然会收到按键等。 绑定。
请注意, devilspie
被写为守护进程并保持活动状态。 守护进程可以在启动后被杀死,窗口的改变仍然有效。 或者它可以保持运行,并且任何时候您的窗口被激活, devilspie
配置将被应用。
(if (is (application_name) "t.tcl") (begin (undecorate)))