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()