Files
wow/wow.md
2026-03-18 09:04:37 +08:00

8.1 KiB

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()