如何实现Tkinter窗口自动隐藏/显示?游戏准星开发报错求助
问题:Apex Legends自定义准星工具开发报错及修复方案
需求背景
为《Apex Legends》制作自动显示/隐藏的屏幕准星,要求在返回桌面、最小化游戏或切换浏览器(游戏无边框运行)时自动消失,使用Python+Tkinter开发。
原代码及报错
原代码
import time from tkinter import * class CrosshairWindow: def __init__(self): self.root = Toplevel() self.root.attributes( '-transparentcolor', '#ffffff', '-topmost', '1' ) self.root.overrideredirect(1) self.screen_width = self.root.winfo_screenwidth() self.screen_height = self.root.winfo_screenheight() def start(self): image = PhotoImage(file='crosshair.png') canvas = Canvas(self.root, width=image.width(), height=image.height(), highlightthickness=0) canvas.pack(fill='both') canvas.create_image(0, 0, anchor=NW, image=image) self.root.geometry(f'%sx%s+%s+%s' % ( image.width(), # width image.height(), # height round(self.screen_width/2 - image.width()/2), # x offset round(self.screen_height/2 - image.height()/2), # y offset )) self.root.lift() self.root.mainloop() if __name__ == '__main__': import win32gui as w print('Started!') while(True): title = w.GetWindowText(w.GetForegroundWindow()) print(title) cw = CrosshairWindow() while 1: time.sleep(1) print(title) title = w.GetWindowText(w.GetForegroundWindow()) if title == "Apex Legends": key = True else: key = False print(title) if key==True: cw.start() else: cw.root.destroy()
报错信息
C:\ProgramData\Anaconda3\python.exe "C:/Users/Hosisora_Ling/Documents/Tencent Files/2374416274/FileRecv/crosshair.py" Started! 新建文件夹 – C:\Users\Hosisora_Ling\Documents\Tencent Files\2374416274\FileRecv\crosshair.py Traceback (most recent call last): File "C:\Users\Hosisora_Ling\Documents\Tencent Files\2374416274\FileRecv\crosshair.py", line 41, in <module> cw.start(0) File "C:\Users\Hosisora_Ling\Documents\Tencent Files\2374416274\FileRecv\crosshair.py", line 33, in start self.root.lift() File "C:\ProgramData\Anaconda3\lib\tkinter\__init__.py", line 1026, in tkraise self.tk.call('raise', self._w, aboveThis) _tkinter.TclError: can't invoke "raise" command: application has been destroyed 进程已结束,退出代码1#program ended with exit code 1
错误分析
- 循环逻辑混乱:外层
while(True)每次循环都新建CrosshairWindow实例,内层循环又重复执行窗口显示/销毁操作,导致窗口被销毁后,后续代码仍尝试调用已失效的实例方法。 - Tkinter窗口使用错误:使用
Toplevel作为主窗口(Toplevel依赖主Tk()实例),且mainloop()会阻塞线程,不能在循环中反复调用,否则会导致线程阻塞和窗口状态混乱。 - 窗口销毁后重复操作:当游戏不是前台窗口时销毁了
cw.root,但切回游戏时仍使用同一个已销毁的实例调用start(),引发"application has been destroyed"错误。 - 图片垃圾回收问题:
PhotoImage在start()函数内定义,函数执行完毕后会被Python垃圾回收,导致准星图片无法显示。
修复方案及代码
修复思路
- 使用
Tk()作为主窗口,避免Toplevel的依赖问题。 - 用Tkinter的
after()方法代替time.sleep()和死循环,实现非阻塞的前台窗口检测。 - 只维护一个准星窗口实例,通过
withdraw()和deiconify()控制显示/隐藏,而非反复销毁重建。 - 将
PhotoImage绑定到实例属性,避免被垃圾回收。
修复后的代码
import win32gui as w from tkinter import Tk, Canvas, PhotoImage class CrosshairWindow: def __init__(self): # 初始化主窗口 self.root = Tk() self.root.attributes( '-transparentcolor', '#ffffff', '-topmost', True ) self.root.overrideredirect(True) # 获取屏幕尺寸 self.screen_width = self.root.winfo_screenwidth() self.screen_height = self.root.winfo_screenheight() # 加载准星图片并绑定为实例属性 self.image = PhotoImage(file='crosshair.png') # 创建画布并添加图片 self.canvas = Canvas(self.root, width=self.image.width(), height=self.image.height(), highlightthickness=0) self.canvas.pack(fill='both') self.canvas.create_image(0, 0, anchor='nw', image=self.image) # 定位准星到屏幕中心 self.root.geometry(f'{self.image.width()}x{self.image.height()}+{round(self.screen_width/2 - self.image.width()/2)}+{round(self.screen_height/2 - self.image.height()/2)}') # 初始隐藏窗口 self.root.withdraw() # 启动窗口检测循环 self.check_foreground_window() def check_foreground_window(self): # 获取当前前台窗口标题 window_title = w.GetWindowText(w.GetForegroundWindow()) # 根据窗口标题控制准星显示/隐藏 if window_title == "Apex Legends": if self.root.state() == 'withdrawn': self.root.deiconify() self.root.lift() else: if self.root.state() != 'withdrawn': self.root.withdraw() # 每隔500毫秒重复检测(可调整间隔) self.root.after(500, self.check_foreground_window) def start(self): self.root.mainloop() if __name__ == '__main__': print('Started!') crosshair = CrosshairWindow() crosshair.start()
说明
- 确保
crosshair.png和脚本在同一目录下,或修改图片路径为绝对路径。 - 检测间隔设为500毫秒,可根据需求调整
after()的第一个参数(单位:毫秒)。 - 窗口始终保持置顶(
-topmost),确保在游戏窗口上方显示。
内容的提问来源于stack exchange,提问作者星空凌
相关产品推荐
相关产品推荐

