Home > Net >  How can I make my wx app open as an active window?
How can I make my wx app open as an active window?

Time:08-19

I'm pretty new to wx. I have a wx app that's semi-transparent, stays on top, you can draw a rectangle on screen like the snipping tool and get the rect area coords. It works fine by itself. But I'd like to open another (already running) window (namely Notepad) below it so you can "draw" over that window.

I'm trying to achieve this by activating Notepad first, then create and open my app. The problem is even when I activate that window BEFORE creating my app and starting the wx MainLoop, my program still doesn't get activated when it opens. I get Notepad activated, then my app is on top of it, but my app's window itself is not activated.

How could I activate my window after activating the Notepad? Calling activate() again on my window doesn't seem to work. Or is there any way to keep the app activated and ontop until the rect is draw on it is interrupted by a key?

Edit 1: So what I'd like to achieve here:

  • Activate an already opened Notepad or any window, really. (Put it to the foreground so later when my app opens, it will overlay the selected window.)
  • Open the overlay above it, and make it the active window, so keystrokes are targeted for my app and not the Notepad.
  • Put a text somewhere on the overlay, preferably somewhere around the middle.
  • I can either hit Escape to close it or draw a rectangle with left click.
  • If a rectangle was drawn, return the starting coordinates and the width/length.

My code is:

import ctypes
import wx
import pygetwindow as gw

global selectionOffset, selectionSize

selectionOffset = ""
selectionSize = ""

class SelectableFrame(wx.Frame):
    c1 = None
    c2 = None

    def __init__(self, parent=None, id=wx.ID_ANY, title="Multitool Capture"):
        self.style = wx.NO_BORDER

        wx.Frame.__init__(self, parent, id, title, size=wx.DisplaySize(), style=self.style)
        self.SetCursor(wx.Cursor(wx.CURSOR_CROSS))
        self.SetWindowStyle(wx.STAY_ON_TOP)

        self.text_canvas = wx.StaticText(self, id, "Select an area while holding down LEFT mouse button.", (0, 300), wx.DisplaySize(), wx.ALIGN_CENTER)
        self.text_canvas.SetFont(wx.Font(26, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD))
        self.text_canvas.SetForegroundColour((255, 0, 0, 0))

        self.text_canvas.Bind(wx.EVT_MOTION, self.OnMouseMove)
        self.text_canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown)
        self.text_canvas.Bind(wx.EVT_LEFT_UP, self.OnMouseUp)
        self.text_canvas.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_CHAR_HOOK, self.OnKey)

        self.SetTransparent(200)

    def OnKey(self, event):
        print("Key pressed!")
        key_code = event.GetKeyCode()
        print(key_code)
        if key_code == wx.WXK_ESCAPE:
            self.Destroy()

    def onm ouseMove(self, event):
        if event.Dragging() and event.LeftIsDown():
            self.c2 = event.GetPosition()
            self.Refresh()

    def onm ouseDown(self, event):
        self.c1 = event.GetPosition()

    def onm ouseUp(self, event):
        self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
        self.Destroy()

    def OnPaint(self, event):
        global selectionOffset, selectionSize
        if self.c1 is None or self.c2 is None: return

        dc = wx.PaintDC(self.text_canvas)
        dc.SetPen(wx.Pen('black', 5))
        dc.SetBrush(wx.Brush(wx.Colour(255, 255, 255)))

        dc.DrawRectangle(self.c1.x, self.c1.y, self.c2.x - self.c1.x, self.c2.y - self.c1.y)
        selectionOffset = (self.c1.x, self.c1.y)
        selectionSize = (abs(self.c2.x - self.c1.x), abs(self.c2.y - self.c1.y))

    def PrintPosition(self, pos):
        return str(pos.x)   "x"   str(pos.y)

class MyApp(wx.App):

    def OnInit(self):
        self.frame = SelectableFrame()
        self.frame.Show()
        return True

def get_area():
    try:
        win = gw.getWindowsWithTitle("Notepad")[0]
        print(win)
        win.activate()
    except Exception:
        ctypes.windll.user32.MessageBoxW(0, u"Notepad is probably not running.", u"An error happened", 0)
        return 0

    app = MyApp(redirect=False)
    app.MainLoop()
    print(f"Offset: {str(selectionOffset)}\nSelection size: {str(selectionSize)}")
    if selectionOffset:
        return selectionOffset, selectionSize
    else:
        return 0

get_area()

CodePudding user response:

I have managed to solve it. Used win32con and win32gui instead of pygetwindow to activate the window. This is the code after the solution:

import ctypes, errorcodes, win32con, win32gui, wx
import pywinauto as pwa

global selectionOffset, selectionSize

selectionOffset = ""
selectionSize = ""

class SelectableFrame(wx.Frame):
    c1 = None
    c2 = None

    def __init__(self, parent=None, id=wx.ID_ANY, title="Multitool Capture"):
        self.style = wx.NO_BORDER

        wx.Frame.__init__(self, parent, id, title, size=wx.DisplaySize(), style=self.style)
        self.SetCursor(wx.Cursor(wx.CURSOR_CROSS))
        self.SetWindowStyle(wx.STAY_ON_TOP)

        self.text_canvas = wx.StaticText(self, id, "Select an area while holding down LEFT mouse button.", (0, 300), wx.DisplaySize(), wx.ALIGN_CENTER)
        self.text_canvas.SetFont(wx.Font(26, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD))
        self.text_canvas.SetForegroundColour((255, 0, 0, 0))

        self.text_canvas.Bind(wx.EVT_MOTION, self.OnMouseMove)
        self.text_canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown)
        self.text_canvas.Bind(wx.EVT_LEFT_UP, self.OnMouseUp)
        self.text_canvas.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_CHAR_HOOK, self.OnKey)

        self.SetTransparent(200)

    def OnKey(self, event):
        print("Key pressed!")
        key_code = event.GetKeyCode()
        print(key_code)
        if key_code == wx.WXK_ESCAPE:
            self.Destroy()

    def onm ouseMove(self, event):
        if event.Dragging() and event.LeftIsDown():
            self.c2 = event.GetPosition()
            self.Refresh()

    def onm ouseDown(self, event):
        self.c1 = event.GetPosition()

    def onm ouseUp(self, event):
        self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
        self.Destroy()

    def OnPaint(self, event):
        global selectionOffset, selectionSize
        if self.c1 is None or self.c2 is None: return

        dc = wx.PaintDC(self.text_canvas)
        dc.SetPen(wx.Pen('black', 5))
        dc.SetBrush(wx.Brush(wx.Colour(255, 255, 255)))

        dc.DrawRectangle(self.c1.x, self.c1.y, self.c2.x - self.c1.x, self.c2.y - self.c1.y)
        selectionOffset = (self.c1.x, self.c1.y)
        selectionSize = (abs(self.c2.x - self.c1.x), abs(self.c2.y - self.c1.y))

    def PrintPosition(self, pos):
        return str(pos.x)   "x"   str(pos.y)

class MyApp(wx.App):

    def OnInit(self):
        self.frame = SelectableFrame()
        self.frame.Show()
        print(self.frame.HasFocus())
        return True

def set_foreground_window(title, winclass = None):
    win_handle = pwa.findwindows.find_window(title_re=title, class_name=winclass)
    win32gui.ShowWindow(win_handle, win32con.SW_RESTORE)
    win32gui.SetWindowPos(win_handle, win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE   win32con.SWP_NOSIZE)  
    win32gui.SetWindowPos(win_handle, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE   win32con.SWP_NOSIZE)  
    win32gui.SetWindowPos(win_handle, win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_SHOWWINDOW   win32con.SWP_NOMOVE   win32con.SWP_NOSIZE)

def get_area():
    try:
        set_foreground_window(".*Notepad.*", "Notepad")
    except Exception:
        ctypes.windll.user32.MessageBoxW(0, u"Notepad is not running.\n\nError code: {0}".format(errorcodes.NOTEPAD_NOT_RUNNING), u"An error happened", 0)
        return 0

    app = MyApp(redirect=False)
    app.MainLoop()
    print(f"Offset: {str(selectionOffset)}\nSelection size: {str(selectionSize)}")
    if selectionOffset and selectionSize:
        return selectionOffset, selectionSize
    else:
        return 0

get_area()

CodePudding user response:

Your code has the feel of being hacked out of something designed to work differently, to get example code, which then doesn't hang together.

I don't have Windows so this code works under Linux but should work with windows as well.
The fundamental difference is it uses wx.Execute to call the external program and it calls Focus via wx.CallLater to ensure the main App gets activated as you put it. The clear advantage being it's pure wxPython.

I'm calling xed a Linux replacement for Notepad and self.text_canvas became a wx.Panel to make the code sensible, to me.

See if you get any mileage out of it.

import wx

global selectionOffset, selectionSize

selectionOffset = ""
selectionSize = ""

class SelectableFrame(wx.Frame):
    c1 = None
    c2 = None

    def __init__(self, parent=None, id=wx.ID_ANY, title="Multitool Capture"):
        self.style = wx.NO_BORDER

        #wx.Frame.__init__(self, parent, id, title, size=wx.DisplaySize(), style=self.style)
        wx.Frame.__init__(self, parent, id, title, size=(500,500))
        self.SetCursor(wx.Cursor(wx.CURSOR_CROSS))
        self.SetWindowStyle(wx.STAY_ON_TOP)

        self.text_canvas = wx.Panel(self, wx.ID_ANY, size=(500,500))
        self.text_canvas.SetForegroundColour((255, 0, 0, 0))

        self.text_canvas.Bind(wx.EVT_MOTION, self.OnMouseMove)
        self.text_canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown)
        self.text_canvas.Bind(wx.EVT_LEFT_UP, self.OnMouseUp)
        self.text_canvas.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_CHAR_HOOK, self.OnKey)

        self.SetTransparent(200)

    def OnKey(self, event):
        print("Key pressed!")
        key_code = event.GetKeyCode()
        print(key_code)
        if key_code == wx.WXK_ESCAPE:
            self.Destroy()

    def onm ouseMove(self, event):
        if event.Dragging() and event.LeftIsDown():
            self.c2 = event.GetPosition()
            self.Refresh()

    def onm ouseDown(self, event):
        self.c1 = event.GetPosition()

    def onm ouseUp(self, event):
        self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
        #self.Destroy()

    def OnPaint(self, event):
        global selectionOffset, selectionSize
        if self.c1 is None or self.c2 is None: return

        dc = wx.PaintDC(self.text_canvas)
        dc.SetPen(wx.Pen('black', 5))
        dc.SetBrush(wx.Brush(wx.Colour(255, 255, 255)))

        dc.DrawRectangle(self.c1.x, self.c1.y, self.c2.x - self.c1.x, self.c2.y - self.c1.y)
        selectionOffset = (self.c1.x, self.c1.y)
        selectionSize = (abs(self.c2.x - self.c1.x), abs(self.c2.y - self.c1.y))

    def PrintPosition(self, pos):
        return str(pos.x)   "x"   str(pos.y)

class MyApp(wx.App):

    def OnInit(self):
        self.frame = SelectableFrame()
        self.frame.Show()
        win = wx.Execute("xed")
        wx.CallLater(2000, self.frame.SetFocus)
        return True

def get_area():
    app = MyApp(redirect=False)
    app.MainLoop()
    print(f"Offset: {str(selectionOffset)}\nSelection size: {str(selectionSize)}")
    if selectionOffset:
        return selectionOffset, selectionSize
    else:
        return 0

get_area()
  • Related