ui界面版本

This commit is contained in:
Ray
2025-10-29 10:49:38 +08:00
parent 8294cab51b
commit 3f1dd4e8c1
12 changed files with 1659 additions and 94 deletions

99
CHANGELOG.md Normal file
View File

@@ -0,0 +1,99 @@
# 更新日志
## 最新更新 - 多组配置系统
### 主要变更
#### 1. 新增文件
- **config.py** - 配置管理器,支持多组配置
- **gui_config.py** - 可视化配置界面tkinter
- **preview.py** - 采集卡预览窗口系统
- **launcher.py** - 统一启动器
- **README.md** - 使用文档
- **config.json** - 配置文件(自动生成)
#### 2. 修改的文件
- **main.py** - 完全重写,支持配置系统
- 从配置文件加载参数
- 支持动态读取v值
- 移除硬编码配置
- **utils/mouse.py** - 移除硬编码串口
- 新增 `init_mouse_keyboard()` 函数
- 支持从配置初始化
- **utils/get_image.py** - 支持从配置初始化
- GetImage改为延迟初始化
### 核心功能
1. **多组配置管理**
- 支持创建多个配置组
- 每个配置组独立的串口、采集卡、速度设置
- 可视化切换活动配置
2. **可视化配置界面**
- GUI界面管理所有配置
- 实时编辑和保存
- 支持添加/删除配置组
3. **采集卡预览系统**
- 网格方式预览多个采集卡
- 点击放大查看
- 实时画面更新
4. **灵活的参数配置**
- 串口配置(端口、波特率)
- 采集卡配置(索引、分辨率)
- 移动速度(v值)配置
- 预览窗口配置
### 使用方式
#### 旧版本(已保留 main_old.py
```bash
python main_old.py # 直接运行,使用硬编码配置
```
#### 新版本
```bash
# 方式1: 使用启动器(推荐)
python launcher.py
# 方式2: 直接运行
python main.py # 读取config.json的active配置
```
### 配置示例
```json
{
"groups": [
{
"name": "配置组1",
"serial_port": "COM6",
"serial_baudrate": 9600,
"camera_index": 0,
"camera_width": 1920,
"camera_height": 1080,
"move_velocity": 470, // ← move_to函数中的v值
"active": true
}
]
}
```
### 兼容性
- ✅ 保留所有原有功能
- ✅ YOLO模型无需修改
- ✅ 游戏逻辑完全兼容
- ⚠️ 旧代码会使用默认配置
### 下一步
如需添加更多配置:
1.`config.py``default_config` 中添加新字段
2.`gui_config.py` 中添加对应的输入框
3.`main.py` 中读取并使用新配置

141
README.md Normal file
View File

@@ -0,0 +1,141 @@
# 火炬之光自动化脚本
## 功能特点
-**多配置组管理** - 支持多组配置,可同时管理多套采集卡和串口设置
- 🖥️ **可视化配置** - 提供图形化界面管理所有配置
- 👁️ **采集卡预览** - 多窗口网格预览,点击可放大查看
- ⚙️ **灵活配置** - 所有参数(串口、采集卡、移动速度等)均可配置
- 🤖 **智能自动化** - 基于YOLO目标检测的游戏自动化
## 快速开始
### 1. 安装依赖
```bash
pip install opencv-python ultralytics ddddocr ch9329Comm serial PIL numpy
```
### 2. 启动程序
```bash
python launcher.py
```
启动器提供三个选项:
- **⚙️ 配置管理** - 配置串口、采集卡等参数
- **👁️ 采集卡预览** - 实时预览采集卡画面
- **🚀 启动自动化** - 启动游戏自动化脚本
### 3. 配置设置
首次使用请先点击"配置管理"
1. **添加配置组** - 点击"添加组"创建新配置
2. **设置参数**
- 串口:选择设备串口号(如 COM6
- 波特率设置串口波特率默认9600
- 采集卡索引:选择采集卡设备号
- 采集宽度/高度:设置采集分辨率
- 移动速度设置角色移动速度v值
3. **设为活动** - 选择要使用的配置组并设为活动
4. **保存配置** - 点击"保存配置"
### 4. 预览采集卡
点击"采集卡预览"可以:
- 网格方式查看所有活动的采集卡
- 点击任意窗口可放大查看
- 实时显示采集卡画面
### 5. 启动自动化
配置完成后,点击"启动自动化"开始游戏自动化。
## 配置说明
### 配置组结构
```json
{
"groups": [
{
"name": "配置组1",
"serial_port": "COM6",
"serial_baudrate": 9600,
"camera_index": 0,
"camera_width": 1920,
"camera_height": 1080,
"move_velocity": 470,
"active": false
}
],
"display": {
"preview_window_width": 640,
"preview_window_height": 360,
"preview_columns": 2,
"preview_rows": 2,
"show_preview": true
}
}
```
### 关键参数说明
- **serial_port**: 串口号,如 COM1, COM6
- **serial_baudrate**: 波特率,通常为 9600
- **camera_index**: 采集卡索引0/1/2...
- **camera_width/height**: 采集分辨率
- **move_velocity**: 角色移动速度v值数值越大移动越快
## 文件结构
```
huojv/
├── launcher.py # 启动器
├── main.py # 主程序(自动游戏)
├── config.py # 配置管理器
├── gui_config.py # 配置GUI界面
├── preview.py # 预览窗口
├── utils/
│ ├── get_image.py # 采集卡管理
│ ├── mouse.py # 鼠标控制
│ ├── shizi.py # OCR识别
│ └── ...
├── best.pt # YOLO模型战斗
├── best0.pt # YOLO模型城镇
└── config.json # 配置文件(自动生成)
```
## 使用流程
1. 运行 `python launcher.py`
2. 点击"配置管理"设置参数
3. 点击"采集卡预览"测试采集
4. 点击"启动自动化"开始游戏
5. 享受自动化!
## 注意事项
- 确保采集卡和串口设备已正确连接
- 串口号和采集卡索引需要根据实际情况设置
- 移动速度(v值)需要根据游戏角色属性调整
- 配置文件保存在 `config.json`
## 问题排查
### 无法打开串口
- 检查串口号是否正确
- 确认串口设备已连接
- 检查其他程序是否占用串口
### 无法打开采集卡
- 检查采集卡索引是否正确0, 1, 2...
- 确认采集卡驱动已安装
- 尝试不同的采集卡索引
### 识别不准确
- 调整移动速度(v值)
- 检查采集卡画面是否清晰
- 确认游戏窗口位置正确

22
config.json Normal file
View File

@@ -0,0 +1,22 @@
{
"groups": [
{
"name": "配置组1",
"serial_port": "COM6",
"serial_baudrate": 9600,
"camera_index": 0,
"camera_width": 1920,
"camera_height": 1080,
"move_velocity": 470,
"active": true
}
],
"display": {
"preview_window_width": 640,
"preview_window_height": 360,
"preview_columns": 2,
"preview_rows": 2,
"show_preview": true
}
}

111
config.py Normal file
View File

@@ -0,0 +1,111 @@
import json
import os
class ConfigManager:
"""配置管理器"""
def __init__(self, config_file='config.json'):
self.config_file = config_file
self.default_config = {
"groups": [
{
"name": "配置组1",
"serial_port": "COM6",
"serial_baudrate": 9600,
"camera_index": 0,
"camera_width": 1920,
"camera_height": 1080,
"move_velocity": 470,
"active": False
}
],
"display": {
"preview_window_width": 640,
"preview_window_height": 360,
"preview_columns": 2,
"preview_rows": 2,
"show_preview": True
}
}
self.config = self.load_config()
def load_config(self):
"""加载配置文件"""
if os.path.exists(self.config_file):
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
# 确保有默认结构
if 'groups' not in config:
config['groups'] = self.default_config['groups']
if 'display' not in config:
config['display'] = self.default_config['display']
return config
except Exception as e:
print(f"加载配置文件失败: {e}")
return self.default_config.copy()
return self.default_config.copy()
def save_config(self):
"""保存配置文件"""
try:
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(self.config, f, ensure_ascii=False, indent=4)
return True
except Exception as e:
print(f"保存配置文件失败: {e}")
return False
def add_group(self, name=None):
"""添加配置组"""
if name is None:
name = f"配置组{len(self.config['groups']) + 1}"
new_group = {
"name": name,
"serial_port": "COM6",
"serial_baudrate": 9600,
"camera_index": len(self.config['groups']),
"camera_width": 1920,
"camera_height": 1080,
"move_velocity": 470,
"active": False
}
self.config['groups'].append(new_group)
return new_group
def delete_group(self, index):
"""删除配置组"""
if 0 <= index < len(self.config['groups']):
del self.config['groups'][index]
return True
return False
def update_group(self, index, **kwargs):
"""更新配置组"""
if 0 <= index < len(self.config['groups']):
self.config['groups'][index].update(kwargs)
return True
return False
def get_active_group(self):
"""获取当前活动的配置组"""
for group in self.config['groups']:
if group.get('active', False):
return group
return None
def set_active_group(self, index):
"""设置活动的配置组"""
for i, group in enumerate(self.config['groups']):
group['active'] = (i == index)
return True
def get_group_by_index(self, index):
"""根据索引获取配置组"""
if 0 <= index < len(self.config['groups']):
return self.config['groups'][index]
return None
# 全局配置管理器实例
config_manager = ConfigManager()

223
gui_config.py Normal file
View File

@@ -0,0 +1,223 @@
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
from config import config_manager
import threading
class ConfigGUI:
"""配置GUI界面"""
def __init__(self):
self.root = tk.Tk()
self.root.title("配置管理 - 火炬之光自动化")
self.root.geometry("800x600")
self.selected_index = 0
self.setup_ui()
self.load_current_config()
def setup_ui(self):
"""设置UI界面"""
# 左侧:配置组列表
left_frame = ttk.Frame(self.root, padding="10")
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=False)
ttk.Label(left_frame, text="配置组列表", font=('Arial', 12, 'bold')).pack()
# 配置组列表
self.group_listbox = tk.Listbox(left_frame, width=20, height=25)
self.group_listbox.pack(fill=tk.BOTH, expand=True, pady=5)
self.group_listbox.bind('<<ListboxSelect>>', self.on_group_select)
# 按钮
btn_frame = ttk.Frame(left_frame)
btn_frame.pack(fill=tk.X)
ttk.Button(btn_frame, text="添加组", command=self.add_group).pack(fill=tk.X, pady=2)
ttk.Button(btn_frame, text="删除组", command=self.delete_group).pack(fill=tk.X, pady=2)
ttk.Button(btn_frame, text="设为活动", command=self.set_active).pack(fill=tk.X, pady=2)
# 右侧:配置详情
right_frame = ttk.Frame(self.root, padding="10")
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
ttk.Label(right_frame, text="配置详情", font=('Arial', 12, 'bold')).pack()
# 配置项
self.config_vars = {}
# 基本配置
basic_frame = ttk.LabelFrame(right_frame, text="基本配置", padding="10")
basic_frame.pack(fill=tk.X, pady=5)
self.create_entry(basic_frame, "name", "配置组名称:")
self.create_entry(basic_frame, "camera_index", "采集卡索引:")
self.create_entry(basic_frame, "camera_width", "采集宽度:")
self.create_entry(basic_frame, "camera_height", "采集高度:")
# 串口配置
serial_frame = ttk.LabelFrame(right_frame, text="串口配置", padding="10")
serial_frame.pack(fill=tk.X, pady=5)
self.create_entry(serial_frame, "serial_port", "串口:")
self.create_entry(serial_frame, "serial_baudrate", "波特率:")
# 游戏配置
game_frame = ttk.LabelFrame(right_frame, text="游戏配置", padding="10")
game_frame.pack(fill=tk.X, pady=5)
self.create_entry(game_frame, "move_velocity", "移动速度(v值):")
# 预览配置
preview_frame = ttk.LabelFrame(right_frame, text="预览配置", padding="10")
preview_frame.pack(fill=tk.X, pady=5)
self.create_entry(preview_frame, "preview_width", "预览宽度:", prefix="display")
self.create_entry(preview_frame, "preview_height", "预览高度:", prefix="display")
self.create_entry(preview_frame, "preview_columns", "预览列数:", prefix="display")
self.create_entry(preview_frame, "preview_rows", "预览行数:", prefix="display")
# 保存按钮
save_frame = ttk.Frame(right_frame)
save_frame.pack(fill=tk.X, pady=10)
ttk.Button(save_frame, text="保存配置", command=self.save_config).pack(side=tk.LEFT, padx=5)
ttk.Button(save_frame, text="启动预览", command=self.start_preview).pack(side=tk.LEFT, padx=5)
ttk.Button(save_frame, text="启动程序", command=self.start_program).pack(side=tk.LEFT, padx=5)
def create_entry(self, parent, key, label, prefix=None):
"""创建输入框"""
frame = ttk.Frame(parent)
frame.pack(fill=tk.X, pady=3)
ttk.Label(frame, text=label, width=15).pack(side=tk.LEFT)
var = tk.StringVar()
entry = ttk.Entry(frame, textvariable=var, width=25)
entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
# 保存变量引用
if prefix:
full_key = f"{prefix}_{key}"
else:
full_key = key
self.config_vars[full_key] = var
def load_current_config(self):
"""加载当前配置到界面"""
# 加载配置组列表
self.update_group_list()
# 加载预览配置
display = config_manager.config.get('display', {})
for key in ['preview_width', 'preview_height', 'preview_columns', 'preview_rows']:
if key in display:
self.config_vars[f"display_{key}"].set(str(display[key]))
def update_group_list(self):
"""更新配置组列表"""
self.group_listbox.delete(0, tk.END)
for i, group in enumerate(config_manager.config['groups']):
status = "" if group.get('active', False) else " "
self.group_listbox.insert(tk.END, f"{status} {group['name']}")
def on_group_select(self, event):
"""选择配置组"""
selection = self.group_listbox.curselection()
if selection:
self.selected_index = selection[0]
self.load_group_config(self.selected_index)
def load_group_config(self, index):
"""加载配置组详情"""
group = config_manager.get_group_by_index(index)
if group:
for key, var in self.config_vars.items():
if not key.startswith('display_'):
if key in group:
var.set(str(group[key]))
def add_group(self):
"""添加配置组"""
name = simpledialog.askstring("添加配置组", "请输入配置组名称:", initialvalue=f"配置组{len(config_manager.config['groups'])+1}")
if name:
config_manager.add_group(name)
self.update_group_list()
messagebox.showinfo("成功", f"已添加配置组: {name}")
def delete_group(self):
"""删除配置组"""
selection = self.group_listbox.curselection()
if not selection:
messagebox.showwarning("警告", "请先选择要删除的配置组")
return
if messagebox.askyesno("确认", "确定要删除选中的配置组吗?"):
config_manager.delete_group(selection[0])
self.update_group_list()
messagebox.showinfo("成功", "配置组已删除")
def set_active(self):
"""设置为活动配置组"""
selection = self.group_listbox.curselection()
if not selection:
messagebox.showwarning("警告", "请先选择要设为活动的配置组")
return
config_manager.set_active_group(selection[0])
self.update_group_list()
messagebox.showinfo("成功", "已设置为活动配置组")
def save_config(self):
"""保存当前编辑的配置"""
# 保存当前组的配置
group = config_manager.get_group_by_index(self.selected_index)
if group:
for key, var in self.config_vars.items():
if not key.startswith('display_'):
try:
value = int(var.get()) if var.get().isdigit() else var.get()
group[key] = value
except:
group[key] = var.get()
# 保存预览配置
display = config_manager.config.get('display', {})
for key in ['preview_width', 'preview_height', 'preview_columns', 'preview_rows']:
var = self.config_vars.get(f"display_{key}")
if var:
try:
display[key] = int(var.get())
except:
pass
config_manager.config['display'] = display
if config_manager.save_config():
messagebox.showinfo("成功", "配置已保存")
else:
messagebox.showerror("错误", "配置保存失败")
def start_preview(self):
"""启动预览窗口"""
# 保存配置
self.save_config()
# 启动预览窗口
from preview import PreviewWindow
preview_thread = threading.Thread(target=lambda: PreviewWindow().run())
preview_thread.daemon = True
preview_thread.start()
def start_program(self):
"""启动主程序"""
# 保存配置
self.save_config()
messagebox.showinfo("提示", "配置已保存,请运行主程序")
def run(self):
"""运行GUI"""
self.root.mainloop()
if __name__ == "__main__":
app = ConfigGUI()
app.run()

68
launcher.py Normal file
View File

@@ -0,0 +1,68 @@
"""
启动器 - 选择启动配置界面或主程序
"""
import tkinter as tk
from tkinter import ttk
import subprocess
import sys
def start_config_gui():
"""启动配置界面"""
subprocess.Popen([sys.executable, "gui_config.py"])
def start_preview():
"""启动预览窗口"""
subprocess.Popen([sys.executable, "preview.py"])
def start_main_program():
"""启动主程序"""
subprocess.Popen([sys.executable, "main.py"])
def create_launcher():
"""创建启动器界面"""
root = tk.Tk()
root.title("火炬之光自动化 - 启动器")
root.geometry("400x300")
root.resizable(False, False)
# 标题
title_frame = tk.Frame(root)
title_frame.pack(fill=tk.X, pady=20)
tk.Label(title_frame, text="火炬之光自动化脚本", font=('Arial', 16, 'bold')).pack()
tk.Label(title_frame, text="请选择要启动的功能", font=('Arial', 10)).pack()
# 按钮区域
button_frame = tk.Frame(root)
button_frame.pack(fill=tk.BOTH, expand=True, padx=40, pady=20)
# 配置界面按钮
config_btn = tk.Button(button_frame, text="⚙️ 配置管理",
command=start_config_gui,
width=30, height=3, font=('Arial', 12))
config_btn.pack(pady=10)
# 预览窗口按钮
preview_btn = tk.Button(button_frame, text="👁️ 采集卡预览",
command=start_preview,
width=30, height=3, font=('Arial', 12))
preview_btn.pack(pady=10)
# 主程序按钮
main_btn = tk.Button(button_frame, text="🚀 启动自动化",
command=start_main_program,
width=30, height=3, font=('Arial', 12))
main_btn.pack(pady=10)
# 说明
info_label = tk.Label(root,
text="提示:首次使用请先点击"配置管理"设置参数",
font=('Arial', 9),
fg='gray')
info_label.pack(side=tk.BOTTOM, pady=10)
root.mainloop()
if __name__ == "__main__":
create_launcher()

69
main.py
View File

@@ -1,37 +1,57 @@
import cv2
from utils.get_image import get_image
from utils.mouse import mouse_gui
from utils.get_image import GetImage
from utils.mouse import init_mouse_keyboard, Mouse_guiji
from ultralytics import YOLO
import time
import serial
import ch9329Comm
import time
import random
import math
from utils import shizi
from config import config_manager
# 加载YOLO模型
model = YOLO(r"best.pt").to('cuda')
model0 = YOLO(r"best0.pt").to('cuda')
# 从配置加载
active_group = config_manager.get_active_group()
if active_group is None:
print("❌ 没有活动的配置组请在gui_config.py中设置")
exit(1)
print(f"📋 使用配置组: {active_group['name']}")
# 初始化串口和鼠标
init_mouse_keyboard(active_group)
# 初始化键盘和鼠标
keyboard = ch9329Comm.keyboard.DataComm()
mouse = ch9329Comm.mouse.DataComm(1920, 1080)
kong_detections = {
'center': None,
'next': None,
'npc1': None,
'npc2': None,
'npc3': None,
'npc4': None,
'boss': None,
'daojv': [],
'gw': [],
'zhaozi': None
}
from utils.mouse import mouse, mouse_gui # 导入已初始化的mouse和mouse_gui
# 创建全局的mouse_gui实例
mouse_gui = Mouse_guiji()
# 初始化采集卡
get_image = GetImage(
cam_index=active_group['camera_index'],
width=active_group['camera_width'],
height=active_group['camera_height']
)
print(f"✅ 初始化完成 - 串口:{active_group['serial_port']} 采集卡:{active_group['camera_index']}")
# 全局变量
left = 0
top = 30
k = 0 # 控制转圈的方向
panduan = False # 是否在图内
boss_pd = False # 是否到boss关卡
rw = (632, 378)
# 从配置读取移动速度
v = active_group['move_velocity']
def yolo_shibie(im_PIL, detections, model):
results = model(im_PIL) # 目标检测
for result in results:
@@ -56,9 +76,11 @@ def yolo_shibie(im_PIL,detections,model):
RW = [player_x, player_y]
detections[label] = RW
return detections
def sq(p1, p2):
"""计算两点之间的欧式距离"""
return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
def process_points(points):
if not points:
return None # 空列表情况
@@ -89,6 +111,7 @@ def process_points(points):
best_point = p
return best_point
def move_randomly(rw, k):
k = k % 4
suiji_t = float(random.randint(10, 13) / 10)
@@ -109,8 +132,11 @@ def move_randomly(rw, k):
time.sleep(suiji_t)
keyboard.release() # Release
return k + 1
def move_to(rw, mb):
v=470
"""使用配置的v值"""
global v
v = active_group['move_velocity'] # 每次都从配置读取最新值
if rw[0] >= mb[0]:
keyboard.send_data("44")
time.sleep(float(abs(rw[0] - mb[0]) / v))
@@ -128,7 +154,9 @@ def move_to(rw,mb):
keyboard.send_data("22")
time.sleep(float(abs(rw[1] - mb[1]) / v))
keyboard.release()
i = 0
print("🚀 开始自动化...")
while True:
detections = {
'center': None,
@@ -141,10 +169,10 @@ while True:
'daojv': [],
'gw': [],
'zhaozi': None
}
im_opencv = get_image.get_frame() # [RGB,PIL]
detections = yolo_shibie(im_opencv[1], detections, model)
if shizi.tuwai(im_opencv[0]): # 进图算法
im_opencv = get_image.get_frame() # [RGB,PIL]
detections = yolo_shibie(im_opencv[1], detections, model0)
@@ -181,7 +209,6 @@ while True:
continue
else:
print("离npc4有点远 点击进入")
move_to(rw, detections['npc4'])
time.sleep(1)
im_opencv = get_image.get_frame() # [RGB,PIL]
@@ -239,7 +266,6 @@ while True:
'daojv': [],
'gw': [],
'zhaozi': None
}
detections = yolo_shibie(im_opencv[1], detections, model)
if detections['next'] is not None or len(detections['daojv']) != 0 or len(detections['gw']) != 0 or detections['boss'] is not None:
@@ -273,8 +299,6 @@ while True:
mb = (rw[0], rw[1] + 100)
move_to(rw, mb)
time.sleep(0.25)
continue
elif boss_pd == True and detections['center'] is None and detections['next'] is None: # boss出现了 但是没有中心
@@ -298,3 +322,4 @@ while True:
mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1)
time.sleep(1)
continue

325
main_new.py Normal file
View File

@@ -0,0 +1,325 @@
import cv2
from utils.get_image import GetImage
from utils.mouse import init_mouse_keyboard, Mouse_guiji
from ultralytics import YOLO
import time
import serial
import ch9329Comm
import random
import math
from utils import shizi
from config import config_manager
# 加载YOLO模型
model = YOLO(r"best.pt").to('cuda')
model0 = YOLO(r"best0.pt").to('cuda')
# 从配置加载
active_group = config_manager.get_active_group()
if active_group is None:
print("❌ 没有活动的配置组请在gui_config.py中设置")
exit(1)
print(f"📋 使用配置组: {active_group['name']}")
# 初始化串口和鼠标
init_mouse_keyboard(active_group)
# 初始化键盘和鼠标
keyboard = ch9329Comm.keyboard.DataComm()
from utils.mouse import mouse, mouse_gui # 导入已初始化的mouse和mouse_gui
# 创建全局的mouse_gui实例
mouse_gui = Mouse_guiji()
# 初始化采集卡
get_image = GetImage(
cam_index=active_group['camera_index'],
width=active_group['camera_width'],
height=active_group['camera_height']
)
print(f"✅ 初始化完成 - 串口:{active_group['serial_port']} 采集卡:{active_group['camera_index']}")
# 全局变量
left = 0
top = 30
k = 0 # 控制转圈的方向
panduan = False # 是否在图内
boss_pd = False # 是否到boss关卡
rw = (632, 378)
# 从配置读取移动速度
v = active_group['move_velocity']
def yolo_shibie(im_PIL, detections, model):
results = model(im_PIL) # 目标检测
for result in results:
for i in range(len(result.boxes.xyxy)):
left, top, right, bottom = result.boxes.xyxy[i]
scalar_tensor = result.boxes.cls[i]
value = scalar_tensor.item()
label = result.names[int(value)]
if label == 'center' or label == 'next' or label == 'boss' or label == 'zhaozi':
player_x = int(left + (right - left) / 2)
player_y = int(top + (bottom - top) / 2) + 30
RW = [player_x, player_y]
detections[label] = RW
elif label == 'daojv' or label == 'gw':
player_x = int(left + (right - left) / 2)
player_y = int(top + (bottom - top) / 2) + 30
RW = [player_x, player_y]
detections[label].append(RW)
elif label == 'npc1' or label == 'npc2' or label == 'npc3' or label == 'npc4':
player_x = int(left + (right - left) / 2)
player_y = int(bottom) + 30
RW = [player_x, player_y]
detections[label] = RW
return detections
def sq(p1, p2):
"""计算两点之间的欧式距离"""
return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
def process_points(points):
if not points:
return None # 空列表情况
n = len(points)
if n == 1:
# 只有一个点,直接返回
return points[0]
elif n == 2:
# 两个点取中点
x = (points[0][0] + points[1][0]) / 2
y = (points[0][1] + points[1][1]) / 2
return [x, y]
else:
# 随机选3个点
sample_points = random.sample(points, 3)
# 对每个点计算到这3个点的总距离
min_sum = float('inf')
best_point = None
for p in points:
dist_sum = sum(sq(p, sp) for sp in sample_points)
if dist_sum < min_sum:
min_sum = dist_sum
best_point = p
return best_point
def move_randomly(rw, k):
k = k % 4
suiji_t = float(random.randint(10, 13) / 10)
if k == 0:
keyboard.send_data("66")
time.sleep(suiji_t)
keyboard.release() # Release
elif k == 1:
keyboard.send_data("88")
time.sleep(suiji_t)
keyboard.release() # Release
elif k == 2:
keyboard.send_data("44")
time.sleep(suiji_t)
keyboard.release() # Release
elif k == 3:
keyboard.send_data("22")
time.sleep(suiji_t)
keyboard.release() # Release
return k + 1
def move_to(rw, mb):
"""使用配置的v值"""
global v
v = active_group['move_velocity'] # 每次都从配置读取最新值
if rw[0] >= mb[0]:
keyboard.send_data("44")
time.sleep(float(abs(rw[0] - mb[0]) / v))
keyboard.release() # Release
else:
keyboard.send_data("66")
time.sleep(float(abs(rw[0] - mb[0]) / v))
keyboard.release() # Release
if rw[1] >= mb[1]:
keyboard.send_data("88")
time.sleep(float(abs(rw[1] - mb[1]) / v))
keyboard.release() # Release
else:
keyboard.send_data("22")
time.sleep(float(abs(rw[1] - mb[1]) / v))
keyboard.release()
i = 0
print("🚀 开始自动化...")
while True:
detections = {
'center': None,
'next': None,
'npc1': None,
'npc2': None,
'npc3': None,
'npc4': None,
'boss': None,
'daojv': [],
'gw': [],
'zhaozi': None
}
im_opencv = get_image.get_frame() # [RGB,PIL]
detections = yolo_shibie(im_opencv[1], detections, model)
if shizi.tuwai(im_opencv[0]): # 进图算法
im_opencv = get_image.get_frame() # [RGB,PIL]
detections = yolo_shibie(im_opencv[1], detections, model0)
print('当前在城镇中')
if detections['npc1'] is not None and sq(rw, detections['npc1']) > 80:
print("向npc1靠近")
print(sq(rw, detections['npc1']))
move_to(rw, detections['npc1'])
continue
elif detections['npc1'] is not None and sq(rw, detections['npc1']) <= 80:
print("在npc旁边向上走")
print(sq(rw, detections['npc1']))
mb = (detections['npc1'][0], detections['npc1'][1] - 1010)
move_to(rw, mb)
continue
elif detections['npc3'] is not None and detections['npc4'] is None:
print("在npc3旁边向右走")
mb = (rw[0], detections['npc3'][1] - 50)
move_to(rw, mb)
mb = (rw[0] + 700, rw[1])
move_to(rw, mb)
continue
elif detections['npc4'] is not None:
if sq(detections['npc4'], rw) < 50:
print("离npc4很近 直接进入")
keyboard.send_data("DD")
time.sleep(0.15)
keyboard.release()
time.sleep(1)
im_opencv = get_image.get_frame() # [RGB,PIL]
if shizi.daoying(im_opencv[0]):
mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1)
time.sleep(1)
continue
else:
print("离npc4有点远 点击进入")
move_to(rw, detections['npc4'])
time.sleep(1)
im_opencv = get_image.get_frame() # [RGB,PIL]
if shizi.daoying(im_opencv[0]):
mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1)
time.sleep(1)
continue
elif shizi.tiaozhan(im_opencv[0]): # 开启挑战
print('进入塔4')
mouse_gui.send_data_absolute(left + 1100, top + 600, may=1)
time.sleep(0.3)
mouse_gui.send_data_absolute(left + 433, top + 455, may=1)
panduan = True
continue
elif shizi.jieshu(im_opencv[0]): # 结束挑战
print('结束挑战')
mouse_gui.send_data_absolute(left + 542, top + 644, may=1)
time.sleep(0.8)
mouse_gui.send_data_absolute(left + 706, top + 454, may=1)
continue
elif panduan: # 图内情况
print("在图内")
if shizi.shuzi(im_opencv[0]):
boss_pd = True
print("进入到boss")
if shizi.fuhuo(im_opencv[0]):
print('点击复活')
mouse_gui.send_data_absolute(left + 536, top + 627, may=1)
mouse_gui.send_data_absolute(rw[0], rw[1], may=0)
continue
if detections['zhaozi'] is not None:
move_to(rw, detections['zhaozi'])
continue
if len(detections['daojv']) != 0:
move_to(rw, process_points(detections['daojv']))
for i in range(3 + len(detections['daojv'])):
keyboard.send_data("AA")
time.sleep(0.15)
keyboard.release()
continue
if shizi.tuichu(im_opencv[0]) and detections['next'] is None and len(detections['daojv']) == 0 and len(detections['gw']) == 0 and boss_pd:
print("识别到可以退出挑战!!!!!!!!!!!!!!!!!!")
for i in range(3):
time.sleep(0.5)
im_opencv = get_image.get_frame() # [RGB,PIL]
detections = {
'center': None,
'next': None,
'npc1': None,
'npc2': None,
'npc3': None,
'npc4': None,
'boss': None,
'daojv': [],
'gw': [],
'zhaozi': None
}
detections = yolo_shibie(im_opencv[1], detections, model)
if detections['next'] is not None or len(detections['daojv']) != 0 or len(detections['gw']) != 0 or detections['boss'] is not None:
break
else:
mouse_gui.send_data_absolute(left + 640, top + 40, may=1) # 点击退出
panduan = False # 退出挑战
boss_pd = False
time.sleep(2.0)
continue
if detections['center'] is None and detections['next'] is None and (len(detections['gw']) != 0 or detections["boss"] is not None): # 识别不到中心情况 但是有怪物
print("未检测到中心点,但是有怪物")
if len(detections['gw']) != 0:
move_to(rw, detections['gw'][0])
time.sleep(0.26)
elif detections['boss'] is not None: # 跟随boss
boss_suiji1 = random.randint(-30, 30)
boss_suiji2 = random.randint(-30, 30)
detections['boss'] = (detections['boss'][0] + boss_suiji1, detections['boss'][1] + boss_suiji2)
move_to(rw, detections['boss'])
time.sleep(0.7)
continue
elif (detections['center'] is not None and detections['next'] is None and len(detections['gw']) != 0) or (boss_pd == True and detections['center'] is not None and detections['next'] is None): # 识别到中心 但是有怪物
if detections['center'][0] >= rw[0] and detections['center'][1] < rw[1]: # 3
mb = (rw[0] + 100, rw[1])
elif detections['center'][0] <= rw[0] and detections['center'][1] < rw[1]: # 4
mb = (rw[0], rw[1] - 100)
elif detections['center'][0] <= rw[0] and detections['center'][1] > rw[1]: # 1
mb = (rw[0] - 100, rw[1])
elif detections['center'][0] >= rw[0] and detections['center'][1] > rw[1]: # 2
mb = (rw[0], rw[1] + 100)
move_to(rw, mb)
time.sleep(0.25)
continue
elif boss_pd == True and detections['center'] is None and detections['next'] is None: # boss出现了 但是没有中心
k = move_randomly(rw, k)
continue
elif detections['next'] is not None:
print('进入下一层啦啦啦啦啦啦')
panduan = True
move_to(rw, detections['next'])
for i in range(2):
keyboard.send_data("DD")
time.sleep(0.15)
keyboard.release()
continue
else:
k = move_randomly(rw, k)
continue
elif shizi.daoying(im_opencv[0]):
mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1)
time.sleep(1)
continue

300
main_old.py Normal file
View File

@@ -0,0 +1,300 @@
import cv2
from utils.get_image import get_image
from utils.mouse import mouse_gui
from ultralytics import YOLO
import time
import serial
import ch9329Comm
import time
import random
import math
from utils import shizi
model = YOLO(r"best.pt").to('cuda')
model0 = YOLO(r"best0.pt").to('cuda')
keyboard = ch9329Comm.keyboard.DataComm()
mouse = ch9329Comm.mouse.DataComm(1920, 1080)
kong_detections = {
'center': None,
'next': None,
'npc1': None,
'npc2': None,
'npc3': None,
'npc4': None,
'boss': None,
'daojv': [],
'gw': [],
'zhaozi': None
}
left=0
top=30
k=0#控制转圈的方向
panduan=False#是否在图内
boss_pd=False#是否到boss关卡
rw=(632,378)
def yolo_shibie(im_PIL,detections,model):
results = model(im_PIL)#目标检测
for result in results:
for i in range(len(result.boxes.xyxy)):
left, top, right, bottom = result.boxes.xyxy[i]
scalar_tensor = result.boxes.cls[i]
value = scalar_tensor.item()
label = result.names[int(value)]
if label=='center'or label=='next' or label=='boss' or label=='zhaozi':
player_x = int(left+(right-left)/2)
player_y = int(top+(bottom-top)/2)+30
RW = [player_x, player_y]
detections[label] = RW
elif label=='daojv' or label=='gw':
player_x = int(left + (right - left) / 2)
player_y = int(top + (bottom - top) / 2) + 30
RW = [player_x, player_y]
detections[label].append(RW)
elif label=='npc1' or label=='npc2' or label=='npc3' or label=='npc4':
player_x = int(left+(right-left)/2)
player_y = int(bottom)+30
RW = [player_x, player_y]
detections[label] = RW
return detections
def sq(p1, p2):
"""计算两点之间的欧式距离"""
return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
def process_points(points):
if not points:
return None # 空列表情况
n = len(points)
if n == 1:
# 只有一个点,直接返回
return points[0]
elif n == 2:
# 两个点取中点
x = (points[0][0] + points[1][0]) / 2
y = (points[0][1] + points[1][1]) / 2
return [x, y]
else:
# 随机选3个点
sample_points = random.sample(points, 3)
# 对每个点计算到这3个点的总距离
min_sum = float('inf')
best_point = None
for p in points:
dist_sum = sum(sq(p, sp) for sp in sample_points)
if dist_sum < min_sum:
min_sum = dist_sum
best_point = p
return best_point
def move_randomly(rw, k):
k = k % 4
suiji_t=float(random.randint(10,13)/10)
if k == 0:
keyboard.send_data("66")
time.sleep(suiji_t)
keyboard.release() # Release
elif k == 1:
keyboard.send_data("88")
time.sleep(suiji_t)
keyboard.release() # Release
elif k == 2:
keyboard.send_data("44")
time.sleep(suiji_t)
keyboard.release() # Release
elif k == 3:
keyboard.send_data("22")
time.sleep(suiji_t)
keyboard.release() # Release
return k + 1
def move_to(rw,mb):
v=470
if rw[0]>=mb[0]:
keyboard.send_data("44")
time.sleep(float(abs(rw[0]-mb[0])/v))
keyboard.release() # Release
else:
keyboard.send_data("66")
time.sleep(float(abs(rw[0] - mb[0]) / v))
keyboard.release() # Release
if rw[1]>=mb[1]:
keyboard.send_data("88")
time.sleep(float(abs(rw[1] - mb[1]) / v))
keyboard.release() # Release
else:
keyboard.send_data("22")
time.sleep(float(abs(rw[1] - mb[1]) / v))
keyboard.release()
i=0
while True:
detections = {
'center': None,
'next': None,
'npc1': None,
'npc2': None,
'npc3': None,
'npc4': None,
'boss': None,
'daojv': [],
'gw': [],
'zhaozi':None
}
im_opencv = get_image.get_frame()#[RGB,PIL]
detections=yolo_shibie(im_opencv[1],detections,model)
if shizi.tuwai(im_opencv[0]): # 进图算法
im_opencv = get_image.get_frame() # [RGB,PIL]
detections = yolo_shibie(im_opencv[1], detections, model0)
print('当前在城镇中')
if detections['npc1'] is not None and sq(rw, detections['npc1']) > 80:
print("向npc1靠近")
print(sq(rw, detections['npc1']))
move_to(rw, detections['npc1'])
continue
elif detections['npc1'] is not None and sq(rw, detections['npc1']) <= 80:
print("在npc旁边向上走")
print(sq(rw, detections['npc1']))
mb = (detections['npc1'][0], detections['npc1'][1] - 1010)
move_to(rw, mb)
continue
elif detections['npc3'] is not None and detections['npc4'] is None:
print("在npc3旁边向右走")
mb = (rw[0], detections['npc3'][1]-50)
move_to(rw, mb)
mb = (rw[0] + 700, rw[1])
move_to(rw, mb)
continue
elif detections['npc4'] is not None:
if sq(detections['npc4'], rw) < 50:
print("离npc4很近 直接进入")
keyboard.send_data("DD")
time.sleep(0.15)
keyboard.release()
time.sleep(1)
im_opencv = get_image.get_frame() # [RGB,PIL]
if shizi.daoying(im_opencv[0]):
mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1)
time.sleep(1)
continue
else:
print("离npc4有点远 点击进入")
move_to(rw, detections['npc4'])
time.sleep(1)
im_opencv = get_image.get_frame() # [RGB,PIL]
if shizi.daoying(im_opencv[0]):
mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1)
time.sleep(1)
continue
elif shizi.tiaozhan(im_opencv[0]):#开启挑战
print('进入塔4')
mouse_gui.send_data_absolute(left+1100,top+600,may=1)
time.sleep(0.3)
mouse_gui.send_data_absolute(left + 433, top + 455,may=1)
panduan = True
continue
elif shizi.jieshu(im_opencv[0]):#结束挑战
print('结束挑战')
mouse_gui.send_data_absolute(left+542,top+644,may=1)
time.sleep(0.8)
mouse_gui.send_data_absolute(left + 706, top + 454,may=1)
continue
elif panduan :#图内情况
print("在图内")
if shizi.shuzi(im_opencv[0]) :
boss_pd = True
print("进入到boss")
if shizi.fuhuo(im_opencv[0]):
print('点击复活')
mouse_gui.send_data_absolute(left + 536, top + 627, may=1)
mouse_gui.send_data_absolute(rw[0], rw[1], may=0)
continue
if detections['zhaozi'] is not None:
move_to(rw,detections['zhaozi'])
continue
if len(detections['daojv'])!=0:
move_to(rw,process_points(detections['daojv']))
for i in range(3+len(detections['daojv'])):
keyboard.send_data("AA")
time.sleep(0.15)
keyboard.release()
continue
if shizi.tuichu(im_opencv[0]) and detections['next'] is None and len(detections['daojv'])==0 and len(detections['gw'])==0 and boss_pd:
print("识别到可以退出挑战!!!!!!!!!!!!!!!!!!")
for i in range(3):
time.sleep(0.5)
im_opencv = get_image.get_frame()#[RGB,PIL]
detections = {
'center': None,
'next': None,
'npc1': None,
'npc2': None,
'npc3': None,
'npc4': None,
'boss': None,
'daojv': [],
'gw': [],
'zhaozi': None
}
detections = yolo_shibie(im_opencv[1], detections,model)
if detections['next'] is not None or len(detections['daojv'])!=0 or len(detections['gw'])!=0 or detections['boss'] is not None:
break
else:
mouse_gui.send_data_absolute(left + 640, top + 40,may=1)#点击退出
panduan = False#退出挑战
boss_pd = False
time.sleep(2.0)
continue
if detections['center'] is None and detections['next'] is None and (len(detections['gw'])!=0 or detections["boss"] is not None):#识别不到中心情况 但是有怪物
print("未检测到中心点,但是有怪物")
if len(detections['gw'])!=0:
move_to(rw,detections['gw'][0])
time.sleep(0.26)
elif detections['boss'] is not None:#跟随boss
boss_suiji1=random.randint(-30,30)
boss_suiji2 = random.randint(-30, 30)
detections['boss']=(detections['boss'][0]+boss_suiji1,detections['boss'][1]+boss_suiji2)
move_to(rw,detections['boss'])
time.sleep(0.7)
continue
elif (detections['center'] is not None and detections['next'] is None and len(detections['gw'])!=0) or (boss_pd==True and detections['center'] is not None and detections['next'] is None) :#识别到中心 但是有怪物
if detections['center'][0]>=rw[0] and detections['center'][1]<rw[1]:#3
mb=(rw[0]+100,rw[1])
elif detections['center'][0]<=rw[0] and detections['center'][1]<rw[1]:#4
mb=(rw[0], rw[1]-100)
elif detections['center'][0]<=rw[0] and detections['center'][1]>rw[1]:#1
mb=(rw[0]-100, rw[1])
elif detections['center'][0]>=rw[0] and detections['center'][1]>rw[1]:#2
mb=(rw[0], rw[1]+100)
move_to(rw, mb)
time.sleep(0.25)
continue
elif boss_pd==True and detections['center'] is None and detections['next'] is None:#boss出现了 但是没有中心
k=move_randomly(rw,k)
continue
elif detections['next'] is not None:
print('进入下一层啦啦啦啦啦啦')
panduan = True
move_to(rw,detections['next'])
for i in range(2):
keyboard.send_data("DD")
time.sleep(0.15)
keyboard.release()
continue
else:
k = move_randomly(rw, k)
continue
elif shizi.daoying(im_opencv[0]):
mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1)
time.sleep(1)
continue

229
preview.py Normal file
View File

@@ -0,0 +1,229 @@
import cv2
import tkinter as tk
from tkinter import Canvas
from PIL import Image, ImageTk
import threading
import numpy as np
from config import config_manager
class PreviewWindow:
"""采集卡预览窗口"""
def __init__(self):
self.config = config_manager.config
self.caps = {}
self.frames = {}
self.large_window = None
self.running = True
def init_cameras(self):
"""初始化所有相机"""
for i, group in enumerate(self.config['groups']):
if group.get('active', True): # 只加载活动的配置
try:
cap = cv2.VideoCapture(group['camera_index'], cv2.CAP_DSHOW)
if cap.isOpened():
cap.set(cv2.CAP_PROP_FRAME_WIDTH, group['camera_width'])
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, group['camera_height'])
self.caps[i] = {
'cap': cap,
'group': group,
'name': group['name']
}
except Exception as e:
print(f"无法打开相机 {group['camera_index']}: {e}")
def capture_frames(self):
"""捕获帧"""
while self.running:
for idx, data in self.caps.items():
ret, frame = data['cap'].read()
if ret:
# 裁剪到配置的区域
height, width = frame.shape[:2]
crop_top = 30
crop_bottom = min(crop_top + 720, height)
crop_width = min(1280, width)
frame = frame[crop_top:crop_bottom, 0:crop_width]
# 转换颜色空间
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
self.frames[idx] = frame_rgb
def create_grid_window(self):
"""创建网格窗口"""
root = tk.Tk()
root.title("采集卡预览 - 点击放大")
root.geometry("1000x700")
# 获取显示配置
display = self.config.get('display', {})
preview_width = display.get('preview_width', 640)
preview_height = display.get('preview_height', 360)
columns = display.get('preview_columns', 2)
rows = display.get('preview_rows', 2)
canvas = Canvas(root, bg='black')
canvas.pack(fill=tk.BOTH, expand=True)
# 存储图像对象
self.photo_objects = {}
def update_frames():
"""更新帧"""
while self.running:
try:
canvas.delete("all")
# 计算每个预览窗口的位置和大小
window_width = root.winfo_width() if root.winfo_width() > 1 else 1000
window_height = root.winfo_height() if root.winfo_height() > 1 else 700
cell_width = window_width // columns
cell_height = window_height // rows
frame_idx = 0
for idx in self.caps.keys():
if idx in self.frames:
row = frame_idx // columns
col = frame_idx % columns
x = col * cell_width
y = row * cell_height
# 调整图像大小
frame = self.frames[idx]
h, w = frame.shape[:2]
scale = min(cell_width / w, cell_height / h) * 0.9
new_w = int(w * scale)
new_h = int(h * scale)
resized_frame = cv2.resize(frame, (new_w, new_h))
# 转换为PIL图像
pil_image = Image.fromarray(resized_frame)
photo = ImageTk.PhotoImage(image=pil_image)
self.photo_objects[idx] = photo
# 绘制图像
center_x = x + cell_width // 2
center_y = y + cell_height // 2
canvas.create_image(center_x, center_y, image=photo, anchor='center')
# 绘制标签
name = self.caps[idx]['name']
canvas.create_text(center_x, y + 20, text=name, fill='white', font=('Arial', 12, 'bold'))
frame_idx += 1
canvas.update()
tk._default_root.update_idletasks()
except Exception as e:
print(f"更新帧错误: {e}")
import time
time.sleep(0.03) # 约30fps
def on_canvas_click(event):
"""点击画布事件"""
window_width = root.winfo_width()
window_height = root.winfo_height()
cell_width = window_width // columns
cell_height = window_height // rows
col = int(event.x // cell_width)
row = int(event.y // cell_height)
index = row * columns + col
# 找到对应的配置
if index < len(self.caps):
idx = list(self.caps.keys())[index]
self.show_large_window(idx)
canvas.bind('<Button-1>', on_canvas_click)
# 启动更新线程
update_thread = threading.Thread(target=update_frames, daemon=True)
update_thread.start()
def on_closing():
"""关闭窗口"""
self.running = False
for data in self.caps.values():
data['cap'].release()
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()
def show_large_window(self, idx):
"""显示大窗口"""
if self.large_window is not None and self.large_window.winfo_exists():
self.large_window.destroy()
self.large_window = tk.Tk()
self.large_window.title(f"放大视图 - {self.caps[idx]['name']}")
self.large_window.geometry("1280x720")
canvas = Canvas(self.large_window, bg='black')
canvas.pack(fill=tk.BOTH, expand=True)
photo_obj = {}
def update_large_frame():
"""更新大窗口帧"""
while self.running:
try:
if idx in self.frames and self.large_window.winfo_exists():
canvas.delete("all")
frame = self.frames[idx]
h, w = frame.shape[:2]
# 调整到窗口大小
window_width = self.large_window.winfo_width() if self.large_window.winfo_width() > 1 else 1280
window_height = self.large_window.winfo_height() if self.large_window.winfo_height() > 1 else 720
scale = min(window_width / w, window_height / h)
new_w = int(w * scale)
new_h = int(h * scale)
resized_frame = cv2.resize(frame, (new_w, new_h))
pil_image = Image.fromarray(resized_frame)
photo = ImageTk.PhotoImage(image=pil_image)
photo_obj['img'] = photo
canvas.delete("all")
canvas.create_image(window_width // 2, window_height // 2, image=photo, anchor='center')
self.large_window.update()
else:
break
except Exception as e:
print(f"更新大窗口错误: {e}")
break
import time
time.sleep(0.03)
update_thread = threading.Thread(target=update_large_frame, daemon=True)
update_thread.start()
self.large_window.mainloop()
def run(self):
"""运行预览"""
self.init_cameras()
# 启动捕获线程
capture_thread = threading.Thread(target=self.capture_frames, daemon=True)
capture_thread.start()
# 创建并显示网格窗口
import time
time.sleep(0.5) # 等待几帧
self.create_grid_window()
if __name__ == "__main__":
preview = PreviewWindow()
preview.run()

View File

@@ -45,6 +45,8 @@ import threading
class GetImage:
def __init__(self, cam_index=0, width=1920, height=1080):
self.cap = cv2.VideoCapture(cam_index, cv2.CAP_DSHOW)
if not self.cap.isOpened():
self.cap = cv2.VideoCapture(cam_index)
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
self.frame = None
@@ -70,4 +72,6 @@ class GetImage:
time.sleep(0.2)
self.cap.release()
cv2.destroyAllWindows()
get_image = GetImage()
# get_image 将在main.py中初始化
get_image = None

View File

@@ -1,17 +1,34 @@
import random
import time
import ch9329Comm
import time
import random
import serial
serial.ser = serial.Serial('COM6', 9600) # 开启串口
mouse = ch9329Comm.mouse.DataComm(1920, 1080)
# 全局变量由main.py初始化
serial.ser = None
mouse = None
def init_mouse_keyboard(config_group):
"""初始化鼠标和串口"""
global serial, mouse
# 初始化串口
serial.ser = serial.Serial(
config_group['serial_port'],
config_group['serial_baudrate']
)
# 初始化鼠标
mouse = ch9329Comm.mouse.DataComm(
config_group['camera_width'],
config_group['camera_height']
)
print(f"✅ 串口已打开: {config_group['serial_port']} {config_group['serial_baudrate']}")
print(f"✅ 鼠标已初始化: {config_group['camera_width']}x{config_group['camera_height']}")
def bezier_point(t, p0, p1, p2, p3):
"""计算三次贝塞尔曲线上的点"""
x = (1-t)**3 * p0[0] + 3*(1-t)**2*t*p1[0] + 3*(1-t)*t**2*p2[0] + t**3*p3[0]
y = (1-t)**3 * p0[1] + 3*(1-t)**2*t*p1[1] + 3*(1-t)*t**2*p2[1] + t**3*p3[1]
return (x, y)
def move_mouse_bezier(mouse, start, end, duration=1, steps=120):
"""
用贝塞尔曲线模拟鼠标移动(安全版)
@@ -53,5 +70,6 @@ class Mouse_guiji():
mouse.click1() # 点击右
self.point=(x,y)
mouse_gui = Mouse_guiji()
# mouse_gui 将在main.py中初始化
mouse_gui = None