utils.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import json
  2. import uiautomator2 as u2
  3. class UiElement(object):
  4. def __init__(self, d: u2.Device, xpath: str = None, description: str = None, debug: bool = True, point: dict = None) -> None:
  5. x, y = 0, 0
  6. self.info = {}
  7. self.d = d
  8. self.debug = debug
  9. if point is not None:
  10. self.info['point'] = point
  11. self.info['xpath'] = xpath
  12. self.info['description'] = description
  13. return
  14. if xpath is not None:
  15. self.info['xpath'] = xpath
  16. el = d.xpath(xpath)
  17. x, y = el.center()
  18. elif description is not None:
  19. self.info['description'] = description
  20. el = d(description=description)
  21. x, y = el.center()
  22. self.info['point'] = {'x': x, 'y': y}
  23. def log(self, *args, sep=' ', end='\n', file=None):
  24. if self.debug:
  25. print(args, sep=sep, end=end, file=file)
  26. def click(self):
  27. """
  28. 点击
  29. """
  30. point = self.info['point']
  31. self.log("点击元素:", self.info)
  32. self.d.shell(f"input tap {point['x']} {point['y']}")
  33. def text(self, text: str = None):
  34. """
  35. 设置文本框内容
  36. text 文本内容
  37. """
  38. point = self.info['point']
  39. self.log("点击元素:", self.info)
  40. self.d.shell(f"input tap {point['x']} {point['y']}")
  41. self.log("设置文本:", text)
  42. self.d.shell(f"input text '{text}'") # 输入文本内容
  43. def clear(self, length: int = 10):
  44. """
  45. 清空文本框
  46. """
  47. point = self.info['point']
  48. self.log("点击元素:", self.info)
  49. self.d.shell(f"input tap {point['x'] + length} {point['y']}")
  50. self.log("清空文本框:")
  51. # 模拟删除键输入
  52. # self.d.shell(f"for i in {{1..{20}}}; do input keyevent KEYCODE_CLEAR; done") # 20 次删除键事件
  53. for _ in range(length):
  54. self.d.shell("input keyevent 67")
  55. class UiFactory(object):
  56. """
  57. 这个类对 u2.connect 的几个方法做了封装,主要解决耗时问题。
  58. 并且做缓存。不重复定位。
  59. 可以使用 load_point 方法 加载 xxx_point.json中,已经配置好的坐标。
  60. """
  61. _cache = {}
  62. def __init__(self, serial: str):
  63. """
  64. :param serial: 设备序列号。 例如 127.0.0.1:6555
  65. 可以通过 `adb devices` 获取
  66. """
  67. self.d = u2.connect(serial)
  68. pass
  69. def xpath(self, alisa: str, xpath: str = None):
  70. """
  71. 通过 xpath获取元素
  72. alisa 别名
  73. xpath 如果别名没有获取缓存, 会通过xpath 重新定位,并且缓存
  74. """
  75. if alisa in self._cache:
  76. return self._cache[alisa]
  77. el = UiElement(self.d, xpath=xpath)
  78. self._cache[alisa] = el
  79. return el
  80. def desc(self, alisa: str, desc: str = None):
  81. """
  82. 通过文本获取元素
  83. alisa 别名
  84. desc 如果别名没有获取缓存, 会通过description 重新定位,并且缓存
  85. """
  86. if alisa in self._cache:
  87. return self._cache[alisa]
  88. el = UiElement(self.d, description=desc)
  89. self._cache[alisa] = el
  90. return el
  91. def load_point(self, point_json: str):
  92. __tmp__ = {}
  93. with open(point_json, "r", encoding="utf-8") as f:
  94. __tmp__ = json.load(f)
  95. for k, v in __tmp__.items():
  96. if 'xpath' in v:
  97. self._cache[k] = UiElement(self.d, xpath=v['xpath'], point=v['point'])
  98. elif 'description' in v:
  99. self._cache[k] = UiElement(self.d, description=v['description'], point=v['point'])
  100. def save_point(self, point_json: str):
  101. """
  102. 保存坐标
  103. """
  104. __tmp__ = {}
  105. for k, v in self._cache.items():
  106. __tmp__[k] = v.info
  107. with open(point_json, "w", encoding="utf-8") as f:
  108. json.dump(__tmp__, f, ensure_ascii=False, indent=4)
  109. def check_app_running(self, appid: str):
  110. # 获取所有运行的APP
  111. running_apps = self.d.app_list_running()
  112. print("安装列表:", running_apps)
  113. # 如果app没有启动, 启动APP
  114. if appid not in running_apps:
  115. print(f"{appid} 重新启动")
  116. self.d.app_start(appid)
  117. return True
  118. return False