| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- # 定义抽象类
- import json
- import logging
- import tempfile
- from abc import ABC, abstractmethod
- import uiautomator2 as u2
- class AbsControl(ABC):
- def __init__(self, name: str):
- """
- # :param : 设备序列号。 例如 127.0.0.1:6555
- # 可以通过 `adb devices` 获取
- """
- # u2.logger.setLevel(logging.DEBUG)
- # 名称
- self.name = name
- self.d = None
- # 屏幕高度
- self.height = 0
- # 屏幕宽度
- self.width = 0
- self._points = {}
- self.point_path = 'point.json'
- self._func = []
- self._log_func = None
- def connect_adb(self, serial: str):
- """
- 连接设备
- """
- self.d = u2.connect(serial)
- self.enable_click_monitor()
- # 如果连接成功,返回截图,否则返回None
- def screenshot(self):
- """
- 截图
- """
- # 获取系统/tmp路径
- tmp = tempfile.gettempdir()
- return self.d.screenshot(f"{tmp}/{self.name}.png")
- def enable_click_monitor(self):
- """
- 启用点击监控,在屏幕上显示点击位置
- """
- # self.d.settings['operation_delay'] = (0.5, 0.5) # 增加操作延迟以便观察
- self.d.debug = True
- self.d.toast.show('点击监控已启用') # 显示提示
- # 确保有悬浮窗权限
- self.d.set_fastinput_ime(True) # 启用ime
- self.d.show_float_window(True) # 显示悬浮窗
- # 可选:打开开发者选项中的"指针位置"
- self.d.shell('settings put system pointer_location 1')
- def to_top_swipe(self, sleep=0.1, times=2):
- """
- 滑动到屏幕最顶部,通过多次滑动确保到达顶部
- :param sleep: 滑动后等待时间
- :param times: 滑动次数
- :return:
- """
- width, height = self.get_screen_size()
- # 循环滑动直到无法继续滑动
- for _ in range(times):
- self.d.swipe(width // 2, height * 0.8, width // 2, height) # 向上滑动
- # 短暂等待确保滑动完成
- self.d.sleep(sleep)
- def click_point(self, x: int, y: int):
- """
- 点击指定坐标
- :param x: x坐标
- :param y: y坐标
- """
- self.d.click(x, y)
- def click_xpath(self, xpath: str):
- """
- 点击指定xpath
- """
- el = self.d.xpath(xpath).get()
- if el:
- el.click()
- else:
- logging.warning(f"未找到元素: {xpath}")
- def to_next_swipe(self, sleep=0.1):
- """
- 向上滑动一屏
- """
- width, height = self.get_screen_size()
- # 从屏幕下方向上滑动到顶部
- self.d.swipe(width // 2, height * 0.8, width // 2, height * 0.2) # 向上滑动
- # 短暂等待确保滑动完成
- self.d.sleep(sleep)
- def get_screen_size(self):
- """
- 获取屏幕尺寸,宽度和高度
- """
- self.width, self.height = self.d.window_size()
- logging.info(f"屏幕尺寸: {self.width}x{self.height}")
- return self.width, self.height
- def drag_slider_ext(self, start_x: int, start_y: int, end_x: int, end_y: int, steps: int = 50):
- """
- 精确拖动滑块
- :param start_x: 起始x坐标
- :param start_y: 起始y坐标
- :param end_x: 结束x坐标
- :param end_y: 结束y坐标
- :param steps: 步数,值越大滑动越平滑
- """
- self.d.swipe_ext(start_x, start_y, end_x, end_y, steps)
- def add_point(self, point=None):
- """
- 添加坐标点
- point = {
- "name": "",
- "x": 0,
- "y": 0,
- "desc": "",
- "xpath": ""
- }
- """
- logging.info("保存坐标点: %s", point)
- self._points[point['name']] = point
- def print_log(self, msg):
- """
- 打印日志
- """
- logging.info(f'>>111>{msg}')
- print(f'>222>>{msg},{self._log_func}')
- if self._log_func:
- self._log_func(msg)
- def add_func(self, func):
- """
- 添加坐标点采集函数
- """
- self._func.append(func)
- def set_log_func(self, func):
- """
- 添加日志函数
- """
- self._log_func = func
- def save_point(self):
- """
- 保存坐标
- """
- self.to_top_swipe()
- self.d.sleep(2)
- for func in self._func:
- func()
- __tmp__ = {}
- for k, v in self._points.items():
- __tmp__[k] = v
- with open(self.point_path, "w", encoding="utf-8") as f:
- json.dump(__tmp__, f, ensure_ascii=False, indent=4)
- def get_point(self, info: dict):
- """
- 获取坐标点
- :param info: 数据结构
- {
- "name": "btn_mairu_kaiduo",
- "desc": "买入/开多",
- "xpath": '//*[@content-desc="买入/开多"]'
- }
- :return: x, y, el
- """
- x, y, el = self.get_point_by_xpath(info['xpath'])
- self.add_point({
- "name": info['name'],
- "x": x,
- "y": y,
- "desc": info['desc'],
- "xpath": info['xpath'],
- })
- return x, y, el
- def get_point_by_xpath(self, xpath: str):
- """
- :param xpath: xpath
- 获取坐标第一个匹配的点
- info 数据结构
- :return: x, y, el
- """
- el = self.d.xpath(xpath).get()
- x, y = el.center()
- return x, y, el
- def get_points_by_xpath(self, xpath: str):
- """
- 获取所有坐标点
- info 数据结构
- """
- els = self.d.xpath(xpath).all()
- items = []
- for el in els:
- x, y = el.center()
- items.append((x, y, el))
- return items
- def check_element_exists(self, xpath: str, timeout: float = 0.05) -> bool:
- """
- 判断指定XPath的元素是否存在
- :param xpath: 元素的XPath
- :param timeout: 超时时间(秒)
- :return: 是否存在
- """
- self.d.sleep(timeout)
- return self.d.xpath(xpath).exists
- def input_xpath(self, xpath: str, text: str, clear: bool = True):
- """
- 给指定xpath的输入框输入文本
- :param xpath: 输入框的xpath
- :param text: 要输入的文本
- :param clear: 输入前是否清空原有内容
- """
- element = self.d.xpath(xpath).get()
- if element:
- if clear:
- element.text.set_text("") # 清空原有内容
- element.set_text(text) # 输入新内容
- else:
- logging.warning(f"未找到输入框: {xpath}")
- def input_by_position(self, x: int, y: int, text: str, clear: bool = True):
- """
- 通过坐标点击并在输入框中输入文本
- :param x: 输入框x坐标
- :param y: 输入框y坐标
- :param text: 要输入的文本
- :param clear: 是否清空原有内容
- """
- self.click_point(x, y) # 先点击获取焦点
- if clear:
- self.d.clear_text() # 清空原有内容
- self.d.send_keys(text) # 输入新文本
- @abstractmethod
- def event_f1(self):
- """
- F1 开仓界面,仓位滑竿百分比(30-60)
- """
- pass
- @abstractmethod
- def event_f2(self):
- """
- F2 确认开仓,开多 限价职中间数值
- 检查撤销限价委托单
- """
- pass
- @abstractmethod
- def event_f3(self):
- """
- F3 平仓界面 平空,仓位滑竿百分比(90-100)确认开仓,限价职中间数值
- """
- pass
- @abstractmethod
- def event_f4(self):
- """
- F4 确认平仓 平多 限价取中间数值 检查撤销限价委托单
- """
- pass
- @abstractmethod
- def event_f5(self):
- """
- F5 开仓界面 仓位滑竿百分比(30-60)
- """
- pass
- @abstractmethod
- def event_f6(self):
- """
- F6 确认开仓, 开空 限价值中间数值
- 检查撤销限价委托单
- """
- pass
- @abstractmethod
- def event_f7(self):
- """
- F7 平仓界面二仓位滑竿百分比(90-100)
- """
- pass
- @abstractmethod
- def event_f8(self):
- """
- F8 确认平仓, 平空 限价职中间数值
- 检查撤销限价委托单
- """
- pass
|