【问题标题】:Tkinter main window focusTkinter 主窗口焦点
【发布时间】:2014-05-10 04:10:18
【问题描述】:

我有以下代码

window = Tk()
window.lift()
window.attributes("-topmost", True)

这段代码的工作原理是它将我的 Tkinter 窗口显示在所有其他窗口之上,但它仍然只解决了一半的问题。虽然该窗口实际上显示在所有其他窗口之上,但该窗口没有焦点。有没有办法不仅让窗口成为​​ Tkinter 中最前面的窗口,而且还把焦点放在它上面?

【问题讨论】:

  • 是什么让你觉得它没有焦点?你试过在窗口上拨打focus_force吗?
  • 我认为它没有焦点,因为窗口是灰色的,我必须点击它才能识别击键,focus_force 也没有解决问题
  • @marczellm,你能具体说明问题吗?我在 Python 3.6 和 Windows 10 上运行了代码,它可以工作
  • @TarunLalwani 具体来说,我希望我的 Tkinter 窗口从所有应用程序中窃取全局焦点并将其设置为 Entry 字段。该窗口确实出现在带有-topmost 的所有窗口的顶部,但没有任何方法将焦点放在 Entry 字段上 - 以前活动的应用程序仍然是活动/聚焦窗口。
  • 请提供您使用的示例测试应用。还有python版本以及如何启动脚本? @marczellm

标签: python tkinter focus


【解决方案1】:

对于 Linux (Ubuntu 16.04.6 LTS),我尝试了很多建议,包括 .grab 破坏了一些东西。最后我用了:

if self.top2_is_active is True: # Are we already playing songs?
    self.top2.focus_force()     # Get focus
    self.top2.lift()            # Raise in stacking order
    root.update()
    return                      # Don't want to start playing again

这并不是真正的“窃取”焦点,因为我在主窗口上有一个按钮,定义为:

''' ▶  Play Button '''
self.play_text="▶  Play"                # play songs window is opened.
self.listbox_btn2 = tk.Button(frame3, text=self.play_text, \
                    width=BTN_WID, command=self.play_items)
self.listbox_btn2.grid(row=0, column=1, padx=2)

点击此按钮后,文字变为:

self.listbox_btn2 ["text"] = "?  Show playing"     # Play button

【讨论】:

    【解决方案2】:

    它对我有用。我在 Macos Mojave,python 3.6.4。将此代码放在 root.mainloop() 之前

    import os
    os.system("open -a Python")
    

    当你打开 -a 一个已经打开的应用程序时,这个应用程序将被聚焦。

    【讨论】:

      【解决方案3】:

      注意:这是特定于 Windows 的。这将聚焦整个主窗口。本质上是强制 alt-tab 到达窗口。


      这些答案都不适合我,tk 窗口会出现在带有-topmost 标志的所有内容的顶部,我的输入框会有焦点,但窗口本身不会,这意味着打字不会t 出现在tk 窗口中。它看起来像这样:

      添加对 Windows API 的调用以窃取焦点:

      import win32gui    
      root = tk.Tk()
      
      # Application specific setup ...
      
      win32gui.SetForegroundWindow(root.winfo_id())
      

      免责声明:窃取焦点是不好的行为,因此只有在必要且有意义的情况下才使用它,例如用户将立即输入的输入框。

      【讨论】:

        【解决方案4】:

        Windows 的解决方案有点棘手 - 您不能只是从另一个窗口中窃取焦点,您应该以某种方式将您的应用程序移到前台。 但首先,我建议您使用 littlebit of windows theory,以便我们能够确认,这就是我们想要实现的目标。

        正如我在 comment 中提到的 - 这是使用 SetForegroundWindow 功能的好机会(也检查限制!)。但是考虑一下这种廉价的黑客攻击,因为用户“拥有”前台,Windows 会不惜一切代价阻止你:

        当用户正在使用另一个窗口时,应用程序不能将一个窗口强制置于前台。相反,Windows 会闪烁窗口的任务栏按钮以通知用户。

        另外,请查看this 页面上的备注:

        如果用户按下 ALT 键或采取一些导致系统本身更改前台窗口的操作(例如,单击背景窗口),系统会自动启用对 SetForegroundWindow 的调用。

        这是一个最简单的解决方案,因为我们能够模拟Alt press:

        import tkinter as tk
        import ctypes
        
        #   store some stuff for win api interaction
        set_to_foreground = ctypes.windll.user32.SetForegroundWindow
        keybd_event = ctypes.windll.user32.keybd_event
        
        alt_key = 0x12
        extended_key = 0x0001
        key_up = 0x0002
        
        
        def steal_focus():
            keybd_event(alt_key, 0, extended_key | 0, 0)
            set_to_foreground(window.winfo_id())
            keybd_event(alt_key, 0, extended_key | key_up, 0)
        
            entry.focus_set()
        
        
        window = tk.Tk()
        
        entry = tk.Entry(window)
        entry.pack()
        
        #   after 2 seconds focus will be stolen
        window.after(2000, steal_focus)
        
        window.mainloop()
        

        一些链接和例子:

        【讨论】:

        • 警告:运行此代码后,即使退出程序,我的电脑也开始出现奇怪的行为(例如,键盘不工作)。必须重新启动才能使一切恢复正常。
        • @Sebastian Nielsen,如果唯一的问题是键盘,那么这并不奇怪,但如果你在中途打断steal_focus 是可能的。无论如何,keybd_event 不久前已被取代,所以也许您应该围绕最新的 SendInput 对应物构建您的解决方案,但我无法重现您描述的行为。
        • 这个解决方案是唯一可行的,但是如何使用 SendInput 代替 keybd_event?
        • @Eli,试试这个:stackoverflow.com/a/62205935/6634373
        • 太棒了!我能够通过发送“alt”按键使其工作。我使用“键盘”库,然后使用 Keyboard.press_and_release('alt'),然后在窗口上执行“focus_force()”
        【解决方案5】:

        不确定您面临什么问题。但下面是对我来说非常有用的示例代码。

        from tkinter import *
        
        window = Tk()
        
        def handle_focus(event):
            if event.widget == window:
                window.focus_set()
                input1.focus_set()
        
        
        label1 = Label(window,text = "Enter Text 2")
        input1 = Entry(window, bd=5)
        
        label2 = Label(window,text = "Enter Text 2")
        input2 = Entry(window, bd=5)
        
        submit = Button(window, text="Submit")
        
        label1.pack()
        input1.pack()
        label2.pack()
        input2.pack()
        submit.pack(side=BOTTOM)
        
        window.lift()
        window.attributes("-topmost", True)
        
        window.bind("<FocusIn>", handle_focus)
        
        hwnd = window.winfo_id()
        
        window.mainloop()
        

        这是在 Windows 10 上使用最新的 Python 3.6 测试的。

        结果是运行程序后,我可以开始打字,他的输入会转到第一个文本框

        【讨论】:

        • 我想要做的是我的应用程序(响应全局键盘事件或定时事件)从其他应用程序(例如 Firefox)窃取焦点。
        • @marczellm,不幸的是,您的要求不是从另一个应用程序中窃取焦点(至少在 win 上),而是用另一个(您的 tk 应用程序)替换一个 foreground 窗口(例如 firefox) )。这并不是看起来那么简单的任务,所以这里有一个similar problem
        • @Tarun Lawani 对此表示感谢。这种方法帮助我解决了在 MacOS 上运行的脚本的焦点捕获问题,而其他方法都不起作用。
        【解决方案6】:

        如果focus_force() 不起作用,您可以尝试这样做:

        window.after(1, lambda: window.focus_force())
        

        本质上是一样的,只是写法不同。我刚刚在 python 2.7 上测试过。

        root.focus_force() 不起作用,但上面的方法可以。

        【讨论】:

        • 你用的是什么操作系统? (我在 OSX 上)
        • @PythonSpoilsYou 抱歉回复晚了,但我在 Windows 上。我刚刚重新测试了这个,它似乎只适用于一个代码,但另一个没有。
        • 你知道为什么吗?
        • @PythonSpoilsYou 不,没有线索。这让我很困惑。而且由于您使用的是OSX,这让我更加一无所知。您可以尝试进行一些艰苦的谷歌搜索,例如“使 tkinter 窗口焦点 OSX”。祝你好运。
        • @BrianFuller 我遇到了一个稍微不同的问题。我使用了以下 3 种方法来聚焦窗口 - window.focus_force()window.wm_attributes("-topmost" , -1) 和你给出的答案 window.after(1, lambda : window.focus_force()) 。在这三种方式中的每一种中,只要我运行我的程序,窗口就会聚焦,但它本身会在 1 秒后最小化(我既没有点击任何地方,也没有最小化它)。为什么?我在 Windows 8 上
        猜你喜欢
        • 1970-01-01
        • 2018-03-15
        • 1970-01-01
        • 2012-12-01
        • 2012-02-12
        • 1970-01-01
        • 2020-12-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多