-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy pathmain_gui.py
More file actions
320 lines (272 loc) · 11.9 KB
/
main_gui.py
File metadata and controls
320 lines (272 loc) · 11.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# -*- coding: utf-8 -*-
# @Author: BugNotFound
# @Date: 2025-10-04
# @FilePath: /DeltaForceScript/main_gui.py
# @Description: 带 PyQt6 GUI 的主程序
import os
import sys
import re
import time
import ctypes
from window_capture import *
from region_selector import RegionSelector
from gui_monitor import MonitorWindow
import numpy
from paddleocr import PaddleOCR
from PyQt6.QtWidgets import QApplication
from PyQt6.QtCore import QThread, pyqtSignal
import pydirectinput
from colormath.color_objects import sRGBColor, LabColor
from colormath.color_diff import delta_e_cie2000
from colormath.color_conversions import convert_color
def patch_asscalar(a):
return a.item()
setattr(numpy, "asscalar", patch_asscalar)
def is_admin():
"""检查是否以管理员权限运行"""
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
def run_as_admin():
"""以管理员权限重新启动程序"""
if not is_admin():
print("正在请求管理员权限...")
# 获取当前脚本路径
script = os.path.abspath(sys.argv[0])
params = ' '.join([script] + sys.argv[1:])
# 使用 ShellExecute 以管理员权限运行
ret = ctypes.windll.shell32.ShellExecuteW(
None, "runas", sys.executable, params, None, 1
)
if ret > 32: # 成功
sys.exit(0)
else:
print("未获得管理员权限,继续以普通权限运行")
return False
return True
def click_region_center(region: tuple, clicks=1, interval=0.1):
"""点击区域的中心位置 - 使用多种方法尝试
Args:
region: (left, top, right, bottom) 格式的区域坐标
"""
left, top, right, bottom = region
center_x = (left + right) // 2
center_y = (top + bottom) // 2
# print(f"准备点击位置: ({center_x}, {center_y})")
# 在20个像素的范围内随机偏移,防止被检测
center_x += int((os.urandom(1)[0] / 255 - 0.5) * 10)
center_y += int((os.urandom(1)[0] / 255 - 0.5) * 10)
pydirectinput.click(x=center_x, y=center_y, clicks=clicks, interval=interval, button=pydirectinput.LEFT)
def extract_and_merge_digits(s: str) -> str:
"""识别字符串中的所有数字并合并为一个新字符串"""
return ''.join(re.findall(r'\d', s))
class ScriptThread(QThread):
"""脚本运行线程"""
status_updated = pyqtSignal(str)
timer_updated = pyqtSignal(str, str)
ocr_updated = pyqtSignal(str, float)
click_performed = pyqtSignal()
task_completed = pyqtSignal()
def __init__(self, selector: RegionSelector, win_cap: WindowCapture, ocr, config):
super().__init__()
self.selector = selector
self.win_cap = win_cap
self.ocr = ocr
self.config = config
self.is_running = True
self.is_paused = False
def frame_cut(self, frame, region):
"""裁剪图像区域"""
left, top, right, bottom = region
return frame[top:bottom, left:right]
def verify_window(self) -> bool:
"""检查确认按钮区域的颜色是否变化"""
frame = self.win_cap.capture()
while frame is None or frame.size == 0: frame = self.win_cap.capture()
region = self.selector.get_region("verify_check")
# 获取区域中心颜色
color_tmp = frame[((region[1] + region[3]) // 2), ((region[0] + region[2]) // 2)]
center_color = convert_color(
sRGBColor(color_tmp[2], color_tmp[1], color_tmp[0]), # BGR to sRGB
LabColor
)
# 预设的确认按钮中心颜色 (BGR)
target_color = convert_color(
sRGBColor(175, 109, 65), # BGR:适用于金色砖皮
LabColor
)
# 计算颜色差异
delta_e = delta_e_cie2000(center_color, target_color)
# 色差小说明显示了确认窗口
self.status_updated.emit(f"颜色:{color_tmp[2], color_tmp[1], color_tmp[0]}")
self.status_updated.emit(f"色差: {delta_e}")
if delta_e < 80:
return True
return False
def ocr_region(self, region):
"""OCR 识别"""
frame = self.win_cap.capture()
# while frame is None or frame.size == 0: frame = self.win_cap.capture()
if frame is None or frame.size == 0: return ""
roi = self.frame_cut(frame, region)
res = self.ocr.ocr(roi)
if not res or not res[0]['rec_texts']:
return ""
return res[0]['rec_texts'][0]
def run(self):
"""运行脚本"""
try:
self.status_updated.emit("初始化中...")
time_region = self.selector.get_region("time")
buy_region = self.selector.get_region("buy")
verify_region = self.selector.get_region("verify")
refresh_region = self.selector.get_region("refresh")
money_region = self.selector.get_region("money")
money = self.ocr_region(money_region)
money = extract_and_merge_digits(money)
self.status_updated.emit(f"初始三角币: {money}")
pattern = re.compile(r'(\d+)\s*分\s*(\d+)\s*秒')
self.status_updated.emit("监控中...")
refreshed = False # 标记是否刚刚点击过刷新
click_region_center(refresh_region)
while self.is_running:
# 暂停时等待
while self.is_paused: time.sleep(0.2); continue
# 截图并OCR识别时间
res = self.ocr_region(time_region)
if "天" in res or "小时" in res: click_region_center(refresh_region); continue
match = pattern.search(res)
if match:
minutes = int(match.group(1))
seconds = int(match.group(2))
# 更新时间显示
self.timer_updated.emit(str(minutes), str(seconds))
# 剩余时间到 0:03 时点击刷新(如果启用)
if minutes == 0 and seconds == 3 and self.config['click_refresh_at_3s'] and not refreshed:
self.status_updated.emit("🔄 点击刷新...")
click_region_center(refresh_region)
refreshed = True
# 剩余时间到 0:01 时执行点击
if minutes == 0 and seconds == 1:
self.status_updated.emit("准备点击...")
time.sleep(self.config['buy_click_delay'])
# 点击购买按钮
click_region_center(buy_region, interval=0)
# 校验点击是否成功(可能造成延迟)
buy_count = 0
while not self.verify_window() and buy_count < 5:
buy_count += 1
if buy_count <= 2:
time.sleep(self.config['buy_interval'])
click_region_center(buy_region, interval=0)
time.sleep(self.config['buy_to_verify_delay'])
# 点击确认按钮
click_region_center(verify_region, interval=self.config['verify_interval'])
self.status_updated.emit("点击确认按钮...")
# 校验点到了确认
verify_counter = 0
while self.verify_window():
verify_counter += 1
if verify_counter > 2:
pydirectinput.click(1, 1, interval=0.1)
click_region_center(verify_region, interval=self.config['verify_interval'])
self.status_updated.emit("等待刷新...")
time.sleep(1.5)
if self.verify_window(): pydirectinput.press('esc')
click_region_center(refresh_region)
# 检查三角币是否变化
now_money = self.ocr_region(money_region)
now_money = extract_and_merge_digits(now_money)
self.status_updated.emit(f"当前三角币: {now_money}")
self.config['continue_after_complete'] &= (now_money == money)
# 根据配置决定是否继续
if not self.config['continue_after_complete']:
self.status_updated.emit("任务完成!")
self.task_completed.emit()
break
else:
refreshed = False
self.status_updated.emit("继续监控中...")
else:
if minutes > 0 or seconds > 5:
time.sleep(self.config['ocr_interval'])
else:
time.sleep(self.config['ocr_interval'])
except Exception as e:
self.status_updated.emit(f"错误: {str(e)}")
print(f"脚本运行错误: {e}")
def pause(self):
self.is_paused = True
def resume(self):
self.is_paused = False
def stop(self):
self.is_running = False
def main():
"""主函数"""
app = QApplication(sys.argv)
selector = RegionSelector()
selector.load_regions_from_file("regions_2k.json")
win_cap = WindowCapture(max_buffer_len=2)
# 初始化 OCR
ocr = PaddleOCR(
use_doc_orientation_classify=False,
use_doc_unwarping=False,
use_textline_orientation=False,
text_detection_model_dir="models/PP-OCRv5_server_det_infer",
text_recognition_model_dir="models/PP-OCRv5_server_rec_infer",
# use_tensorrt=True,
device='gpu:0'
)
window = MonitorWindow()
window.show()
# 移动到屏幕右下角
screen = app.primaryScreen().geometry()
win_h = window.height()
x = screen.x() + 10
y = screen.y() + screen.height() - win_h - 30
window.move(x, y)
window.add_log("程序已启动")
window.add_log("点击 [开始] 按钮启动监控")
script_thread = None
def on_start():
nonlocal script_thread
window.add_log("正在启动监控线程...")
# 获取当前配置
config = window.get_config()
window.add_log(f"配置: 购买延迟={config['buy_click_delay']}秒")
script_thread = ScriptThread(selector, win_cap, ocr, config)
script_thread.status_updated.connect(lambda s: window.update_status(s))
script_thread.status_updated.connect(lambda s: window.add_log(s))
script_thread.timer_updated.connect(lambda m, s: window.update_timer(m, s))
script_thread.task_completed.connect(lambda: window.on_complete())
script_thread.start()
def on_pause():
if script_thread:
script_thread.pause()
def on_resume():
if script_thread:
script_thread.resume()
def on_stop():
if script_thread:
script_thread.stop()
script_thread.wait()
window.controller.start_requested.connect(on_start)
window.controller.pause_requested.connect(on_pause)
window.controller.resume_requested.connect(on_resume)
window.controller.stop_requested.connect(on_stop)
def cleanup():
if script_thread and script_thread.isRunning():
script_thread.stop()
script_thread.wait()
win_cap.stop()
app.aboutToQuit.connect(cleanup)
sys.exit(app.exec())
if __name__ == "__main__":
# 检查并请求管理员权限
if not is_admin():
print("检测到程序未以管理员权限运行")
run_as_admin()
else:
print("Delta Force 自动购买脚本 - PyQt6 GUI版本 (管理员模式)")
main()