wxPython, how to steal focus from another app when using pyxhook?

75 Views Asked by At

I tried to use pyxhook with wxPython. What the program suppose to do is when the window is hid or minimized, when the user press "R_Control", even when another application is still in focus, wxPython window should be shown, and set in focus. But instead, what it did just RequestUserAttention by blinking in the taskbar. I've tried using Raise(), Restore(), but it still doesn't work. Here's the simplified version of the code

import wx, pyxhook

class Frame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, name='', parent=parent, title='test')
        self.panel = wx.Panel(self, wx.ID_ANY)
        self.text_input = wx.TextCtrl(self.panel)
        self.moduleSizer = wx.BoxSizer(wx.VERTICAL)
        self.panel.SetSizer(self.moduleSizer)
        self.moduleSizer.Add(self.text_input, flag=wx.ALIGN_CENTER)
        self.text_input.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
        self.moduleSizer.Fit(self)
        self.text_input.SetFocus()

    def onKeyDown(self, event):
        if event.GetKeyCode() == wx.WXK_ESCAPE: self.Hide()
        event.Skip()

class HookMan(pyxhook.HookManager):
    def __init__(self,frame,master=None):
        pyxhook.HookManager.__init__(self,master)
        self.frame = frame
        self.KeyDown = self.key_down
        self.HookKeyboard()

    def key_down(self,event):
        if event.Key == 'Control_R':
            self.frame.Show()

if __name__ == '__main__':
    app = wx.App()
    frame = Frame(None)
    hookman = HookMan(frame)
    hookman.start()
    frame.Show()
    app.MainLoop()

I've tried this with TkInter, and it just works. root.deconify() just set the window on focus. How do i recreate this behaviour in wxPython?

import pyxhook
import tkinter as tk

class Root(tk.Tk):
    def __init__(self,master=None):
        tk.Tk.__init__(self,master)
        self.title('test')
        self.text_input = tk.Entry(self)
        self.text_input.pack()
        self.bind("<Escape>", self.minimize)

    def minimize(self,event):
        self.withdraw()

class HookMan(pyxhook.HookManager):
    def __init__(self,frame,master=None):
        pyxhook.HookManager.__init__(self,master)
        self.frame = frame
        self.KeyDown = self.key_down
        self.HookKeyboard()

    def key_down(self,event):
        if event.Key == 'Control_R':
            self.frame.deiconify()

if __name__ == '__main__':
    root = Root()
    hookman = HookMan(root)
    hookman.start()
    root.text_input.focus_set()
    root.mainloop()
1

There are 1 best solutions below

2
Rolf of Saxony On

For the record, the code you submitted (at least on Linux with wx 4.1.0) did in fact, work. The window was resurrected underneath existing windows.
By using SetWindowStyle and rejigging the calls to pyxhook, we can get this code to function appropriately.

import wx
import pyxhook

class Frame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, name='', parent=parent, title='test')
        self.SetWindowStyle(wx.STAY_ON_TOP)
        self.panel = wx.Panel(self, wx.ID_ANY)
        self.text_input = wx.TextCtrl(self.panel)
        self.moduleSizer = wx.BoxSizer(wx.VERTICAL)
        self.panel.SetSizer(self.moduleSizer)
        self.moduleSizer.Add(self.text_input, flag=wx.ALIGN_CENTER)
        self.text_input.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
        self.Bind(wx.EVT_CLOSE, self.onExit)
        self.moduleSizer.Fit(self)
        self.text_input.SetFocus()
        self.hookman = HookMan(self)
        self.hookman.start()

    def onKeyDown(self, event):
        if event.GetKeyCode() == wx.WXK_ESCAPE:
            self.Hide()
        event.Skip()

    def onExit(self, event):
        self.hookman.cancel()
        self.hookman.join()
        self.Destroy()

class HookMan(pyxhook.HookManager):
    def __init__(self,parent,master=None):
        pyxhook.HookManager.__init__(self,master)
        self.frame = parent
        self.KeyDown = self.key_down

    def key_down(self,event):
        if event.Key == 'Control_R':
            self.frame.Show()
    
    def close(self):
        self.close()

if __name__ == '__main__':
    app = wx.App()
    frame = Frame(None)
    frame.Show()
    app.MainLoop()