Browse Source

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	README.md
#	main.py
#	plat/base/base_control.py
#	plat/deepcoin_control.py
#	pyproject.toml
#	test/deepcoin/test_deepcoin_control.py
xiao.qiang 6 tháng trước cách đây
mục cha
commit
c6387bbe11

+ 1 - 55
.gitignore

@@ -105,59 +105,5 @@ atlassian-ide-plugin.xml
 com_crashlytics_export_strings.xml
 crashlytics.properties
 crashlytics-build.properties
-
-/.venv/bin/activate
-/.venv/bin/activate.csh
-/.venv/bin/activate.fish
-/.venv/bin/Activate.ps1
-/.venv/bin/fastapi
-/.venv/bin/normalizer
-/.venv/bin/pip
-/.venv/bin/pip3
-/.venv/bin/pip3.10
-/.venv/bin/python
-/.venv/bin/python3
-/.venv/bin/python3.10
-/.venv/bin/readelf.py
-/.venv/bin/uiautomator2
-/.venv/bin/weditor
-/.venv/lib64/
-/.venv/pyvenv.cfg
-/.venv/lib64/
-/poetry.lock
-/.venv/lib64/
-!/build/
-
-!/*.spec
-
-!/dist/
-
-!/poetry.lock
-/.venv/bin/f2py
-/.venv/bin/helpviewer
-/.venv/bin/img2png
-/.venv/bin/img2py
-/.venv/bin/img2xpm
-/.venv/bin/numpy-config
-/.venv/bin/py.test
-/.venv/bin/pycrust
-/.venv/bin/pyi-archive_viewer
-/.venv/bin/pyi-bindepend
-/.venv/bin/pyi-grab_version
-/.venv/bin/pyi-makespec
-/.venv/bin/pyi-set_version
-/.venv/bin/pyinstaller
-/.venv/bin/pyshell
-/.venv/bin/pyslices
-/.venv/bin/pyslicesshell
-/.venv/bin/pytest
-/.venv/bin/pywxrc
-/.venv/bin/uvicorn
-/.venv/bin/wxdemo
-/.venv/bin/wxdocs
-/.venv/bin/wxget
-/.venv/lib64/
-/mobile-tools.spec
-/poetry.lock
-/test02.spec
+/.venv
 /1.xml

+ 113 - 60
plat/base/base_control.py → control/base/base_control.py

@@ -16,6 +16,7 @@ class BaseControl(ABC):
     #     'connect': 1,
     #     'platform': 2,
     # }
+    # 连接信息
     connect_dict = {
 
     }
@@ -25,6 +26,11 @@ class BaseControl(ABC):
     }
 
     def __init__(self, name: str, ctx: int = 2):
+        """
+        软件UI交互的控制器
+        :param name:
+        :param ctx:
+        """
         # 名称
         self.name = name
         self.ctx = ctx
@@ -38,6 +44,13 @@ class BaseControl(ABC):
         """
         pass
 
+    def devices_list(self):
+        """
+        获取设备列表 adb devices
+        :return:
+        """
+        pass
+
     def print_log(self, msg):
         logging.info(f'打印信息 {msg}')
 
@@ -117,22 +130,87 @@ class BaseControl(ABC):
         pass
 
 
-class AbsControl(BaseControl):
+class UIControl(ABC):
+    @abstractmethod
+    def event_1(self):
+        """
+        F1  开仓界面,仓位滑竿百分比(30-60)
+        """
+        pass
 
-    def __init__(self, name: str, ctx: int = 2, *args, **kwargs):
+    @abstractmethod
+    def event_2(self):
         """
-        # :param : 设备序列号。 例如 127.0.0.1:6555
-        #     可以通过 `adb devices`  获取
+        F2 确认开仓,开多 限价职中间数值
+        检查撤销限价委托单
+        """
+        pass
+
+    @abstractmethod
+    def event_3(self):
+        """
+        F3 平仓界面 平空,仓位滑竿百分比(90-100)确认开仓,限价职中间数值
+        """
+        pass
+
+    @abstractmethod
+    def event_4(self):
+        """
+        F4   确认平仓 平多 限价取中间数值 检查撤销限价委托单
+        """
+        pass
+
+    @abstractmethod
+    def event_5(self):
+        """
+        F5 开仓界面 仓位滑竿百分比(30-60)
+        """
+        pass
+
+    @abstractmethod
+    def event_6(self):
+        """
+        F6 确认开仓, 开空 限价值中间数值
+        检查撤销限价委托单
+        """
+        pass
+
+    @abstractmethod
+    def event_7(self):
+        """
+        F7    平仓界面二仓位滑竿百分比(90-100)
+        """
+        pass
+
+    @abstractmethod
+    def event_8(self):
+        """
+        F8 确认平仓, 平空 限价职中间数值
+        检查撤销限价委托单
+        """
+        pass
+
+
+class AbsControl(UIControl):
+
+    def __init__(self, serial: str, ctx: dict, *args, **kwargs):
+
+        """
+        用于平台交互的控制器
+        :param name: 当前名称
+        :param ctx: 控制器上下文
+        :param args:
+        :param kwargs:
         """
         # u2.logger.setLevel(logging.DEBUG)
 
-        super().__init__(name)
+        self.serial = serial
 
         self.ctx = ctx
 
-        self.info = {}
+        self.info = ctx['info']
         #
-        self._d = None
+        self.d = ctx['d']
 
         # 屏幕高度
         self.height = 0
@@ -141,42 +219,17 @@ class AbsControl(BaseControl):
 
         self._points = {}
 
-        self.point_path = 'point.json'
-
         self._func = []
 
         self._log_func = None
 
-    def d(self, serial: str = ''):
-        """
-        连接设备
-        """
-        if self._d is not None:
-            return self._d
-
-        if serial in self.connect_dict:
-            self.info = self.connect_dict[serial]
-            self._d = self.info['d']
-        else:
-            self._d = u2.connect(serial)
-
-        return self._d
-
-    def re_connect(self):
-
-        pass
-
-    # @abstractmethod
-    # def get_app_package_name(self):
-    #     return ""
-
     def prevent_sleep(self):
         """
         防止设备休眠
         """
-        self.d().wake()
-        self.d().screen_on()
-        self.d().unlock()
+        self.d.wake()
+        self.d.screen_on()
+        self.d.unlock()
 
     def screenshot(self):
         """
@@ -184,7 +237,7 @@ class AbsControl(BaseControl):
         """
         # 获取系统/tmp路径
         tmp = tempfile.gettempdir()
-        return self.d().screenshot(f"{tmp}/{self.name}.png")
+        return self.d.screenshot(f"{tmp}/{self.serial.replace(':', '_')}.png")
 
     def to_top_swipe(self, sleep=0.1, times=2):
         """
@@ -196,9 +249,9 @@ class AbsControl(BaseControl):
         width, height = self.get_screen_size()
         # 循环滑动直到无法继续滑动
         for _ in range(times):
-            self.d().swipe(width // 2, height * 0.8, width // 2, height)  # 向上滑动
+            self.d.swipe(width // 2, height * 0.8, width // 2, height)  # 向上滑动
             # 短暂等待确保滑动完成
-            self.d().sleep(sleep)
+            self.d.sleep(sleep)
 
     def click_point(self, x: int, y: int):
         """
@@ -206,13 +259,13 @@ class AbsControl(BaseControl):
         :param x: x坐标
         :param y: y坐标
         """
-        self.d().click(x, y)
+        self.d.click(x, y)
 
     def click_xpath(self, xpath: str):
         """
         点击指定xpath
         """
-        el = self.d().xpath(xpath).get()
+        el = self.d.xpath(xpath).get()
         if el:
             el.click()
         else:
@@ -224,16 +277,16 @@ class AbsControl(BaseControl):
         """
         width, height = self.get_screen_size()
         # 从屏幕下方向上滑动到顶部
-        self.d().swipe(width // 2, height * 0.8, width // 2, height * 0.2)  # 向上滑动
+        self.d.swipe(width // 2, height * 0.8, width // 2, height * 0.2)  # 向上滑动
 
         # 短暂等待确保滑动完成
-        self.d().sleep(sleep)
+        self.d.sleep(sleep)
 
     def get_screen_size(self):
         """
         获取屏幕尺寸,宽度和高度
         """
-        self.width, self.height = self.d().window_size()
+        self.width, self.height = self.d.window_size()
         logging.info(f"屏幕尺寸: {self.width}x{self.height}")
         return self.width, self.height
 
@@ -246,7 +299,7 @@ class AbsControl(BaseControl):
         :param end_y: 结束y坐标
         :param steps: 步数,值越大滑动越平滑
         """
-        self.d().swipe_ext(start_x, start_y, end_x, end_y, steps)
+        self.d.swipe_ext(start_x, start_y, end_x, end_y, steps)
 
     def add_point(self, point=None):
         """
@@ -288,7 +341,7 @@ class AbsControl(BaseControl):
         保存坐标
         """
         self.to_top_swipe()
-        self.d().sleep(2)
+        self.d.sleep(2)
 
         for func in self._func:
             func()
@@ -329,7 +382,7 @@ class AbsControl(BaseControl):
         info 数据结构
         :return: x, y, el
         """
-        el = self.d().xpath(xpath).get()
+        el = self.d.xpath(xpath).get()
         x, y = el.center()
 
         return x, y, el
@@ -339,7 +392,7 @@ class AbsControl(BaseControl):
         获取所有坐标点
         info 数据结构
         """
-        els = self.d().xpath(xpath).all()
+        els = self.d.xpath(xpath).all()
 
         items = []
 
@@ -357,8 +410,8 @@ class AbsControl(BaseControl):
         :param timeout: 超时时间(秒)
         :return: 是否存在
         """
-        self.d().sleep(timeout)
-        return self.d().xpath(xpath).exists
+        self.d.sleep(timeout)
+        return self.d.xpath(xpath).exists
 
     def input_xpath(self, xpath: str, text: str, clear: bool = True):
         """
@@ -368,7 +421,7 @@ class AbsControl(BaseControl):
         :param text: 要输入的文本
         :param clear: 输入前是否清空原有内容
         """
-        element = self.d().xpath(xpath).get()
+        element = self.d.xpath(xpath).get()
         if element:
             if clear:
                 element.text.set_text("")  # 清空原有内容
@@ -387,18 +440,18 @@ class AbsControl(BaseControl):
         """
         self.click_point(x, y)  # 先点击获取焦点
         if clear:
-            self.d().clear_text()  # 清空原有内容
-        self.d().send_keys(text)  # 输入新文本
+            self.d.clear_text()  # 清空原有内容
+        self.d.send_keys(text)  # 输入新文本
 
     @abstractmethod
-    def event_f1(self):
+    def event_1(self):
         """
         F1  开仓界面,仓位滑竿百分比(30-60)
         """
         pass
 
     @abstractmethod
-    def event_f2(self):
+    def event_2(self):
         """
         F2 确认开仓,开多 限价职中间数值
         检查撤销限价委托单
@@ -406,28 +459,28 @@ class AbsControl(BaseControl):
         pass
 
     @abstractmethod
-    def event_f3(self):
+    def event_3(self):
         """
         F3 平仓界面 平空,仓位滑竿百分比(90-100)确认开仓,限价职中间数值
         """
         pass
 
     @abstractmethod
-    def event_f4(self):
+    def event_4(self):
         """
         F4   确认平仓 平多 限价取中间数值 检查撤销限价委托单
         """
         pass
 
     @abstractmethod
-    def event_f5(self):
+    def event_5(self):
         """
         F5 开仓界面 仓位滑竿百分比(30-60)
         """
         pass
 
     @abstractmethod
-    def event_f6(self):
+    def event_6(self):
         """
         F6 确认开仓, 开空 限价值中间数值
         检查撤销限价委托单
@@ -435,14 +488,14 @@ class AbsControl(BaseControl):
         pass
 
     @abstractmethod
-    def event_f7(self):
+    def event_7(self):
         """
         F7    平仓界面二仓位滑竿百分比(90-100)
         """
         pass
 
     @abstractmethod
-    def event_f8(self):
+    def event_8(self):
         """
         F8 确认平仓, 平空 限价职中间数值
         检查撤销限价委托单

+ 15 - 13
plat/deepcoin_control.py → control/plat/deepcoin_control.py

@@ -1,16 +1,13 @@
 # from utils.point_utils import UiFactory
 import logging
 
-from plat.base.base_control import AbsControl
+from control.base.base_control import AbsControl
 
 
 class DeepCoinControl(AbsControl):
 
-    def get_app_package_name(self):
-        return 'com.niocpeed.dna'
-
     def __init__(self, serial: str, *args, **kwargs):
-        super().__init__(serial)
+        super().__init__(serial, *args, **kwargs)
 
         self.package_name = 'com.niocpeed.dna'
 
@@ -270,12 +267,17 @@ class DeepCoinControl(AbsControl):
         y1 = y + 50
         self.input_by_position(x1, y1, str(text), True)
 
-    def event_f1(self):
+    def event_1(self):
         self.click_kaicang()
         self.slider_baozhengjin_kaicang()
         pass
 
-    def event_f2(self):
+    def event_2(self):
+        """
+        开仓情况  保证金滑块
+        :return:
+        """
+
         self.to_top_swipe(sleep=0.2)
         self.click_kaicang()
         self.select_kaicang_weituo_xianjia()
@@ -289,13 +291,13 @@ class DeepCoinControl(AbsControl):
         self.click_mairu_kaiduo()
         pass
 
-    def event_f3(self):
+    def event_3(self):
         self.to_top_swipe(sleep=0.2)
         self.click_pingchang()
         self.slider_baozhengjin_pingcang(offset=1)
         pass
 
-    def event_f4(self):
+    def event_4(self):
         self.to_top_swipe(sleep=0.2)
         self.click_pingchang()
         self.select_kaicang_weituo_xianjia()
@@ -305,13 +307,13 @@ class DeepCoinControl(AbsControl):
         self.click_maichu_pingduo()
         pass
 
-    def event_f5(self):
+    def event_5(self):
         self.to_top_swipe(sleep=0.2)
         self.click_kaicang()
         self.slider_baozhengjin_kaicang()
         pass
 
-    def event_f6(self):
+    def event_6(self):
         self.to_top_swipe(sleep=0.1)
         self.click_kaicang()
         self.select_kaicang_weituo_xianjia()
@@ -320,13 +322,13 @@ class DeepCoinControl(AbsControl):
         self.click_maichu_kaikong()
         pass
 
-    def event_f7(self):
+    def event_7(self):
         self.to_top_swipe(sleep=0.1)
         self.click_pingchang()
         self.slider_baozhengjin_pingcang(offset=2)
         pass
 
-    def event_f8(self):
+    def event_8(self):
         self.to_top_swipe(sleep=0.2)
 
         self.click_pingchang()

+ 25 - 6
plat/settings_control.py → control/ui/settings_control.py

@@ -5,12 +5,13 @@ from typing import List
 import tkinter as tk
 from adbutils import adb_path
 
-from plat.base.base_control import BaseControl
+from control.base.base_control import BaseControl
+from control.plat.deepcoin_control import DeepCoinControl
 
 
 class SettingsControl(BaseControl):
 
-    def __init__(self, name: str, ctx: int = 1):
+    def __init__(self, name: str):
 
         """
         设置相关的控制器
@@ -18,8 +19,6 @@ class SettingsControl(BaseControl):
         """
         super().__init__(name)
 
-        self.ctx = ctx
-
     def init_adb(self, commands: List[str], *args, **kwargs):
 
         def func():
@@ -68,12 +67,32 @@ class SettingsControl(BaseControl):
         d = self.connect_adb(cmd)
         info = d.info
         logging.info(f'设备信息{info}')
+        pkg = info['currentPackageName']
         self.connect_dict[cmd] = {
             'd': d,
             'info': info,
-            'device': cmd,
+            'serial': cmd,
             'status': 'online',
-            'pkg': self.app_pkg[info['currentPackageName']],
+            'pkg': self.app_pkg[pkg],
             'name': str(d.shell('settings get global device_name').output).strip(),
         }
+        if pkg == 'com.niocpeed.dna':
+            self.connect_dict[cmd]['control'] = DeepCoinControl(serial=cmd, ctx=self.connect_dict[cmd])
+
         pass
+
+    def devices_list(self) -> List[str]:
+        """
+        获取设备列表
+        :param command:
+        :return:
+        """
+        command = [adb_path(), 'devices']
+        logging.info(f"获取设备列表:{command}")
+        result = subprocess.run(command, capture_output=True, text=True, check=True)
+        devices = result.stdout.splitlines()
+        logging.info(f"设备列表:{devices}")
+        a = [x for x in devices if x and ':' in x]
+        # 去除每一项中的 \tdevice
+        a = [x.split('\t')[0] for x in a]
+        return a

+ 232 - 0
gui/main_ui.py

@@ -0,0 +1,232 @@
+import logging
+import tempfile
+import tkinter as tk
+from tkinter import Menu, Text, Label, Entry, Button, ttk
+from typing import Dict
+
+from PIL import Image, ImageTk  # 需要安装 Pillow 库
+
+from control.base.base_control import AbsControl, BaseControl
+from control.ui.settings_control import SettingsControl
+from gui.phone_list import PhoneListUI
+from gui.settings import SettingUI
+from utils.config import version
+from utils.control_util import ControlUtils
+
+CACHE_KEY_SETTING = 'settings'
+
+CACHE_KEY_PHONE_LIST = 'phone_list'
+
+keys_mapping = {
+    "F1": "开仓滑竿",
+    "F2": "买入/开多",
+    "F3": "平仓滑竿",
+    "F4": "卖出/平多",
+    "F5": "开仓滑竿",
+    "F6": "开仓/开空",
+    "F7": "平仓滑竿",
+    "F8": "卖出/平空"
+}
+
+
+class Application(tk.Tk):
+    """
+    页面布局
+    """
+
+    def __init__(self, controls=None):
+        super().__init__()
+        self.notebook = None
+        if controls is None:
+            controls = {}
+        self._controls: Dict[str, AbsControl] = controls  # 控件
+        # self.content_frame = None
+        self.navigation_buttons = None
+        self.navigation_frame = None
+        self.title(f"群控小助手[{version}]")
+        self.geometry("1366x768")
+
+        self.create_menu()
+        # self.create_navigation()
+        self.create_content_area()
+        # self._event_build()
+        self._content_cache = {}  # 缓存内容
+
+    def create_menu(self):
+        menubar = Menu(self)
+        self.config(menu=menubar)
+
+        menu1 = Menu(menubar, tearoff=0)
+        menu1.add_command(label="设置(S)", command=lambda: self.click_settings())
+        menu1.add_command(label="手机(T)", command=lambda: self.click_phone_list())
+        menu1.add_command(label="退出(Q)", command=self.quit)
+        menubar.add_cascade(label="视图", menu=menu1)
+
+        menu1 = Menu(menubar, tearoff=0)
+        menu1.add_command(label="关于", command=lambda: self.more_click('', "关于"))
+        menubar.add_cascade(label="帮助", menu=menu1)
+
+    def create_content_area(self):
+        # """创建主内容区域"""
+        # self.content_frame = tk.Frame(self)
+        # self.content_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+
+        # 创建一个 Style 实例
+        style = ttk.Style()
+
+        notebook_style_name = "main_ui.TNotebook"
+        style.configure(notebook_style_name)  # 可以进行其他配置,如果需要
+        style.layout(notebook_style_name + ".Tab", [])
+
+        self.notebook = ttk.Notebook(self, style=notebook_style_name)  # 将自定义样式应用到这个 Notebook
+        self.notebook.pack(fill="both", expand=True)
+
+    def _toggle_content(self, name: str):
+        """根据标签文本选择 Notebook 中的选项卡。"""
+        logging.info(f"打开页面 '{name}' 。")
+        for index in range(self.notebook.index("end")):
+            if self.notebook.tab(index, "text") == name:
+                self.notebook.select(index)
+                return True
+
+        return False
+
+    def click_settings(self):
+        """显示设置界面"""
+        # has = CACHE_KEY_SETTING in self._content_cache
+        logging.info(f"点击了设置:{SettingUI.__name__}")
+        #
+        # # 创建或获取UI
+        if not self._toggle_content(SettingUI.__name__):
+            ui = SettingUI(control=self._controls[SettingsControl.__name__])
+            self.notebook.add(ui, text=SettingUI.__name__)
+            self._toggle_content(SettingUI.__name__)
+
+    def click_phone_list(self):
+
+        """
+        显示手机列表
+        """
+        logging.info(f"点击了设置,缓存命中:{PhoneListUI.__name__}")
+
+        # 清空其他所有内容
+        if not self._toggle_content(PhoneListUI.__name__):
+            ui = PhoneListUI(control=ControlUtils())
+            self.notebook.add(ui, text=PhoneListUI.__name__)
+            self._toggle_content(PhoneListUI.__name__)
+
+    # 多点击事件,循环调用
+    def more_click(self, key, msg):
+        print(msg)
+
+    #
+    #     for item in self._controls:
+    #         item.print_log(msg)
+    #         met_name = f'event_{key.lower()}'
+    #         if hasattr(item, met_name):
+    #             evt = getattr(item, met_name)
+    #             evt()
+    #         else:
+    #             print(f"实例 {item.name}没有名为 '{key}' 的方法")
+    #
+    # def add_control(self, control: AbsControl):
+    #     self._controls.append(control)
+    #
+    # def _event_build(self):
+    #     def _bind_command(key, label):
+    #         self.bind_all(f"<{key}>", lambda event: self.more_click(key, label))
+    #
+    #     for key, value in keys_mapping.items():
+    #         label = f"{value}({key})"
+    #         logging.info(f'绑定事件:{label}')
+    #         _bind_command(key, label)
+
+    # def create_navigation(self):
+    #     self.navigation_frame = tk.Frame(self, width=150, bg="#f0f0f0")
+    #     self.navigation_frame.pack(side=tk.LEFT, fill=tk.Y)
+    #
+    #     for item in self._controls:
+    #         # image = Image.open(item['logo'])
+    #         # photo = ImageTk.PhotoImage(image)
+    #         logging.info(f"导航按钮:{item.name}->{item.ctx}")
+    #         button = tk.Button(self.navigation_frame, text=item.name, command=lambda i=item:
+    #         self.init_connect_content(i) if i.ctx == 1 else \
+    #             self.plat_content(i)
+    #                            # if item.ctx == 1 else self.plat_content(i),
+    #                            , compound=tk.LEFT)
+    #         button.pack(fill=tk.X, pady=5)
+    #         # self.navigation_buttons.append(button)
+
+    # def test_connect(self, control: AbsControl, serial: str, img: Label = None):
+    #     logging.info(f"测试连接:{serial}")
+    #
+    #     if serial.startswith("adb connect"):
+    #         serial = serial.split('connect')[1].strip()
+    #
+    #     control.connect_adb(serial)
+    #
+    #     self.flush_screenshot(control, img)
+
+    def flush_screenshot(self, control: AbsControl, image_label: Label):
+        """
+        刷新截图
+        """
+        control.screenshot()
+        try:
+            tmp = tempfile.gettempdir()
+            image = Image.open(f"{tmp}/{control.name}.png")
+            logging.info(f"图片存放路径:{tmp}/{control.name}.png")
+            w = 380
+            image = image.resize((w, w * 16 // 9))
+            logging.info(f"刷新截图:{image}")
+            photo = ImageTk.PhotoImage(image)
+            image_label.config(image=photo)
+            image_label.image = photo  # 保持对图片的引用
+        except FileNotFoundError:
+            logging.error(f"图片不存在")
+        pass
+
+    # def plat_content(self, item: AbsControl):
+    #     print(f"plat_content---> {item.name},{self._content_cache}")
+    #     for key, value in self._content_cache.items():
+    #         if key != item.name:
+    #             value.pack_forget()
+    #
+    #     content: tk.Frame
+    #
+    #     if item.name in self._content_cache:
+    #         content = self._content_cache[item.name]
+    #     else:
+    #         content = tk.Frame(self.content_frame)
+    #         # 表单区域
+    #         form_frame = tk.Frame(content)
+    #         form_frame.pack(side=tk.TOP, fill=tk.X)
+    #         # 设置第二列的权重
+    #         # form_frame.columnconfigure(1, weight=1)
+    #         tk.Label(form_frame, text="连接:").grid(row=0, column=0, sticky="w")
+    #         name_entry = Entry(form_frame, width=100)
+    #         name_entry.grid(row=0, column=1)
+    #         info = BaseControl.connect_dict[item.get_app_package_name()]
+    #         name_entry.insert(0, info['device'])
+    #         # 图片预览区域
+    #         image_label = Label(content, width=200, height=380)
+    #         Button(form_frame, text="测试连接",
+    #                command=lambda n=name_entry: self.test_connect(item, name_entry.get(), image_label)).grid(row=0,
+    #                                                                                                          column=1,
+    #                                                                                                          sticky="e")
+    #         Button(form_frame, text="查看手机",
+    #                command=lambda n=name_entry: self.flush_screenshot(item, image_label)).grid(row=0, column=2,
+    #                                                                                            sticky="e")
+    #
+    #         # 日志区域
+    #         log_text = Text(content, height=10)
+    #         log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+    #         log_text.insert(tk.END, f"导航 {item.name} 被点击\n")
+    #
+    #         item.set_log_func(lambda msg: log_text.insert(tk.END, f"{msg}\n"))
+    #
+    #         image_label.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
+    #         # self.flush_screenshot(image_label)
+    #         self._content_cache[item.name] = content  # 缓存内容
+    #
+    #     content.pack(fill=tk.BOTH, expand=True)

+ 41 - 36
gui/phone_list.py

@@ -2,12 +2,16 @@ import logging
 import tkinter as tk
 from tkinter import ttk
 
-from plat.base.base_control import BaseControl
+from control.base.base_control import BaseControl, UIControl
 
 
 class LeftPanel(tk.LabelFrame):
-    def __init__(self, master=None):
+    def __init__(self, master=None, control: UIControl = None):
         super().__init__(master, text="手机列表")
+        self.scrollbar = None
+        self.canvas = None
+        self.list_frame = None
+        self.control = control
         self.master = master
         self.phone_items_data = []  # 用于存储每个手机项的数据 (包含 CheckVar)
         self.create_widgets()
@@ -26,7 +30,8 @@ class LeftPanel(tk.LabelFrame):
         name_label.grid(row=0, column=1, padx=5, pady=10, sticky="e")
         # 选择框
         check_all = tk.BooleanVar()
-        check_button = ttk.Checkbutton(title_label, variable=check_all, command=lambda v=check_all: self.toggle_check(v))
+        check_button = ttk.Checkbutton(title_label, variable=check_all,
+                                       command=lambda v=check_all: self.toggle_check(v))
         check_button.grid(row=0, column=2, padx=5, pady=10, sticky="e")
 
         # 添加数据按钮
@@ -76,7 +81,6 @@ class LeftPanel(tk.LabelFrame):
         item_frame = ttk.Frame(self.list_frame, padding=(5, 5))
         item_frame.pack(fill="x", expand=True)
 
-        name = device['device']
         status = device['status']
 
         # 选择框
@@ -101,7 +105,7 @@ class LeftPanel(tk.LabelFrame):
         status_button = ttk.Label(item_frame, text="@", width=3, anchor="center", relief="groove")
         status_button.pack(side="right", padx=5)
 
-        self.phone_items_data.append((check_var, name, status))  # 存储 CheckVar 和数据
+        self.phone_items_data.append((check_var, device["serial"], status))  # 存储 CheckVar 和数据
 
     def toggle_check(self, check_var):
         if check_var.get():
@@ -116,10 +120,6 @@ class LeftPanel(tk.LabelFrame):
         """
         动态添加数据
         """
-        # logging.info(f"==手机信息=>{BaseControl.connect_dict}")
-        # new_index = len(self.phone_data) + 1
-        # new_phone = {"name": f"Tel - {new_index:02d} [New Device]", "status": "pending"}
-        # self.phone_data.append(new_phone)
         self._populate_list()  # 重新填充列表以显示新数据
         self._check_scrollable()  # 添加数据后检查是否需要滚动条
 
@@ -160,8 +160,9 @@ class LeftPanel(tk.LabelFrame):
 
 
 class RightPanel(tk.Frame):
-    def __init__(self, master=None, *args, **kwargs):
-        super().__init__(master)
+    def __init__(self, master=None, control: UIControl = None, *args, **kwargs):
+        super().__init__(master, *args, **kwargs)
+        self.control = control
         self.master = master
         self.create_args()
         self.create_info_ares()
@@ -182,19 +183,21 @@ class RightPanel(tk.Frame):
         fieldset.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
         # fieldset.grid_columnconfigure(0, weight=1)
         # fieldset.grid_rowconfigure(1, weight=1)
-        name_label = ttk.Label(fieldset, text="币种:", width=5)
-        name_label.grid(row=0, column=0, padx=5, pady=5, sticky="nw")
-
-        name_val = tk.Text(fieldset, width=20, height=1.2, wrap='word')
-        name_val.grid(row=0, column=1, padx=5, pady=5, sticky="nw")
-        name_val.insert('1.0', 'HMSTR')  # 插入文本
-        name_val.config(state='disabled')  # 再禁用
+        # name_label = ttk.Label(fieldset, text="币种:", width=5)
+        # name_label.grid(row=0, column=0, padx=5, pady=5, sticky="nw")
+        #
+        # name_val = tk.Text(fieldset, width=20, height=1.2, wrap='word')
+        # name_val.grid(row=0, column=1, padx=5, pady=5, sticky="nw")
+        # name_val.insert('1.0', 'HMSTR')  # 插入文本
+        # name_val.config(state='disabled')  # 再禁用
 
         cmd_label = ttk.Label(fieldset, text="数量:", width=5)
-        cmd_label.grid(row=0, column=2, padx=5, pady=5, sticky="nw")
+        cmd_label.grid(row=0, column=1, padx=5, pady=5, sticky="nw")
 
         cmd_text = tk.Text(fieldset, height=1.2, width=10, wrap='word')
-        cmd_text.grid(row=0, column=3, padx=5, pady=5, sticky="nw")
+        cmd_text.grid(row=0, column=2, padx=5, pady=5, sticky="nw")
+        button1 = ttk.Button(fieldset, text="设置", command=lambda: print('----'))
+        button1.grid(row=0, column=3, padx=2, pady=(5, 2), sticky="ew")
 
     def create_info_ares(self):
         # 创建一个 Frame 来模拟 <fieldset>
@@ -202,14 +205,15 @@ class RightPanel(tk.Frame):
         fieldset.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
 
         # 设置 fieldset 的网格权重配置
-        fieldset.grid_columnconfigure(0, weight=3)  # 让信息区域占据更多水平空间
-        fieldset.grid_columnconfigure(1, weight=1)  # 操作区域占据较少水平空间
+        fieldset.grid_columnconfigure(1, weight=3)  # 让信息区域占据更多水平空间
+        fieldset.grid_columnconfigure(0, weight=1)  # 操作区域占据较少水平空间
         fieldset.grid_rowconfigure(0, weight=1)  # 让内容区域可以垂直扩展
 
         fieldset_action = ttk.LabelFrame(fieldset, text="操作")
-        fieldset_action.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
+        fieldset_action.grid(row=0, column=0, padx=10, pady=10, sticky="new")
+        # 设置列权重使按钮平分宽度
         fieldset_action.grid_columnconfigure(0, weight=1)
-        fieldset_action.grid_rowconfigure(1, weight=1)
+        fieldset_action.grid_columnconfigure(1, weight=1)
 
         fieldset_log = ttk.LabelFrame(fieldset, text="操作日志")
         fieldset_log.grid(row=0, column=1, padx=10, pady=10, sticky="nsew")
@@ -241,19 +245,20 @@ class RightPanel(tk.Frame):
 
         gray_btn_style = ttk.Style()
         gray_btn_style.configure('Gray.TButton', background='#888888', foreground='#666666')
-        button1 = ttk.Button(fieldset_action, text="买入/开多", command=lambda: print("买入/开多"), style='Green.TButton')
-        button1.grid(row=0, column=0, padx=5, pady=5, sticky="nw")
-
-        button2 = ttk.Button(fieldset_action, text="买入/平多", command=lambda: print("买入/平多"), style='Green.TButton')
-        button2.grid(row=0, column=1, padx=5, pady=5, sticky="nw")
+        button1 = ttk.Button(fieldset_action, text="买入/开多", command=lambda: self.control.event_2(),
+                             style='Green.TButton')
+        button1.grid(row=0, column=0, padx=2, pady=(5, 2), sticky="ew")
+        button2 = ttk.Button(fieldset_action, text="买入/平多", command=lambda: print("买入/平多"),
+                             style='Green.TButton')
+        button2.grid(row=0, column=1, padx=2, pady=(5, 2), sticky="ew")
         button3 = ttk.Button(fieldset_action, text="卖出/开空", command=lambda: print("卖出/开空"), style='Red.TButton')
-        button3.grid(row=1, column=0, padx=5, pady=5, sticky="nw")
+        button3.grid(row=1, column=0, padx=2, pady=2, sticky="ew")
         button4 = ttk.Button(fieldset_action, text="卖出/平空", command=lambda: print("卖出/平空"), style='Red.TButton')
-        button4.grid(row=1, column=1, padx=5, pady=5, sticky="nw")
+        button4.grid(row=1, column=1, padx=2, pady=2, sticky="ew")
         button5 = ttk.Button(fieldset_action, text="撤销委托", command=lambda: print("撤销委托"))
-        button5.grid(row=2, column=0, padx=5, pady=5, sticky="nw")
+        button5.grid(row=2, column=0, padx=2, pady=(2, 5), sticky="ew")
         button6 = ttk.Button(fieldset_action, text="一键撤单", command=lambda: print("一键撤单"))
-        button6.grid(row=2, column=1, padx=5, pady=5, sticky="nw")
+        button6.grid(row=2, column=1, padx=2, pady=(2, 5), sticky="ew")
 
 
 class PhoneListUI(tk.Frame):
@@ -261,15 +266,15 @@ class PhoneListUI(tk.Frame):
     手机列表控制页面
     """
 
-    def __init__(self, control: BaseControl = None, master=None, cnf=None, **kw):
+    def __init__(self, control: UIControl = None, master=None, cnf=None, **kw):
         super().__init__(master, cnf if cnf is not None else {}, **kw)
         self.control = control
         self.init_ui()
 
     def init_ui(self):
-        left_panel = LeftPanel(self)
+        left_panel = LeftPanel(self, control=self.control)
         left_panel.pack(side="left", fill="y")
 
         # 为了演示,我们创建一个简单的右侧区域
-        right_panel = RightPanel(self, width=400, height=400)
+        right_panel = RightPanel(self, width=400, height=400, control=self.control)
         right_panel.pack(side="left", fill="both", expand=True)

+ 16 - 5
gui/settings.py

@@ -3,7 +3,7 @@ import tkinter as tk
 from tkinter import ttk
 from typing import List
 
-from plat.base.base_control import BaseControl, AbsControl
+from control.base.base_control import BaseControl, AbsControl
 from utils.config import version
 
 
@@ -12,7 +12,7 @@ class _SettingsConnect(tk.Frame):
     连接云手机
     """
 
-    def __init__(self, master=None, control: AbsControl = None, cnf=None, **kw):
+    def __init__(self, master=None, control: BaseControl = None, cnf=None, **kw):
         super().__init__(master, cnf if cnf is not None else {}, **kw)
 
         self.control = control
@@ -41,10 +41,21 @@ class _SettingsConnect(tk.Frame):
         cmd_label.grid(row=0, column=0, padx=5, pady=5, sticky="nw")
 
         cmd_text = tk.Text(fieldset, height=10)
-        cmd_text.grid(row=1, column=0, padx=5, pady=5, sticky="nsew")
+        cmd_text.grid(row=1, column=0, columnspan=2, padx=5, pady=5, sticky="nsew")
+
+        def insert_advice_list() -> None:
+            devices = self.control.devices_list()
+            if devices:
+                for device in devices:
+                    cmd_text.insert(tk.END, f"adb connect {device}\n")
+
+        auto_btn = ttk.Button(fieldset, text="检查连接", command=lambda: insert_advice_list())
+        auto_btn.grid(row=2, column=1, padx=(5, 2), pady=5, sticky="nw")
 
-        test_btn = ttk.Button(fieldset, text="连接云手机", command=lambda: self.control.init_adb(cmd_text.get('1.0', tk.END).split('\n'), log_text, **{'ui': self}))
-        test_btn.grid(row=2, column=0, padx=5, pady=5, sticky="nw")
+        test_btn = ttk.Button(fieldset, text="连接云手机",
+                              command=lambda: self.control.init_adb(cmd_text.get('1.0', tk.END).split('\n'), log_text,
+                                                                    **{'ui': self}))
+        test_btn.grid(row=2, column=0, padx=(2, 5), pady=5, sticky="nw")
 
         # 日志区域
         log_label = ttk.Label(content, text="连接信息:")

+ 0 - 219
gui/ui.py

@@ -1,219 +0,0 @@
-import logging
-import tempfile
-import tkinter as tk
-from tkinter import Menu, Text, Label, Entry, Button
-from typing import List
-
-from PIL import Image, ImageTk  # 需要安装 Pillow 库
-
-from plat.base.base_control import AbsControl
-
-keys_mapping = {
-    "F1": "开仓滑竿",
-    "F2": "买入/开多",
-    "F3": "平仓滑竿",
-    "F4": "卖出/平多",
-    "F5": "开仓滑竿",
-    "F6": "开仓/开空",
-    "F7": "平仓滑竿",
-    "F8": "卖出/平空"
-}
-
-
-class Application(tk.Tk):
-    """
-    页面布局
-    """
-
-    def __init__(self, controls=None):
-        super().__init__()
-        if controls is None:
-            controls = []
-        self._controls: List[AbsControl] = controls  # 控件
-        self.content_frame = None
-        self.navigation_buttons = None
-        self.navigation_frame = None
-        self.title("群控小助手")
-        self.geometry("1366x768")
-
-        self.create_menu()
-        self.create_navigation()
-        self.create_content_area()
-        self._event_build()
-        self._content_cache = {}  # 缓存内容
-
-        # ===========================
-        # adb 连接
-        # self.adb_serial = tk.StringVar()
-
-    def create_menu(self):
-        menubar = Menu(self)
-        self.config(menu=menubar)
-
-        menu1 = Menu(menubar, tearoff=0)
-        menu1.add_command(label="配置", command=lambda: self.more_click("配置"))
-        menu1.add_command(label="退出", command=self.quit)
-        menubar.add_cascade(label="管理", menu=menu1)
-
-        menu2 = Menu(menubar, tearoff=0)
-
-        def _bind_command(key, label):
-            menu2.add_command(label=label, accelerator=key, command=lambda k=key, v=label: self.more_click(k, v))
-
-        # 循环 keys_mapping 字典 ,生成菜单
-        for key, value in keys_mapping.items():
-            label = f"{value}({key})"
-            logging.info(f"生成菜单项:{label}")
-            _bind_command(key, label)
-
-        # key = "F1"
-        # label = f"开仓滑竿({key})"
-        # menu2.add_command(label=label, accelerator=key, command=lambda: self.more_click(key, label))
-        # key = "F2"
-        # label = f"买入/开多({key})"
-        # menu2.add_command(label=label, accelerator=key, command=lambda: self.more_click(key, label))
-        # key = "F3"
-        # label = f"平仓滑竿({key})"
-        # menu2.add_command(label=label, accelerator=key, command=lambda: self.more_click(key, label))
-        # key = "F4"
-        # label = f"卖出/平多({key})"
-        # menu2.add_command(label=label, accelerator=key, command=lambda: self.more_click(key, label))
-        # key = "F5"
-        # label = f"开仓滑竿({key})"
-        # menu2.add_command(label=label, accelerator=key, command=lambda: self.more_click(key, label))
-        # key = "F6"
-        # label = f"开仓/开空({key})"
-        # menu2.add_command(label=label, accelerator=key, command=lambda: self.more_click(key, label))
-        # key = "F7"
-        # label = f"平仓滑竿({key})"
-        # menu2.add_command(label=label, accelerator=key, command=lambda: self.more_click(key, label))
-        # key = "F8"
-        # label = f"卖出/平空({key})"
-        # menu2.add_command(label=label, accelerator=key, command=lambda: self.more_click(key, label))
-
-        menubar.add_cascade(label="操作", menu=menu2)
-
-        menu1 = Menu(menubar, tearoff=0)
-        menu1.add_command(label="关于", command=lambda: self.more_click('', "关于"))
-        menubar.add_cascade(label="帮助", menu=menu1)
-
-    # 多点击事件,循环调用
-    def more_click(self, key, msg):
-        print(msg)
-
-        for item in self._controls:
-            item.print_log(msg)
-            met_name = f'event_{key.lower()}'
-            if hasattr(item, met_name):
-                evt = getattr(item, met_name)
-                evt()
-            else:
-                print(f"实例 {item.name}没有名为 '{key}' 的方法")
-
-    def add_control(self, control: AbsControl):
-        self._controls.append(control)
-
-    def _event_build(self):
-
-        def _bind_command(key, label):
-            self.bind_all(f"<{key}>", lambda event: self.more_click(key, label))
-
-        for key, value in keys_mapping.items():
-            label = f"{value}({key})"
-            logging.info(f'绑定事件:{label}')
-            _bind_command(key, label)
-
-        # self.bind_all("<F2>", lambda event: self.more_click("买入/开多(F2)"))
-        # self.bind_all("<F3>", lambda event: self.more_click("平仓滑竿(F3)"))
-        # self.bind_all("<F4>", lambda event: self.more_click("卖出/平多(F4)"))
-        # self.bind_all("<F5>", lambda event: self.more_click("开仓滑竿(F5)"))
-        # self.bind_all("<F6>", lambda event: self.more_click("开仓/开空(F6)"))
-        # self.bind_all("<F7>", lambda event: self.more_click("平仓滑竿(F7)"))
-        # self.bind_all("<F8>", lambda event: self.more_click("卖出/平空(F8)"))
-
-    def create_navigation(self):
-        self.navigation_frame = tk.Frame(self, width=150, bg="#f0f0f0")
-        self.navigation_frame.pack(side=tk.LEFT, fill=tk.Y)
-
-        for item in self._controls:
-            # image = Image.open(item['logo'])
-            # photo = ImageTk.PhotoImage(image)
-            button = tk.Button(self.navigation_frame, text=item.name, command=lambda i=item: self.show_content(i), compound=tk.LEFT)
-            button.pack(fill=tk.X, pady=5)
-            # self.navigation_buttons.append(button)
-
-    def test_connect(self, control: AbsControl, serial: str, img: Label = None):
-
-        logging.info(f"测试连接:{serial}")
-
-        control.connect_adb(serial)
-
-        self.flush_screenshot(control, img)
-
-    def flush_screenshot(self, control: AbsControl, image_label: Label):
-        """
-        刷新截图
-        """
-        control.screenshot()
-        try:
-            tmp = tempfile.gettempdir()
-            image = Image.open(f"{tmp}/{control.name}.png")
-            w = 380
-            image = image.resize((w, w * 16 // 9))
-            logging.info(f"刷新截图:{image}")
-            photo = ImageTk.PhotoImage(image)
-            image_label.config(image=photo)
-            image_label.image = photo  # 保持对图片的引用
-        except FileNotFoundError:
-            logging.error(f"图片不存在")
-        pass
-
-    def create_content_area(self):
-        self.content_frame = tk.Frame(self)
-        self.content_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
-
-    def show_content(self, item: AbsControl):
-        print(f"---> {item.name},{self._content_cache}")
-        for key, value in self._content_cache.items():
-            if key != item.name:
-                value.pack_forget()
-        if item.name in self._content_cache:
-            content = self._content_cache[item.name]
-            content.pack(fill=tk.BOTH, expand=True)
-        else:
-            content = tk.Frame(self.content_frame)
-            content.pack(fill=tk.BOTH, expand=True)
-
-            # 表单区域
-            form_frame = tk.Frame(content)
-            form_frame.pack(side=tk.TOP, fill=tk.X)
-            # 设置第二列的权重
-            # form_frame.columnconfigure(1, weight=1)
-
-            tk.Label(form_frame, text="连接:").grid(row=0, column=0, sticky="w")
-            name_entry = Entry(form_frame, width=100)
-            name_entry.grid(row=0, column=1)
-            name_entry.insert(0, '127.0.0.1:6555')
-            # 图片预览区域
-            image_label = Label(content, width=200, height=380)
-            Button(form_frame, text="测试连接", command=lambda n=name_entry: self.test_connect(item, name_entry.get(), image_label)).grid(row=0, column=1, sticky="e")
-            Button(form_frame, text="查看手机", command=lambda n=name_entry: self.flush_screenshot(item, image_label)).grid(row=0, column=2, sticky="e")
-
-            # tk.Label(form_frame, text="年龄:").grid(row=1, column=0, sticky="w")
-            # age_entry = Entry(form_frame)
-            # age_entry.grid(row=1, column=1)
-            #
-            # Button(form_frame, text="保存").grid(row=2, column=1, sticky="e")
-
-            # 日志区域
-            log_text = Text(content, height=10)
-            log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
-            log_text.insert(tk.END, f"导航 {item} 被点击\n")
-
-            item.set_log_func(lambda msg: log_text.insert(tk.END, f"{msg}\n"))
-
-            image_label.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
-
-            # self.flush_screenshot(image_label)
-
-            self._content_cache[item.name] = content  # 缓存内容

+ 2 - 3
main.py

@@ -1,8 +1,7 @@
 import logging
 
+from control.ui.settings_control import SettingsControl
 from gui.main_ui import Application
-from plat.deepcoin_control import DeepCoinControl
-from plat.settings_control import SettingsControl
 
 logging.basicConfig(level=logging.INFO, force=True)  # 设置日志级别
 
@@ -11,7 +10,7 @@ if __name__ == "__main__":
     app = Application(
         controls={
             SettingsControl.__name__: settings,
-            'deepCoin': DeepCoinControl('DeepCoin', settings)
+            # 'deepCoin': DeepCoinControl('DeepCoin', settings)
         }
         # controls=[
         #     adb

+ 1 - 1
test/deepcoin/test_deepcoin_control.py

@@ -1,7 +1,7 @@
 import logging
 import unittest
 
-from plat.deepcoin_control import DeepCoinControl
+from control.plat.deepcoin_control import DeepCoinControl
 
 logging.basicConfig(level=logging.INFO, force=True)  # 设置日志级别
 

+ 1 - 0
utils/config.py

@@ -0,0 +1 @@
+version = 'v0.0.1'