You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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()

关键修改说明

  1. 窗口激活修复:

    • 在SelectableFrame的初始化方法中添加self.Raise()和self.SetFocus(),强制将窗口置于顶层并获取焦点
    • 激活记事本后添加wx.MilliSleep(100),确保系统完成窗口激活操作后再启动应用
  2. 文本与绘制分层:

    • 将原StaticText替换为Panel作为绘图底层,文本放在面板之上,避免文本遮挡矩形绘制
    • 调整文本颜色为不透明红色,提升可见性
  3. 坐标计算优化:

    • 在鼠标释放和绘制时,统一计算最小坐标和正的宽高,避免因拖动方向导致的负尺寸问题
  4. 全局变量初始化:

    • 重置全局变量初始值为None,避免残留旧数据
    • Esc键按下时重置全局变量,确保关闭时返回正确状态
  5. 事件处理优化:

    • 重命名方法为PEP8规范的小写下划线格式
    • 按键事件中添加event.Skip(),确保其他事件能正常传递

内容的提问来源于stack exchange,提问作者TheReshi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 02:18:08