| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026 |
- import sys
- import time
- import win32gui
- import win32con
- import win32api
- import win32process
- import threading
- from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
- QHBoxLayout, QPushButton, QListWidget, QListWidgetItem,
- QMessageBox, QLabel, QLineEdit, QGroupBox, QTextEdit,
- QSplitter, QComboBox, QSpinBox, QCheckBox, QRadioButton,
- QButtonGroup)
- from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QObject
- from PyQt5.QtGui import QFont
- class WorkerSignals(QObject):
- """工作线程信号"""
- status = pyqtSignal(str)
- progress = pyqtSignal(int, int) # 当前索引, 总数
- finished = pyqtSignal()
- class WindowManagerApp(QMainWindow):
- def __init__(self):
- super().__init__()
- self.window_handles = []
- self.all_windows = []
- self.is_running = False
- self.should_stop = False # 停止标志
- self.initUI()
- def initUI(self):
- """初始化界面"""
- self.setWindowTitle('霸器窗口自动点击工具 - 多窗口批量执行')
- self.setGeometry(100, 100, 950, 800)
- # 设置主窗口样式
- self.setStyleSheet("""
- QMainWindow {
- background-color: #2b2b2b;
- }
- QGroupBox {
- color: #ffffff;
- border: 2px solid #555;
- border-radius: 5px;
- margin-top: 10px;
- font-weight: bold;
- }
- QGroupBox::title {
- subcontrol-origin: margin;
- left: 10px;
- padding: 0 5px 0 5px;
- }
- QPushButton {
- background-color: #4a4a4a;
- color: white;
- border: 1px solid #555;
- padding: 8px 16px;
- border-radius: 4px;
- font-weight: bold;
- font-size: 12px;
- }
- QPushButton:hover {
- background-color: #5a5a5a;
- }
- QPushButton:pressed {
- background-color: #3a3a3a;
- }
- QPushButton#refreshBtn {
- background-color: #0078d4;
- }
- QPushButton#refreshBtn:hover {
- background-color: #1a8ad4;
- }
- QPushButton#startBtn {
- background-color: #107c10;
- font-size: 14px;
- padding: 12px 24px;
- }
- QPushButton#startBtn:hover {
- background-color: #1a8c1a;
- }
- QPushButton#startBtn:disabled {
- background-color: #555;
- }
- QPushButton#stopBtn {
- background-color: #d13438;
- font-size: 14px;
- padding: 12px 24px;
- }
- QPushButton#stopBtn:hover {
- background-color: #e14448;
- }
- QPushButton#funcBtn {
- background-color: #8b5cf6;
- font-size: 13px;
- padding: 10px 20px;
- }
- QPushButton#funcBtn:hover {
- background-color: #7c3aed;
- }
- QPushButton#funcBtn:disabled {
- background-color: #555;
- }
- QPushButton#palaceBtn {
- background-color: #f59e0b;
- font-size: 13px;
- padding: 10px 20px;
- }
- QPushButton#palaceBtn:hover {
- background-color: #d97706;
- }
- QPushButton#palaceBtn:disabled {
- background-color: #555;
- }
- QListWidget {
- background-color: #3c3c3c;
- color: #ffffff;
- border: 1px solid #555;
- border-radius: 4px;
- padding: 5px;
- }
- QListWidget::item {
- padding: 8px;
- border-bottom: 1px solid #444;
- }
- QListWidget::item:selected {
- background-color: #0078d4;
- }
- QLabel {
- color: #cccccc;
- }
- QCheckBox {
- color: #ffffff;
- font-size: 12px;
- padding: 5px;
- }
- QCheckBox::indicator {
- width: 18px;
- height: 18px;
- }
- QCheckBox::indicator:unchecked {
- background-color: #3c3c3c;
- border: 2px solid #555;
- border-radius: 3px;
- }
- QCheckBox::indicator:checked {
- background-color: #0078d4;
- border: 2px solid #0078d4;
- border-radius: 3px;
- }
- QRadioButton {
- color: #ffffff;
- font-size: 12px;
- padding: 5px;
- }
- QRadioButton::indicator {
- width: 18px;
- height: 18px;
- }
- QRadioButton::indicator:unchecked {
- background-color: #3c3c3c;
- border: 2px solid #555;
- border-radius: 9px;
- }
- QRadioButton::indicator:checked {
- background-color: #0078d4;
- border: 2px solid #0078d4;
- border-radius: 9px;
- }
- QLineEdit, QComboBox, QSpinBox {
- background-color: #3c3c3c;
- color: #ffffff;
- border: 1px solid #555;
- border-radius: 4px;
- padding: 5px;
- }
- QTextEdit {
- background-color: #1e1e1e;
- color: #00ff00;
- border: 1px solid #555;
- border-radius: 4px;
- font-family: 'Consolas', monospace;
- font-size: 10pt;
- }
- """)
- # 创建中央窗口
- central_widget = QWidget()
- self.setCentralWidget(central_widget)
- main_layout = QVBoxLayout(central_widget)
- # 标题
- title_label = QLabel('霸器窗口自动点击工具 - 批量执行')
- title_label.setAlignment(Qt.AlignCenter)
- title_font = QFont('Arial', 18, QFont.Bold)
- title_label.setFont(title_font)
- title_label.setStyleSheet('color: #00b4d8; margin: 15px;')
- main_layout.addWidget(title_label)
- # 控制区域
- control_group = QGroupBox('控制面板')
- control_layout = QHBoxLayout()
- self.refresh_btn = QPushButton('🔄 加载窗口')
- self.refresh_btn.setObjectName('refreshBtn')
- self.refresh_btn.clicked.connect(self.load_processes)
- control_layout.addWidget(self.refresh_btn)
- self.filter_label = QLabel('筛选:')
- control_layout.addWidget(self.filter_label)
- self.filter_input = QLineEdit()
- self.filter_input.setPlaceholderText('输入标题关键词过滤...')
- self.filter_input.textChanged.connect(self.filter_list)
- control_layout.addWidget(self.filter_input)
- # 显示模式选择
- self.show_mode_combo = QComboBox()
- self.show_mode_combo.addItems(['所有窗口', '仅可见窗口', '仅隐藏窗口'])
- self.show_mode_combo.currentTextChanged.connect(self.on_show_mode_changed)
- control_layout.addWidget(QLabel('显示:'))
- control_layout.addWidget(self.show_mode_combo)
- control_group.setLayout(control_layout)
- main_layout.addWidget(control_group)
- # ==================== 功能选择区域 ====================
- func_group = QGroupBox('功能选择 (联赛相关)')
- func_main_layout = QVBoxLayout()
- # 复选框
- checkbox_layout = QHBoxLayout()
- self.cb_molong = QCheckBox('争夺魔龙之心')
- self.cb_molong.setChecked(False)
- checkbox_layout.addWidget(self.cb_molong)
- self.cb_feicui = QCheckBox('翡翠荣耀旗')
- self.cb_feicui.setChecked(False)
- checkbox_layout.addWidget(self.cb_feicui)
- self.cb_jinyan = QCheckBox('金岩霸者棋')
- self.cb_jinyan.setChecked(False)
- checkbox_layout.addWidget(self.cb_jinyan)
- self.cb_chiyan = QCheckBox('炽焰战神棋')
- self.cb_chiyan.setChecked(False)
- checkbox_layout.addWidget(self.cb_chiyan)
- checkbox_layout.addStretch()
- func_main_layout.addLayout(checkbox_layout)
- # 功能选择执行按钮
- func_btn_layout = QHBoxLayout()
- self.func_btn = QPushButton('⚡ 单独执行功能选择')
- self.func_btn.setObjectName('funcBtn')
- self.func_btn.clicked.connect(lambda: self.start_batch('function'))
- func_btn_layout.addWidget(self.func_btn)
- func_btn_layout.addStretch()
- func_main_layout.addLayout(func_btn_layout)
- func_group.setLayout(func_main_layout)
- main_layout.addWidget(func_group)
- # ==================== 皇宫占位区域 ====================
- palace_group = QGroupBox('皇宫占位位置')
- palace_main_layout = QVBoxLayout()
- # 单选按钮
- radio_layout = QHBoxLayout()
- self.radio_random = QRadioButton('随机')
- self.radio_random.setChecked(True)
- radio_layout.addWidget(self.radio_random)
- self.radio_door = QRadioButton('门口')
- radio_layout.addWidget(self.radio_door)
- self.radio_corner = QRadioButton('角落')
- radio_layout.addWidget(self.radio_corner)
- # 将单选按钮分组
- self.radio_button_group = QButtonGroup()
- self.radio_button_group.addButton(self.radio_random)
- self.radio_button_group.addButton(self.radio_door)
- self.radio_button_group.addButton(self.radio_corner)
- radio_layout.addStretch()
- palace_main_layout.addLayout(radio_layout)
- # 皇宫占位执行按钮
- palace_btn_layout = QHBoxLayout()
- self.palace_btn = QPushButton('👑 单独执行皇宫占位')
- self.palace_btn.setObjectName('palaceBtn')
- self.palace_btn.clicked.connect(lambda: self.start_batch('palace'))
- palace_btn_layout.addWidget(self.palace_btn)
- palace_btn_layout.addStretch()
- palace_main_layout.addLayout(palace_btn_layout)
- palace_group.setLayout(palace_main_layout)
- main_layout.addWidget(palace_group)
- # 窗口列表 - 支持多选
- list_group = QGroupBox('窗口列表 (霸器-开头) - 可多选')
- list_layout = QVBoxLayout()
- self.window_list = QListWidget()
- self.window_list.setSelectionMode(QListWidget.MultiSelection) # 改为多选模式
- self.window_list.itemDoubleClicked.connect(self.show_selected_window)
- list_layout.addWidget(self.window_list)
- # 添加全选/取消全选按钮
- select_layout = QHBoxLayout()
- self.select_all_btn = QPushButton('全选')
- self.select_all_btn.clicked.connect(self.select_all_items)
- select_layout.addWidget(self.select_all_btn)
- self.deselect_all_btn = QPushButton('取消全选')
- self.deselect_all_btn.clicked.connect(self.deselect_all_items)
- select_layout.addWidget(self.deselect_all_btn)
- self.selected_count_label = QLabel('已选: 0 个窗口')
- self.selected_count_label.setStyleSheet('color: #00b4d8;')
- select_layout.addWidget(self.selected_count_label)
- select_layout.addStretch()
- list_layout.addLayout(select_layout)
- list_group.setLayout(list_layout)
- main_layout.addWidget(list_group)
- # 操作按钮区域
- action_group = QGroupBox('批量操作')
- action_layout = QHBoxLayout()
- self.start_btn = QPushButton('▶️ 全部执行 (功能+皇宫)')
- self.start_btn.setObjectName('startBtn')
- self.start_btn.clicked.connect(lambda: self.start_batch('all'))
- action_layout.addWidget(self.start_btn)
- self.stop_btn = QPushButton('⏹️ 停止')
- self.stop_btn.setObjectName('stopBtn')
- self.stop_btn.clicked.connect(self.stop_auto_click)
- self.stop_btn.setEnabled(False)
- action_layout.addWidget(self.stop_btn)
- self.show_btn = QPushButton('👁️ 显示选中窗口')
- self.show_btn.setObjectName('showBtn')
- self.show_btn.clicked.connect(self.show_selected_window)
- action_layout.addWidget(self.show_btn)
- action_group.setLayout(action_layout)
- main_layout.addWidget(action_group)
- # 日志区域
- log_group = QGroupBox('运行日志')
- log_layout = QVBoxLayout()
- self.log_text = QTextEdit()
- self.log_text.setReadOnly(True)
- self.log_text.setMaximumHeight(150)
- log_layout.addWidget(self.log_text)
- log_group.setLayout(log_layout)
- main_layout.addWidget(log_group)
- # 状态栏
- self.status_label = QLabel('就绪')
- self.status_label.setStyleSheet('color: #888; margin: 5px;')
- main_layout.addWidget(self.status_label)
- # 自动加载
- self.load_processes()
- # 工作线程信号
- self.signals = WorkerSignals()
- self.signals.status.connect(self.update_status)
- self.signals.progress.connect(self.update_progress)
- self.signals.finished.connect(self.on_finished)
- def select_all_items(self):
- """全选所有窗口"""
- for i in range(self.window_list.count()):
- item = self.window_list.item(i)
- if not item.isHidden():
- item.setSelected(True)
- self.update_selected_count()
- def deselect_all_items(self):
- """取消全选"""
- self.window_list.clearSelection()
- self.update_selected_count()
- def update_selected_count(self):
- """更新已选数量"""
- count = len(self.window_list.selectedItems())
- self.selected_count_label.setText(f'已选: {count} 个窗口')
- def get_window_title(self, hwnd):
- """获取窗口标题"""
- try:
- return win32gui.GetWindowText(hwnd)
- except:
- return ''
- def is_window_visible(self, hwnd):
- """检查窗口是否可见"""
- try:
- return win32gui.IsWindowVisible(hwnd)
- except:
- return False
- def enum_all_windows_callback(self, hwnd, windows_list):
- """枚举所有窗口的回调函数(包括隐藏窗口)"""
- if win32gui.IsWindow(hwnd):
- title = self.get_window_title(hwnd)
- if title: # 只记录有标题的窗口
- windows_list.append((hwnd, title))
- return True
- def load_processes(self):
- """加载所有进程窗口(包括隐藏的)"""
- try:
- self.window_list.clear()
- self.window_handles = []
- self.all_windows = []
- windows_list = []
- win32gui.EnumWindows(self.enum_all_windows_callback, windows_list)
- self.all_windows = windows_list.copy()
- # 筛选以"霸器-"开头的窗口
- baqi_windows = [(hwnd, title) for hwnd, title in windows_list if title.startswith('霸器-')]
- if not baqi_windows:
- self.status_label.setText(f'未找到以"霸器-"开头的窗口 (共找到 {len(windows_list)} 个窗口)')
- self.log_text.append(f'⚠️ 未找到霸器窗口,共找到 {len(windows_list)} 个窗口')
- return
- # 按标题排序
- baqi_windows.sort(key=lambda x: x[1])
- for hwnd, title in baqi_windows:
- item = QListWidgetItem(title)
- item.setData(Qt.UserRole, hwnd)
- visible = self.is_window_visible(hwnd)
- status_text = " ✓" if visible else " ✗"
- item.setText(title + status_text)
- item.setData(Qt.UserRole + 1, visible)
- self.window_list.addItem(item)
- self.window_handles.append(hwnd)
- visible_count = sum(1 for _, title in baqi_windows if self.is_window_visible(_))
- hidden_count = len(baqi_windows) - visible_count
- self.status_label.setText(f'找到 {len(baqi_windows)} 个"霸器-"窗口 (可见: {visible_count}, 隐藏: {hidden_count})')
- self.log_text.append(f'✅ 加载完成,找到 {len(baqi_windows)} 个霸器窗口')
- # 应用显示模式过滤
- self.apply_show_mode()
- self.update_selected_count()
- except Exception as e:
- QMessageBox.critical(self, '错误', f'加载进程失败: {str(e)}')
- self.status_label.setText('加载失败')
- self.log_text.append(f'❌ 加载失败: {str(e)}')
- def apply_show_mode(self):
- """应用显示模式过滤"""
- mode = self.show_mode_combo.currentText()
- for i in range(self.window_list.count()):
- item = self.window_list.item(i)
- visible = item.data(Qt.UserRole + 1)
- if mode == '所有窗口':
- item.setHidden(False)
- elif mode == '仅可见窗口':
- item.setHidden(not visible)
- elif mode == '仅隐藏窗口':
- item.setHidden(visible)
- self.update_selected_count()
- def on_show_mode_changed(self):
- """显示模式改变时重新过滤"""
- self.apply_show_mode()
- def filter_list(self):
- """根据输入过滤列表"""
- filter_text = self.filter_input.text().lower()
- for i in range(self.window_list.count()):
- item = self.window_list.item(i)
- title = item.text().replace(' ✓', '').replace(' ✗', '')
- if filter_text in title.lower():
- item.setHidden(False)
- else:
- item.setHidden(True)
- self.apply_show_mode()
- def get_selected_windows(self):
- """获取所有选中的窗口"""
- selected_items = self.window_list.selectedItems()
- windows = []
- for item in selected_items:
- hwnd = item.data(Qt.UserRole)
- title = item.text().replace(' ✓', '').replace(' ✗', '')
- windows.append((hwnd, title))
- return windows
- def show_selected_window(self):
- """显示选中的窗口(只显示第一个选中的)"""
- selected = self.get_selected_windows()
- if not selected:
- QMessageBox.warning(self, '警告', '请先选择窗口')
- return
- hwnd, title = selected[0]
- try:
- win32gui.ShowWindow(hwnd, win32con.SW_SHOW)
- win32gui.SetForegroundWindow(hwnd)
- if win32gui.IsIconic(hwnd):
- win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
- self.status_label.setText(f'已显示窗口: {title}')
- self.log_text.append(f'👁️ 显示窗口: {title}')
- self.refresh_window_visibility()
- except Exception as e:
- QMessageBox.critical(self, '错误', f'显示窗口失败: {str(e)}')
- def refresh_window_visibility(self):
- """刷新窗口中显示的可见状态"""
- for i in range(self.window_list.count()):
- item = self.window_list.item(i)
- hwnd = item.data(Qt.UserRole)
- title = item.text().replace(' ✓', '').replace(' ✗', '')
- visible = self.is_window_visible(hwnd)
- status_text = " ✓" if visible else " ✗"
- item.setText(title + status_text)
- item.setData(Qt.UserRole + 1, visible)
- self.apply_show_mode()
- def minimize_window(self, hwnd):
- """最小化窗口"""
- try:
- win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE)
- self.signals.status.emit(f'📉 已最小化窗口')
- return True
- except Exception as e:
- self.signals.status.emit(f'❌ 最小化失败: {str(e)}')
- return False
- def click_at_position(self, hwnd, x_offset, y_offset, button='left'):
- """
- 封装的点击函数
- :param hwnd: 窗口句柄
- :param x_offset: X轴偏移量
- :param y_offset: Y轴偏移量
- :param button: 'left' 或 'right'
- """
- # 获取窗口位置
- rect = win32gui.GetWindowRect(hwnd)
- window_x = rect[0]
- window_y = rect[1]
- # 计算点击位置
- click_x = window_x + x_offset
- click_y = window_y + y_offset
- # 移动鼠标到目标位置
- win32api.SetCursorPos((click_x, click_y))
- time.sleep(0.2)
- # 模拟鼠标点击
- if button == 'left':
- win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, click_x, click_y, 0, 0)
- time.sleep(0.1)
- win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, click_x, click_y, 0, 0)
- self.signals.status.emit(f'🖱️ 左键点击: ({click_x}, {click_y})')
- elif button == 'right':
- win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTDOWN, click_x, click_y, 0, 0)
- time.sleep(0.1)
- win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTUP, click_x, click_y, 0, 0)
- self.signals.status.emit(f'🖱️ 右键点击: ({click_x}, {click_y})')
- return click_x, click_y
- def get_pixel_color(self, hwnd, x_offset, y_offset):
- try:
- # 获取窗口位置
- rect = win32gui.GetWindowRect(hwnd)
- window_x = rect[0]
- window_y = rect[1]
- # 计算屏幕坐标
- screen_x = window_x + x_offset
- screen_y = window_y + y_offset
- # 获取桌面DC
- hdc = win32gui.GetDC(0) # 0 表示桌面
- color_ref = win32gui.GetPixel(hdc, screen_x, screen_y)
- win32gui.ReleaseDC(0, hdc)
- red = color_ref & 0xFF
- green = (color_ref >> 8) & 0xFF
- blue = (color_ref >> 16) & 0xFF
- return f"{red:02X}{green:02X}{blue:02X}"
- except Exception as e:
- self.log_text.append(f'❌ 取色失败: {str(e)}')
- return "000000"
- def check_and_click(self, hwnd, check_x, check_y, target_color,
- click_x, click_y, button='left', check_type='equal'):
- """
- 检查颜色并执行点击
- :param hwnd: 窗口句柄
- :param check_x: 检查颜色的X偏移
- :param check_y: 检查颜色的Y偏移
- :param target_color: 目标颜色 (如 "FFFFFF")
- :param click_x: 点击的X偏移
- :param click_y: 点击的Y偏移
- :param button: 'left' 或 'right'
- :param check_type: 'equal' 或 'not_equal'
- :return: 是否执行了点击
- """
- # 获取当前颜色
- current_color = self.get_pixel_color(hwnd, check_x, check_y)
- self.log_text.append(f'🎨 检测位置({check_x},{check_y})颜色: {current_color} (目标: {target_color})')
- should_click = False
- if check_type == 'equal':
- should_click = (current_color == target_color)
- elif check_type == 'not_equal':
- should_click = (current_color != target_color)
- if should_click:
- self.log_text.append(f'✅ 条件满足,执行点击 ({click_x}, {click_y})')
- self.click_at_position(hwnd, click_x, click_y, button)
- return True
- else:
- self.log_text.append(f'❌ 条件不满足,跳过点击')
- return False
- def start_batch(self, mode):
- """开始批量执行 - 支持单独模式
- mode: 'all' | 'function' | 'palace'
- """
- if self.is_running:
- return
- # 获取所有选中的窗口
- selected_windows = self.get_selected_windows()
- if not selected_windows:
- QMessageBox.warning(self, '警告', '请至少选择一个窗口')
- return
- # 根据模式验证
- if mode == 'function':
- if not (self.cb_molong.isChecked() or self.cb_feicui.isChecked() or
- self.cb_jinyan.isChecked() or self.cb_chiyan.isChecked()):
- QMessageBox.warning(self, '警告', '请至少选择一个功能')
- return
- elif mode == 'all':
- if not (self.cb_molong.isChecked() or self.cb_feicui.isChecked() or
- self.cb_jinyan.isChecked() or self.cb_chiyan.isChecked()):
- QMessageBox.warning(self, '警告', '请至少选择一个功能,或单独执行皇宫占位')
- return
- # palace 模式不需要额外验证
- self.is_running = True
- self.should_stop = False
- self.start_btn.setEnabled(False)
- self.func_btn.setEnabled(False)
- self.palace_btn.setEnabled(False)
- self.stop_btn.setEnabled(True)
- # 记录选择的选项
- selected = []
- if self.cb_molong.isChecked(): selected.append('争夺魔龙之心')
- if self.cb_feicui.isChecked(): selected.append('翡翠荣耀旗')
- if self.cb_jinyan.isChecked(): selected.append('金岩霸者棋')
- if self.cb_chiyan.isChecked(): selected.append('炽焰战神棋')
- # 记录皇宫占位选择
- if self.radio_random.isChecked():
- palace_pos = '随机'
- palace_y = 444
- elif self.radio_door.isChecked():
- palace_pos = '门口'
- palace_y = 457
- else: # corner
- palace_pos = '角落'
- palace_y = 470
- # 根据模式显示不同的日志
- if mode == 'all':
- self.log_text.append(f'▶️ 开始【全部执行】,共 {len(selected_windows)} 个窗口')
- self.log_text.append(f'📋 已选功能: {", ".join(selected) if selected else "无"}')
- self.log_text.append(f'🏰 皇宫占位: {palace_pos} (Y+{palace_y})')
- elif mode == 'function':
- self.log_text.append(f'▶️ 开始【单独执行功能选择】,共 {len(selected_windows)} 个窗口')
- self.log_text.append(f'📋 已选功能: {", ".join(selected)}')
- elif mode == 'palace':
- self.log_text.append(f'▶️ 开始【单独执行皇宫占位】,共 {len(selected_windows)} 个窗口')
- self.log_text.append(f'🏰 皇宫占位: {palace_pos} (Y+{palace_y})')
- self.log_text.append('=' * 50)
- # 在新线程中执行
- thread = threading.Thread(target=self.batch_auto_click_worker,
- args=(selected_windows, palace_y, mode))
- thread.daemon = True
- thread.start()
- def batch_auto_click_worker(self, selected_windows, palace_y, mode):
- """批量自动点击工作线程
- mode: 'all' | 'function' | 'palace'
- """
- total = len(selected_windows)
- for idx, (hwnd, title) in enumerate(selected_windows, 1):
- # 检查是否被停止
- if self.should_stop:
- self.signals.status.emit(f'⏹️ 用户停止执行')
- break
- self.signals.status.emit(f'📌 正在处理 [{idx}/{total}] {title}')
- self.signals.progress.emit(idx, total)
- try:
- # 根据模式执行不同的流程
- if mode == 'all':
- # 全部执行:功能 + 皇宫
- self.execute_single_window_all(hwnd, title, palace_y)
- elif mode == 'function':
- # 只执行功能选择
- self.execute_single_window_function(hwnd, title)
- elif mode == 'palace':
- # 只执行皇宫占位
- self.execute_single_window_palace(hwnd, title, palace_y)
- # 等待5秒后最小化窗口
- self.signals.status.emit(f'⏳ 等待5秒后最小化窗口...')
- time.sleep(5)
- # 最小化窗口
- self.signals.status.emit(f'📉 最小化窗口: {title}')
- self.minimize_window(hwnd)
- # 窗口间间隔1秒
- if idx < total and not self.should_stop:
- self.signals.status.emit(f'⏳ 等待1秒后处理下一个窗口...')
- time.sleep(1)
- except Exception as e:
- self.signals.status.emit(f'❌ 处理窗口失败: {str(e)}')
- import traceback
- self.log_text.append(traceback.format_exc())
- # 出错后继续下一个窗口
- if not self.should_stop:
- self.signals.status.emit(f'✅ 批量执行完成!共处理 {idx if idx < total else total} 个窗口')
- self.signals.finished.emit()
- def activate_window(self, hwnd, title):
- """激活窗口"""
- self.signals.status.emit(f'🔄 正在激活窗口: {title}')
- # 显示窗口
- win32gui.ShowWindow(hwnd, win32con.SW_SHOW)
- if win32gui.IsIconic(hwnd):
- win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
- # 激活窗口
- win32gui.SetForegroundWindow(hwnd)
- time.sleep(1) # 等待窗口激活
- def execute_single_window_all(self, hwnd, title, palace_y):
- """执行单个窗口的完整流程(功能+皇宫)"""
- # 1. 激活窗口
- self.activate_window(hwnd, title)
- # 执行功能选择部分
- self._do_function_clicks(hwnd)
- # 执行皇宫占位部分
- self._do_palace_clicks(hwnd, palace_y)
- self.signals.status.emit(f'✅ 窗口 {title} 全部执行完成')
- def execute_single_window_function(self, hwnd, title):
- """只执行功能选择部分"""
- # 1. 激活窗口
- self.activate_window(hwnd, title)
- # 执行功能选择部分
- self._do_function_clicks(hwnd)
- self.signals.status.emit(f'✅ 窗口 {title} 功能选择执行完成')
- def execute_single_window_palace(self, hwnd, title, palace_y):
- """只执行皇宫占位部分"""
- # 1. 激活窗口
- self.activate_window(hwnd, title)
- # 执行皇宫占位部分
- self._do_palace_clicks(hwnd, palace_y)
- self.signals.status.emit(f'✅ 窗口 {title} 皇宫占位执行完成')
- def _do_function_clicks(self, hwnd):
- """功能选择的点击逻辑(可复用)"""
- # 步骤1: 左键点击 X+63, Y+81
- self.click_at_position(hwnd, 29, 41, 'left')
- time.sleep(1)
- self.signals.status.emit(f'📍 步骤1: 左键点击 X+63, Y+81')
- self.click_at_position(hwnd, 63, 81, 'left')
- # 等待5秒
- self.signals.status.emit(f'⏳ 等待5秒...')
- time.sleep(5)
- # 步骤2: 右键点击 X+413, Y+168
- self.signals.status.emit(f'📍 步骤2: 右键点击 X+413, Y+168')
- self.click_at_position(hwnd, 413, 168, 'right')
- # 等待6秒
- self.signals.status.emit(f'⏳ 等待6秒...')
- time.sleep(6)
- # 处理"争夺魔龙之心"
- if self.cb_molong.isChecked():
- self.signals.status.emit(f'📍 检查争夺魔龙之心')
- self.check_and_click(
- hwnd=hwnd,
- check_x=642,
- check_y=439,
- target_color="FFFFFF",
- click_x=642,
- click_y=439,
- button='left',
- check_type='equal'
- )
- else:
- self.signals.status.emit(f'📍 未选争夺魔龙之心,执行相反逻辑')
- self.check_and_click(
- hwnd=hwnd,
- check_x=642,
- check_y=437,
- target_color="FFFFFF",
- click_x=642,
- click_y=437,
- button='left',
- check_type='not_equal'
- )
- # 等待1秒
- time.sleep(0.2)
- # 处理"翡翠荣耀旗"
- if self.cb_feicui.isChecked():
- self.signals.status.emit(f'📍 检查翡翠荣耀旗')
- self.check_and_click(
- hwnd=hwnd,
- check_x=769,
- check_y=386,
- target_color="FFFFFF",
- click_x=769,
- click_y=386,
- button='left',
- check_type='equal'
- )
- else:
- self.signals.status.emit(f'📍 未选翡翠荣耀旗,执行相反逻辑')
- self.check_and_click(
- hwnd=hwnd,
- check_x=769,
- check_y=386,
- target_color="FFFFFF",
- click_x=769,
- click_y=386,
- button='left',
- check_type='not_equal'
- )
- # 等待1秒
- time.sleep(0.2)
- # 处理"金岩霸者棋"
- if self.cb_jinyan.isChecked():
- self.signals.status.emit(f'📍 检查金岩霸者棋')
- self.check_and_click(
- hwnd=hwnd,
- check_x=769,
- check_y=400,
- target_color="FFFFFF",
- click_x=769,
- click_y=400,
- button='left',
- check_type='equal'
- )
- else:
- self.signals.status.emit(f'📍 未选金岩霸者棋,执行相反逻辑')
- self.check_and_click(
- hwnd=hwnd,
- check_x=769,
- check_y=400,
- target_color="FFFFFF",
- click_x=769,
- click_y=400,
- button='left',
- check_type='not_equal'
- )
- # 等待1秒
- time.sleep(0.2)
- # 处理"炽焰战神棋"
- if self.cb_chiyan.isChecked():
- self.signals.status.emit(f'📍 检查炽焰战神棋')
- self.check_and_click(
- hwnd=hwnd,
- check_x=767,
- check_y=415,
- target_color="FFFFFF",
- click_x=767,
- click_y=415,
- button='left',
- check_type='equal'
- )
- else:
- self.signals.status.emit(f'📍 未选炽焰战神棋,执行相反逻辑')
- self.check_and_click(
- hwnd=hwnd,
- check_x=767,
- check_y=415,
- target_color="FFFFFF",
- click_x=767,
- click_y=415,
- button='left',
- check_type='not_equal'
- )
- # 等待1秒
- time.sleep(0.2)
- # 最后点击 X+871, Y+266
- self.signals.status.emit(f'📍 最后点击: X+871, Y+266')
- self.click_at_position(hwnd, 871, 266, 'left')
- def _do_palace_clicks(self, hwnd, palace_y):
- """皇宫占位的点击逻辑(可复用)"""
- time.sleep(0.5)
- self.click_at_position(hwnd, 671, 70, 'right')
- time.sleep(1)
- self.click_at_position(hwnd, 714, 426, 'left')
- time.sleep(1)
- # 皇宫占位点击 - 根据选择的选项
- self.signals.status.emit(f'🏰 皇宫占位点击: X+716, Y+{palace_y}')
- self.click_at_position(hwnd, 716, palace_y, 'left')
- time.sleep(1)
- self.click_at_position(hwnd, 901, 311, 'left')
- time.sleep(1)
- self.click_at_position(hwnd, 1133, 8, 'left')
- def stop_auto_click(self):
- """停止自动点击"""
- self.should_stop = True
- self.is_running = False
- self.start_btn.setEnabled(True)
- self.func_btn.setEnabled(True)
- self.palace_btn.setEnabled(True)
- self.stop_btn.setEnabled(False)
- self.status_label.setText('正在停止...')
- self.log_text.append('⏹️ 正在停止执行...')
- def update_status(self, message):
- """更新状态"""
- self.status_label.setText(message)
- self.log_text.append(message)
- # 滚动到底部
- self.log_text.verticalScrollBar().setValue(
- self.log_text.verticalScrollBar().maximum()
- )
- def update_progress(self, current, total):
- """更新进度"""
- self.status_label.setText(f'进度: {current}/{total}')
- def on_finished(self):
- """完成回调"""
- self.is_running = False
- self.start_btn.setEnabled(True)
- self.func_btn.setEnabled(True)
- self.palace_btn.setEnabled(True)
- self.stop_btn.setEnabled(False)
- self.status_label.setText('就绪')
- self.log_text.append('=' * 50)
- def main():
- app = QApplication(sys.argv)
- app.setStyle('Fusion')
- window = WindowManagerApp()
- window.show()
- sys.exit(app.exec_())
- if __name__ == '__main__':
- main()
|