Tkinter 的 overrideredirect 阻止了 Mac 和 Linux 中的某些事件 [英] Tkinter's overrideredirect prevents certain events in Mac and Linux

查看:18
本文介绍了Tkinter 的 overrideredirect 阻止了 Mac 和 Linux 中的某些事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用 Python 编写一个带有 Tkinter UI 的程序.我想要一个没有标题栏的小窗口.此窗口必须接收键盘输入.无论是 Entry 小部件的形式还是仅绑定到 KeyPress,我都不挑剔.overrideredirect(True) 通常是禁用标题栏的方式.不幸的是,(除了在 Windows 中),这似乎阻止了许多事件的接收.我写了这段代码来说明问题:

I am writing a program in Python with a Tkinter UI. I want to have a small window with no title bar. This window must receive keyboard input. I am not picky whether this is in the form of an Entry widget or just binding to KeyPress. overrideredirect(True) is typically how the title bar is disabled. Unfortunately, (except in Windows), this seems to prevent many events from being received. I wrote this code to illustrate the problem:

#!/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.

This creates a little window (with no title bar) that prints the name of common events when it receives them. I have run this script on Windows 7, Mac OSX (El Capitan), and Ubuntu 14.04.1. I ran only Ubuntu in a virtual machine (VMWare).

  • 在 Windows 中,这似乎按预期工作.可以接收我的代码测试的所有事件.

  • In Windows, this seems to work as expected. All events that my code tests for can be received.

在 Ubuntu 中,Tkinter 窗口接收 > 事件按预期进行,但从未收到 .事实上,即使在窗口被点击之后,最后一个具有焦点的窗口仍会继续接收按键操作.

In Ubuntu, the Tkinter window receives <Enter>, <Leave>, and <Button-1> events as expected, but <KeyPress>, <FocusIn>, and <FocusOut> are never received. In fact, even after the window has been clicked on, the last window with focus continues to receive the key presses.

在 OSX 中,Tkinter 窗口按预期接收 事件,但 从未收到.最后一个具有焦点的窗口不会像在 Ubuntu 中那样继续接收按键. 事件的行为有点奇怪. 事件直到窗口被点击才被接收.然后,一旦 事件发生,需要再次点击窗口以接收另一个 事件.

In OSX, the Tkinter window receives <Button-1> events as expected, but <KeyPress>, <FocusIn>, and <FocusOut> are never received. The last window with focus does not continue to receive key presses like in Ubuntu. The <Enter> and <Leave> events behave a little oddly. The <Enter> event is not received until the window is clicked. Then, once the <Leave> event occurs, the window needs to be clicked again to receive another <Enter> event.

我也在 __init__ 函数结束之前尝试了 self.focus_force().这会导致窗口在程序启动时收到 事件,但不再接收 , 或 事件永远不会收到.

I have also tried self.focus_force() just before the end of the __init__ function. This causes the window to receive a <FocusIn> event when the program starts, but no further <KeyPress>, <FocusIn>, or <FocusOut> events are never received.

最终,我的问题是:有没有办法隐藏标题栏,但在 OSX 和 Linux 中继续接收键盘输入?

Ultimately, my question is this: is there any way to hide the title bar but continue to receive keyboard input in OSX and Linux?

我知道还有其他一些问题与此相同.在这三个问题中:

I am aware of some other questions dealing with this same problem. In these three questions:

接受的答案是使用 self.attributes('-fullscreen', True),这对我不起作用,因为我想要一个小窗口,而不是全屏应用程序.

The accepted answer is to use self.attributes('-fullscreen', True), which will not work for me as I want a tiny little window, not a fullscreen application.

还有一个问题:Tkinter overrideredirect 不再接收事件绑定.这似乎与我的问题非常接近,但提供的细节较少且没有答案.

There is one other question: Tkinter overrideredirect no longer receiving event bindings. This seems very close to my question, but provided less detail and has no answer.

更新:我一直在尝试调查问题的潜在机制.我知道 Tkinter 是 Tcl/Tk 的包装器,所以我想我会尝试用 Tcl 重写我的代码.我不太了解 Tcl,但我想我设法(或多或少)翻译了我的 Python:

Update: I have been attempting to investigate the underlying mechanism of my problem. I know that Tkinter is a wrapper around Tcl/Tk, so I thought I would try rewriting my code in Tcl. I don't really know Tcl, but I think I managed to (more or less) translate my 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 中我收到了 事件,但在 OSX 中我没有.没有 wm overrideredirect .True 行,OSX 确实收到 事件.因此看起来这个问题不是 Python 的问题,而是 Tcl/Tk 的问题.

I tried the resulting program in Windows and Mac OSX. In Windows I received <KeyPress> events, but in OSX I did not. Without the wm overrideredirect . True line, OSX does receive the <KeyPress> events. Therefore it looks like this problem is not with Python, but with Tcl/Tk.

推荐答案

我已针对此情况向 Tk 提交了错误报告.

I have submitted a bug report to Tk for this situation.

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

You can use the devilspie program to remove the decorations from your window. Use the wm title . myname command to give your window a specific name and use that name in the devilspie configuration fragment below. Remove the overrideredirect command from your program.

我已经对此进行了测试(作为 Tk 程序),未修饰的窗口仍然会收到按键等.绑定.

I have tested this (as a Tk program), and the undecorated window will still receive the keypress &etc. bindings.

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

Note that devilspie is written as a daemon process and stays active. The daemon can be killed after it is started and the window changes it made will still be in effect. Or it can be left running, and any time your window is activated, the devilspie configuration will be applied.

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

这篇关于Tkinter 的 overrideredirect 阻止了 Mac 和 Linux 中的某些事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆