写着玩儿的。
获取窗口与操作窗口
最基础的功能,在UI层进行测试要对窗口数据进行获取,以便于后续处理,同时也要能够模拟人对窗口进行操作行为。
使用Spy++获取句柄

首先获得雷电模拟器窗口的句柄(窗口不能最小化),以便于进行相应操作。
获取窗口截图
handle = win32gui.FindWindow("LDPlayerMainFrame", "雷电模拟器") self.hWnd = handle self.chWnd = win32gui.FindWindowEx(handle, None, "RenderWindow", "TheRender") self.app = QApplication(sys.argv) self.screen = QApplication.primaryScreen()
有了句柄之后就可以通过QApplication获取当前窗口的截图并保存。
def grab_snapshot(self, x, y, width, height): img = self.screen.grabWindow(self.hWnd, x, y, width, height).toImage() img.save("snapshot.bmp")
模拟点击、拖拽等操作
不仅如此,点击,拖拽等操作也可以通过win32api库进行实现。
def drag(self, cx, cy, d_x=0, d_y=0, drag_times=3): self.windowRec = win32gui.GetWindowRect(self.chWnd) # 目标子句柄窗口的坐标 x = cx + self.windowRec[0] + int((random.random() - 1) * 6) y = cy + self.windowRec[1] + int((random.random() - 1) * 6) focus_pos = win32gui.ScreenToClient(self.chWnd, (x, y)) long_position = win32api.MAKELONG(focus_pos[0], focus_pos[1]) win32api.PostMessage(self.chWnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, long_position) # 模拟鼠标按下 for num in range(drag_times): x += int(d_x / drag_times) y += int(d_y / drag_times) focus_pos = win32gui.ScreenToClient(self.chWnd, (x, y)) long_position = win32api.MAKELONG(focus_pos[0], focus_pos[1]) win32api.PostMessage(self.chWnd, win32con.WM_MOUSEMOVE, 0, long_position) time.sleep(0.05) win32api.PostMessage(self.chWnd, win32con.WM_LBUTTONUP, 0000, long_position) # 模拟鼠标弹起 def click_xy(self, cx, cy, d_x=0, d_y=0): if self.shouldInterrupt == 1: return self.windowRec = win32gui.GetWindowRect(self.chWnd) # 目标子句柄窗口的坐标 x = cx + d_x + self.windowRec[0] + int((random.random() - 1) * 2) # 计算相对x坐标,并增加扰动 y = cy + d_y + self.windowRec[1] + int((random.random() - 1) * 2) # 计算相对y坐标,并增加扰动 print(x, y) # 检查坐标 click_pos = win32gui.ScreenToClient(self.chWnd, (x, y)) long_position = win32api.MAKELONG(click_pos[0], click_pos[1]) # 模拟鼠标指针 传送到指定坐标 # win32gui.PostMessage(self.chWnd, win32con.WM_ACTIVATE, win32con.WA_ACTIVE, 0) win32api.PostMessage(self.chWnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, long_position) # 模拟鼠标按下 win32api.PostMessage(self.chWnd, win32con.WM_LBUTTONUP, 0000, long_position) # 模拟鼠标弹起 def doClick(self, pos, click_type): cx = 0 cy = 0 # 点击正中心 if click_type == 5: cx = int(pos['rectangle'][0][0] + pos['shape'][0] / 2) cy = int(pos['rectangle'][0][1] + pos['shape'][1] / 2 - 34) # 点击正右侧 elif click_type == 6: cx = int(pos['rectangle'][0][0] + pos['shape'][0]) cy = int(pos['rectangle'][0][1] + pos['shape'][1] / 2 - 34) # 点击正下方 elif click_type == 8: cx = int(pos['rectangle'][0][0] + pos['shape'][0] / 2) cy = int(pos['rectangle'][0][1] + pos['shape'][1] - 34) # 点击左下角 elif click_type == 9: cx = int(pos['rectangle'][0][0] + pos['shape'][0]) cy = int(pos['rectangle'][0][1] + pos['shape'][1] - 34) self.click_xy(cx, cy)
图标比对
获取了窗口截图之后要查找需求点击的图标在图片中的位置。封装功能后具体工作逻辑交由工作流程来调用。这里使用了网易的aircv库,其实就是个opencv调用模板匹配算法。
# 使用aircv库进行图像匹配(底层是opencv) def match_img(self, imgsrc, imgobj, confidencevalue=0.85): if self.shouldInterrupt == 1: return False imsrc = ac.imread(imgsrc) imobj = ac.imread(imgobj) match_result = ac.find_template(imsrc, imobj, confidencevalue) if match_result is not None: match_result['shape'] = (imsrc.shape[1], imsrc.shape[0]) print(match_result) return match_result else: print("Cannot find pic related") return None
无脑调库就是没啥技术含量的活了。比对原图和需求图标,设置一个置信度作为阈值。然后再封装一层弄出各种方便调用的函数。比如基于图标位置点击,基于图标位置拖动,设定时长循环查找等等。
简易UI部分
Python的UI做起来真的蛋疼,特别是没怎么用过Python。使用wxPython做了个简单(丑陋)的UI。
使用订阅发布模式进行主线程与工作线程间交互
Pubsub模式在应用过MQTT协议后已经很熟悉了。所有的工作线程都通过订阅主线程信息完成状态的更变,主线程也通过接收工作线程的信息完成按钮的禁用。
#发布 wx.CallAfter(pub.sendMessage, "interrupt", msg=0) #接收 pub.subscribe(self.check_interrupt, "interrupt")