87总控.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. import sys
  2. import time
  3. import win32gui
  4. import win32con
  5. import win32api
  6. import win32process
  7. import threading
  8. from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
  9. QHBoxLayout, QPushButton, QListWidget, QListWidgetItem,
  10. QMessageBox, QLabel, QLineEdit, QGroupBox, QTextEdit,
  11. QSplitter, QComboBox, QSpinBox, QCheckBox, QRadioButton,
  12. QButtonGroup)
  13. from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QObject
  14. from PyQt5.QtGui import QFont
  15. class WorkerSignals(QObject):
  16. """工作线程信号"""
  17. status = pyqtSignal(str)
  18. progress = pyqtSignal(int, int) # 当前索引, 总数
  19. finished = pyqtSignal()
  20. class WindowManagerApp(QMainWindow):
  21. def __init__(self):
  22. super().__init__()
  23. self.window_handles = []
  24. self.all_windows = []
  25. self.is_running = False
  26. self.should_stop = False # 停止标志
  27. self.initUI()
  28. def initUI(self):
  29. """初始化界面"""
  30. self.setWindowTitle('霸器窗口自动点击工具 - 多窗口批量执行')
  31. self.setGeometry(100, 100, 950, 750)
  32. # 设置主窗口样式
  33. self.setStyleSheet("""
  34. QMainWindow {
  35. background-color: #2b2b2b;
  36. }
  37. QGroupBox {
  38. color: #ffffff;
  39. border: 2px solid #555;
  40. border-radius: 5px;
  41. margin-top: 10px;
  42. font-weight: bold;
  43. }
  44. QGroupBox::title {
  45. subcontrol-origin: margin;
  46. left: 10px;
  47. padding: 0 5px 0 5px;
  48. }
  49. QPushButton {
  50. background-color: #4a4a4a;
  51. color: white;
  52. border: 1px solid #555;
  53. padding: 8px 16px;
  54. border-radius: 4px;
  55. font-weight: bold;
  56. font-size: 12px;
  57. }
  58. QPushButton:hover {
  59. background-color: #5a5a5a;
  60. }
  61. QPushButton:pressed {
  62. background-color: #3a3a3a;
  63. }
  64. QPushButton#refreshBtn {
  65. background-color: #0078d4;
  66. }
  67. QPushButton#refreshBtn:hover {
  68. background-color: #1a8ad4;
  69. }
  70. QPushButton#startBtn {
  71. background-color: #107c10;
  72. font-size: 14px;
  73. padding: 12px 24px;
  74. }
  75. QPushButton#startBtn:hover {
  76. background-color: #1a8c1a;
  77. }
  78. QPushButton#startBtn:disabled {
  79. background-color: #555;
  80. }
  81. QPushButton#stopBtn {
  82. background-color: #d13438;
  83. font-size: 14px;
  84. padding: 12px 24px;
  85. }
  86. QPushButton#stopBtn:hover {
  87. background-color: #e14448;
  88. }
  89. QListWidget {
  90. background-color: #3c3c3c;
  91. color: #ffffff;
  92. border: 1px solid #555;
  93. border-radius: 4px;
  94. padding: 5px;
  95. }
  96. QListWidget::item {
  97. padding: 8px;
  98. border-bottom: 1px solid #444;
  99. }
  100. QListWidget::item:selected {
  101. background-color: #0078d4;
  102. }
  103. QLabel {
  104. color: #cccccc;
  105. }
  106. QCheckBox {
  107. color: #ffffff;
  108. font-size: 12px;
  109. padding: 5px;
  110. }
  111. QCheckBox::indicator {
  112. width: 18px;
  113. height: 18px;
  114. }
  115. QCheckBox::indicator:unchecked {
  116. background-color: #3c3c3c;
  117. border: 2px solid #555;
  118. border-radius: 3px;
  119. }
  120. QCheckBox::indicator:checked {
  121. background-color: #0078d4;
  122. border: 2px solid #0078d4;
  123. border-radius: 3px;
  124. }
  125. QRadioButton {
  126. color: #ffffff;
  127. font-size: 12px;
  128. padding: 5px;
  129. }
  130. QRadioButton::indicator {
  131. width: 18px;
  132. height: 18px;
  133. }
  134. QRadioButton::indicator:unchecked {
  135. background-color: #3c3c3c;
  136. border: 2px solid #555;
  137. border-radius: 9px;
  138. }
  139. QRadioButton::indicator:checked {
  140. background-color: #0078d4;
  141. border: 2px solid #0078d4;
  142. border-radius: 9px;
  143. }
  144. QLineEdit, QComboBox, QSpinBox {
  145. background-color: #3c3c3c;
  146. color: #ffffff;
  147. border: 1px solid #555;
  148. border-radius: 4px;
  149. padding: 5px;
  150. }
  151. QTextEdit {
  152. background-color: #1e1e1e;
  153. color: #00ff00;
  154. border: 1px solid #555;
  155. border-radius: 4px;
  156. font-family: 'Consolas', monospace;
  157. font-size: 10pt;
  158. }
  159. """)
  160. # 创建中央窗口
  161. central_widget = QWidget()
  162. self.setCentralWidget(central_widget)
  163. main_layout = QVBoxLayout(central_widget)
  164. # 标题
  165. title_label = QLabel('霸器窗口自动点击工具 - 批量执行')
  166. title_label.setAlignment(Qt.AlignCenter)
  167. title_font = QFont('Arial', 18, QFont.Bold)
  168. title_label.setFont(title_font)
  169. title_label.setStyleSheet('color: #00b4d8; margin: 15px;')
  170. main_layout.addWidget(title_label)
  171. # 控制区域
  172. control_group = QGroupBox('控制面板')
  173. control_layout = QHBoxLayout()
  174. self.refresh_btn = QPushButton('🔄 加载窗口')
  175. self.refresh_btn.setObjectName('refreshBtn')
  176. self.refresh_btn.clicked.connect(self.load_processes)
  177. control_layout.addWidget(self.refresh_btn)
  178. self.filter_label = QLabel('筛选:')
  179. control_layout.addWidget(self.filter_label)
  180. self.filter_input = QLineEdit()
  181. self.filter_input.setPlaceholderText('输入标题关键词过滤...')
  182. self.filter_input.textChanged.connect(self.filter_list)
  183. control_layout.addWidget(self.filter_input)
  184. # 显示模式选择
  185. self.show_mode_combo = QComboBox()
  186. self.show_mode_combo.addItems(['所有窗口', '仅可见窗口', '仅隐藏窗口'])
  187. self.show_mode_combo.currentTextChanged.connect(self.on_show_mode_changed)
  188. control_layout.addWidget(QLabel('显示:'))
  189. control_layout.addWidget(self.show_mode_combo)
  190. control_group.setLayout(control_layout)
  191. main_layout.addWidget(control_group)
  192. # 复选框区域 - 功能选择
  193. checkbox_group = QGroupBox('功能选择')
  194. checkbox_layout = QHBoxLayout()
  195. self.cb_molong = QCheckBox('争夺魔龙之心')
  196. self.cb_molong.setChecked(False)
  197. checkbox_layout.addWidget(self.cb_molong)
  198. self.cb_feicui = QCheckBox('翡翠荣耀旗')
  199. self.cb_feicui.setChecked(False)
  200. checkbox_layout.addWidget(self.cb_feicui)
  201. self.cb_jinyan = QCheckBox('金岩霸者棋')
  202. self.cb_jinyan.setChecked(False)
  203. checkbox_layout.addWidget(self.cb_jinyan)
  204. self.cb_chiyan = QCheckBox('炽焰战神棋')
  205. self.cb_chiyan.setChecked(False)
  206. checkbox_layout.addWidget(self.cb_chiyan)
  207. checkbox_group.setLayout(checkbox_layout)
  208. main_layout.addWidget(checkbox_group)
  209. # 单选按钮区域 - 皇宫占位
  210. radio_group = QGroupBox('皇宫占位位置')
  211. radio_layout = QHBoxLayout()
  212. self.radio_random = QRadioButton('随机')
  213. self.radio_random.setChecked(True)
  214. radio_layout.addWidget(self.radio_random)
  215. self.radio_door = QRadioButton('门口')
  216. radio_layout.addWidget(self.radio_door)
  217. self.radio_corner = QRadioButton('角落')
  218. radio_layout.addWidget(self.radio_corner)
  219. # 将单选按钮分组
  220. self.radio_button_group = QButtonGroup()
  221. self.radio_button_group.addButton(self.radio_random)
  222. self.radio_button_group.addButton(self.radio_door)
  223. self.radio_button_group.addButton(self.radio_corner)
  224. radio_group.setLayout(radio_layout)
  225. main_layout.addWidget(radio_group)
  226. # 窗口列表 - 支持多选
  227. list_group = QGroupBox('窗口列表 (霸器-开头) - 可多选')
  228. list_layout = QVBoxLayout()
  229. self.window_list = QListWidget()
  230. self.window_list.setSelectionMode(QListWidget.MultiSelection) # 改为多选模式
  231. self.window_list.itemDoubleClicked.connect(self.show_selected_window)
  232. list_layout.addWidget(self.window_list)
  233. # 添加全选/取消全选按钮
  234. select_layout = QHBoxLayout()
  235. self.select_all_btn = QPushButton('全选')
  236. self.select_all_btn.clicked.connect(self.select_all_items)
  237. select_layout.addWidget(self.select_all_btn)
  238. self.deselect_all_btn = QPushButton('取消全选')
  239. self.deselect_all_btn.clicked.connect(self.deselect_all_items)
  240. select_layout.addWidget(self.deselect_all_btn)
  241. self.selected_count_label = QLabel('已选: 0 个窗口')
  242. self.selected_count_label.setStyleSheet('color: #00b4d8;')
  243. select_layout.addWidget(self.selected_count_label)
  244. select_layout.addStretch()
  245. list_layout.addLayout(select_layout)
  246. list_group.setLayout(list_layout)
  247. main_layout.addWidget(list_group)
  248. # 操作按钮区域
  249. action_group = QGroupBox('操作')
  250. action_layout = QHBoxLayout()
  251. self.start_btn = QPushButton('▶️ 开始批量执行')
  252. self.start_btn.setObjectName('startBtn')
  253. self.start_btn.clicked.connect(self.start_auto_click)
  254. action_layout.addWidget(self.start_btn)
  255. self.stop_btn = QPushButton('⏹️ 停止')
  256. self.stop_btn.setObjectName('stopBtn')
  257. self.stop_btn.clicked.connect(self.stop_auto_click)
  258. self.stop_btn.setEnabled(False)
  259. action_layout.addWidget(self.stop_btn)
  260. self.show_btn = QPushButton('👁️ 显示选中窗口')
  261. self.show_btn.setObjectName('showBtn')
  262. self.show_btn.clicked.connect(self.show_selected_window)
  263. action_layout.addWidget(self.show_btn)
  264. action_group.setLayout(action_layout)
  265. main_layout.addWidget(action_group)
  266. # 日志区域
  267. log_group = QGroupBox('运行日志')
  268. log_layout = QVBoxLayout()
  269. self.log_text = QTextEdit()
  270. self.log_text.setReadOnly(True)
  271. self.log_text.setMaximumHeight(150)
  272. log_layout.addWidget(self.log_text)
  273. log_group.setLayout(log_layout)
  274. main_layout.addWidget(log_group)
  275. # 状态栏
  276. self.status_label = QLabel('就绪')
  277. self.status_label.setStyleSheet('color: #888; margin: 5px;')
  278. main_layout.addWidget(self.status_label)
  279. # 自动加载
  280. self.load_processes()
  281. # 工作线程信号
  282. self.signals = WorkerSignals()
  283. self.signals.status.connect(self.update_status)
  284. self.signals.progress.connect(self.update_progress)
  285. self.signals.finished.connect(self.on_finished)
  286. def select_all_items(self):
  287. """全选所有窗口"""
  288. for i in range(self.window_list.count()):
  289. item = self.window_list.item(i)
  290. if not item.isHidden():
  291. item.setSelected(True)
  292. self.update_selected_count()
  293. def deselect_all_items(self):
  294. """取消全选"""
  295. self.window_list.clearSelection()
  296. self.update_selected_count()
  297. def update_selected_count(self):
  298. """更新已选数量"""
  299. count = len(self.window_list.selectedItems())
  300. self.selected_count_label.setText(f'已选: {count} 个窗口')
  301. def get_window_title(self, hwnd):
  302. """获取窗口标题"""
  303. try:
  304. return win32gui.GetWindowText(hwnd)
  305. except:
  306. return ''
  307. def is_window_visible(self, hwnd):
  308. """检查窗口是否可见"""
  309. try:
  310. return win32gui.IsWindowVisible(hwnd)
  311. except:
  312. return False
  313. def enum_all_windows_callback(self, hwnd, windows_list):
  314. """枚举所有窗口的回调函数(包括隐藏窗口)"""
  315. if win32gui.IsWindow(hwnd):
  316. title = self.get_window_title(hwnd)
  317. if title: # 只记录有标题的窗口
  318. windows_list.append((hwnd, title))
  319. return True
  320. def load_processes(self):
  321. """加载所有进程窗口(包括隐藏的)"""
  322. try:
  323. self.window_list.clear()
  324. self.window_handles = []
  325. self.all_windows = []
  326. windows_list = []
  327. win32gui.EnumWindows(self.enum_all_windows_callback, windows_list)
  328. self.all_windows = windows_list.copy()
  329. # 筛选以"霸器-"开头的窗口
  330. baqi_windows = [(hwnd, title) for hwnd, title in windows_list if title.startswith('霸器-')]
  331. if not baqi_windows:
  332. self.status_label.setText(f'未找到以"霸器-"开头的窗口 (共找到 {len(windows_list)} 个窗口)')
  333. self.log_text.append(f'⚠️ 未找到霸器窗口,共找到 {len(windows_list)} 个窗口')
  334. return
  335. # 按标题排序
  336. baqi_windows.sort(key=lambda x: x[1])
  337. for hwnd, title in baqi_windows:
  338. item = QListWidgetItem(title)
  339. item.setData(Qt.UserRole, hwnd)
  340. visible = self.is_window_visible(hwnd)
  341. status_text = " ✓" if visible else " ✗"
  342. item.setText(title + status_text)
  343. item.setData(Qt.UserRole + 1, visible)
  344. self.window_list.addItem(item)
  345. self.window_handles.append(hwnd)
  346. visible_count = sum(1 for _, title in baqi_windows if self.is_window_visible(_))
  347. hidden_count = len(baqi_windows) - visible_count
  348. self.status_label.setText(f'找到 {len(baqi_windows)} 个"霸器-"窗口 (可见: {visible_count}, 隐藏: {hidden_count})')
  349. self.log_text.append(f'✅ 加载完成,找到 {len(baqi_windows)} 个霸器窗口')
  350. # 应用显示模式过滤
  351. self.apply_show_mode()
  352. self.update_selected_count()
  353. except Exception as e:
  354. QMessageBox.critical(self, '错误', f'加载进程失败: {str(e)}')
  355. self.status_label.setText('加载失败')
  356. self.log_text.append(f'❌ 加载失败: {str(e)}')
  357. def apply_show_mode(self):
  358. """应用显示模式过滤"""
  359. mode = self.show_mode_combo.currentText()
  360. for i in range(self.window_list.count()):
  361. item = self.window_list.item(i)
  362. visible = item.data(Qt.UserRole + 1)
  363. if mode == '所有窗口':
  364. item.setHidden(False)
  365. elif mode == '仅可见窗口':
  366. item.setHidden(not visible)
  367. elif mode == '仅隐藏窗口':
  368. item.setHidden(visible)
  369. self.update_selected_count()
  370. def on_show_mode_changed(self):
  371. """显示模式改变时重新过滤"""
  372. self.apply_show_mode()
  373. def filter_list(self):
  374. """根据输入过滤列表"""
  375. filter_text = self.filter_input.text().lower()
  376. for i in range(self.window_list.count()):
  377. item = self.window_list.item(i)
  378. title = item.text().replace(' ✓', '').replace(' ✗', '')
  379. if filter_text in title.lower():
  380. item.setHidden(False)
  381. else:
  382. item.setHidden(True)
  383. self.apply_show_mode()
  384. def get_selected_windows(self):
  385. """获取所有选中的窗口"""
  386. selected_items = self.window_list.selectedItems()
  387. windows = []
  388. for item in selected_items:
  389. hwnd = item.data(Qt.UserRole)
  390. title = item.text().replace(' ✓', '').replace(' ✗', '')
  391. windows.append((hwnd, title))
  392. return windows
  393. def show_selected_window(self):
  394. """显示选中的窗口(只显示第一个选中的)"""
  395. selected = self.get_selected_windows()
  396. if not selected:
  397. QMessageBox.warning(self, '警告', '请先选择窗口')
  398. return
  399. hwnd, title = selected[0]
  400. try:
  401. win32gui.ShowWindow(hwnd, win32con.SW_SHOW)
  402. win32gui.SetForegroundWindow(hwnd)
  403. if win32gui.IsIconic(hwnd):
  404. win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
  405. self.status_label.setText(f'已显示窗口: {title}')
  406. self.log_text.append(f'👁️ 显示窗口: {title}')
  407. self.refresh_window_visibility()
  408. except Exception as e:
  409. QMessageBox.critical(self, '错误', f'显示窗口失败: {str(e)}')
  410. def refresh_window_visibility(self):
  411. """刷新窗口中显示的可见状态"""
  412. for i in range(self.window_list.count()):
  413. item = self.window_list.item(i)
  414. hwnd = item.data(Qt.UserRole)
  415. title = item.text().replace(' ✓', '').replace(' ✗', '')
  416. visible = self.is_window_visible(hwnd)
  417. status_text = " ✓" if visible else " ✗"
  418. item.setText(title + status_text)
  419. item.setData(Qt.UserRole + 1, visible)
  420. self.apply_show_mode()
  421. def minimize_window(self, hwnd):
  422. """最小化窗口"""
  423. try:
  424. win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE)
  425. self.signals.status.emit(f'📉 已最小化窗口')
  426. return True
  427. except Exception as e:
  428. self.signals.status.emit(f'❌ 最小化失败: {str(e)}')
  429. return False
  430. def click_at_position(self, hwnd, x_offset, y_offset, button='left'):
  431. """
  432. 封装的点击函数
  433. :param hwnd: 窗口句柄
  434. :param x_offset: X轴偏移量
  435. :param y_offset: Y轴偏移量
  436. :param button: 'left' 或 'right'
  437. """
  438. # 获取窗口位置
  439. rect = win32gui.GetWindowRect(hwnd)
  440. window_x = rect[0]
  441. window_y = rect[1]
  442. # 计算点击位置
  443. click_x = window_x + x_offset
  444. click_y = window_y + y_offset
  445. # 移动鼠标到目标位置
  446. win32api.SetCursorPos((click_x, click_y))
  447. time.sleep(0.2)
  448. # 模拟鼠标点击
  449. if button == 'left':
  450. win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, click_x, click_y, 0, 0)
  451. time.sleep(0.1)
  452. win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, click_x, click_y, 0, 0)
  453. self.signals.status.emit(f'🖱️ 左键点击: ({click_x}, {click_y})')
  454. elif button == 'right':
  455. win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTDOWN, click_x, click_y, 0, 0)
  456. time.sleep(0.1)
  457. win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTUP, click_x, click_y, 0, 0)
  458. self.signals.status.emit(f'🖱️ 右键点击: ({click_x}, {click_y})')
  459. return click_x, click_y
  460. def get_pixel_color(self, hwnd, x_offset, y_offset):
  461. try:
  462. # 获取窗口位置
  463. rect = win32gui.GetWindowRect(hwnd)
  464. window_x = rect[0]
  465. window_y = rect[1]
  466. # 计算屏幕坐标
  467. screen_x = window_x + x_offset
  468. screen_y = window_y + y_offset
  469. # 获取桌面DC
  470. hdc = win32gui.GetDC(0) # 0 表示桌面
  471. color_ref = win32gui.GetPixel(hdc, screen_x, screen_y)
  472. win32gui.ReleaseDC(0, hdc)
  473. red = color_ref & 0xFF
  474. green = (color_ref >> 8) & 0xFF
  475. blue = (color_ref >> 16) & 0xFF
  476. return f"{red:02X}{green:02X}{blue:02X}"
  477. except Exception as e:
  478. self.log_text.append(f'❌ 取色失败: {str(e)}')
  479. return "000000"
  480. def check_and_click(self, hwnd, check_x, check_y, target_color,
  481. click_x, click_y, button='left', check_type='equal'):
  482. """
  483. 检查颜色并执行点击
  484. :param hwnd: 窗口句柄
  485. :param check_x: 检查颜色的X偏移
  486. :param check_y: 检查颜色的Y偏移
  487. :param target_color: 目标颜色 (如 "FFFFFF")
  488. :param click_x: 点击的X偏移
  489. :param click_y: 点击的Y偏移
  490. :param button: 'left' 或 'right'
  491. :param check_type: 'equal' 或 'not_equal'
  492. :return: 是否执行了点击
  493. """
  494. # 获取当前颜色
  495. current_color = self.get_pixel_color(hwnd, check_x, check_y)
  496. self.log_text.append(f'🎨 检测位置({check_x},{check_y})颜色: {current_color} (目标: {target_color})')
  497. should_click = False
  498. if check_type == 'equal':
  499. should_click = (current_color == target_color)
  500. elif check_type == 'not_equal':
  501. should_click = (current_color != target_color)
  502. if should_click:
  503. self.log_text.append(f'✅ 条件满足,执行点击 ({click_x}, {click_y})')
  504. self.click_at_position(hwnd, click_x, click_y, button)
  505. return True
  506. else:
  507. self.log_text.append(f'❌ 条件不满足,跳过点击')
  508. return False
  509. def start_auto_click(self):
  510. """开始自动点击 - 批量执行"""
  511. if self.is_running:
  512. return
  513. # 获取所有选中的窗口
  514. selected_windows = self.get_selected_windows()
  515. if not selected_windows:
  516. QMessageBox.warning(self, '警告', '请至少选择一个窗口')
  517. return
  518. # 检查是否至少选择了一个功能
  519. if not (self.cb_molong.isChecked() or self.cb_feicui.isChecked() or
  520. self.cb_jinyan.isChecked() or self.cb_chiyan.isChecked()):
  521. QMessageBox.warning(self, '警告', '请至少选择一个功能')
  522. return
  523. self.is_running = True
  524. self.should_stop = False
  525. self.start_btn.setEnabled(False)
  526. self.stop_btn.setEnabled(True)
  527. # 记录选择的选项
  528. selected = []
  529. if self.cb_molong.isChecked(): selected.append('争夺魔龙之心')
  530. if self.cb_feicui.isChecked(): selected.append('翡翠荣耀旗')
  531. if self.cb_jinyan.isChecked(): selected.append('金岩霸者棋')
  532. if self.cb_chiyan.isChecked(): selected.append('炽焰战神棋')
  533. # 记录皇宫占位选择
  534. if self.radio_random.isChecked():
  535. palace_pos = '随机'
  536. palace_y = 444
  537. elif self.radio_door.isChecked():
  538. palace_pos = '门口'
  539. palace_y = 457
  540. else: # corner
  541. palace_pos = '角落'
  542. palace_y = 470
  543. self.log_text.append(f'▶️ 开始批量执行,共 {len(selected_windows)} 个窗口')
  544. self.log_text.append(f'📋 已选功能: {", ".join(selected)}')
  545. self.log_text.append(f'🏰 皇宫占位: {palace_pos} (Y+{palace_y})')
  546. self.log_text.append('=' * 50)
  547. # 在新线程中执行
  548. thread = threading.Thread(target=self.batch_auto_click_worker,
  549. args=(selected_windows, palace_y))
  550. thread.daemon = True
  551. thread.start()
  552. def batch_auto_click_worker(self, selected_windows, palace_y):
  553. """批量自动点击工作线程"""
  554. total = len(selected_windows)
  555. for idx, (hwnd, title) in enumerate(selected_windows, 1):
  556. # 检查是否被停止
  557. if self.should_stop:
  558. self.signals.status.emit(f'⏹️ 用户停止执行')
  559. break
  560. self.signals.status.emit(f'📌 正在处理 [{idx}/{total}] {title}')
  561. self.signals.progress.emit(idx, total)
  562. try:
  563. # 执行单个窗口的点击流程
  564. self.execute_single_window(hwnd, title, palace_y)
  565. # 等待5秒后最小化窗口
  566. self.signals.status.emit(f'⏳ 等待5秒后最小化窗口...')
  567. time.sleep(5)
  568. # 最小化窗口
  569. self.signals.status.emit(f'📉 最小化窗口: {title}')
  570. self.minimize_window(hwnd)
  571. # 窗口间间隔1秒
  572. if idx < total and not self.should_stop:
  573. self.signals.status.emit(f'⏳ 等待1秒后处理下一个窗口...')
  574. time.sleep(1)
  575. except Exception as e:
  576. self.signals.status.emit(f'❌ 处理窗口失败: {str(e)}')
  577. import traceback
  578. self.log_text.append(traceback.format_exc())
  579. # 出错后继续下一个窗口
  580. if not self.should_stop:
  581. self.signals.status.emit(f'✅ 批量执行完成!共处理 {idx if idx < total else total} 个窗口')
  582. self.signals.finished.emit()
  583. def execute_single_window(self, hwnd, title, palace_y):
  584. """执行单个窗口的点击流程"""
  585. # 1. 激活窗口
  586. self.signals.status.emit(f'🔄 正在激活窗口: {title}')
  587. # 显示窗口
  588. win32gui.ShowWindow(hwnd, win32con.SW_SHOW)
  589. if win32gui.IsIconic(hwnd):
  590. win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
  591. # 激活窗口
  592. win32gui.SetForegroundWindow(hwnd)
  593. time.sleep(1) # 等待窗口激活
  594. # 步骤1: 左键点击 X+63, Y+81
  595. self.click_at_position(hwnd, 29, 41, 'left')
  596. time.sleep(1)
  597. self.signals.status.emit(f'📍 步骤1: 左键点击 X+63, Y+81')
  598. self.click_at_position(hwnd, 63, 81, 'left')
  599. # 等待5秒
  600. self.signals.status.emit(f'⏳ 等待5秒...')
  601. time.sleep(5)
  602. # 步骤2: 右键点击 X+413, Y+168
  603. self.signals.status.emit(f'📍 步骤2: 右键点击 X+413, Y+168')
  604. self.click_at_position(hwnd, 413, 168, 'right')
  605. # 等待6秒
  606. self.signals.status.emit(f'⏳ 等待6秒...')
  607. time.sleep(6)
  608. # 处理"争夺魔龙之心"
  609. if self.cb_molong.isChecked():
  610. self.signals.status.emit(f'📍 检查争夺魔龙之心')
  611. self.check_and_click(
  612. hwnd=hwnd,
  613. check_x=642,
  614. check_y=439,
  615. target_color="FFFFFF",
  616. click_x=642,
  617. click_y=439,
  618. button='left',
  619. check_type='equal'
  620. )
  621. else:
  622. self.signals.status.emit(f'📍 未选争夺魔龙之心,执行相反逻辑')
  623. self.check_and_click(
  624. hwnd=hwnd,
  625. check_x=642,
  626. check_y=437,
  627. target_color="FFFFFF",
  628. click_x=642,
  629. click_y=437,
  630. button='left',
  631. check_type='not_equal'
  632. )
  633. # 等待1秒
  634. time.sleep(0.2)
  635. # 处理"翡翠荣耀旗"
  636. if self.cb_feicui.isChecked():
  637. self.signals.status.emit(f'📍 检查翡翠荣耀旗')
  638. self.check_and_click(
  639. hwnd=hwnd,
  640. check_x=769,
  641. check_y=386,
  642. target_color="FFFFFF",
  643. click_x=769,
  644. click_y=386,
  645. button='left',
  646. check_type='equal'
  647. )
  648. else:
  649. self.signals.status.emit(f'📍 未选翡翠荣耀旗,执行相反逻辑')
  650. self.check_and_click(
  651. hwnd=hwnd,
  652. check_x=769,
  653. check_y=386,
  654. target_color="FFFFFF",
  655. click_x=769,
  656. click_y=386,
  657. button='left',
  658. check_type='not_equal'
  659. )
  660. # 等待1秒
  661. time.sleep(0.2)
  662. # 处理"金岩霸者棋"
  663. if self.cb_jinyan.isChecked():
  664. self.signals.status.emit(f'📍 检查金岩霸者棋')
  665. self.check_and_click(
  666. hwnd=hwnd,
  667. check_x=769,
  668. check_y=400,
  669. target_color="FFFFFF",
  670. click_x=769,
  671. click_y=400,
  672. button='left',
  673. check_type='equal'
  674. )
  675. else:
  676. self.signals.status.emit(f'📍 未选金岩霸者棋,执行相反逻辑')
  677. self.check_and_click(
  678. hwnd=hwnd,
  679. check_x=769,
  680. check_y=400,
  681. target_color="FFFFFF",
  682. click_x=769,
  683. click_y=400,
  684. button='left',
  685. check_type='not_equal'
  686. )
  687. # 等待1秒
  688. time.sleep(0.2)
  689. # 处理"炽焰战神棋"
  690. if self.cb_chiyan.isChecked():
  691. self.signals.status.emit(f'📍 检查炽焰战神棋')
  692. self.check_and_click(
  693. hwnd=hwnd,
  694. check_x=767,
  695. check_y=415,
  696. target_color="FFFFFF",
  697. click_x=767,
  698. click_y=415,
  699. button='left',
  700. check_type='equal'
  701. )
  702. else:
  703. self.signals.status.emit(f'📍 未选炽焰战神棋,执行相反逻辑')
  704. self.check_and_click(
  705. hwnd=hwnd,
  706. check_x=767,
  707. check_y=415,
  708. target_color="FFFFFF",
  709. click_x=767,
  710. click_y=415,
  711. button='left',
  712. check_type='not_equal'
  713. )
  714. # 等待1秒
  715. time.sleep(0.2)
  716. # 最后点击 X+871, Y+266
  717. self.signals.status.emit(f'📍 最后点击: X+871, Y+266')
  718. self.click_at_position(hwnd, 871, 266, 'left')
  719. time.sleep(0.5)
  720. self.click_at_position(hwnd, 671, 70, 'right')
  721. time.sleep(1)
  722. self.click_at_position(hwnd, 714, 426, 'left')
  723. time.sleep(1)
  724. # 皇宫占位点击 - 根据选择的选项
  725. self.signals.status.emit(f'🏰 皇宫占位点击: X+716, Y+{palace_y}')
  726. self.click_at_position(hwnd, 716, palace_y, 'left')
  727. time.sleep(1)
  728. self.click_at_position(hwnd, 901, 311, 'left')
  729. time.sleep(1)
  730. self.click_at_position(hwnd, 1133, 8, 'left')
  731. self.signals.status.emit(f'✅ 窗口 {title} 执行完成')
  732. def stop_auto_click(self):
  733. """停止自动点击"""
  734. self.should_stop = True
  735. self.is_running = False
  736. self.start_btn.setEnabled(True)
  737. self.stop_btn.setEnabled(False)
  738. self.status_label.setText('正在停止...')
  739. self.log_text.append('⏹️ 正在停止执行...')
  740. def update_status(self, message):
  741. """更新状态"""
  742. self.status_label.setText(message)
  743. self.log_text.append(message)
  744. # 滚动到底部
  745. self.log_text.verticalScrollBar().setValue(
  746. self.log_text.verticalScrollBar().maximum()
  747. )
  748. def update_progress(self, current, total):
  749. """更新进度"""
  750. self.status_label.setText(f'进度: {current}/{total}')
  751. def on_finished(self):
  752. """完成回调"""
  753. self.is_running = False
  754. self.start_btn.setEnabled(True)
  755. self.stop_btn.setEnabled(False)
  756. self.status_label.setText('就绪')
  757. self.log_text.append('=' * 50)
  758. def main():
  759. app = QApplication(sys.argv)
  760. app.setStyle('Fusion')
  761. window = WindowManagerApp()
  762. window.show()
  763. sys.exit(app.exec_())
  764. if __name__ == '__main__':
  765. main()