niumasoftware/ui/wechat_multi.py

235 lines
7.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
微信多开对话框
"""
import os
import subprocess
import tempfile
from PyQt6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
QPushButton, QSpinBox, QFileDialog, QTextEdit, QWidget
)
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QIcon
import ui.theme as theme
from db import database
_DB_KEY_PATH = "wechat_multi_path"
_DB_KEY_COUNT = "wechat_multi_count"
_DEFAULT_PATHS = [
r"D:\Softwares\Tencent\Weixin\Weixin.exe",
r"C:\Program Files\Tencent\WeChat\WeChat.exe",
r"C:\Program Files (x86)\Tencent\WeChat\WeChat.exe",
]
_USAGE = """使用说明:
1. 填写微信 Weixin.exe 的完整路径。
点击右侧「浏览」按钮可以直接选择文件。
2. 设置多开数量(建议不超过 5 个,
数量过多可能导致电脑卡顿)。
3. 点击「开始多开」,程序会依次启动
对应数量的微信进程。
4. 每个微信实例需要单独登录账号。
注意:微信官方不支持多开,使用本功能
请自行承担相关风险。
"""
def _detect_wechat() -> str:
"""尝试自动检测微信路径。"""
saved = database.get_setting(_DB_KEY_PATH, "")
if saved and os.path.isfile(saved):
return saved
for p in _DEFAULT_PATHS:
if os.path.isfile(p):
return p
return ""
class WechatMultiDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("微信多开")
self.setMinimumWidth(460)
self.setWindowFlags(
Qt.WindowType.Dialog | Qt.WindowType.FramelessWindowHint
)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self._drag_pos = None
self._build()
self._apply_theme()
def _build(self):
saved_path = _detect_wechat()
saved_count = int(database.get_setting(_DB_KEY_COUNT, "2"))
root = QVBoxLayout(self)
root.setContentsMargins(0, 0, 0, 0)
self._card = QWidget()
self._card.setObjectName("wechat_card")
card_layout = QVBoxLayout(self._card)
card_layout.setContentsMargins(20, 16, 20, 16)
card_layout.setSpacing(12)
# 标题栏
title_row = QHBoxLayout()
lbl_title = QLabel("微信多开")
lbl_title.setStyleSheet("font-size:14px; font-weight:bold;")
close_btn = QPushButton("")
close_btn.setFixedSize(24, 24)
close_btn.setStyleSheet("border:none; background:transparent; font-size:13px;")
close_btn.clicked.connect(self.reject)
title_row.addWidget(lbl_title)
title_row.addStretch()
title_row.addWidget(close_btn)
card_layout.addLayout(title_row)
# 路径
card_layout.addWidget(QLabel("微信程序路径Weixin.exe"))
path_row = QHBoxLayout()
self._path_edit = QLineEdit(saved_path)
self._path_edit.setPlaceholderText("请输入或浏览 Weixin.exe 路径")
browse_btn = QPushButton("浏览")
browse_btn.setFixedWidth(56)
browse_btn.clicked.connect(self._browse)
path_row.addWidget(self._path_edit)
path_row.addWidget(browse_btn)
card_layout.addLayout(path_row)
# 数量
count_row = QHBoxLayout()
count_row.addWidget(QLabel("多开数量:"))
self._spin = QSpinBox()
self._spin.setRange(2, 9)
self._spin.setValue(saved_count)
self._spin.setFixedWidth(70)
count_row.addWidget(self._spin)
count_row.addStretch()
card_layout.addLayout(count_row)
# 使用说明
usage = QTextEdit()
usage.setReadOnly(True)
usage.setPlainText(_USAGE)
usage.setFixedHeight(150)
usage.setStyleSheet("font-size:11px; border-radius:6px;")
card_layout.addWidget(usage)
# 按钮行
btn_row = QHBoxLayout()
btn_row.addStretch()
self._start_btn = QPushButton("开始多开")
self._start_btn.setFixedHeight(32)
self._start_btn.setMinimumWidth(90)
self._start_btn.clicked.connect(self._start)
cancel_btn = QPushButton("取消")
cancel_btn.setFixedHeight(32)
cancel_btn.setMinimumWidth(70)
cancel_btn.clicked.connect(self.reject)
btn_row.addWidget(self._start_btn)
btn_row.addWidget(cancel_btn)
card_layout.addLayout(btn_row)
root.addWidget(self._card)
def _apply_theme(self):
t = theme.current()
is_dark = theme.name() == "dark"
self._card.setStyleSheet(f"""
QWidget#wechat_card {{
background: {t['panel_bg']};
border-radius: 10px;
border: 1px solid {t['panel_border']};
}}
QLabel {{ color: {t['search_color']}; background: transparent; }}
QLineEdit {{
background: {t['search_bg']};
border: 1px solid {t['search_border']};
border-radius: 5px;
color: {t['search_color']};
padding: 4px 8px;
}}
QSpinBox {{
background: {t['search_bg']};
border: 1px solid {t['search_border']};
border-radius: 5px;
color: {t['search_color']};
padding: 2px 4px;
}}
QTextEdit {{
background: {t['search_bg']};
color: {t['search_color']};
border: 1px solid {t['search_border']};
}}
QPushButton {{
border: 1px solid {t['search_border']};
border-radius: 5px;
color: {t['btn_color']};
background: {t['search_bg']};
font-size: 12px;
padding: 0 10px;
}}
QPushButton:hover {{ background: {t['header_hover']}; }}
""")
def _browse(self):
path, _ = QFileDialog.getOpenFileName(
self, "选择 Weixin.exe", "", "可执行文件 (*.exe)"
)
if path:
self._path_edit.setText(path)
def _start(self):
exe = self._path_edit.text().strip()
count = self._spin.value()
if not exe:
self._path_edit.setPlaceholderText("⚠ 请先填写路径")
return
if not os.path.isfile(exe):
self._path_edit.setStyleSheet(
self._path_edit.styleSheet() + "border-color: red;"
)
return
# 保存设置
database.set_setting(_DB_KEY_PATH, exe)
database.set_setting(_DB_KEY_COUNT, str(count))
# 写临时 bat连续 start 多次
lines = ["@echo off"]
for _ in range(count):
lines.append(f'start "" "{exe}"')
bat_content = "\r\n".join(lines) + "\r\n"
tmp = tempfile.NamedTemporaryFile(
delete=False, suffix=".bat", mode="w", encoding="gbk"
)
tmp.write(bat_content)
tmp.close()
subprocess.Popen(
["cmd", "/c", tmp.name],
creationflags=subprocess.CREATE_NO_WINDOW
)
self.accept()
# 无边框拖动
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self._drag_pos = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
def mouseMoveEvent(self, event):
if self._drag_pos and event.buttons() & Qt.MouseButton.LeftButton:
self.move(event.globalPosition().toPoint() - self._drag_pos)
def mouseReleaseEvent(self, event):
self._drag_pos = None