更新bug
This commit is contained in:
+288
-70
@@ -14,7 +14,9 @@ from pathlib import Path
|
||||
|
||||
from PySide6.QtCore import (
|
||||
QEasingCurve,
|
||||
QEvent,
|
||||
QPoint,
|
||||
QPointF,
|
||||
QPropertyAnimation,
|
||||
QRect,
|
||||
QTimer,
|
||||
@@ -22,7 +24,15 @@ from PySide6.QtCore import (
|
||||
QUrl,
|
||||
Signal,
|
||||
)
|
||||
from PySide6.QtGui import QColor, QDesktopServices, QFont, QIcon, QPainter, QPixmap
|
||||
from PySide6.QtGui import (
|
||||
QColor,
|
||||
QDesktopServices,
|
||||
QFont,
|
||||
QIcon,
|
||||
QPainter,
|
||||
QPen,
|
||||
QPixmap,
|
||||
)
|
||||
from PySide6.QtWidgets import (
|
||||
QAbstractItemView,
|
||||
QApplication,
|
||||
@@ -74,6 +84,15 @@ except ImportError:
|
||||
|
||||
BotThread = _legacy.BotThread
|
||||
LogQueue = _legacy.LogQueue
|
||||
MESSAGE_BATCH_WINDOW_SECONDS = getattr(
|
||||
_legacy, "MESSAGE_BATCH_WINDOW_SECONDS", 20.0
|
||||
)
|
||||
MESSAGE_BATCH_WINDOW_MIN_SECONDS = int(getattr(
|
||||
_legacy, "MESSAGE_BATCH_WINDOW_MIN_SECONDS", 1.0
|
||||
))
|
||||
MESSAGE_BATCH_WINDOW_MAX_SECONDS = int(getattr(
|
||||
_legacy, "MESSAGE_BATCH_WINDOW_MAX_SECONDS", 120.0
|
||||
))
|
||||
SCRIPT_DIR = application_data_dir()
|
||||
APP_SETTINGS_FILE = SCRIPT_DIR / "app_settings.json"
|
||||
CUSTOMER_SERVICE_URL = "http://kf.zhenyangtang.com.cn/"
|
||||
@@ -307,7 +326,7 @@ def _shadow(widget: QWidget, blur: int = 28, y: int = 8, alpha: int = 24) -> Non
|
||||
effect = QGraphicsDropShadowEffect(widget)
|
||||
effect.setBlurRadius(blur)
|
||||
effect.setOffset(0, y)
|
||||
effect.setColor(QColor(22, 58, 43, alpha))
|
||||
effect.setColor(QColor(45, 52, 49, alpha))
|
||||
widget.setGraphicsEffect(effect)
|
||||
|
||||
|
||||
@@ -593,7 +612,11 @@ class SettingsPage(QScrollArea):
|
||||
layout = QVBoxLayout(root)
|
||||
layout.setContentsMargins(28, 25, 28, 28)
|
||||
layout.setSpacing(18)
|
||||
layout.addLayout(_page_header("03", "通用设置", "修改后自动保存,并在下次监听时生效。"))
|
||||
layout.addLayout(_page_header(
|
||||
"03",
|
||||
"通用设置",
|
||||
"修改后自动保存;连续消息合并等待从下一轮回复生效。",
|
||||
))
|
||||
|
||||
runtime, runtime_layout = _card(
|
||||
"运行参数", "参数直接参与企业微信监听,不是静态展示。"
|
||||
@@ -601,12 +624,13 @@ class SettingsPage(QScrollArea):
|
||||
runtime_grid = QGridLayout()
|
||||
runtime_grid.setHorizontalSpacing(18)
|
||||
runtime_grid.setVerticalSpacing(8)
|
||||
runtime_grid.addWidget(_label("固定回复(关闭 AI 时使用)", "CardSubtitle"), 0, 0, 1, 2)
|
||||
runtime_grid.addWidget(_label("固定回复(关闭 AI 时使用)", "CardSubtitle"), 0, 0, 1, 3)
|
||||
self.reply = QLineEdit(str(settings["auto_reply_text"]))
|
||||
self.reply.setPlaceholderText("请输入固定回复")
|
||||
runtime_grid.addWidget(self.reply, 1, 0, 1, 2)
|
||||
runtime_grid.addWidget(self.reply, 1, 0, 1, 3)
|
||||
runtime_grid.addWidget(_label("轮询间隔(秒)", "CardSubtitle"), 2, 0)
|
||||
runtime_grid.addWidget(_label("鼠标静止等待(秒)", "CardSubtitle"), 2, 1)
|
||||
runtime_grid.addWidget(_label("连续消息合并等待(秒)", "CardSubtitle"), 2, 2)
|
||||
self.poll = QDoubleSpinBox()
|
||||
self.poll.setRange(0.2, 300.0)
|
||||
self.poll.setDecimals(1)
|
||||
@@ -615,10 +639,33 @@ class SettingsPage(QScrollArea):
|
||||
self.idle_seconds.setRange(0.0, 3600.0)
|
||||
self.idle_seconds.setDecimals(1)
|
||||
self.idle_seconds.setValue(float(settings["mouse_idle_seconds"]))
|
||||
self.batch_window = QDoubleSpinBox()
|
||||
self.batch_window.setRange(
|
||||
float(MESSAGE_BATCH_WINDOW_MIN_SECONDS),
|
||||
float(MESSAGE_BATCH_WINDOW_MAX_SECONDS),
|
||||
)
|
||||
self.batch_window.setDecimals(1)
|
||||
self.batch_window.setSingleStep(1.0)
|
||||
self.batch_window.setValue(float(settings["message_batch_window_seconds"]))
|
||||
self.batch_window.setToolTip(
|
||||
"同一客户连续发送多条消息时,等待后合并为一次回复;默认 20 秒。"
|
||||
)
|
||||
runtime_grid.addWidget(self.poll, 3, 0)
|
||||
runtime_grid.addWidget(self.idle_seconds, 3, 1)
|
||||
runtime_grid.addWidget(self.batch_window, 3, 2)
|
||||
runtime_grid.setColumnStretch(0, 1)
|
||||
runtime_grid.setColumnStretch(1, 1)
|
||||
runtime_grid.setColumnStretch(2, 1)
|
||||
runtime_grid.addWidget(
|
||||
_label(
|
||||
"同一客户的连续消息会在该窗口内合并为一次回复(1–120 秒,默认 20 秒)。",
|
||||
"CardSubtitle",
|
||||
),
|
||||
4,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
)
|
||||
runtime_layout.addLayout(runtime_grid)
|
||||
self.save_status = _label("已加载保存配置", "SuccessText")
|
||||
self.save_status.setAlignment(Qt.AlignRight)
|
||||
@@ -665,6 +712,7 @@ class SettingsPage(QScrollArea):
|
||||
self.reply.textChanged.connect(self._schedule_save)
|
||||
self.poll.valueChanged.connect(self._schedule_save)
|
||||
self.idle_seconds.valueChanged.connect(self._schedule_save)
|
||||
self.batch_window.valueChanged.connect(self._schedule_save)
|
||||
self.mouse_idle.toggled.connect(self._schedule_save)
|
||||
|
||||
def values(self) -> dict:
|
||||
@@ -673,6 +721,7 @@ class SettingsPage(QScrollArea):
|
||||
"poll_interval": self.poll.value(),
|
||||
"mouse_idle_enabled": self.mouse_idle.isChecked(),
|
||||
"mouse_idle_seconds": self.idle_seconds.value(),
|
||||
"message_batch_window_seconds": float(self.batch_window.value()),
|
||||
}
|
||||
|
||||
def _schedule_save(self, *_args) -> None:
|
||||
@@ -1275,6 +1324,9 @@ class PersonaPage(QScrollArea):
|
||||
|
||||
result = backend_client.sync_cloud_config()
|
||||
message = str(result.get("message") or "云端配置同步完成")
|
||||
diagnostics = [str(item) for item in result.get("diagnostics") or []]
|
||||
if diagnostics:
|
||||
message += "\n" + "\n".join(diagnostics)
|
||||
except Exception as exc:
|
||||
self.cloudSyncFinished.emit(False, str(exc))
|
||||
else:
|
||||
@@ -1287,7 +1339,7 @@ class PersonaPage(QScrollArea):
|
||||
self._cloud_sync_running = False
|
||||
if ok:
|
||||
self.reload_from_ai_config()
|
||||
if not silent or not ok:
|
||||
if not silent or not ok or "[开发模式]" in message:
|
||||
self.saved.emit(ok, message if ok else f"云端配置同步失败:{message}")
|
||||
|
||||
def reload_from_ai_config(self) -> None:
|
||||
@@ -1354,6 +1406,62 @@ class LogPage(QWidget):
|
||||
self.editor.clear()
|
||||
|
||||
|
||||
class CapsuleActionButton(QPushButton):
|
||||
"""不依赖字体字形的胶囊浮窗矢量操作按钮。"""
|
||||
|
||||
def __init__(self, icon_kind: str, parent: QWidget | None = None):
|
||||
super().__init__(parent)
|
||||
self.icon_kind = icon_kind
|
||||
self.setCursor(Qt.PointingHandCursor)
|
||||
self.setFixedSize(30, 34)
|
||||
self.setFocusPolicy(Qt.NoFocus)
|
||||
self.setStyleSheet(
|
||||
"QPushButton{background:transparent;border:none;border-radius:9px;}"
|
||||
"QPushButton:hover{background:#F1F5F3;}"
|
||||
"QPushButton:pressed{background:#E4ECE8;}"
|
||||
)
|
||||
|
||||
def paintEvent(self, event) -> None:
|
||||
super().paintEvent(event)
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
||||
color = "#006C51" if self.underMouse() or self.isDown() else "#5A5F5D"
|
||||
pen = QPen(QColor(color))
|
||||
center_x = self.width() / 2.0
|
||||
center_y = self.height() / 2.0
|
||||
|
||||
if self.icon_kind == "pause":
|
||||
pen.setWidthF(3.0)
|
||||
pen.setCapStyle(Qt.PenCapStyle.FlatCap)
|
||||
painter.setPen(pen)
|
||||
painter.drawLine(
|
||||
QPointF(center_x - 3.5, center_y - 6.0),
|
||||
QPointF(center_x - 3.5, center_y + 6.0),
|
||||
)
|
||||
painter.drawLine(
|
||||
QPointF(center_x + 3.5, center_y - 6.0),
|
||||
QPointF(center_x + 3.5, center_y + 6.0),
|
||||
)
|
||||
else:
|
||||
pen.setWidthF(1.8)
|
||||
pen.setCapStyle(Qt.PenCapStyle.SquareCap)
|
||||
painter.setPen(pen)
|
||||
left, right = center_x - 7.0, center_x + 7.0
|
||||
top, bottom = center_y - 7.0, center_y + 7.0
|
||||
arm = 4.5
|
||||
for start, end in (
|
||||
(QPointF(left, top + arm), QPointF(left, top)),
|
||||
(QPointF(left, top), QPointF(left + arm, top)),
|
||||
(QPointF(right - arm, top), QPointF(right, top)),
|
||||
(QPointF(right, top), QPointF(right, top + arm)),
|
||||
(QPointF(left, bottom - arm), QPointF(left, bottom)),
|
||||
(QPointF(left, bottom), QPointF(left + arm, bottom)),
|
||||
(QPointF(right - arm, bottom), QPointF(right, bottom)),
|
||||
(QPointF(right, bottom), QPointF(right, bottom - arm)),
|
||||
):
|
||||
painter.drawLine(start, end)
|
||||
|
||||
|
||||
class CapsuleWindow(QWidget):
|
||||
expandRequested = Signal()
|
||||
stopRequested = Signal()
|
||||
@@ -1368,76 +1476,108 @@ class CapsuleWindow(QWidget):
|
||||
)
|
||||
self.setAttribute(Qt.WA_TranslucentBackground)
|
||||
self.setAttribute(Qt.WA_ShowWithoutActivating)
|
||||
self.setFixedSize(430, 82)
|
||||
self.setFixedSize(436, 112)
|
||||
self._drag_origin: QPoint | None = None
|
||||
self._window_origin = QPoint()
|
||||
|
||||
outer = QVBoxLayout(self)
|
||||
outer.setContentsMargins(12, 8, 12, 14)
|
||||
panel = QFrame()
|
||||
panel.setObjectName("CapsulePanel")
|
||||
panel.setStyleSheet(
|
||||
"QFrame#CapsulePanel{background:#FFFFFF;border:1px solid #CFE0D8;border-radius:16px;}"
|
||||
outer.setContentsMargins(8, 7, 8, 11)
|
||||
self.panel = QFrame()
|
||||
self.panel.setObjectName("CapsulePanel")
|
||||
self.panel.setAccessibleName("监听状态浮窗")
|
||||
self.panel.setToolTip("按住空白区域可拖动浮窗")
|
||||
self.panel.setCursor(Qt.SizeAllCursor)
|
||||
self.panel.setStyleSheet(
|
||||
"QFrame#CapsulePanel{background:#FEFEFE;"
|
||||
"border:1px solid #EFF2F0;border-radius:47px;}"
|
||||
"QFrame#CapsulePanel QLabel{background:transparent;border:none;}"
|
||||
)
|
||||
_shadow(panel, 28, 7, 38)
|
||||
row = QHBoxLayout(panel)
|
||||
row.setContentsMargins(17, 10, 10, 10)
|
||||
row.setSpacing(11)
|
||||
grip = QLabel("•••")
|
||||
grip.setAlignment(Qt.AlignCenter)
|
||||
grip.setFixedSize(38, 38)
|
||||
grip.setStyleSheet(
|
||||
"QLabel{background:#F0F6F3;border:1px solid #D8E6DF;border-radius:19px;"
|
||||
"color:#789087;font-family:'Cascadia Mono';font-weight:700;}"
|
||||
_shadow(self.panel, 20, 4, 18)
|
||||
row = QHBoxLayout(self.panel)
|
||||
row.setContentsMargins(44, 0, 10, 0)
|
||||
row.setSpacing(0)
|
||||
|
||||
self.status_group = QWidget()
|
||||
self.status_group.setObjectName("CapsuleStatusGroup")
|
||||
self.status_group.setFixedSize(108, 46)
|
||||
status_row = QHBoxLayout(self.status_group)
|
||||
status_row.setContentsMargins(0, 0, 0, 0)
|
||||
status_row.setSpacing(4)
|
||||
|
||||
self.dot = QFrame()
|
||||
self.dot.setObjectName("CapsuleStatusDot")
|
||||
self.dot.setAccessibleName("监听状态")
|
||||
self.dot.setFixedSize(10, 10)
|
||||
self.dot.setStyleSheet(
|
||||
"QFrame#CapsuleStatusDot{background:#7AB2A4;"
|
||||
"border:none;border-radius:5px;}"
|
||||
)
|
||||
row.addWidget(grip)
|
||||
self.dot = QLabel("●")
|
||||
self.dot.setStyleSheet("QLabel{color:#10A57A;font-size:17px;}")
|
||||
row.addWidget(self.dot)
|
||||
status_row.addWidget(self.dot, 0, Qt.AlignVCenter)
|
||||
|
||||
copy = QVBoxLayout()
|
||||
copy.setSpacing(1)
|
||||
copy.setContentsMargins(0, 0, 0, 0)
|
||||
copy.setSpacing(0)
|
||||
copy.addStretch(1)
|
||||
self.status = QLabel("监听中")
|
||||
self.status.setMinimumWidth(94)
|
||||
self.status.setStyleSheet(
|
||||
"QLabel{color:#087A59;font-size:14px;font-weight:700;}"
|
||||
"QLabel{color:#006C51;font-size:13px;font-weight:600;}"
|
||||
)
|
||||
self.hint = QLabel("安全运行中")
|
||||
self.hint.setStyleSheet("QLabel{color:#7B8D84;font-size:11px;}")
|
||||
self.hint.setStyleSheet(
|
||||
"QLabel{color:#666A68;font-size:10px;}"
|
||||
)
|
||||
copy.addWidget(self.status)
|
||||
copy.addWidget(self.hint)
|
||||
row.addLayout(copy, 1)
|
||||
copy.addStretch(1)
|
||||
status_row.addLayout(copy)
|
||||
row.addWidget(self.status_group, 0, Qt.AlignVCenter)
|
||||
row.addStretch(14)
|
||||
|
||||
self.timer = QLabel("00:00:00")
|
||||
self.timer.setObjectName("CapsuleTimer")
|
||||
self.timer.setAccessibleName("监听运行时长")
|
||||
self.timer.setAlignment(Qt.AlignCenter)
|
||||
self.timer.setMinimumWidth(82)
|
||||
self.timer.setFixedSize(84, 28)
|
||||
self.timer.setStyleSheet(
|
||||
"QLabel{background:#F2F7F4;border:1px solid #DCE8E2;border-radius:9px;"
|
||||
"padding:7px 9px;font-family:'Cascadia Mono';font-size:12px;color:#30473D;}"
|
||||
"QLabel#CapsuleTimer{background:#F5F5F5;border:1px solid #E8E8E8;"
|
||||
"border-radius:8px;font-family:'Cascadia Mono','Consolas';"
|
||||
"font-size:10px;font-weight:600;color:#1A1C1C;}"
|
||||
)
|
||||
row.addWidget(self.timer)
|
||||
expand = QPushButton("↗")
|
||||
expand.setToolTip("展开控制台")
|
||||
expand.setCursor(Qt.PointingHandCursor)
|
||||
expand.setFixedSize(34, 34)
|
||||
expand.setStyleSheet(
|
||||
"QPushButton{background:#E7F4EE;color:#087A59;border:1px solid #CFE8DD;"
|
||||
"border-radius:8px;font-size:17px;font-weight:700;}"
|
||||
"QPushButton:hover{background:#D4EFE4;border-color:#AEDCC9;}"
|
||||
"QPushButton:pressed{background:#C5E8D9;}"
|
||||
row.addWidget(self.timer, 0, Qt.AlignVCenter)
|
||||
row.addStretch(39)
|
||||
|
||||
self.divider = QFrame()
|
||||
self.divider.setObjectName("CapsuleDivider")
|
||||
self.divider.setFixedSize(1, 22)
|
||||
self.divider.setStyleSheet(
|
||||
"QFrame#CapsuleDivider{background:#EFEFEF;border:none;}"
|
||||
)
|
||||
expand.clicked.connect(self.expandRequested)
|
||||
row.addWidget(expand)
|
||||
stop = QPushButton("×")
|
||||
stop.setToolTip("停止监听")
|
||||
stop.setCursor(Qt.PointingHandCursor)
|
||||
stop.setFixedSize(34, 34)
|
||||
stop.setStyleSheet(
|
||||
"QPushButton{background:#FBEAEC;color:#C74452;border:1px solid #F2CDD2;"
|
||||
"border-radius:8px;font-size:18px;}"
|
||||
"QPushButton:hover{background:#D85260;color:#FFFFFF;border-color:#D85260;}"
|
||||
)
|
||||
stop.clicked.connect(self.stopRequested)
|
||||
row.addWidget(stop)
|
||||
outer.addWidget(panel)
|
||||
row.addWidget(self.divider, 0, Qt.AlignVCenter)
|
||||
row.addSpacing(5)
|
||||
|
||||
actions = QWidget()
|
||||
actions.setObjectName("CapsuleActions")
|
||||
actions.setFixedSize(62, 34)
|
||||
action_row = QHBoxLayout(actions)
|
||||
action_row.setContentsMargins(0, 0, 0, 0)
|
||||
action_row.setSpacing(2)
|
||||
|
||||
self.stop_button = CapsuleActionButton("pause")
|
||||
self.stop_button.setObjectName("CapsuleStopButton")
|
||||
self.stop_button.setAccessibleName("停止监听")
|
||||
self.stop_button.setToolTip("停止监听")
|
||||
self.stop_button.clicked.connect(self.stopRequested)
|
||||
action_row.addWidget(self.stop_button)
|
||||
|
||||
self.expand_button = CapsuleActionButton("fullscreen")
|
||||
self.expand_button.setObjectName("CapsuleExpandButton")
|
||||
self.expand_button.setAccessibleName("展开控制台")
|
||||
self.expand_button.setToolTip("展开控制台")
|
||||
self.expand_button.clicked.connect(self.expandRequested)
|
||||
action_row.addWidget(self.expand_button)
|
||||
row.addWidget(actions, 0, Qt.AlignVCenter)
|
||||
outer.addWidget(self.panel)
|
||||
|
||||
def show_near(self, window: QWidget) -> None:
|
||||
screen = window.screen() or QApplication.primaryScreen()
|
||||
@@ -1464,12 +1604,12 @@ class CapsuleWindow(QWidget):
|
||||
|
||||
def set_status(self, state: str, text: str) -> None:
|
||||
colors = {
|
||||
"running": "#10A57A",
|
||||
"waiting": "#B67820",
|
||||
"connecting": "#B67820",
|
||||
"stopping": "#B67820",
|
||||
"error": "#D85260",
|
||||
"verification": "#D85260",
|
||||
"running": ("#006C51", "#7AB2A4"),
|
||||
"waiting": ("#9B681D", "#D2A85F"),
|
||||
"connecting": ("#9B681D", "#D2A85F"),
|
||||
"stopping": ("#9B681D", "#D2A85F"),
|
||||
"error": ("#B84552", "#DF8E98"),
|
||||
"verification": ("#B84552", "#DF8E98"),
|
||||
}
|
||||
hints = {
|
||||
"running": "安全运行中",
|
||||
@@ -1480,11 +1620,13 @@ class CapsuleWindow(QWidget):
|
||||
"verification": "请先用手机扫码验证",
|
||||
"stopped": "监听已停止",
|
||||
}
|
||||
color = colors.get(state, "#7B8D84")
|
||||
self.dot.setStyleSheet(f"QLabel{{color:{color};font-size:17px;}}")
|
||||
title_color, dot_color = colors.get(state, ("#66736D", "#A8B2AD"))
|
||||
self.dot.setStyleSheet(
|
||||
f"QFrame#CapsuleStatusDot{{background:{dot_color};border:none;border-radius:5px;}}"
|
||||
)
|
||||
self.status.setText(text)
|
||||
self.status.setStyleSheet(
|
||||
f"QLabel{{color:{color};font-size:14px;font-weight:700;}}"
|
||||
f"QLabel{{color:{title_color};font-size:13px;font-weight:600;}}"
|
||||
)
|
||||
self.hint.setText(hints.get(state, text))
|
||||
|
||||
@@ -1583,6 +1725,7 @@ class MainWindow(QMainWindow):
|
||||
self._stdout_proxy = None
|
||||
self._saved_geometry = None
|
||||
self._was_maximized = False
|
||||
self._capsule_minimize_pending = False
|
||||
self.runtime_settings = self._load_runtime_settings()
|
||||
|
||||
root = QWidget()
|
||||
@@ -1671,6 +1814,7 @@ class MainWindow(QMainWindow):
|
||||
"poll_interval": 2.0,
|
||||
"mouse_idle_enabled": True,
|
||||
"mouse_idle_seconds": 20.0,
|
||||
"message_batch_window_seconds": MESSAGE_BATCH_WINDOW_SECONDS,
|
||||
}
|
||||
|
||||
def _load_runtime_settings(self) -> dict:
|
||||
@@ -1684,6 +1828,11 @@ class MainWindow(QMainWindow):
|
||||
try:
|
||||
settings["poll_interval"] = max(0.2, float(settings["poll_interval"]))
|
||||
settings["mouse_idle_seconds"] = max(0.0, float(settings["mouse_idle_seconds"]))
|
||||
settings["message_batch_window_seconds"] = (
|
||||
_legacy.normalize_message_batch_window_seconds(
|
||||
settings["message_batch_window_seconds"]
|
||||
)
|
||||
)
|
||||
settings["mouse_idle_enabled"] = bool(settings["mouse_idle_enabled"])
|
||||
settings["auto_reply_text"] = (
|
||||
str(settings["auto_reply_text"]).strip()
|
||||
@@ -1706,6 +1855,11 @@ class MainWindow(QMainWindow):
|
||||
self.settings_page.mark_saved(False, f"保存失败:{exc}")
|
||||
self.append_log(f"运行参数保存失败:{exc}", "err")
|
||||
else:
|
||||
thread = getattr(self, "_thread", None)
|
||||
if thread is not None and thread.is_alive():
|
||||
thread.set_message_batch_window_seconds(float(
|
||||
self.runtime_settings["message_batch_window_seconds"]
|
||||
))
|
||||
self.settings_page.mark_saved(True, "已自动保存")
|
||||
|
||||
def show_page(self, index: int) -> None:
|
||||
@@ -1730,6 +1884,9 @@ class MainWindow(QMainWindow):
|
||||
float(self.runtime_settings["poll_interval"]),
|
||||
mouse_idle_enabled=bool(self.runtime_settings["mouse_idle_enabled"]),
|
||||
mouse_idle_seconds=float(self.runtime_settings["mouse_idle_seconds"]),
|
||||
message_batch_window_seconds=float(
|
||||
self.runtime_settings["message_batch_window_seconds"]
|
||||
),
|
||||
)
|
||||
self._running = True
|
||||
self._start_time = time.time()
|
||||
@@ -1762,16 +1919,75 @@ class MainWindow(QMainWindow):
|
||||
self.dashboard_page.set_status(state, text, hint)
|
||||
self.capsule.set_status(state, text)
|
||||
|
||||
def enter_capsule_mode(self) -> None:
|
||||
def changeEvent(self, event) -> None:
|
||||
if (
|
||||
event.type() == QEvent.Type.WindowStateChange
|
||||
and self.isMinimized()
|
||||
and getattr(self, "_running", False)
|
||||
and not self.capsule.isVisible()
|
||||
and not self._capsule_minimize_pending
|
||||
):
|
||||
self._capsule_minimize_pending = True
|
||||
previous_state = event.oldState()
|
||||
saved_geometry = self.saveGeometry()
|
||||
was_maximized = bool(previous_state & Qt.WindowMaximized)
|
||||
portal_was_visible = bool(
|
||||
self.stack.currentIndex() == 0
|
||||
and not self.portal_page.view.isHidden()
|
||||
)
|
||||
QTimer.singleShot(
|
||||
0,
|
||||
lambda: self._enter_capsule_after_minimize(
|
||||
saved_geometry,
|
||||
was_maximized,
|
||||
portal_was_visible,
|
||||
),
|
||||
)
|
||||
super().changeEvent(event)
|
||||
|
||||
def _enter_capsule_after_minimize(
|
||||
self,
|
||||
saved_geometry,
|
||||
was_maximized: bool,
|
||||
portal_was_visible: bool,
|
||||
) -> None:
|
||||
self._capsule_minimize_pending = False
|
||||
if (
|
||||
not self._running
|
||||
or not self.isMinimized()
|
||||
or self.capsule.isVisible()
|
||||
):
|
||||
return
|
||||
self.enter_capsule_mode(
|
||||
saved_geometry=saved_geometry,
|
||||
was_maximized=was_maximized,
|
||||
portal_was_visible=portal_was_visible,
|
||||
)
|
||||
|
||||
def enter_capsule_mode(
|
||||
self,
|
||||
*,
|
||||
saved_geometry=None,
|
||||
was_maximized: bool | None = None,
|
||||
portal_was_visible: bool | None = None,
|
||||
) -> None:
|
||||
if self.capsule.isVisible():
|
||||
return
|
||||
self._saved_geometry = self.saveGeometry()
|
||||
self._was_maximized = self.isMaximized()
|
||||
self._saved_geometry = (
|
||||
self.saveGeometry() if saved_geometry is None else saved_geometry
|
||||
)
|
||||
self._was_maximized = (
|
||||
self.isMaximized() if was_maximized is None else bool(was_maximized)
|
||||
)
|
||||
# QWebEngineView owns a native Chromium surface. Hiding the top-level
|
||||
# window while that surface is still visible can leave a large black DWM
|
||||
# window above WeCom on some Windows/GPU combinations. Tear down the
|
||||
# visible surface first and let Qt flush that state before hiding us.
|
||||
self._portal_was_visible = self.portal_page.view.isVisible()
|
||||
self._portal_was_visible = (
|
||||
self.portal_page.view.isVisible()
|
||||
if portal_was_visible is None
|
||||
else bool(portal_was_visible)
|
||||
)
|
||||
if self._portal_was_visible:
|
||||
self.portal_page.view.hide()
|
||||
QApplication.processEvents()
|
||||
@@ -2032,6 +2248,8 @@ def main() -> None:
|
||||
return
|
||||
|
||||
window = MainWindow()
|
||||
for diagnostic in startup_result.get("diagnostics") or []:
|
||||
window.append_log(str(diagnostic), "notify")
|
||||
console_shutdown = install_console_shutdown_handler(app, window)
|
||||
window.show()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user