def __init__(self, addr=DEFAULT_ADDR): self.reactor = StdRpcReactor() self.reactor.register('Dump', self.Dump) self.reactor.register('GetSDKVersion', self.GetSDKVersion) self.reactor.register('GetDebugProfilingData', self.GetDebugProfilingData) self.reactor.register('GetScreenSize', self.GetScreenSize) self.reactor.register('Screenshot', self.Screenshot) self.reactor.register('Click', self.Click) self.reactor.register('Swipe', self.Swipe) self.reactor.register('LongClick', self.LongClick) self.reactor.register('SetForeground', self.SetForeground) self.reactor.register('ConnectWindow', self.ConnectWindow) transport = TcpSocket() transport.bind(addr) self.rpc = StdRpcEndpointController(transport, self.reactor) self.running = False self.root = None
def run(self): self.reactor = StdRpcReactor() self.reactor.register('Dump', self.Dump) self.reactor.register('GetSDKVersion', self.GetSDKVersion) self.reactor.register('GetDebugProfilingData', self.GetDebugProfilingData) self.reactor.register('GetScreenSize', self.GetScreenSize) self.reactor.register('Screenshot', self.Screenshot) self.reactor.register('Click', self.Click) self.reactor.register('Swipe', self.Swipe) self.reactor.register('LongClick', self.LongClick) self.reactor.register('SetForeground', self.SetForeground) self.reactor.register('ConnectWindow', self.ConnectWindow) self.reactor.register('Scroll', self.Scroll) self.reactor.register('RClick', self.RClick) self.reactor.register('DoubleClick', self.DoubleClick) self.reactor.register('KeyEvent', self.KeyEvent) transport = TcpSocket() transport.bind(self.addr) self.rpc = StdRpcEndpointController(transport, self.reactor) if self.running is False: self.running = True self.rpc.serve_forever()
class PocoSDKOSX(object): def __init__(self, addr=DEFAULT_ADDR): self.reactor = StdRpcReactor() self.reactor.register('Dump', self.Dump) self.reactor.register('GetSDKVersion', self.GetSDKVersion) self.reactor.register('GetDebugProfilingData', self.GetDebugProfilingData) self.reactor.register('GetScreenSize', self.GetScreenSize) self.reactor.register('Screenshot', self.Screenshot) self.reactor.register('Click', self.Click) self.reactor.register('Swipe', self.Swipe) self.reactor.register('LongClick', self.LongClick) self.reactor.register('SetForeground', self.SetForeground) self.reactor.register('ConnectWindow', self.ConnectWindow) transport = TcpSocket() transport.bind(addr) self.rpc = StdRpcEndpointController(transport, self.reactor) self.running = False self.root = None def Dump(self, _): res = OSXUIDumper(self.root).dumpHierarchy() return res def SetForeground(self): self.root.AXMinimized = False self.app.AXFrontmost = True def GetSDKVersion(self): return '0.0.1' def GetDebugProfilingData(self): return {} def GetScreenSize(self): Width = self.root.AXSize[0] Height = self.root.AXSize[1] return [Width, Height] def Screenshot(self, width): self.SetForeground() size = self.root.AXSize pos = self.root.AXPosition pyautogui.screenshot( 'Screenshot.png', (pos[0], pos[1], size[0], size[1])).save('Screenshot.png') f = open(r'Screenshot.png', 'rb') deflated = zlib.compress(f.read()) ls_f = base64.b64encode(deflated) f.close() return [ls_f, "png.deflate"] # self.root.ToBitmap().ToFile('Screenshot.bmp') # f = open(r'Screenshot.bmp', 'rb') # ls_f = base64.b64encode(f.read()) # f.close() # return [ls_f, "bmp"] def Click(self, x, y): self.SetForeground() size = self.root.AXSize pos = self.root.AXPosition pyautogui.moveTo(pos[0] + size[0] * x, pos[1] + size[1] * y) pyautogui.click(pos[0] + size[0] * x, pos[1] + size[1] * y) return True def Swipe(self, x1, y1, x2, y2, duration): self.SetForeground() Left = self.root.AXPosition[0] Top = self.root.AXPosition[1] Width = self.root.AXSize[0] Height = self.root.AXSize[1] x1 = Left + Width * x1 y1 = Top + Height * y1 x2 = Left + Width * x2 y2 = Top + Height * y2 pyautogui.moveTo(x1, y1) pyautogui.dragTo(x2, y2, duration) return True def LongClick(self, x, y, duration): self.SetForeground() Left = self.root.AXPosition[0] Top = self.root.AXPosition[1] Width = self.root.AXSize[0] Height = self.root.AXSize[1] x = Left + Width * x y = Top + Height * y pyautogui.moveTo(x, y) pyautogui.dragTo(x, y, duration) return True def EnumWindows(self, selector): names = [] if 'bundleid' in selector: self.app = atomac.getAppRefByBundleId(selector['bundleid']) windows = self.app.windows() for i, w in enumerate(windows): names.append((w.AXTitle, i)) return names if 'appname' in selector: self.app = atomac.getAppRefByLocalizedName(selector['appname']) windows = self.app.windows() for i, w in enumerate(windows): names.append((w.AXTitle, i)) return names if 'appname_re' in selector: # 此方法由于MacOS API,问题较多 apps = atomac.NativeUIElement._getRunningApps() # 获取当前运行的所有应用程序 appset = set() # 应用程序集合 appnameset = set() # 应用程序标题集合 for t in apps: tempapp = atomac.getAppRefByPid(t.processIdentifier()) if str(tempapp) == str(atomac.AXClasses.NativeUIElement() ): # 通过trick判断应用程序是都否为空 continue attrs = tempapp.getAttributes() if 'AXTitle' in attrs: tit = tempapp.AXTitle if re.match(selector['appname_re'], tit): appset.add(tempapp) appnameset.add( tit) # 这里有Bug,可能会获取到进程的不同副本,所以要通过名字去判断是否唯一 if len(appnameset) is 0: raise InvalidSurfaceException( selector, "Can't find any applications by the given parameter") if len(appnameset) != 1: raise NonuniqueSurfaceException(selector) while len(names) is 0: # 有可能有多个副本,但只有一个真的应用程序有窗口,所以要枚举去找 if len(appset) is 0: return names self.app = appset.pop() windows = self.app.windows() # 获取当前应用程序的所有窗口 for i, w in enumerate(windows): names.append((w.AXTitle, i)) return names return names def ConnectWindowsByWindowTitle(self, selector, wlist): hn = set() for n in wlist: if selector['windowtitle'] == n[0]: hn.add(n[1]) if len(hn) == 0: return -1 return hn def ConnectWindowsByWindowTitleRe(self, selector, wlist): hn = set() for n in wlist: if re.match(selector['windowtitle_re'], n[0]): hn.add(n[1]) if len(hn) == 0: return -1 return hn def ConnectWindow(self, selector): # 目前来说,如下处理,以后添加更多的参数后需修改代码逻辑 argunums = 0 if 'bundleid' in selector: argunums += 1 if 'appname' in selector: argunums += 1 if 'appname_re' in selector: argunums += 1 if argunums == 0: raise ValueError("Expect bundleid or appname, got none") elif argunums != 1: raise ValueError( "Too many arguments, only need bundleid or appname or appname_re" ) winlist = self.EnumWindows(selector) handleSetList = [] if 'windowtitle' in selector: handleSetList.append( self.ConnectWindowsByWindowTitle(selector, winlist)) if 'windowindex' in selector: handleSetList.append(set([selector['windowindex']])) if "windowtitle_re" in selector: handleSetList.append( self.ConnectWindowsByWindowTitleRe(selector, winlist)) while -1 in handleSetList: handleSetList.remove(-1) if len(handleSetList) == 0: # 三种方法都找不到窗口 raise InvalidSurfaceException( selector, "Can't find any applications by the given parameter") handleSet = reduce(operator.__and__, handleSetList) if len(handleSet) == 0: raise InvalidSurfaceException( selector, "Can't find any applications by the given parameter") elif len(handleSet) != 1: raise NonuniqueSurfaceException(selector) else: hn = handleSet.pop() w = self.app.windows() if len(w) <= hn: raise IndexError( "Unable to find the specified window through the index, you may have closed the specified window during the run" ) self.root = self.app.windows()[hn] self.SetForeground() def run(self): if self.running is False: self.running = True self.rpc.serve_forever()
class PocoSDKWindows(object): def __init__(self, addr=DEFAULT_ADDR): self.reactor = StdRpcReactor() self.reactor.register('Dump', self.Dump) self.reactor.register('SetText', self.SetText) self.reactor.register('GetSDKVersion', self.GetSDKVersion) self.reactor.register('GetDebugProfilingData', self.GetDebugProfilingData) self.reactor.register('GetScreenSize', self.GetScreenSize) self.reactor.register('Screenshot', self.Screenshot) self.reactor.register('Click', self.Click) self.reactor.register('Swipe', self.Swipe) self.reactor.register('LongClick', self.LongClick) self.reactor.register('KeyEvent', self.KeyEvent) self.reactor.register('SetForeground', self.SetForeground) self.reactor.register('ConnectWindow', self.ConnectWindow) transport = TcpSocket() transport.bind(addr) self.rpc = StdRpcEndpointController(transport, self.reactor) self.running = False UIAuto.OPERATION_WAIT_TIME = 0.1 # make operation faster self.root = None def Dump(self, _): res = WindowsUIDumper(self.root).dumpHierarchy() return res def SetText(self, id, val2): control = UIAuto.ControlFromHandle(id) if not control or not isinstance(val2, string_types): raise UnableToSetAttributeException("text", control) else: control.SetValue(val2) def GetSDKVersion(self): return '0.0.1' def GetDebugProfilingData(self): return {} def GetScreenSize(self): Width = self.root.BoundingRectangle[2] - self.root.BoundingRectangle[0] Height = self.root.BoundingRectangle[3] - self.root.BoundingRectangle[1] return [Width, Height] def JudgeSize(self): size = self.GetScreenSize() if size[0] == 0 or size[1] == 0: raise InvalidSurfaceException(self, "You may have minimized your window or the window is too small!") def Screenshot(self, width): self.JudgeSize() self.root.ToBitmap().ToFile('Screenshot.bmp') f = open(r'Screenshot.bmp', 'rb') deflated = zlib.compress(f.read()) ls_f = base64.b64encode(deflated) f.close() return [ls_f, "bmp.deflate"] # self.root.ToBitmap().ToFile('Screenshot.bmp') # f = open(r'Screenshot.bmp', 'rb') # ls_f = base64.b64encode(f.read()) # f.close() # return [ls_f, "bmp"] def Click(self, x, y): self.JudgeSize() self.root.Click(x, y) return True def Swipe(self, x1, y1, x2, y2, duration): self.JudgeSize() Left = self.root.BoundingRectangle[0] Top = self.root.BoundingRectangle[1] Width = self.root.BoundingRectangle[2] - self.root.BoundingRectangle[0] Height = self.root.BoundingRectangle[3] - self.root.BoundingRectangle[1] x1 = Left + Width * x1 y1 = Top + Height * y1 x2 = Left + Width * x2 y2 = Top + Height * y2 UIAuto.MAX_MOVE_SECOND = duration * 10 UIAuto.DragDrop(int(x1), int(y1), int(x2), int(y2)) return True def LongClick(self, x, y, duration): self.JudgeSize() Left = self.root.BoundingRectangle[0] Top = self.root.BoundingRectangle[1] Width = self.root.BoundingRectangle[2] - self.root.BoundingRectangle[0] Height = self.root.BoundingRectangle[3] - self.root.BoundingRectangle[1] x = Left + Width * x y = Top + Height * y UIAuto.MAX_MOVE_SECOND = duration * 10 UIAuto.DragDrop(int(x), int(y), int(x), int(y)) return True def KeyEvent(self, keycode): UIAuto.SendKeys(keycode) return True def SetForeground(self): win32gui.ShowWindow(self.root.Handle, win32con.SW_SHOWNORMAL) UIAuto.Win32API.SetForegroundWindow(self.root.Handle) return True def EnumWindows(self): hWndList = [] def foo(hwnd, mouse): if win32gui.IsWindow(hwnd): hWndList.append(hwnd) win32gui.EnumWindows(foo, 0) return hWndList def ConnectWindowsByTitle(self, title): hn = set() hWndList = self.EnumWindows() for handle in hWndList: title_temp = win32gui.GetWindowText(handle) if PY2: title_temp = title_temp.decode("gbk") if title == title_temp: hn.add(handle) if len(hn) == 0: print return hn def ConnectWindowsByTitleRe(self, title_re): hn = set() hWndList = self.EnumWindows() for handle in hWndList: title = win32gui.GetWindowText(handle) if PY2: title = title.decode("gbk") if re.match(title_re, title): hn.add(handle) if len(hn) == 0: return -1 return hn def ConnectWindowsByHandle(self, handle): hn = set() hWndList = self.EnumWindows() for handle_temp in hWndList: if int(handle_temp) == int(handle): hn.add(handle) break if len(hn) == 0: return -1 return hn def ConnectWindow(self, selector, foreground=False): if foreground: time.sleep(1) self.root = UIAuto.GetForegroundControl() return True handleSetList = [] if 'title' in selector: handleSetList.append(self.ConnectWindowsByTitle(selector['title'])) if 'handle' in selector: handleSetList.append(self.ConnectWindowsByHandle(selector['handle'])) if "title_re" in selector: handleSetList.append(self.ConnectWindowsByTitleRe(selector['title_re'])) while -1 in handleSetList: handleSetList.remove(-1) if len(handleSetList) == 0: return False handleSet = handleSetList[0] for s in handleSetList: handleSet = s & handleSet if len(handleSet) == 0: return False elif len(handleSet) != 1: raise NonuniqueSurfaceException(selector) else: hn = handleSet.pop() if hn: self.root = UIAuto.ControlFromHandle(hn) return True else: return False def run(self, use_foregrond_window=False): if self.running is False: self.running = True if use_foregrond_window: self.ConnectWindow(set(), True) self.rpc.serve_forever()
import threading from poco.sdk.std.rpc.controller import StdRpcEndpointController from poco.sdk.std.rpc.reactor import StdRpcReactor from poco.utils.net.transport.tcp import TcpSocket from poco.utils.net.stdbroker import StdBroker def Dump(arg): return 'this is Dump ' + arg if __name__ == '__main__': # broker = StdBroker('tcp://*:15003', 'tcp://*:15004') reactor = StdRpcReactor() reactor.register('Dump', Dump) responser = TcpSocket() responser.connect(('localhost', 15003)) rpc_responser = StdRpcEndpointController(responser, reactor) t = threading.Thread(target=rpc_responser.serve_forever) t.daemon = True t.start() requester = TcpSocket() requester.connect(('localhost', 15004)) rpc_requester = StdRpcEndpointController(requester, StdRpcReactor()) t = threading.Thread(target=rpc_requester.serve_forever) t.daemon = True
class PocoSDKWindows(object): def __init__(self, addr=DEFAULT_ADDR): self.reactor = None self.addr = addr self.running = False UIAuto.OPERATION_WAIT_TIME = 0.05 # make operation faster self.root = None def Dump(self, _): res = WindowsUIDumper(self.root).dumpHierarchy() return res def SetText(self, id, val2): control = UIAuto.ControlFromHandle(id) if not control or not isinstance(val2, string_types): raise UnableToSetAttributeException("text", control) else: control.SetValue(val2) def GetSDKVersion(self): return '0.0.1' def GetDebugProfilingData(self): return {} def GetScreenSize(self): Width = self.root.BoundingRectangle[2] - self.root.BoundingRectangle[0] Height = self.root.BoundingRectangle[3] - self.root.BoundingRectangle[1] return [Width, Height] def GetWindowRect(self): return self.root.BoundingRectangle def JudgeSize(self): self.SetForeground() # 先打开窗口再获取大小,否则最小化的窗口获取到的大小会为0 size = self.GetScreenSize() if size[0] == 0 or size[1] == 0: raise InvalidSurfaceException( self, "You may have minimized or closed your window or the window is too small!" ) def Screenshot(self, width): self.JudgeSize() self.root.ToBitmap().ToFile('Screenshot.bmp') f = open(r'Screenshot.bmp', 'rb') deflated = zlib.compress(f.read()) # 压缩图片 ls_f = base64.b64encode(deflated) f.close() return [ls_f, "bmp.deflate"] # self.root.ToBitmap().ToFile('Screenshot.bmp') # f = open(r'Screenshot.bmp', 'rb') # ls_f = base64.b64encode(f.read()) # f.close() # return [ls_f, "bmp"] def Click(self, x, y): self.JudgeSize() self.root.Click(x, y) return True def RClick(self, x, y): self.JudgeSize() self.root.RightClick(x, y) return True def DoubleClick(self, x, y): self.JudgeSize() self.root.DoubleClick(x, y) return True def Swipe(self, x1, y1, x2, y2, duration, **kwargs): self.JudgeSize() Left = self.root.BoundingRectangle[0] Top = self.root.BoundingRectangle[1] Width = self.root.BoundingRectangle[2] - self.root.BoundingRectangle[0] Height = self.root.BoundingRectangle[3] - self.root.BoundingRectangle[1] x1 = int(Left + Width * x1) # 比例换算 y1 = int(Top + Height * y1) x2 = int(Left + Width * x2) y2 = int(Top + Height * y2) UIAuto.MAX_MOVE_SECOND = duration * 10 # 同步到跟UIAutomation库的时间设定一样 UIAuto.DragDrop(int(x1), int(y1), int(x2), int(y2)) return True def LongClick(self, x, y, duration, **kwargs): self.JudgeSize() Left = self.root.BoundingRectangle[0] Top = self.root.BoundingRectangle[1] Width = self.root.BoundingRectangle[2] - self.root.BoundingRectangle[0] Height = self.root.BoundingRectangle[3] - self.root.BoundingRectangle[1] x = Left + Width * x y = Top + Height * y UIAuto.MAX_MOVE_SECOND = duration * 10 UIAuto.DragDrop(int(x), int(y), int(x), int(y)) return True def Scroll(self, direction, percent, duration): if direction not in ('vertical', 'horizontal'): return False if direction == 'horizontal': return False self.JudgeSize() x = 0.5 # 先把鼠标移到窗口中间,这样才能保证滚动的是这个窗口。 y = 0.5 steps = percent Left = self.root.BoundingRectangle[0] Top = self.root.BoundingRectangle[1] Width = self.root.BoundingRectangle[2] - self.root.BoundingRectangle[0] Height = self.root.BoundingRectangle[3] - self.root.BoundingRectangle[1] x = Left + Width * x y = Top + Height * y x = int(x) y = int(y) UIAuto.MoveTo(x, y) interval = float(duration - 0.3 * steps) / (abs(steps) + 1) # 实现滚动时间 if interval <= 0: interval = 0.1 if steps < 0: for i in range(0, abs(steps)): time.sleep(interval) UIAuto.WheelUp(1) else: for i in range(0, abs(steps)): time.sleep(interval) UIAuto.WheelDown(1) return True def KeyEvent(self, keycode): self.JudgeSize() UIAuto.SendKeys(keycode) return True def SetForeground(self): win32gui.ShowWindow(self.root.Handle, win32con.SW_SHOWNORMAL) # 先把窗口取消最小化 UIAuto.Win32API.SetForegroundWindow( self.root.Handle) # 再把窗口设为前台,方便点击和截图 return True def EnumWindows(self): hWndList = [] # 枚举所有窗口,并把有效窗口handle保存在hwndlist里 def foo(hwnd, mouse): if win32gui.IsWindow(hwnd): hWndList.append(hwnd) win32gui.EnumWindows(foo, 0) return hWndList def ConnectWindowsByTitle(self, title): hn = set() # 匹配窗口的集合,把所有标题匹配上的窗口handle都保存在这个集合里 hWndList = self.EnumWindows() for handle in hWndList: title_temp = win32gui.GetWindowText(handle) if PY2: title_temp = title_temp.decode( "gbk") # py2要解码成GBK,WindowsAPI中文返回的一般都是GBK if title == title_temp: hn.add(handle) if len(hn) == 0: return -1 return hn def ConnectWindowsByTitleRe(self, title_re): hn = set() # 匹配窗口的集合,把所有标题(正则表达式)匹配上的窗口handle都保存在这个集合里 hWndList = self.EnumWindows() for handle in hWndList: title = win32gui.GetWindowText(handle) if PY2: title = title.decode("gbk") if re.match(title_re, title): hn.add(handle) if len(hn) == 0: return -1 return hn def ConnectWindowsByHandle(self, handle): hn = set() # 匹配窗口的集合,把所有handle匹配上的窗口handle都保存在这个集合里 hWndList = self.EnumWindows() for handle_temp in hWndList: if int(handle_temp) == int(handle): hn.add(handle) break if len(hn) == 0: return -1 return hn def ConnectWindow(self, selector): # 目前来说,如下处理,以后添加更多的参数后需修改代码逻辑 argunums = 0 if 'handle' in selector: argunums += 1 if 'title' in selector: argunums += 1 if 'title_re' in selector: argunums += 1 if argunums == 0: raise ValueError("Expect handle or title, got none") elif argunums != 1: raise ValueError( "Too many arguments, only need handle or title or title_re") handleSetList = [] if 'title' in selector: handleSetList.append(self.ConnectWindowsByTitle(selector['title'])) if 'handle' in selector: handleSetList.append( self.ConnectWindowsByHandle(selector['handle'])) if "title_re" in selector: handleSetList.append( self.ConnectWindowsByTitleRe(selector['title_re'])) while -1 in handleSetList: handleSetList.remove(-1) # 有些参数没有提供会返回-1.把所有的-1去掉 if len(handleSetList) is 0: raise InvalidSurfaceException( selector, "Can't find any windows by the given parameter") handleSet = reduce(operator.__and__, handleSetList) # 提供了多个参数来确定唯一一个窗口,所以要做交集,取得唯一匹配的窗口 if len(handleSet) == 0: raise InvalidSurfaceException( selector, "Can't find any windows by the given parameter") elif len(handleSet) != 1: raise NonuniqueSurfaceException(selector) else: hn = handleSet.pop() # 取得那个唯一的窗口 self.root = UIAuto.ControlFromHandle(hn) if self.root is None: raise InvalidSurfaceException( selector, "Can't find any windows by the given parameter") self.SetForeground() def run(self): self.reactor = StdRpcReactor() self.reactor.register('Dump', self.Dump) # 注册各种函数 self.reactor.register('SetText', self.SetText) self.reactor.register('GetSDKVersion', self.GetSDKVersion) self.reactor.register('GetDebugProfilingData', self.GetDebugProfilingData) self.reactor.register('GetScreenSize', self.GetScreenSize) self.reactor.register('Screenshot', self.Screenshot) self.reactor.register('Click', self.Click) self.reactor.register('Swipe', self.Swipe) self.reactor.register('LongClick', self.LongClick) self.reactor.register('KeyEvent', self.KeyEvent) self.reactor.register('SetForeground', self.SetForeground) self.reactor.register('ConnectWindow', self.ConnectWindow) self.reactor.register('Scroll', self.Scroll) self.reactor.register('RClick', self.RClick) self.reactor.register('DoubleClick', self.DoubleClick) transport = TcpSocket() transport.bind(self.addr) self.rpc = StdRpcEndpointController(transport, self.reactor) if self.running is False: self.running = True self.rpc.serve_forever()
class PocoSDKOSX(object): def __init__(self, addr=DEFAULT_ADDR): self.reactor = None self.addr = addr self.running = False self.root = None self.keyboard = Controller() def Dump(self, _): res = OSXUIDumper(self.root).dumpHierarchy() return res def SetForeground(self): self.root.AXMinimized = False self.app.AXFrontmost = True def GetSDKVersion(self): return '0.0.1' def GetDebugProfilingData(self): return {} def GetScreenSize(self): Width = self.root.AXSize[0] Height = self.root.AXSize[1] return [Width, Height] def GetWindowRect(self): Width = self.root.AXSize[0] Height = self.root.AXSize[1] return [ self.root.AXPosition[0], self.root.AXPosition[1], self.root.AXPosition[0] + Width, self.root.AXPosition[1] + Height ] def KeyEvent(self, keycode): waittime = 0.05 for c in keycode: self.keyboard.press(key=c) self.keyboard.release(key=c) time.sleep(waittime) return True def Screenshot(self, width): self.SetForeground() size = self.root.AXSize pos = self.root.AXPosition pyautogui.screenshot( 'Screenshot.png', (pos[0], pos[1], size[0], size[1])).save('Screenshot.png') f = open(r'Screenshot.png', 'rb') deflated = zlib.compress(f.read()) ls_f = base64.b64encode(deflated) f.close() return [ls_f, "png.deflate"] # self.root.ToBitmap().ToFile('Screenshot.bmp') # f = open(r'Screenshot.bmp', 'rb') # ls_f = base64.b64encode(f.read()) # f.close() # return [ls_f, "bmp"] def Click(self, x, y): self.SetForeground() size = self.root.AXSize pos = self.root.AXPosition OSXFunc.click(pos[0] + size[0] * x, pos[1] + size[1] * y) return True def RClick(self, x, y): self.SetForeground() size = self.root.AXSize pos = self.root.AXPosition OSXFunc.rclick(pos[0] + size[0] * x, pos[1] + size[1] * y) return True def DoubleClick(self, x, y): self.SetForeground() size = self.root.AXSize pos = self.root.AXPosition OSXFunc.doubleclick(pos[0] + size[0] * x, pos[1] + size[1] * y) return True def Swipe(self, x1, y1, x2, y2, duration): self.SetForeground() Left = self.root.AXPosition[0] Top = self.root.AXPosition[1] Width = self.root.AXSize[0] Height = self.root.AXSize[1] x1 = Left + Width * x1 y1 = Top + Height * y1 x2 = Left + Width * x2 y2 = Top + Height * y2 sx = abs(x1 - x2) sy = abs(y1 - y2) stepx = sx / (duration * 10.0) # 将滑动距离分割,实现平滑的拖动 stepy = sy / (duration * 10.0) OSXFunc.move(x1, y1) OSXFunc.press(x1, y1) duration = int(duration * 10.0) for i in range(duration + 1): OSXFunc.drag(x1 + stepx * i, y1 + stepy * i) time.sleep(0.1) OSXFunc.release(x2, y2) return True def LongClick(self, x, y, duration, **kwargs): self.SetForeground() # poco std暂不支持选择鼠标按键 button = kwargs.get("button", "left") if button not in ("left", "right"): raise ValueError("Unknow button: " + button) if button is "left": button = 1 else: button = 2 Left = self.root.AXPosition[0] Top = self.root.AXPosition[1] Width = self.root.AXSize[0] Height = self.root.AXSize[1] x = Left + Width * x y = Top + Height * y OSXFunc.move(x, y) OSXFunc.press(x, y, button=button) time.sleep(duration) OSXFunc.release(x, y, button=button) return True def Scroll(self, direction, percent, duration): if direction not in ('vertical', 'horizontal'): raise ValueError( 'Argument `direction` should be one of "vertical" or "horizontal". Got {}' .format(repr(direction))) x = 0.5 # 先把鼠标移到窗口中间,这样才能保证滚动的是这个窗口。 y = 0.5 steps = percent Left = self.GetWindowRect()[0] Top = self.GetWindowRect()[1] Width = self.GetScreenSize()[0] Height = self.GetScreenSize()[1] x = Left + Width * x y = Top + Height * y x = int(x) y = int(y) OSXFunc.move(x, y) if direction == 'horizontal': interval = float(duration) / (abs(steps) + 1) if steps < 0: for i in range(0, abs(steps)): time.sleep(interval) OSXFunc.scroll(None, 1) else: for i in range(0, abs(steps)): time.sleep(interval) OSXFunc.scroll(None, -1) else: interval = float(duration) / (abs(steps) + 1) if steps < 0: for i in range(0, abs(steps)): time.sleep(interval) OSXFunc.scroll(1) else: for i in range(0, abs(steps)): time.sleep(interval) OSXFunc.scroll(-1) return True def EnumWindows(self, selector): names = [] # 一个应用程序会有多个窗口,因此我们要先枚举一个应用程序里的所有窗口 if 'bundleid' in selector: self.app = OSXFunc.getAppRefByBundleId(selector['bundleid']) windows = self.app.windows() for i, w in enumerate(windows): names.append((w.AXTitle, i)) return names if 'appname' in selector: self.app = OSXFunc.getAppRefByLocalizedName(selector['appname']) windows = self.app.windows() for i, w in enumerate(windows): names.append((w.AXTitle, i)) return names if 'appname_re' in selector: # 此方法由于MacOS API,问题较多 apps = OSXFunc.getRunningApps() # 获取当前运行的所有应用程序 appset = [] # 应用程序集合 appnameset = set() # 应用程序标题集合 for t in apps: tempapp = OSXFunc.getAppRefByPid(t.processIdentifier()) if str(tempapp) == str(atomac.AXClasses.NativeUIElement() ): # 通过trick判断应用程序是都否为空 continue attrs = tempapp.getAttributes() if 'AXTitle' in attrs: tit = tempapp.AXTitle if re.match(selector['appname_re'], tit): appset.append(tempapp) appnameset.add( tit) # 这里有Bug,可能会获取到进程的不同副本,所以要通过名字去判断是否唯一 if len(appnameset) is 0: raise InvalidSurfaceException( selector, "Can't find any applications by the given parameter") if len(appnameset) != 1: raise NonuniqueSurfaceException(selector) while len(names) is 0: # 有可能有多个副本,但只有一个真的应用程序有窗口,所以要枚举去找 if len(appset) is 0: return names self.app = appset.pop() windows = self.app.windows() # 获取当前应用程序的所有窗口 for i, w in enumerate(windows): names.append((w.AXTitle, i)) return names return names def ConnectWindowsByWindowTitle(self, selector, wlist): hn = set() for n in wlist: if selector['windowtitle'] == n[0]: hn.add(n[1]) # 添加窗口索引到集合里 if len(hn) == 0: return -1 return hn def ConnectWindowsByWindowTitleRe(self, selector, wlist): hn = set() for n in wlist: if re.match(selector['windowtitle_re'], n[0]): hn.add(n[1]) # 添加窗口索引到集合里 if len(hn) == 0: return -1 return hn def ConnectWindow(self, selector): # 目前来说,如下处理,以后添加更多的参数后需修改代码逻辑 argunums = 0 if 'bundleid' in selector: argunums += 1 if 'appname' in selector: argunums += 1 if 'appname_re' in selector: argunums += 1 if argunums == 0: raise ValueError("Expect bundleid or appname, got none") elif argunums != 1: raise ValueError( "Too many arguments, only need bundleid or appname or appname_re" ) winlist = self.EnumWindows(selector) handleSetList = [] if 'windowtitle' in selector: handleSetList.append( self.ConnectWindowsByWindowTitle(selector, winlist)) if 'windowindex' in selector: handleSetList.append(set([selector['windowindex']])) if "windowtitle_re" in selector: handleSetList.append( self.ConnectWindowsByWindowTitleRe(selector, winlist)) while -1 in handleSetList: handleSetList.remove(-1) # 有些参数没有提供会返回-1.把所有的-1去掉 if len(handleSetList) == 0: # 三种方法都找不到窗口 raise InvalidSurfaceException( selector, "Can't find any applications by the given parameter") handleSet = reduce(operator.__and__, handleSetList) # 提供了多个参数来确定唯一一个窗口,所以要做交集,取得唯一匹配的窗口 if len(handleSet) == 0: raise InvalidSurfaceException( selector, "Can't find any applications by the given parameter") elif len(handleSet) != 1: raise NonuniqueSurfaceException(selector) else: hn = handleSet.pop() # 取得该窗口的索引 w = self.app.windows() if len(w) <= hn: raise IndexError( "Unable to find the specified window through the index, you may have closed the specified window during the run" ) self.root = self.app.windows()[hn] self.SetForeground() return True def run(self): self.reactor = StdRpcReactor() self.reactor.register('Dump', self.Dump) self.reactor.register('GetSDKVersion', self.GetSDKVersion) self.reactor.register('GetDebugProfilingData', self.GetDebugProfilingData) self.reactor.register('GetScreenSize', self.GetScreenSize) self.reactor.register('Screenshot', self.Screenshot) self.reactor.register('Click', self.Click) self.reactor.register('Swipe', self.Swipe) self.reactor.register('LongClick', self.LongClick) self.reactor.register('SetForeground', self.SetForeground) self.reactor.register('ConnectWindow', self.ConnectWindow) self.reactor.register('Scroll', self.Scroll) self.reactor.register('RClick', self.RClick) self.reactor.register('DoubleClick', self.DoubleClick) self.reactor.register('KeyEvent', self.KeyEvent) transport = TcpSocket() transport.bind(self.addr) self.rpc = StdRpcEndpointController(transport, self.reactor) if self.running is False: self.running = True self.rpc.serve_forever()
class PocoSDKWindows(object): def __init__(self, addr=DEFAULT_ADDR): self.reactor = StdRpcReactor() self.reactor.register('Dump', self.Dump) self.reactor.register('SetText', self.SetText) self.reactor.register('GetSDKVersion', self.GetSDKVersion) self.reactor.register('GetDebugProfilingData', self.GetDebugProfilingData) self.reactor.register('GetScreenSize', self.GetScreenSize) self.reactor.register('Screenshot', self.Screenshot) self.reactor.register('Click', self.Click) self.reactor.register('Swipe', self.Swipe) self.reactor.register('LongClick', self.LongClick) self.reactor.register('KeyEvent', self.KeyEvent) self.reactor.register('SetForeground', self.SetForeground) self.reactor.register('ConnectWindow', self.ConnectWindow) transport = TcpSocket() transport.bind(addr) self.rpc = StdRpcEndpointController(transport, self.reactor) self.running = False UIAuto.OPERATION_WAIT_TIME = 0.1 # make operation faster self.root = None def Dump(self, _): res = WindowsUIDumper(self.root).dumpHierarchy() return res def SetText(self, id, val2): control = UIAuto.ControlFromHandle(id) if not control or not isinstance(val2, string_types): raise UnableToSetAttributeException("text", control) else: control.SetValue(val2) def GetSDKVersion(self): return '0.0.1' def GetDebugProfilingData(self): return {} def GetScreenSize(self): Width = self.root.BoundingRectangle[2] - self.root.BoundingRectangle[0] Height = self.root.BoundingRectangle[3] - self.root.BoundingRectangle[1] return [Width, Height] def JudgeSize(self): self.SetForeground() # 先打开窗口再获取大小,否则最小化的窗口获取到的大小会为0 size = self.GetScreenSize() if size[0] == 0 or size[1] == 0: raise InvalidSurfaceException( self, "You may have minimized your window or the window is too small!" ) def Screenshot(self, width): self.JudgeSize() self.root.ToBitmap().ToFile('Screenshot.bmp') f = open(r'Screenshot.bmp', 'rb') deflated = zlib.compress(f.read()) ls_f = base64.b64encode(deflated) f.close() return [ls_f, "bmp.deflate"] # self.root.ToBitmap().ToFile('Screenshot.bmp') # f = open(r'Screenshot.bmp', 'rb') # ls_f = base64.b64encode(f.read()) # f.close() # return [ls_f, "bmp"] def Click(self, x, y): self.JudgeSize() self.root.Click(x, y) return True def Swipe(self, x1, y1, x2, y2, duration): self.JudgeSize() Left = self.root.BoundingRectangle[0] Top = self.root.BoundingRectangle[1] Width = self.root.BoundingRectangle[2] - self.root.BoundingRectangle[0] Height = self.root.BoundingRectangle[3] - self.root.BoundingRectangle[1] x1 = Left + Width * x1 y1 = Top + Height * y1 x2 = Left + Width * x2 y2 = Top + Height * y2 UIAuto.MAX_MOVE_SECOND = duration * 10 # 同步到跟UIAutomation库的时间设定一样 UIAuto.DragDrop(int(x1), int(y1), int(x2), int(y2)) return True def LongClick(self, x, y, duration): self.JudgeSize() Left = self.root.BoundingRectangle[0] Top = self.root.BoundingRectangle[1] Width = self.root.BoundingRectangle[2] - self.root.BoundingRectangle[0] Height = self.root.BoundingRectangle[3] - self.root.BoundingRectangle[1] x = Left + Width * x y = Top + Height * y UIAuto.MAX_MOVE_SECOND = duration * 10 UIAuto.DragDrop(int(x), int(y), int(x), int(y)) return True def KeyEvent(self, keycode): self.JudgeSize() UIAuto.SendKeys(keycode) return True def SetForeground(self): win32gui.ShowWindow(self.root.Handle, win32con.SW_SHOWNORMAL) # 先把窗口取消最小化 UIAuto.Win32API.SetForegroundWindow( self.root.Handle) # 再把窗口设为前台,方便点击和截图 return True def EnumWindows(self): hWndList = [] def foo(hwnd, mouse): if win32gui.IsWindow(hwnd): hWndList.append(hwnd) win32gui.EnumWindows(foo, 0) return hWndList def ConnectWindowsByTitle(self, title): hn = set() hWndList = self.EnumWindows() for handle in hWndList: title_temp = win32gui.GetWindowText(handle) if PY2: title_temp = title_temp.decode( "gbk") # py2要解码成GBK,WindowsAPI中文返回的一般都是GBK if title == title_temp: hn.add(handle) if len(hn) == 0: return -1 return hn def ConnectWindowsByTitleRe(self, title_re): hn = set() hWndList = self.EnumWindows() for handle in hWndList: title = win32gui.GetWindowText(handle) if PY2: title = title.decode("gbk") if re.match(title_re, title): hn.add(handle) if len(hn) == 0: return -1 return hn def ConnectWindowsByHandle(self, handle): hn = set() hWndList = self.EnumWindows() for handle_temp in hWndList: if int(handle_temp) == int(handle): hn.add(handle) break if len(hn) == 0: return -1 return hn def ConnectWindow(self, selector): # 目前来说,如下处理,以后添加更多的参数后需修改代码逻辑 argunums = 0 if 'handle' in selector: argunums += 1 if 'title' in selector: argunums += 1 if 'title_re' in selector: argunums += 1 if argunums == 0: raise ValueError("Expect handle or title, got none") elif argunums != 1: raise ValueError( "Too many arguments, only need handle or title or title_re") handleSetList = [] if 'title' in selector: handleSetList.append(self.ConnectWindowsByTitle(selector['title'])) if 'handle' in selector: handleSetList.append( self.ConnectWindowsByHandle(selector['handle'])) if "title_re" in selector: handleSetList.append( self.ConnectWindowsByTitleRe(selector['title_re'])) while -1 in handleSetList: handleSetList.remove(-1) if len(handleSetList) is 0: raise InvalidSurfaceException( selector, "Can't find any windows by the given parameter") handleSet = reduce(operator.__and__, handleSetList) if len(handleSet) == 0: raise InvalidSurfaceException( selector, "Can't find any windows by the given parameter") elif len(handleSet) != 1: raise NonuniqueSurfaceException(selector) else: hn = handleSet.pop() self.root = UIAuto.ControlFromHandle(hn) if self.root is None: raise InvalidSurfaceException( selector, "Can't find any windows by the given parameter") def run(self): if self.running is False: self.running = True self.rpc.serve_forever()