如何让wx应用窗口打开后处于激活状态?(截图工具场景)
问题描述
我是wxPython开发新手,做了一个半透明、始终置顶的应用,功能类似截图工具,能在屏幕上绘制矩形并获取区域坐标,单独运行正常。现在想实现:先激活已运行的记事本(或任意窗口),再打开我的应用,让应用覆盖在该窗口上方且自身处于激活状态,确保按键操作响应我的应用而非记事本。
目前尝试先激活记事本再启动wx主循环,但应用打开后并未激活——记事本仍处于激活状态,我的应用虽然在上方但没激活,调用窗口的activate()方法也没用。
核心需求
- 激活已打开的记事本或任意窗口,使其前置,方便应用打开后覆盖它
- 打开覆盖层并使其成为激活窗口,确保按键响应自身
- 在覆盖层中部添加文本,且不影响矩形绘制
- 按Esc键可关闭应用,左键拖动可绘制矩形
- 绘制矩形后返回起始坐标及宽高
修改后的代码
import ctypes import wx import pygetwindow as gw # 全局变量存储选择区域信息 selection_offset = None selection_size = None class SelectableFrame(wx.Frame): def __init__(self, parent=None, id=wx.ID_ANY, title="Multitool Capture"): # 组合窗口样式:无边框+始终置顶 style = wx.NO_BORDER | wx.STAY_ON_TOP super().__init__(parent, id, title, size=wx.DisplaySize(), style=style) # 设置光标和透明度 self.SetCursor(wx.Cursor(wx.CURSOR_CROSS)) self.SetTransparent(200) # 创建绘图面板(替代原StaticText,避免文本遮挡绘制) self.canvas = wx.Panel(self, size=wx.DisplaySize()) self.canvas.Bind(wx.EVT_MOTION, self.on_mouse_move) self.canvas.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) self.canvas.Bind(wx.EVT_LEFT_UP, self.on_mouse_up) self.canvas.Bind(wx.EVT_PAINT, self.on_paint) # 在面板上添加居中文本 self.text = wx.StaticText(self.canvas, label="按住左键选择区域", pos=(0, 0), size=wx.DisplaySize(), style=wx.ALIGN_CENTER) self.text.SetFont(wx.Font(26, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)) self.text.SetForegroundColour((255, 0, 0)) # 绑定全局按键钩子(确保能捕获Esc键) self.Bind(wx.EVT_CHAR_HOOK, self.on_key_press) # 强制激活窗口 self.Raise() self.SetFocus() self.Show(True) def on_key_press(self, event): if event.GetKeyCode() == wx.WXK_ESCAPE: global selection_offset, selection_size selection_offset = None selection_size = None self.Destroy() event.Skip() def on_mouse_down(self, event): self.c1 = event.GetPosition() def on_mouse_move(self, event): if event.Dragging() and event.LeftIsDown(): self.c2 = event.GetPosition() self.Refresh() def on_mouse_up(self, event): global selection_offset, selection_size if hasattr(self, 'c1') and hasattr(self, 'c2'): # 计算正确的坐标和尺寸(确保宽高为正) x = min(self.c1.x, self.c2.x) y = min(self.c1.y, self.c2.y) width = abs(self.c2.x - self.c1.x) height = abs(self.c2.y - self.c1.y) selection_offset = (x, y) selection_size = (width, height) self.Destroy() def on_paint(self, event): if not hasattr(self, 'c1') or not hasattr(self, 'c2'): return dc = wx.PaintDC(self.canvas) # 设置绘制参数:黑色边框,半透明白色填充 dc.SetPen(wx.Pen('black', 5)) dc.SetBrush(wx.Brush(wx.Colour(255, 255, 255, 128))) # 绘制矩形(处理坐标顺序问题) x = min(self.c1.x, self.c2.x) y = min(self.c1.y, self.c2.y) width = abs(self.c2.x - self.c1.x) height = abs(self.c2.y - self.c1.y) dc.DrawRectangle(x, y, width, height) class MyApp(wx.App): def OnInit(self): self.frame = SelectableFrame() return True def get_area(): global selection_offset, selection_size selection_offset = None selection_size = None # 激活记事本窗口 try: win = gw.getWindowsWithTitle("Notepad")[0] win.activate() # 短暂延迟确保窗口激活完成 wx.MilliSleep(100) except Exception: ctypes.windll.user32.MessageBoxW(0, "记事本未运行", "错误", 0) return None # 启动wx应用 app = MyApp(redirect=False) app.MainLoop() if selection_offset and selection_size: print(f"偏移坐标:{selection_offset}\n区域尺寸:{selection_size}") return selection_offset, selection_size else: return None if __name__ == "__main__": get_area()
关键修改说明
窗口激活修复:
- 在
SelectableFrame的初始化方法中添加self.Raise()和self.SetFocus(),强制将窗口置于顶层并获取焦点 - 激活记事本后添加
wx.MilliSleep(100),确保系统完成窗口激活操作后再启动应用
- 在
文本与绘制分层:
- 将原
StaticText替换为Panel作为绘图底层,文本放在面板之上,避免文本遮挡矩形绘制 - 调整文本颜色为不透明红色,提升可见性
- 将原
坐标计算优化:
- 在鼠标释放和绘制时,统一计算最小坐标和正的宽高,避免因拖动方向导致的负尺寸问题
全局变量初始化:
- 重置全局变量初始值为
None,避免残留旧数据 - Esc键按下时重置全局变量,确保关闭时返回正确状态
- 重置全局变量初始值为
事件处理优化:
- 重命名方法为PEP8规范的小写下划线格式
- 按键事件中添加
event.Skip(),确保其他事件能正常传递
内容的提问来源于stack exchange,提问作者TheReshi
相关产品推荐
相关产品推荐

