Ver código fonte

deepcoin的一些坐标采集和动作模拟

xiao.qiang 7 meses atrás
pai
commit
3f4c7c0a9d

+ 0 - 0
.venv/__init__.py


+ 7 - 1
README.md

@@ -54,4 +54,10 @@ adb shell pm list packages -f
 - point坐标
 point 可以 使用weditor获取到的坐标
 
-![img.png](assets/img.png)
+![img.png](assets/img.png)
+
+
+## 打包
+```shell
+pyinstaller  --clean  -i assets/logo/32x32.ico test/deepcoin/test02.py 
+```

BIN
assets/logo/16x16.ico


BIN
assets/logo/24x24.ico


BIN
assets/logo/32x32.ico


BIN
assets/logo/64x64.ico


BIN
assets/logo/logo.png


BIN
assets/西海情歌-刀郎~铃声.mp3


+ 158 - 0
plat/base/abs_ui.py

@@ -0,0 +1,158 @@
+# 定义抽象类
+import json
+import logging
+from abc import ABC, abstractmethod
+
+import uiautomator2 as u2
+
+
+class AbsUi(ABC):
+
+    def __init__(self, serial: str):
+        """
+        :param serial: 设备序列号。 例如 127.0.0.1:6555
+            可以通过 `adb devices`  获取
+        """
+        # u2.logger.setLevel(logging.DEBUG)
+
+        # 屏幕高度
+        self.height = 0
+        # 屏幕宽度
+        self.width = 0
+        self.d = u2.connect(serial)
+
+        self._points = {}
+
+        self.point_path = 'point.json'
+        self.enable_click_monitor()
+        self._func = []
+
+    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):
+        """
+        滑动到屏幕最顶部,通过多次滑动确保到达顶部
+        """
+        width, height = self.get_screen_size()
+        # 循环滑动直到无法继续滑动
+        for _ in range(3):
+            self.d.swipe(width // 2, height * 0.8, width // 2, height)  # 向上滑动
+            # 短暂等待确保滑动完成
+            self.d.sleep(0.1)
+
+    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):
+        """
+        向上滑动一屏
+        """
+        width, height = self.get_screen_size()
+        # 从屏幕下方向上滑动到顶部
+        self.d.swipe(width // 2, height * 0.8, width // 2, height * 0.2, duration=0.1)  # 向上滑动
+
+    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 add_func(self, func):
+        """
+        添加坐标点采集函数
+        """
+        self._func.append(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):
+        """
+        获取坐标点
+        info 数据结构
+        {
+            "name": "btn_mairu_kaiduo",
+            "desc": "买入/开多",
+            "xpath": '//*[@content-desc="买入/开多"]'
+
+        """
+        el = self.d.xpath(info['xpath']).get()
+        x, y = el.center()
+        self.add_point({
+            "name": info['name'],
+            "x": x,
+            "y": y,
+            "desc": info['desc'],
+            "xpath": info['xpath'],
+        })
+
+        return x, y

+ 147 - 0
plat/deepcoin/point_collection.py

@@ -0,0 +1,147 @@
+# from utils.point_utils import UiFactory
+import logging
+
+from plat.base.abs_ui import AbsUi
+
+
+class PointCollection(AbsUi):
+
+    def __init__(self, serial: str):
+        super().__init__(serial)
+        self.point_path = '../../assets/point/deepcoin_point.json'
+        self.add_func(self.btn_jiao_yi)
+        self.add_func(self.btn_mairu_kaiduo)
+        self.add_func(self.btn_shijia_quanping)
+
+    def btn_jiao_yi(self):
+        return self.get_point({
+            "name": "btn_jiao_yi",
+            "desc": "交易按钮",
+            "xpath": '//*[@content-desc="交易"]'
+        })
+
+    def btn_mairu_kaiduo(self):
+        return self.get_point({
+            "name": "btn_mairu_kaiduo",
+            "desc": "买入/开多",
+            "xpath": '//*[@content-desc="买入/开多"]'
+        })
+
+    def btn_shijia_quanping(self):
+        self.to_next_swipe()
+        return self.get_point({
+            "name": "btn_shijia_quanping",
+            "desc": "买入/开多",
+            "xpath": '//*[@content-desc="市价全平"]'
+        })
+
+    def btn_kaichang(self):
+        return self.get_point({
+            "name": "btn_kaichang",
+            "desc": "开仓",
+            "xpath": '//*[@content-desc="开仓"]'
+        })
+
+    def slider_baozhengjin(self, offset=0, sleep=0.1):
+        """
+        保证金滑块
+           :param sleep 休眠
+           :param offset 价格偏移量 默认取50% x=368 每偏移69 向下多一个 偏移-69向上多一个 取值范围 [-2,2]
+        坐标: 0% 230  25% 299  50% 368  75% 437  100% 506
+
+        """
+        self.to_top_swipe()
+        self.d.sleep(0.1)
+        x, y = self.btn_mairu_kaiduo()
+
+        x1 = x + offset * 69
+        y1 = y - 140
+        logging.info(f"开多坐标: ({x},{y})")
+        logging.info(f"保证金50%坐标: ({x1},{y1})")
+
+        self.click_point(x1, y1)
+        self.d.sleep(sleep)
+
+    def click_kaicang(self):
+        """
+        开仓
+        """
+        self.click_xpath('//*[@content-desc="开仓"]')
+
+    def click_pingchang(self, sleep=0.1):
+        """
+        开仓
+        :param sleep: 等待时间
+        """
+        self.click_xpath('//*[@content-desc="平仓"]')
+        self.d.sleep(sleep)
+
+    def select_weituo_xianjia(self):
+        """
+        选择限价委托
+
+        """
+        x, y = self.btn_kaichang()
+        logging.info(f"开仓按钮坐标: ({x},{y})")
+        x1 = x + 50
+        y1 = y + 55
+        logging.info(f"选择委托框: ({x1},{y1})")
+        self.click_point(x1, y1)
+        x2 = x1
+        y2 = y1 + 100
+        self.d.sleep(0.2)
+        logging.info(f"选择限价委托: ({x2},{y2})")
+        self.click_point(x2, y2)
+
+    def click_price_red(self, offset=0):
+        """
+        点击价格 开多 平空, 点击红色价格
+
+        :param offset 价格偏移量 默认取第5个, 每偏移20 向下多一个 偏移-20 向上多一个 取值范围 [-4,4]
+        """
+        self.select_weituo_xianjia()
+        self.d.sleep(0.1)
+
+        x1 = 50
+        y1 = 330 + offset * 20
+        logging.info(f"选择价格: ({x1},{y1})")
+        self.click_point(x1, y1)
+
+    def click_price_green(self, offset=0):
+        """
+        点击价格 开空 平多, 点击红色价格
+        :param offset 价格偏移量 默认取第5个, 每偏移20 向下多一个,偏移-20 向上多一个
+        """
+
+        self.select_weituo_xianjia()
+
+        self.d.sleep(0.1)
+
+        x1 = 50
+        y1 = 330 + offset
+        logging.info(f"选择价格: ({x1},{y1})")
+        self.click_point(x1, y1)
+
+    def click_mairu_kaiduo(self):
+        """
+        买入开多
+        """
+        self.click_xpath('//*[@content-desc="买入/开多"]')
+
+    def click_mairu_pingkong(self):
+        """
+        买入平空
+        """
+        self.click_xpath('//*[@content-desc="买入/平空"]')
+
+    def click_maichu_kaikong(self):
+        """
+        卖出开空
+        """
+        self.click_xpath('//*[@content-desc="卖出/开空"]')
+
+    def click_maichu_pingduo(self):
+        """
+        卖出平多
+        """
+        self.click_xpath('//*[@content-desc="卖出/平多"]')

+ 11 - 1
pyproject.toml

@@ -6,10 +6,15 @@ authors = ["fzxs88 <fzxs88@yeah.net>"]
 readme = "README.md"
 
 [tool.poetry.dependencies]
-python = "^3.10"
+python = "~3.10"
 fastapi = "^0.115.8"
 uiautomator2 = "^3.2.8"
 weditor = "^0.7.3"
+uvicorn = "^0.34.0"
+numpy = "^2.2.4"
+opencv-python = "^4.11.0.86"
+wxpython = "^4.2.2"
+pyinstaller = "^6.12.0"
 
 
 [[tool.poetry.source]]
@@ -17,6 +22,11 @@ name = "tsinghua"
 url = "https://pypi.tuna.tsinghua.edu.cn/simple"
 priority = "explicit"
 
+
+[tool.poetry.group.dev.dependencies]
+pyinstaller = "^6.12.0"
+pytest = "^8.3.5"
+
 [build-system]
 requires = ["poetry-core"]
 build-backend = "poetry.core.masonry.api"

+ 31 - 4
server.py

@@ -1,10 +1,37 @@
-from fastapi import FastAPI
+import time
 
-app = FastAPI()
+from fastapi import FastAPI, HTTPException
+from fastapi.responses import StreamingResponse
 
+app = FastAPI()
 
 @app.get("/")
-async def root():
-    return {"message": "Hello World"}
+def read_root():
+    return {"Hello": "World"}
+
+def generate_audio():
+    try:
+        for chunk in get_mp3_chunks():
+            yield chunk
+    except FileNotFoundError:
+        # 文件不存在时,返回 404 错误
+        raise HTTPException(status_code=404, detail="Audio file not found")
+    except Exception as e:
+        # 其他错误,返回 500 错误
+        raise HTTPException(status_code=500, detail=f"An error occurred: {e}")
 
+@app.get('/audio')
+async def audio_stream():
+    return StreamingResponse(generate_audio(), media_type='audio/mpeg')
 
+def get_mp3_chunks():
+    try:
+        with open('assets/西海情歌-刀郎~铃声.mp3', 'rb') as f:  # 使用相对路径
+            while True:
+                chunk = f.read(4096)
+                if not chunk:
+                    break
+                time.sleep(0.1)
+                yield chunk
+    except FileNotFoundError:
+        raise  # 重新抛出异常,由 generate_audio() 函数处理

+ 0 - 37
test/deepcion_point.json

@@ -1,37 +0,0 @@
-{
-  "btn_jy": {
-    "xpath": "//*[@content-desc=\"交易\"]",
-    "point": {
-      "x": 720,
-      "y": 3606
-    }
-  },
-  "input_weituo": {
-    "xpath": "//*[@content-desc=\"最优\"]",
-    "point": {
-      "x": 1354,
-      "y": 491
-    }
-  },
-  "input_num": {
-    "xpath": "//android.widget.EditText",
-    "point": {
-      "x": 938,
-      "y": 675
-    }
-  },
-  "btn_buy": {
-    "description": "买入/开多",
-    "point": {
-      "x": 988.0,
-      "y": 1066.5
-    }
-  },
-  "btn_ping": {
-    "description": "平仓",
-    "point": {
-      "x": 823,
-      "y": 1903
-    }
-  }
-}

+ 0 - 76
test/deepcoin.py

@@ -1,76 +0,0 @@
-import time
-
-import uiautomator2 as u2
-
-from utils.utils import UiFactory
-
-# 连接到设备,默认连接第一个设备,如果有多个设备可以指定序列号
-ui = UiFactory('127.0.0.1:6555')
-# 从配置中加载坐标
-# 执行deepcoin_point.py 文件进行采集。
-ui.load_point("deepcion_point.json")
-
-is_start = ui.check_app_running("com.niocpeed.dna")
-# tab 交易按钮
-btn_jy = ui.xpath("btn_jy")
-
-if is_start:
-    print("点击交易")
-    btn_jy.click()
-
-# 设置委托
-input_weituo = ui.xpath("input_weituo")
-# 设置数量
-input_num = ui.xpath('input_num')
-
-# 设置数量
-btn_ping = ui.xpath('btn_ping')
-
-start1 = time.time()
-input_num.clear()
-end1 = time.time()
-print("清空数量:操作耗时:", end1 - start1)
-start1 = time.time()
-input_num.text("41")
-end1 = time.time()
-print("设置数量:操作耗时:", end1 - start1)
-
-btn_buy = ui.desc("btn_buy")
-
-start1 = time.time()
-input_weituo.click()
-end1 = time.time()
-print("点击:最优委托 操作耗时:", end1 - start1)
-
-start1 = time.time()
-btn_ping.click()
-end1 = time.time()
-print("点击:平仓 操作耗时:", end1 - start1)
-#
-# for i in range(10):
-#     # 初始化点击按钮
-#     start = time.time()
-#     # start1 = time.time()
-#     # input_weituo.click()
-#     # end1 = time.time()
-#     # print("点击:最优委托 操作耗时:", end1 - start1)
-#     #
-#     # # start1 = time.time()
-#     # # input_weituo.set_text("0.114")
-#     # # end1 = time.time()
-#     # # print("设置:操作耗时:", end1 - start1)
-#     # start1 = time.time()
-#     # input_num.click()
-#     # end1 = time.time()
-#     # print("点击:操作耗时:", end1 - start1)
-#     # start1 = time.time()
-#     # input_num.set_text("40")
-#     # end1 = time.time()
-#     # print("设置:操作耗时:", end1 - start1)
-#     start1 = time.time()
-#     btn_buy.click()
-#     end1 = time.time()
-#     print("点击下单:操作耗时:", end1 - start1)
-#     end = time.time()
-#     # print("点击整体:操作耗时:", end - start)
-#     time.sleep(5)

+ 9 - 0
test/deepcoin/__init__.py

@@ -0,0 +1,9 @@
+# import logging
+# print("日志级别:", logging.getLogger().getEffectiveLevel())
+# print("日志类:", logging.getLogger().handlers)
+# # Configure logging with basic settings
+# logging.basicConfig(
+#     level=logging.INFO,
+#     format='%(asctime)s - %(levelname)s - %(message)s'
+# )
+# logging.info("测试日志消息")

+ 76 - 0
test/deepcoin/deepcoin.py

@@ -0,0 +1,76 @@
+import time
+
+import uiautomator2 as u2
+
+from utils.utils import UiFactory
+
+# 连接到设备,默认连接第一个设备,如果有多个设备可以指定序列号
+ui = UiFactory('emulator-5554')
+# 从配置中加载坐标
+# 执行deepcoin_point.py 文件进行采集。
+ui.load_point("deepcion_point.json")
+
+is_start = ui.check_app_running("com.niocpeed.dna")
+# tab 交易按钮
+# btn_jy = ui.xpath("btn_jy")
+#
+# if is_start:
+#     print("点击交易")
+#     btn_jy.click()
+
+# 设置委托
+input_weituo = ui.xpath("input_weituo")
+# 设置数量
+input_num = ui.xpath('input_num')
+
+# # 设置数量
+# btn_ping = ui.xpath('btn_ping')
+
+# start1 = time.time()
+# input_num.clear()
+# end1 = time.time()
+# print("清空数量:操作耗时:", end1 - start1)
+# start1 = time.time()
+# input_num.text("41")
+# end1 = time.time()
+# print("设置数量:操作耗时:", end1 - start1)
+#
+btn_buy = ui.desc("btn_buy")
+#
+# start1 = time.time()
+# input_weituo.click()
+# end1 = time.time()
+# print("点击:最优委托 操作耗时:", end1 - start1)
+
+# start1 = time.time()
+# btn_ping.click()
+# end1 = time.time()
+# print("点击:平仓 操作耗时:", end1 - start1)
+#
+for i in range(10):
+    # 初始化点击按钮
+    start = time.time()
+    # start1 = time.time()
+    # input_weituo.click()
+    # end1 = time.time()
+    # print("点击:最优委托 操作耗时:", end1 - start1)
+    #
+    # # start1 = time.time()
+    # # input_weituo.set_text("0.114")
+    # # end1 = time.time()
+    # # print("设置:操作耗时:", end1 - start1)
+    # start1 = time.time()
+    # input_num.click()
+    # end1 = time.time()
+    # print("点击:操作耗时:", end1 - start1)
+    # start1 = time.time()
+    # input_num.set_text("40")
+    # end1 = time.time()
+    # print("设置:操作耗时:", end1 - start1)
+    start1 = time.time()
+    btn_buy.click()
+    end1 = time.time()
+    print("点击下单:操作耗时:", end1 - start1)
+    end = time.time()
+    # print("点击整体:操作耗时:", end - start)
+    time.sleep(5)

+ 0 - 0
test/deepcoin_test.py → test/deepcoin/deepcoin_test.py


+ 0 - 0
test/sample.py → test/deepcoin/sample.py


+ 182 - 0
test/deepcoin/screenshots.py

@@ -0,0 +1,182 @@
+import logging
+import subprocess
+from PIL import Image
+import os
+import time
+import cv2
+import numpy as np
+import uiautomator2 as u2
+
+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
+
+
+class ScreenshotStitcher:
+    def __init__(self, device_serial, adb_path):
+        self.device_serial = device_serial
+        self.adb = adb_path
+        self.d = u2.connect(device_serial)
+        self.screen_width = 0
+        self.screen_height = 0
+
+    def check_adb_connection(self):
+        try:
+            output = subprocess.check_output([self.adb, '-s', self.device_serial, 'devices']).decode('utf-8')
+            if self.device_serial in output and 'device' in output:
+                logging.info(f"ADB 设备 {self.device_serial} 连接正常")
+                return True
+            logging.error(f"ADB 设备 {self.device_serial} 连接异常")
+            return False
+        except FileNotFoundError:
+            logging.error("ADB 未找到,请确保已安装并配置 ADB")
+            return False
+
+    def adb_shell(self, command):
+        try:
+            output = subprocess.check_output(
+                [self.adb, '-s', self.device_serial, 'shell', command],
+                stderr=subprocess.STDOUT
+            ).decode('utf-8')
+            return output
+        except subprocess.CalledProcessError as e:
+            logging.error(f"ADB 命令执行失败:{e.output.decode('utf-8')}")
+            raise
+
+    def get_screen_size(self):
+        output = self.adb_shell('wm size')
+        if "Physical size: " in output:
+            size = output.split('Physical size: ')[1].strip().split('x')
+            self.screen_width, self.screen_height = int(size[0]), int(size[1])
+        elif "Size: " in output:
+            size = output.split('Size: ')[1].strip().split('x')
+            self.screen_width, self.screen_height = int(size[0]), int(size[1])
+        else:
+            raise ValueError("无法获取屏幕尺寸")
+
+    def get_screenshot(self, filename):
+        self.adb_shell(f'screencap -p /sdcard/{filename}')
+        subprocess.call([self.adb, '-s', self.device_serial, 'pull', f'/sdcard/{filename}', filename])
+        self.adb_shell(f'rm /sdcard/{filename}')
+
+    def merge_screenshots_vertical(self, screenshots):
+        images = [np.array(img) for img in screenshots]
+        max_width = max(img.shape[1] for img in images)
+
+        aligned_images = []
+        for img in images:
+            if img.shape[1] != max_width:
+                img = cv2.resize(img, (max_width, int(img.shape[0] * max_width / img.shape[1])))
+            aligned_images.append(img)
+
+        return cv2.vconcat(aligned_images)
+
+    def is_bottom_reached(self, last_screenshot, current_screenshot):
+        """检查是否到达底部"""
+        # 将图片转换为numpy数组
+        last_img = np.array(last_screenshot)
+        current_img = np.array(current_screenshot)
+
+        # 计算最后一部分的相似度
+        last_portion = last_img[-100:, :]
+        current_portion = current_img[-100:, :]
+
+        # 使用结构相似性指数(SSIM)比较
+        similarity = cv2.matchTemplate(last_portion, current_portion, cv2.TM_CCOEFF_NORMED)
+        return similarity.max() > 0.95
+
+    def scroll_to_top(self):
+        """滚动到页面顶部"""
+        try:
+            # 多次向上滑动以确保到达顶部
+            for _ in range(3):
+                # 从屏幕底部往上滑
+                self.adb_shell(
+                    f'input swipe {self.screen_width // 2} {self.screen_height - 200} '
+                    f'{self.screen_width // 2} {200} 800'
+                )
+                time.sleep(1)
+            logging.info("已滚动到页面顶部")
+            return True
+        except subprocess.CalledProcessError as e:
+            logging.error(f"滚动到顶部失败:{e}")
+            return False
+
+    def images_are_different(self, img1, img2, threshold=0.95):
+        """检查两张图片是否不同"""
+        # 转换为OpenCV格式
+        img1_cv = cv2.cvtColor(np.array(img1), cv2.COLOR_RGB2BGR)
+        img2_cv = cv2.cvtColor(np.array(img2), cv2.COLOR_RGB2BGR)
+
+        # 计算相似度
+        result = cv2.matchTemplate(img1_cv, img2_cv, cv2.TM_CCOEFF_NORMED)
+        similarity = np.max(result)
+
+        return similarity < threshold
+
+    def capture_long_screenshot(self, output_filename='full_screenshot.png', max_screenshots=10):
+        """改进的长截图函数"""
+        if not self.check_adb_connection():
+            return False
+
+        self.get_screen_size()
+        screenshots = []
+
+        # 第一次截图
+        self.get_screenshot('temp_screenshot_0.png')
+        last_screenshot = Image.open('temp_screenshot_0.png')
+        screenshots.append(last_screenshot)
+
+        # 循环滚动和截图
+        for i in range(1, max_screenshots):
+            try:
+                # 从屏幕底部向上滑动
+                self.adb_shell(
+                    f'input swipe {self.screen_width // 2} {self.screen_height - 200} '
+                    f'{self.screen_width // 2} {200} 800'
+                )
+                time.sleep(1.5)  # 等待滚动完成
+
+                # 截取当前页面
+                self.get_screenshot(f'temp_screenshot_{i}.png')
+                current_screenshot = Image.open(f'temp_screenshot_{i}.png')
+
+                # 只有当图片不同时才添加
+                if self.images_are_different(last_screenshot, current_screenshot):
+                    screenshots.append(current_screenshot)
+                    last_screenshot = current_screenshot
+                else:
+                    logging.info("检测到重复页面,停止截图")
+                    break
+
+            except subprocess.CalledProcessError:
+                break
+
+        try:
+            if screenshots:
+                result = self.merge_screenshots_vertical(screenshots)
+                cv2.imwrite(output_filename, result)
+                logging.info(f"长截图已保存至 {output_filename}")
+        except Exception as e:
+            logging.error(f"截图拼接失败:{e}")
+            return False
+        finally:
+            # 清理临时文件
+            for i in range(max_screenshots):
+                temp_file = f'temp_screenshot_{i}.png'
+                if os.path.exists(temp_file):
+                    os.remove(temp_file)
+
+        return True
+def main():
+    # 配置参数
+    adb_path = '/home/fzxs/Android/Sdk/platform-tools/adb'  # 替换为实际的ADB路径
+    device_serial = '127.0.0.1:6555'  # 替换为实际的设备序列号
+
+    # 创建截图工具实例
+    stitcher = ScreenshotStitcher(device_serial, adb_path)
+
+    # 执行长截图
+    stitcher.capture_long_screenshot('long_screenshot.png')
+
+
+if __name__ == '__main__':
+    main()

+ 33 - 0
test/deepcoin/test.py

@@ -0,0 +1,33 @@
+#使用python构建一个UI页面 上面有3个按钮
+#点击按钮1,弹出toast提示“点击了按钮1”
+#点击按钮2,弹出toast提示“点击了按钮2”
+#点击按钮3,弹出toast提示“点击了按钮3”
+import tkinter as tk
+from tkinter import messagebox
+
+
+class ToastApp:
+    def __init__(self):
+        self.root = tk.Tk()
+        self.root.title("Button Demo")
+        self.root.geometry("300x200")
+
+        # Create and pack buttons
+        for i in range(3):
+            btn = tk.Button(
+                self.root,
+                text=f"Button {i + 1}",
+                command=lambda x=i + 1: self.show_toast(x)
+            )
+            btn.pack(pady=10)
+
+    def show_toast(self, button_num):
+        messagebox.showinfo("Toast", f"Clicked Button {button_num}")
+
+    def run(self):
+        self.root.mainloop()
+
+
+if __name__ == "__main__":
+    app = ToastApp()
+    app.run()

+ 58 - 0
test/deepcoin/test01.py

@@ -0,0 +1,58 @@
+# 坐标测试
+
+import uiautomator2 as u2
+
+# 连接设备
+d = u2.connect()
+# 获取屏幕的尺寸
+width, height = d.window_size()
+print(f"屏幕尺寸: {width}x{height}")
+# 使用xpath定位按钮并获取其位置信息
+# button = d.xpath('//*[@content-desc="买入/开多"]').get()
+# if button:
+#     # bounds返回(left, top, right, bottom)坐标
+#     print(f"按钮坐标(left, top, right, bottom): {button.bounds}")
+#     top_distance = button.bounds[1]  # top坐标即是距离顶部的距离
+#     print(f"按钮距离顶部: {top_distance}像素")
+
+# 屏幕向上滚动以查看底部内容 (从屏幕下方向上滑动)
+
+# fx, fy: 滑动的起始坐标点
+#
+#
+# fx: 起始点的 x 坐标
+# fy: 起始点的 y 坐标
+# tx, ty: 滑动的目标坐标点
+#
+#
+# tx: 目标点的 x 坐标
+# ty: 目标点的 y 坐标
+# duration: 滑动持续时间(以秒为单位)
+#
+# 可选参数,默认为 None
+# 用于控制滑动的速度
+# 如果同时设置了 steps 参数,duration 将被忽略
+# steps: 滑动的步数
+#
+#
+# 可选参数,默认为 None
+# 每一步大约需要 5 毫秒
+# 例如:设置 steps=200,整个滑动过程将持续约 1 秒(200 * 5ms = 1000ms)
+# 当设置了 steps 时,会忽略 duration 参数
+# d.swipe(width // 2, height * 0.8, width // 2, height * 0.1)
+
+# buttons = d.xpath('//*[starts-with(@content-desc, "持仓")]').all()
+# button = d.xpath('//*[@content-desc="买入/开多"]').get()
+button = d.xpath('//*[@content-desc="市价全平"]').get()
+print(f"第一个 坐标(left, top, right, bottom): {button.bounds}")
+buttons = d.xpath('//*[@content-desc="市价全平"]').all()
+
+for idx, button in enumerate(buttons):
+    print(f"按钮 {idx + 1} 坐标(left, top, right, bottom): {button.bounds}")
+
+# if button:
+#     # bounds返回(left, top, right, bottom)坐标
+#     print(f"按钮坐标(left, top, right, bottom): {button.bounds}")
+#     top_distance = button.bounds[1]  # top坐标即是距离顶部的距离
+#     print(f"按钮距离顶部: {top_distance}像素")
+# d.swipe(width // 2, height * 0.8, width // 2, height * 0.3, duration=0.5)

+ 27 - 0
test/deepcoin/test02.py

@@ -0,0 +1,27 @@
+import wx
+
+
+# 快捷键。
+class MyFrame(wx.Frame):
+    def __init__(self, parent, title):
+        super().__init__(parent, title=title, size=(300, 200))
+
+        panel = wx.Panel(self)
+        self.text = wx.TextCtrl(panel)
+
+        self.text.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
+
+        self.Show(True)
+
+    def on_key_down(self, event):
+        keycode = event.GetKeyCode()
+        control_down = event.ControlDown()
+
+        if control_down and keycode == ord('S'):
+            wx.MessageBox("Ctrl+S Pressed!", "Info")
+        event.Skip()  # 如果没有这一句,那么在textctrl内,按键就没有效果。
+
+
+app = wx.App()
+frame = MyFrame(None, "wxPython Keyboard Event Example")
+app.MainLoop()

+ 119 - 0
test/deepcoin/test_point_collection.py

@@ -0,0 +1,119 @@
+import logging
+import unittest
+
+from plat.deepcoin.point_collection import PointCollection
+
+logging.basicConfig(level=logging.INFO, force=True)  # 设置日志级别
+
+
+class MyTestCase(unittest.TestCase):
+    def setUp(self):
+        self.pc = PointCollection('127.0.0.1:6555')
+
+    def test_to_top_swipe(self):
+        """
+        滑动到顶部
+        """
+        self.pc.to_top_swipe()
+
+    def test_to_next_swipe(self):
+        """
+        滑动到下一屏
+        """
+        self.pc.to_next_swipe()
+
+    def test_save_point(self):
+        """
+        保存坐标
+        """
+        self.pc.save_point()
+
+    def test_slider_baozhengjin(self):
+        """
+        保证金滑块
+        """
+        self.pc.slider_baozhengjin()
+
+    def test_click_kaicang(self):
+        """
+        开仓
+        """
+        self.pc.click_kaicang()
+
+    def test_click_pingchang(self):
+        """
+        平仓
+        """
+        self.pc.click_pingchang()
+
+    def test_select_weituo_xianjia(self):
+        """
+        选择限价委托
+        """
+        self.pc.select_weituo_xianjia()
+
+    def test_click_price(self):
+        """
+        点击价格
+        """
+        self.pc.click_price_red()
+
+    def test_f1(self):
+        """
+        f1 开仓界面,仓位滑竿百分比(30-60)
+        """
+        self.pc.click_kaicang()
+        self.pc.slider_baozhengjin()
+
+    def test_f2(self):
+        """
+        f2 确认开仓,限价职中间数值
+        检查撤销限价委托单
+        """
+
+    def test_f3(self):
+        """
+        F3 开仓界面,仓位滑竿百分比(30-60)确认开仓,限价职中间数值
+        """
+
+    def test_f4(self):
+        """
+        F4   确认开仓限价职中间数值 检查撤销限价委托单
+        """
+
+    def test_f5(self):
+        """
+        F5 开仓界面胂仓位滑竿百分比(30-60)
+        """
+
+    def test_f6(self):
+        """
+        F6 确认开仓,限价职中间数值
+        检查撤销限价委托单
+        """
+
+    def test_f7(self):
+        """
+        F7    平仓界面二仓位滑竿百分比(90-100)
+        """
+
+    def test_f8(self):
+        """
+        F8 确认平仓,限价职中间数值
+        检查撤销限价委托单
+        """
+
+    def test_jiao_yi(self):
+        x, y = self.pc.btn_jiao_yi()
+        logging.info(f"交易坐标: ({x},{y})")
+
+    def test_mairu_kaiduo(self):
+        x, y = self.pc.btn_mairu_kaiduo()
+        logging.info(f"买入开多: ({x},{y})")
+
+    def test_chichang(self):
+        self.pc.d.xpath()
+
+
+if __name__ == '__main__':
+    unittest.main()

+ 0 - 19
test/deepcoin_point.py

@@ -1,19 +0,0 @@
-from utils.utils import UiFactory
-
-#  坐标采集
-# 连接到设备,默认连接第一个设备,如果有多个设备可以指定序列号
-ui = UiFactory('127.0.0.1:6555')
-
-is_start = ui.check_app_running("com.niocpeed.dna")
-# tab 交易按钮
-btn_jy = ui.xpath("btn_jy", '//*[@content-desc="交易"]')
-
-# 设置委托
-input_weituo = ui.xpath("input_weituo", '//*[@content-desc="最优"]')
-# 设置数量
-# 设置数量
-input_num = ui.xpath('input_num', '//android.widget.EditText')
-
-btn_buy = ui.desc("btn_buy", "买入/开多")
-
-ui.save_point("deepcion_point.json")

+ 0 - 0
utils/utils.py → utils/point_utils.py