262 lines
8.1 KiB
Markdown
262 lines
8.1 KiB
Markdown
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() |