import sys from PyQt6.QtWidgets import * from PyQt6.QtCore import QThread, pyqtSignal, Qt from PyQt6.QtGui import QIcon import win32gui, win32api, win32con import time, random, json, os class KeyConfigWidget(QWidget): """单个按键配置组件""" def __init__(self, key_name, default_config): super().__init__() self.key_name = key_name self.config = default_config or {'enabled': False, 'interval': 10, 'jitter': 2} self.init_ui() def init_ui(self): layout = QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) # 启用复选框 self.checkbox = QCheckBox(f"按键 [{self.key_name}]") self.checkbox.setChecked(self.config['enabled']) layout.addWidget(self.checkbox) # 间隔 layout.addWidget(QLabel("间隔:")) self.interval_spin = QSpinBox() self.interval_spin.setRange(1, 60) self.interval_spin.setValue(self.config['interval']) layout.addWidget(self.interval_spin) layout.addWidget(QLabel("秒")) # 随机延迟 layout.addWidget(QLabel("随机:")) self.jitter_spin = QSpinBox() self.jitter_spin.setRange(0, 10) self.jitter_spin.setValue(self.config['jitter']) layout.addWidget(self.jitter_spin) layout.addWidget(QLabel("秒")) layout.addStretch() # 连接信号 self.checkbox.toggled.connect(self.on_toggle) self.on_toggle() def on_toggle(self): enabled = self.checkbox.isChecked() self.interval_spin.setEnabled(enabled) self.jitter_spin.setEnabled(enabled) def get_config(self): return { 'enabled': self.checkbox.isChecked(), 'interval': self.interval_spin.value(), 'jitter': self.jitter_spin.value() } class KeyWorker(QThread): """按键工作线程""" log_signal = pyqtSignal(str) def __init__(self, hwnd, key_name, config): super().__init__() self.hwnd = hwnd self.key_name = key_name self.config = config self.running = True def run(self): while self.running: if self.config['enabled']: self.press_key() # 随机间隔 sleep_time = self.config['interval'] + random.uniform(-self.config['jitter'], self.config['jitter']) sleep_time = max(1, sleep_time) for _ in range(int(sleep_time * 2)): if not self.running: break time.sleep(0.5) def press_key(self): """发送按键""" if not self.hwnd: return key_map = { '空格(跳跃)': win32con.VK_SPACE, '1': 0x31, '2': 0x32, '3': 0x33, '4': 0x34, '5': 0x35 } vk = key_map.get(self.key_name) if vk: try: win32api.PostMessage(self.hwnd, win32con.WM_KEYDOWN, vk, 0) time.sleep(random.uniform(0.05, 0.15)) win32api.PostMessage(self.hwnd, win32con.WM_KEYUP, vk, 0) key_type = "跳跃" if self.key_name == '空格(跳跃)' else f"技能 {self.key_name}" self.log_signal.emit(f"➡️ 执行 {key_type}") except Exception as e: self.log_signal.emit(f"❌ 按键 {self.key_name} 失败: {e}") class WoWMultiKeyGUI(QMainWindow): def __init__(self): super().__init__() self.config_file = "wow_multikey_qt.json" self.config = self.load_config() self.workers = {} self.hwnd = None self.init_ui() self.find_wow_window() def init_ui(self): self.setWindowTitle("WoW多键控制器") self.setGeometry(100, 100, 700, 550) central = QWidget() self.setCentralWidget(central) layout = QVBoxLayout(central) # 窗口状态 self.window_label = QLabel("正在查找窗口...") layout.addWidget(self.window_label) self.refresh_btn = QPushButton("刷新窗口") self.refresh_btn.clicked.connect(self.find_wow_window) layout.addWidget(self.refresh_btn) # 按键配置 self.key_widgets = {} keys = ['空格(跳跃)', '1', '2', '3', '4', '5'] scroll = QScrollArea() scroll.setWidgetResizable(True) scroll_widget = QWidget() scroll_layout = QVBoxLayout(scroll_widget) for key in keys: widget = KeyConfigWidget(key, self.config.get(key)) scroll_layout.addWidget(widget) self.key_widgets[key] = widget scroll_layout.addStretch() scroll.setWidget(scroll_widget) layout.addWidget(scroll) # 按钮 btn_layout = QHBoxLayout() self.start_btn = QPushButton("🚀 启动") self.start_btn.clicked.connect(self.start_all) self.stop_btn = QPushButton("⏹ 停止") self.stop_btn.clicked.connect(self.stop_all) self.stop_btn.setEnabled(False) btn_layout.addWidget(self.start_btn) btn_layout.addWidget(self.stop_btn) btn_layout.addStretch() layout.addLayout(btn_layout) # 日志 self.log_text = QTextEdit() self.log_text.setReadOnly(True) self.log_text.setMaximumHeight(150) layout.addWidget(self.log_text) # 状态栏 self.status_bar = self.statusBar() self.status_bar.showMessage("就绪") def load_config(self): if os.path.exists(self.config_file): try: with open(self.config_file, "r") as f: return json.load(f) except: pass return {} def save_config(self): config = {key: widget.get_config() for key, widget in self.key_widgets.items()} with open(self.config_file, "w") as f: json.dump(config, f, indent=2) def find_wow_window(self): titles = ["魔兽世界", "World of Warcraft"] for title in titles: self.hwnd = win32gui.FindWindow(None, title) if self.hwnd: self.window_label.setText(f"✅ 已找到: {title}") self.log(f"成功绑定窗口: {title}") return self.window_label.setText("❌ 未找到游戏窗口") self.hwnd = None def log(self, message): self.log_text.append(f"[{time.strftime('%H:%M:%S')}] {message}") def start_all(self): if not self.hwnd: QMessageBox.warning(self, "警告", "未找到游戏窗口!") return any_enabled = any(w.get_config()['enabled'] for w in self.key_widgets.values()) if not any_enabled: QMessageBox.warning(self, "警告", "请至少启用一个按键!") return self.save_config() # 启动工作线程 for key_name, widget in self.key_widgets.items(): config = widget.get_config() if config['enabled']: worker = KeyWorker(self.hwnd, key_name, config) worker.log_signal.connect(self.log) worker.start() self.workers[key_name] = worker self.start_btn.setEnabled(False) self.stop_btn.setEnabled(True) self.status_bar.showMessage("🟢 运行中") self.log("🎮 自动按键已启动") def stop_all(self): for worker in self.workers.values(): worker.running = False worker.wait() self.workers.clear() self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) self.status_bar.showMessage("⏹️ 已停止") self.log("🛑 所有按键已停止") def closeEvent(self, event): if self.workers: reply = QMessageBox.question(self, "退出", "确认退出?") if reply == QMessageBox.StandardButton.Yes: self.stop_all() event.accept() else: event.ignore() else: event.accept() def main(): app = QApplication(sys.argv) window = WoWMultiKeyGUI() window.show() sys.exit(app.exec()) if __name__ == "__main__": main()