Tkinter的overrideredirect可防止Mac和Linux中的某些事件

时间:2016-01-03 23:02:45

标签: python linux tkinter tcl tk

我正在使用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。

1 个答案:

答案 0 :(得分:3)

我已经向Tk提交了一份针对此情况的错误报告。

您可以使用devilspie程序从窗口中删除装饰。使用wm title . myname命令为您的窗口指定一个特定名称,并在下面的devilspie配置片段中使用该名称。从程序中删除overrideredirect命令。

我测试了这个(作为Tk程序),未修饰的窗口仍然会收到按键等。绑定。

请注意,devilspie被写为守护程序进程并保持活动状态。该守护进程在启动后可以被杀死,并且它所做的窗口更改仍然有效。或者它可以保持运行,只要您的窗口被激活,就会应用devilspie配置。

(if (is (application_name) "t.tcl")
   (begin (undecorate)))