简介:一款专为三角洲行动设计的交易行自动化助手,基于PyQt5图形界面与Python 3.11开发,面向游戏玩家与自动化脚本学习者。工具通过解析游戏本地SQLite数据库与JSON缓存文件识别仓库非绑定道具,无需内存读取或API调用,即可按关键词、稀有度、分类等条件筛选道具,并完成一键上架、实时状态同步、批量下架等全流程操作,同时内置冷却检测、重复上架拦截等十余项前置校验。功能上支持离线缓存、分级日志、全局快捷键与五种定价策略。资源共49个文件,以Python源码、Markdown文档、YAML配置、文本说明及Word附赠文档为主,压缩包仅1.7MB,目录结构清晰,包含核心逻辑、界面、解析器、网络层与单元测试用例。目前已有64人学习下载,附赠127页详细资料涵盖道具ID映射表、交易行分类编码、错误代码释义,另有安装步骤、依赖清单和防杀软误报指南,适合希望快速部署或深入理解实现细节的读者。
1. 三角洲行动交易行助手:一个用 PyQt5 做操作台、用 Python 3.11 跑批量的自动化脚本
三角洲行动玩到中后期,仓库里堆满「非绑定」枪械配件、护甲和杂物,手动上架一件平均要点五六下鼠标,挂五十件就是两三百次点击,中间还得重复核对价格、避开绑定道具,又慢又容易出错。这个压缩包解决的正是这个场景:一位作者基于 PyQt5 图形用户界面和 Python 3.11 写了一套交易行自动上架与下架助手,通过截图识别仓库里的非绑定道具、读取参考价、自动填价并确认上架,在架商品到期后还能自动下架重挂。它不碰游戏内存,纯模拟鼠标键盘操作。适合玩三角洲行动、想省掉重复点击的玩家,也适合正在用 PyQt5 做桌面工具、想学「界面 + 后台任务 + 图像识别」怎么配合的新手。
2. 自动上下架的底层逻辑:截图识别、坐标点击与状态轮询
做这种交易助手,难点不是 PyQt5 窗口怎么写,而是把「交易行里人工操作」翻译成机器能执行的步骤。人工挂一件装备的路径是:打开交易行 → 切到上架页签 → 在仓库里找到非绑定道具 → 填起拍价和一口价 → 确认 → 等卖出或到期 → 下架。自动化要做的就是把这个循环里的每一步变成「截图 → 识别 → 点击 → 校验」。
2.1 交易行操作循环:上架、确认、下架三段式
常见做法是把整个交易流程拆成三段状态机:上架态、确认态、下架态。
上架态要做的事:定位仓库里第一个非绑定道具,选中它,等待右侧价格输入框弹出。这里不读取游戏内存,而是用 opencv 模板匹配去屏幕上找对应按钮图标。确认态要做的事:价格填完后点「上架」,然后轮询屏幕,看是否出现「上架成功」的弹窗或提示条。下架态要做的事:定时切到「在售」页签,找到倒计时结束还没卖出去的商品,点下架,重新走一遍上架。
为什么不用内存读取或者 hook 消息?原因很简单:游戏一更新,内存偏移量和消息结构全变,维护成本极高,而且这类手段风险太大,远不如「看着屏幕操作」来得稳。截图识别虽然慢一点,但原理上和人眼操作一致,单次循环多花一两秒完全可以接受。
用代码描述这个主循环大概是:
while running: if need_self_off(): do_self_off() # 下架到期商品 if has_available_slot(): do_self_on() # 寻找非绑定道具并上架 time.sleep(scan_interval)scan_interval是每次扫描间隔,我一般会设成 3~5 秒。太短会被游戏判定为异常操作,太长又浪费在售空位。这里的三个函数分别对应三段状态,每一段都返回 True/False 给主循环决定下一步走哪里。
2.2 识别非绑定道具:从仓库列表到「可交易」标记
三角洲行动的仓库里,绑定道具会有明确的角标,非绑定道具是干净格子。识别策略分两道:第一道是利用游戏自带筛选,进仓库后先点击「仅显示非绑定」筛选按钮,把可见列表缩小到全部是可挂商品;第二道是兜底校验,对筛选后的每个道具格子截图,和「已绑定」角标模板做匹配,相似度低于阈值才认为可挂。
第二道兜底很有必要,因为部分任务道具或活动道具明明带着绑定标志,筛选并不会把它们完全过滤掉。模板匹配用 opencv 的matchTemplate就够:
import cv2 import numpy as np def match_template(screen, template, threshold=0.85): result = cv2.matchTemplate(screen, template, cv2.TM_CCOEFF_NORMED) _, max_val, _, max_loc = cv2.minMaxLoc(result) return max_val >= threshold, max_locTM_CCOEFF_NORMED对亮度变化不敏感,适合游戏界面这种高饱和色块场景。threshold=0.85是经验值,如果游戏画面压缩得比较狠,降到 0.80 能减少漏检,但代价是可能把「已绑定」误判成「非绑定」,所以阈值这个参数宁高勿低。还有个大坑:模板图必须从游戏原生分辨率的截图中裁,不能拿 PyQt5 窗口背景里的小图标当模板,两套分辨率下匹配率会低到让你怀疑代码写错了。
2.3 价格设置的三个参数:最低价、起拍价与自动降档
价格策略直接决定道具卖不卖得出去。常见做法是:读交易行当前最低一口价,乘以 config 里的下浮比例,得到本次挂单价。比如当前最低一口价 1000,下浮 5%,就挂 950。如果两小时没卖掉,重新下架再挂时,在上一次基础上再降 2%,直到降到「最低售价」这条红线就收手。
价格这块设计成三个参数就够了:
| 参数 | 默认值 | 作用 |
|---|---|---|
| 下浮比例 | 0.05 | 相对当前最低一口价下浮几个点,挂出去更容易成交 |
| 降档步长 | 0.02 | 每次重挂时在上次挂单价基础上再降的幅度 |
| 最低售价 | 100 | 低于这个价不挂,防止批量清理背包时把值钱货贱卖 |
读取最低一口价用数字模板匹配就能完成,游戏里价格数字字体是固定的,不需要上 OCR 引擎,按十个数字建模板逐位匹配,又快又准。这里要提醒一句:价格识别完成后,脚本会把计算出的价格写入输入框,写完之后还要截图回读一遍,确认界面显示的数字和预期一致,再点确认。少这一步,输入法或界面延迟都可能让你把 950 挂成 95。
3. 环境准备:Python 3.11 虚拟环境下的 PyQt5 安装实践
这套代码依赖 PyQt5、opencv-python、pyautogui、pyperclip 这几个核心库,环境搭不对,后面全是坑。我复现这个资源时把环境完整走了一遍,踩了几个真实的雷,直接说结论。
3.1 为什么选 Python 3.11:版本选择与依赖兼容
Python 3.11 是目前跑这类桌面自动化脚本最省心的版本。它比 3.10 启动更快,解释器内部性能提升明显;比 3.12 和 3.13 更稳,因为 opencv-python、PyQt5 这些带 C 扩展的库在 3.11 下都有现成的 wheel,pip 直接装不会现场编译。下载 python311 时记得只从 python.org 官方渠道拿安装包,安装第一步勾选Add Python 3.11 to PATH,后面省掉很多麻烦。
不建议用 3.12 的原因很实际:pyautogui 的部分依赖在 3.12 下对 Windows 平台的支持还不全,装的时候 pip 可能给你现场拉源码编译,一旦缺 VC 运行库直接失败。既然资源作者写的是 Python 3.11,就老老实实按 3.11 复现,不要自作主张换版本。
3.2 PyQt5 安装:固定版本、虚拟环境和 labelme 冲突
环境搭建第一步是建虚拟环境,这是最重要的后悔药:
python -m venv .venv .venv\Scripts\activate pip install pyqt5==5.15.10 pyautogui opencv-python pyperclip pillowPyQt5 必须固定到 5.15.10。这个版本是官方维护线的最后一版,轮子全,跑起来稳;5.15.11 在某些环境下装完会缺 Qt 平台插件,启动时直接报Could not find the Qt platform plugin "windows"。这类报错九成是版本没固定住,不是代码问题。
这里有个高频翻车场景:很多人在同一台机器上装过 labelme,然后发现 labelme 无法安装 pyqt5,或者装完 labelme 之后本项目的 PyQt5 环境被破坏。原因通常是 pip 在解析 labelme 的依赖时把 PyQt5 拉到了被 labelme 元数据限定住的版本,两边冲突,pip 干脆报错。我一般这样处理:先pip uninstall labelme pyqt5 pyqt5-qt5 pyqt5-sip全部卸干净,再装pyqt5==5.15.10,最后单独装 labelme,并且把 labelme 放到另一个虚拟环境里,两个项目彻底隔离。
3.3 压缩包解压后的目录结构与 PyCharm 配置
解压这个压缩包后,目录形态大体是:
| 文件/目录 | 作用 |
|---|---|
| main.py | 入口,初始化 QApplication 并启动主窗口 |
| ui_main.py | PyQt5 界面代码,窗口布局和控件绑定 |
| worker.py | 后台工作线程,负责截图、识别、点击 |
| config.ini | 价格参数、扫描间隔、坐标区域配置 |
| templates/ | 按钮、角标、数字的模板截图 |
| requirements.txt | 项目依赖清单 |
在 PyCharm 里跑的时候,注意右下角把解释器切换到.venv下的 python.exe,如果沿用全局解释器,前面 pip 装的依赖全部白费。如果你是先在 Qt Designer 里画界面再转代码,记得用pyuic5 -o ui_main.py main.ui命令把.ui文件编译成.py,编译完直接 import 就能用,手动重排布局是浪费时间。
4. 把界面做成能用的工具:PyQt5 配置面板、启停状态机与彩色日志
这个项目最值得学的地方,是把「交易操作逻辑」和「界面交互」解耦的方式。自动化操作跑在后台线程,PyQt5 界面只负责下发参数和展示状态,两者之间通过信号通信,界面永远不卡。
4.1 界面布局:交易区选择、价格策略与批量数量
主窗口不需要复杂设计,两列布局就能说清楚:
| 控件类型 | 内容 | 说明 |
|---|---|---|
| QComboBox | 交易类别 | 选择武器、护甲、材料等交易区 |
| QLineEdit | 下浮比例 / 降档步长 | 对应 config 里的价格参数 |
| QLineEdit | 最低售价 | 价格红线 |
| QSpinBox | 扫描间隔 | 默认 3 秒,取值范围 1~10 |
| QPushButton | 开始 / 停止 | 控制工作线程启停 |
| QTextBrowser | 运行日志 | 展示执行进度和识别结果 |
日志区为什么要用 QTextBrowser 而不是 QPlainTextEdit?因为 QTextBrowser 支持显示 HTML,可以给不同状态的日志上色。这也是 PyQt5 显示 html 最实用的场景:上架成功一行绿色,识别失败一行红色,一眼扫过去就知道当前状态。
self.log_browser.append( f'<span style="color:green">✓ {item_name} 上架成功,挂单价 {price}</span>' ) self.log_browser.append( f'<span style="color:red">✗ 识别失败:未找到可交易道具格子</span>' )append是追加而非覆盖,日志区天然变成滚动记录,不用手动管理滚动条。颜色值可以按你的需求改,#ff0000这类十六进制色值同样支持。
4.2 启停逻辑:用 QTimer 做轮询,别把 while True 塞进 GUI 线程
新手最容易犯的错是把循环写在按钮点击事件里:
def on_start_clicked(self): while self.running: # 错误写法,界面直接假死 do_scan()这样写按钮按下去之后,事件循环被 while 卡住,窗口整个无响应,连「停止」按钮都点不了,只能杀进程。正确做法是把扫描逻辑交给 QTimer 定时触发:
from PyQt5.QtCore import QTimer self.timer = QTimer(self) self.timer.setInterval(self.delay_spin.value() * 1000) self.timer.timeout.connect(self.scan_once)setInterval的单位是毫秒,delay_spin.value()是用户在界面上填的秒数,乘以 1000 转成毫秒。timeout信号每次触发只调用一次scan_once,执行完立即返回,界面事件循环始终有空档处理按钮点击和窗口拖动。停止时只需self.timer.stop(),干净利落。
4.3 运行日志:每一单的耗时、价格与结果都要留痕
日志不是给用户看热闹的,是给维护者留证据的。每次识别到道具、每次填价、每次确认上架,都按固定格式输出一行:
[12:03:45] 识别 -> M700 狙击枪 x5, 非绑定, 参考价 2400 [12:03:47] 填价 -> 挂600, 降档步长 2% [12:03:50] 确认 -> 上架成功, 耗时 5.2s日志行必须包含三个关键信息:识别的道具名、最终填的价格、耗时。为什么?批量跑几十件时,只有日志能告诉你哪件挂高了、哪件挂低了、哪件因为识别失败被跳过。我一般还会把每次上架前的屏幕截图保存到logs/目录,文件名带上时间戳,这样第二天想复盘时能对着截图逐件核对。
5. 避坑记录:分辨率缩放、识别偏移和输入法引起的翻车现场
这个项目最磨人的不是功能实现,而是真实环境里各种「看起来没问题但就是跑不对」的问题。下面四条是我复现时踩过或者旁观踩过的坑,每一条都是血泪经验。
5.1 高分屏与 125% 缩放下点击坐标整体偏移
现象:PyQt5 界面显示正常,但脚本在游戏窗口里点击的位置总是偏右下,点不到目标按钮。
原因:Windows 默认把显示缩放设成了 125% 或 150%,PyAutoGUI 拿到的屏幕尺寸和游戏实际渲染分辨率不一致,坐标换算出现系统性偏移。
解决:先检测缩放比例,在程序入口强制感知 DPI:
import ctypes ctypes.windll.shcore.SetProcessDpiAwareness(1)这行代码要在创建 QApplication 之前执行。如果游戏本机和脚本跑在同一台电脑,更省事的方案是直接把 Windows 缩放临时调到 100%,跑完再调回来,能少写很多兼容代码。
5.2 中文输入法让价格栏数字乱跳
现象:脚本用键盘输入价格 950,结果游戏输入框里出现的是「9 5 0」被输入法吃掉一部分,或者直接变成乱码。
原因:pyautogui.typewrite()本质是模拟键盘按键,中文输入法激活时,数字键被输入法拦截后进入候选词流程,游戏控件收到的根本不是预期字符。
解决:绕开键盘模拟,改用剪贴板粘贴,稳得一批:
import pyperclip pyperclip.copy(str(price)) pyautogui.hotkey("ctrl", "v")从那以后我所有需要填数字的自动化操作都走这条路径,再也没出现过数字乱跳。注意粘贴完之后要加一个极短的延迟再点确认,留出游戏控件刷新时间。
5.3 labelme 装完之后 PyQt5 被回退,助手启动报错
现象:同一台机器上装过 labelme 后,助手启动直接报Could not find the Qt platform plugin "windows"。
原因:pip 安装 labelme 时按依赖重新解析了 PyQt5,把 5.15.10 回退到了 labelme 声明兼容的旧版本,或者装成了不完整的新版本。
解决:严格按虚拟环境隔离,不要和 labelme 混在同一个环境里。如果已经混了,执行pip uninstall pyqt5 pyqt5-qt5 pyqt5-sip labelme全清,再按 3.2 节的顺序重新安装。PyQt5 版本以pip show pyqt5 | findstr Version输出的实际值为准,别只看 requirements.txt 里写的数字。
5.4 上架成功后弹窗被公告栏遮挡,判定失败
现象:单件道具测试时一切正常,批量跑十几件后偶尔出现「确认上架后没等到成功提示」,但手动打开交易行发现货其实已经挂上去了。
原因:游戏交易行顶部有动态公告,概率性覆盖住确认按钮或成功弹窗,模板匹配在那一帧没找到目标区域。
解决:用多帧确认替代单帧判定。连续两帧都识别到同一个结果才算成功,识别置信度低于阈值的帧直接丢弃,不进入状态流转。
def stable_detect(frame_func, max_retries=3): hit_count = 0 for i in range(max_retries): _, _loc = match_template(frame_func(), tpl_ok_btn) if _loc: hit_count += 1 if hit_count >= 2: return True time.sleep(0.3) return Falsestable_detect的核心是连续两帧命中才返回成功,单帧偶发遮挡不会干扰判断。这套「多帧确认」的思路同样适用于识别非绑定道具格子,把它抽成通用函数后,整个脚本的误判率能下来一个量级。
6. 验证方法与进阶思路:先跑通一单,再谈批量
任何 GUI 自动化工具,直接拿真实环境做全量测试都是灾难。我会先把它跑成「只读模式」,验证完再开真实操作。
6.1 dry_run 模式:只识别不点击
在 config.ini 里加一个开关:
[behavior] dry_run = true开启后脚本照常截图识别、照常计算价格、照常输出日志,但所有鼠标点击动作都换成一行提示「[dry_run] 准备点击确认按钮」。跑一遍 dry_run,人工核对每一件道具的识别结果和价格是否正确,全部符合预期再改成false跑真实流程。第一次上手时强制走一遍这个流程,能省掉后面大量返工。
6.2 截图回放校准
日志只能告诉你结果,截图才能告诉你原因。每次识别前把屏幕截图存下来,文件名带上时间戳,跑完对着截图逐帧核对:
import datetime ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") cv2.imwrite(f"logs/screen_{ts}.png", frame)校准重点是看模板匹配的max_loc位置是不是每次都落在同一个按钮中心点。如果位置跳动超过 5 个像素,大概率是模板图切得不干净,重新裁一下模板再试。
6.3 顺手还能改出什么
这套「PyQt5 界面 + 后台 worker + 截图识别 + dry_run 开关」的骨架是通用的,顺着它的思路,你还能接出下架倒计时提醒、多套价格策略切换、指定物品保留下架等小功能。我是拿它当模板,每次做 GUI 自动化工具都先铺好这套骨架再填业务逻辑。从那以后,我写任何自动化脚本都要强制走一遍「dry_run → 截图回放 → 小批量试跑」,这个习惯救了我好多次。希望帮到你。
本文还有配套的精品资源,点击获取