侧边栏壁纸
博主头像
guolin

这个站长还在上小学,平时没多少时间上网,并不是懒,是妈妈不让……

  • 累计撰写 22 篇文章
  • 累计收到 12 条评论

shuidown

2026-6-19 / 0 评论 / 174 阅读

"""
Windows智能关机助手 - 重构版
保持原界面布局,改进代码质量、错误处理和稳定性
功能:关机/重启/睡眠选择、倒计时/定时执行、无人操作自动关机/睡眠、系统托盘后台运行
作者:郭麟
版本:2.0
"""

import sys
import os
import time
import json
import logging
import threading
import ctypes
import ctypes.wintypes
import platform
from datetime import datetime, timedelta
from typing import Optional
from enum import Enum

from PyQt5.QtWidgets import
from PyQt5.QtCore import

from PyQt5.QtGui import *

========== 日志配置 ==========

def setup_logging():
"""配置日志系统"""
log_dir = os.path.join(os.path.expanduser("~"), "AppData", "Local", "ShutdownHelper")
os.makedirs(log_dir, exist_ok=True)
log_file = os.path.join(log_dir, "shutdown_helper.log")

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(log_file, encoding='utf-8'),
        logging.StreamHandler()
    ]
)
return logging.getLogger(__name__)

logger = setup_logging()

========== 配置管理 ==========

class Config:
"""配置管理"""
CONFIG_FILE = os.path.join(os.path.expanduser("~"), "AppData", "Local", "ShutdownHelper", "config.json")

DEFAULTS = {
    "idle_threshold": 300,
    "idle_action": "sleep",  # "shutdown" 或 "sleep"
    "default_hours": 0,
    "default_minutes": 30,
    "default_seconds": 0
}

@classmethod
def load(cls):
    try:
        if os.path.exists(cls.CONFIG_FILE):
            with open(cls.CONFIG_FILE, 'r', encoding='utf-8') as f:
                config = json.load(f)
                for key, value in cls.DEFAULTS.items():
                    if key not in config:
                        config[key] = value
                return config
    except Exception as e:
        logger.error(f"加载配置失败: {e}")
    return cls.DEFAULTS.copy()

@classmethod
def save(cls, config):
    try:
        os.makedirs(os.path.dirname(cls.CONFIG_FILE), exist_ok=True)
        with open(cls.CONFIG_FILE, 'w', encoding='utf-8') as f:
            json.dump(config, f, indent=2, ensure_ascii=False)
        return True
    except Exception as e:
        logger.error(f"保存配置失败: {e}")
        return False

========== Windows API ==========

class WinAPI:
"""Windows API 封装"""

@staticmethod
def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

@staticmethod
def request_admin():
    if not WinAPI.is_admin():
        args = ' '.join(f'"{arg}"' if ' ' in arg else arg for arg in sys.argv[1:])
        ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, args, None, 1)
        sys.exit()

@staticmethod
def get_idle_seconds():
    try:
        class LASTINPUTINFO(ctypes.Structure):
            _fields_ = [("cbSize", ctypes.c_uint), ("dwTime", ctypes.c_uint)]

        lii = LASTINPUTINFO()
        lii.cbSize = ctypes.sizeof(LASTINPUTINFO)
        if ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lii)):
            tick = ctypes.windll.kernel32.GetTickCount()
            return (tick - lii.dwTime) // 1000
    except Exception as e:
        logger.error(f"获取空闲时间失败: {e}")
    return 0

@staticmethod
def shutdown():
    try:
        ctypes.windll.advapi32.SetProcessShutdownParameters(0x100, 0)
        return ctypes.windll.user32.ExitWindowsEx(0x00000001, 0) != 0
    except Exception as e:
        logger.error(f"关机失败: {e}")
        return False

@staticmethod
def restart():
    try:
        ctypes.windll.advapi32.SetProcessShutdownParameters(0x100, 0)
        return ctypes.windll.user32.ExitWindowsEx(0x00000002, 0) != 0
    except Exception as e:
        logger.error(f"重启失败: {e}")
        return False

@staticmethod
def sleep():
    try:
        return ctypes.windll.powrprof.SetSuspendState(0, 1, 0) != 0
    except Exception as e:
        logger.error(f"睡眠失败: {e}")
        return False

@staticmethod
def cancel_shutdown():
    try:
        os.system("shutdown /a 2>nul")
        return True
    except:
        return False

========== 空闲监控线程 ==========

class IdleMonitor(QThread):
"""监控系统空闲时间"""
idle_timeout = pyqtSignal()

def __init__(self, threshold_seconds):
    super().__init__()
    self.threshold = threshold_seconds
    self.is_running = True
    self._consecutive_idle = 0

def run(self):
    while self.is_running:
        idle_sec = WinAPI.get_idle_seconds()

        if idle_sec >= self.threshold:
            self._consecutive_idle += 1
            if self._consecutive_idle >= 3:
                self.idle_timeout.emit()
                self._consecutive_idle = 0
                self.sleep(30)
                continue
        else:
            self._consecutive_idle = 0

        # 动态调整轮询间隔
        if idle_sec > self.threshold * 0.7:
            self.sleep(10)
        else:
            self.sleep(5)

def stop(self):
    self.is_running = False
    self.quit()
    self.wait()

========== 倒计时线程 ==========

class ShutdownTimer(QThread):
"""倒计时线程"""
countdown_signal = pyqtSignal(str, int, str) # 时间文本, 进度, 描述
shutdown_signal = pyqtSignal(str) # 操作类型

def __init__(self, seconds, operation):
    super().__init__()
    self.seconds = seconds
    self.total_seconds = seconds
    self.operation = operation
    self.is_running = True
    self.is_paused = False
    self.pause_cond = threading.Condition()

def run(self):
    while self.is_running and self.seconds > 0:
        if not self.is_paused:
            remaining = self.seconds
            hours = remaining // 3600
            minutes = (remaining % 3600) // 60
            seconds = remaining % 60

            countdown_text = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
            progress = int((self.total_seconds - self.seconds) / self.total_seconds * 100)

            if hours > 0:
                time_desc = f"{hours}小时{minutes}分"
            elif minutes > 0:
                time_desc = f"{minutes}分钟{seconds}秒"
            else:
                time_desc = f"{seconds}秒"

            self.countdown_signal.emit(countdown_text, progress, time_desc)
            self.seconds -= 1
            time.sleep(1)
        else:
            with self.pause_cond:
                self.pause_cond.wait()

    if self.is_running and self.seconds <= 0:
        self.shutdown_signal.emit(self.operation)

def pause(self):
    self.is_paused = True

def resume(self):
    self.is_paused = False
    with self.pause_cond:
        self.pause_cond.notify()

def stop(self):
    self.is_running = False
    self.is_paused = False
    with self.pause_cond:
        self.pause_cond.notify()
    self.quit()
    self.wait()

========== 主窗口 ==========

class WindowsShutdownApp(QMainWindow):
def init(self):
super().init()

    # 加载配置
    self.config = Config.load()

    # 状态变量
    self.shutdown_timer = None
    self.current_operation = "关机"
    self.is_minimized_mode = False
    self.original_geometry = None

    # 空闲监控
    self.idle_monitor = None
    self.auto_idle_enabled = False
    self.idle_threshold = self.config.get("idle_threshold", 300)
    self.idle_action = self.config.get("idle_action", "sleep")  # "shutdown" 或 "sleep"

    # 系统托盘
    self.tray_icon = None

    # 初始化UI
    self.init_ui()
    self.setup_tray_icon()

    # 加载配置到控件
    self.load_config()

    # 日志
    self.logger = logger

def init_ui(self):
    """初始化UI - 保持原界面布局"""
    self.setWindowTitle("智能关机助手")
    self.setFixedSize(500, 820)  # 稍微加高以容纳新控件

    central_widget = QWidget()
    self.setCentralWidget(central_widget)
    main_layout = QVBoxLayout(central_widget)
    main_layout.setSpacing(8)
    main_layout.setContentsMargins(25, 15, 25, 15)
    central_widget.setStyleSheet("background-color: #f5f5f5;")

    # ========== 标题 ==========
    title_label = QLabel("🖥️ 智能关机助手")
    title_label.setStyleSheet("font-size: 22px; font-weight: bold; color: #333; padding: 5px 0;")
    title_label.setAlignment(Qt.AlignCenter)
    main_layout.addWidget(title_label)

    # ========== 倒计时显示 ==========
    self.countdown_label = QLabel("00:00:00")
    self.countdown_label.setStyleSheet("""
        font-size: 56px; font-weight: bold; color: #2196F3;
        font-family: 'Consolas', monospace; padding: 20px;
        background-color: white; border-radius: 15px; border: 2px solid #e0e0e0;
    """)
    self.countdown_label.setAlignment(Qt.AlignCenter)
    main_layout.addWidget(self.countdown_label)

    # ========== 进度条 ==========
    self.progress_bar = QProgressBar()
    self.progress_bar.setRange(0, 100)
    self.progress_bar.setValue(0)
    self.progress_bar.setTextVisible(False)
    self.progress_bar.setFixedHeight(8)
    self.progress_bar.setStyleSheet("""
        QProgressBar { border: none; background-color: #e0e0e0; border-radius: 4px; }
        QProgressBar::chunk { background-color: #2196F3; border-radius: 4px; }
    """)
    main_layout.addWidget(self.progress_bar)

    # ========== 操作选择分组框 ==========
    op_group = QGroupBox("选择操作")
    op_group.setStyleSheet("""
        QGroupBox { font-size: 13px; font-weight: bold; color: #555; border: 1px solid #ddd;
            border-radius: 8px; margin-top: 5px; padding-top: 12px; background-color: white; }
        QGroupBox::title { subcontrol-origin: margin; left: 15px; padding: 0 8px; }
    """)
    op_layout = QHBoxLayout()
    op_layout.setSpacing(15)
    op_layout.setContentsMargins(15, 10, 15, 10)

    self.btn_shutdown = self._create_op_button("🔌 关机", "#f44336")
    self.btn_restart = self._create_op_button("🔄 重启", "#ff9800")
    self.btn_sleep = self._create_op_button("😴 睡眠", "#4caf50")

    self.btn_shutdown.setChecked(True)
    self.btn_shutdown.clicked.connect(lambda: self.on_operation_changed("关机"))
    self.btn_restart.clicked.connect(lambda: self.on_operation_changed("重启"))
    self.btn_sleep.clicked.connect(lambda: self.on_operation_changed("睡眠"))

    op_layout.addWidget(self.btn_shutdown)
    op_layout.addWidget(self.btn_restart)
    op_layout.addWidget(self.btn_sleep)
    op_group.setLayout(op_layout)
    main_layout.addWidget(op_group)

    # ========== 时间设置分组框 ==========
    time_group = QGroupBox("时间设置")
    time_group.setStyleSheet("""
        QGroupBox { font-size: 13px; font-weight: bold; color: #555; border: 1px solid #ddd;
            border-radius: 8px; margin-top: 5px; padding-top: 12px; background-color: white; }
        QGroupBox::title { subcontrol-origin: margin; left: 15px; padding: 0 8px; }
    """)
    time_layout = QVBoxLayout()
    time_layout.setSpacing(10)
    time_layout.setContentsMargins(15, 10, 15, 10)

    # 执行模式选择
    mode_layout = QHBoxLayout()
    mode_layout.addWidget(QLabel("执行模式:"))
    self.mode_combo = QComboBox()
    self.mode_combo.addItems(["立即执行", "倒计时", "定时"])
    self.mode_combo.setStyleSheet("padding: 5px; border: 1px solid #ddd; border-radius: 5px; min-width: 100px;")
    self.mode_combo.currentTextChanged.connect(self.on_mode_changed)
    mode_layout.addWidget(self.mode_combo)
    mode_layout.addStretch()
    time_layout.addLayout(mode_layout)

    # 倒计时设置
    self.timer_widget = QWidget()
    timer_layout = QHBoxLayout(self.timer_widget)
    timer_layout.setContentsMargins(0, 0, 0, 0)
    timer_layout.setSpacing(10)
    timer_layout.addWidget(QLabel("时长:"))

    self.hour_spin = QSpinBox()
    self.hour_spin.setRange(0, 23)
    self.hour_spin.setValue(self.config.get("default_hours", 0))
    self.hour_spin.setSuffix(" 时")
    self.hour_spin.setFixedWidth(70)
    timer_layout.addWidget(self.hour_spin)

    self.minute_spin = QSpinBox()
    self.minute_spin.setRange(0, 59)
    self.minute_spin.setValue(self.config.get("default_minutes", 30))
    self.minute_spin.setSuffix(" 分")
    self.minute_spin.setFixedWidth(70)
    timer_layout.addWidget(self.minute_spin)

    self.second_spin = QSpinBox()
    self.second_spin.setRange(0, 59)
    self.second_spin.setValue(self.config.get("default_seconds", 0))
    self.second_spin.setSuffix(" 秒")
    self.second_spin.setFixedWidth(70)
    timer_layout.addWidget(self.second_spin)
    timer_layout.addStretch()
    time_layout.addWidget(self.timer_widget)

    # 定时设置
    self.schedule_widget = QWidget()
    schedule_layout = QHBoxLayout(self.schedule_widget)
    schedule_layout.setContentsMargins(0, 0, 0, 0)
    schedule_layout.setSpacing(10)
    schedule_layout.addWidget(QLabel("时间:"))

    self.date_edit = QDateEdit()
    self.date_edit.setDate(QDate.currentDate())
    self.date_edit.setDisplayFormat("yyyy-MM-dd")
    self.date_edit.setCalendarPopup(True)
    self.date_edit.setFixedWidth(120)
    schedule_layout.addWidget(self.date_edit)

    self.time_edit = QTimeEdit()
    self.time_edit.setTime(QTime.currentTime().addSecs(1800))
    self.time_edit.setDisplayFormat("HH:mm:ss")
    self.time_edit.setFixedWidth(90)
    schedule_layout.addWidget(self.time_edit)
    schedule_layout.addStretch()
    time_layout.addWidget(self.schedule_widget)
    self.schedule_widget.hide()

    time_group.setLayout(time_layout)
    main_layout.addWidget(time_group)

    # ========== 无人操作自动关机/睡眠分组框 ==========
    idle_group = QGroupBox("无人操作自动处理")
    idle_group.setStyleSheet("""
        QGroupBox { font-size: 13px; font-weight: bold; color: #555; border: 1px solid #ddd;
            border-radius: 8px; margin-top: 5px; padding-top: 12px; background-color: white; }
        QGroupBox::title { subcontrol-origin: margin; left: 15px; padding: 0 8px; }
    """)
    idle_layout = QVBoxLayout()
    idle_layout.setSpacing(8)
    idle_layout.setContentsMargins(15, 10, 15, 10)

    # 第一行:启用开关 + 时间设置
    row1_layout = QHBoxLayout()
    row1_layout.setSpacing(10)

    self.idle_checkbox = QCheckBox("当用户无操作超过")
    self.idle_checkbox.setStyleSheet("font-size: 12px;")
    self.idle_checkbox.stateChanged.connect(self.on_idle_checkbox_changed)
    row1_layout.addWidget(self.idle_checkbox)

    self.idle_min_spin = QSpinBox()
    self.idle_min_spin.setRange(1, 60)
    self.idle_min_spin.setValue(self.idle_threshold // 60)
    self.idle_min_spin.setSuffix(" 分钟")
    self.idle_min_spin.setFixedWidth(80)
    self.idle_min_spin.setEnabled(False)
    self.idle_min_spin.valueChanged.connect(self.on_idle_threshold_changed)
    row1_layout.addWidget(self.idle_min_spin)

    row1_layout.addStretch()
    idle_layout.addLayout(row1_layout)

    # 第二行:操作选择(关机/睡眠)
    row2_layout = QHBoxLayout()
    row2_layout.setSpacing(10)
    row2_layout.addWidget(QLabel("执行操作:"))

    self.idle_action_shutdown = QRadioButton("关机")
    self.idle_action_sleep = QRadioButton("睡眠")

    # 设置默认选中
    if self.idle_action == "shutdown":
        self.idle_action_shutdown.setChecked(True)
    else:
        self.idle_action_sleep.setChecked(True)

    # 默认禁用
    self.idle_action_shutdown.setEnabled(False)
    self.idle_action_sleep.setEnabled(False)

    self.idle_action_shutdown.toggled.connect(self.on_idle_action_changed)
    self.idle_action_sleep.toggled.connect(self.on_idle_action_changed)

    row2_layout.addWidget(self.idle_action_shutdown)
    row2_layout.addWidget(self.idle_action_sleep)
    row2_layout.addStretch()
    idle_layout.addLayout(row2_layout)

    idle_group.setLayout(idle_layout)
    main_layout.addWidget(idle_group)

    # ========== 控制按钮 ==========
    btn_layout = QHBoxLayout()
    btn_layout.setSpacing(15)

    self.start_btn = QPushButton("开始")
    self.start_btn.setFixedHeight(45)
    self.start_btn.setStyleSheet("""
        QPushButton { background-color: #2196F3; color: white; font-size: 15px;
            font-weight: bold; border: none; border-radius: 8px; }
        QPushButton:hover { background-color: #1976D2; }
        QPushButton:disabled { background-color: #bdbdbd; }
    """)
    self.start_btn.clicked.connect(self.start_operation)
    btn_layout.addWidget(self.start_btn)

    self.pause_btn = QPushButton("暂停")
    self.pause_btn.setFixedHeight(45)
    self.pause_btn.setStyleSheet("""
        QPushButton { background-color: #ff9800; color: white; font-size: 15px;
            font-weight: bold; border: none; border-radius: 8px; }
        QPushButton:hover { background-color: #f57c00; }
        QPushButton:disabled { background-color: #bdbdbd; }
    """)
    self.pause_btn.clicked.connect(self.pause_operation)
    self.pause_btn.setEnabled(False)
    btn_layout.addWidget(self.pause_btn)

    self.cancel_btn = QPushButton("取消")
    self.cancel_btn.setFixedHeight(45)
    self.cancel_btn.setStyleSheet("""
        QPushButton { background-color: #f44336; color: white; font-size: 15px;
            font-weight: bold; border: none; border-radius: 8px; }
        QPushButton:hover { background-color: #d32f2f; }
        QPushButton:disabled { background-color: #bdbdbd; }
    """)
    self.cancel_btn.clicked.connect(self.cancel_operation)
    self.cancel_btn.setEnabled(False)
    btn_layout.addWidget(self.cancel_btn)
    main_layout.addLayout(btn_layout)

    # ========== 小窗控制按钮 ==========
    self.mini_control_widget = QWidget()
    mini_layout = QHBoxLayout(self.mini_control_widget)
    mini_layout.setSpacing(10)
    mini_layout.setContentsMargins(0, 0, 0, 0)

    self.mini_pause_btn = QPushButton("⏸")
    self.mini_pause_btn.setFixedSize(40, 40)
    self.mini_pause_btn.setStyleSheet(self._get_mini_btn_style("#ff9800"))
    self.mini_pause_btn.clicked.connect(self.pause_operation)
    self.mini_pause_btn.setEnabled(False)
    mini_layout.addWidget(self.mini_pause_btn)

    self.mini_cancel_btn = QPushButton("✕")
    self.mini_cancel_btn.setFixedSize(40, 40)
    self.mini_cancel_btn.setStyleSheet(self._get_mini_btn_style("#f44336"))
    self.mini_cancel_btn.clicked.connect(self.cancel_operation)
    self.mini_cancel_btn.setEnabled(False)
    mini_layout.addWidget(self.mini_cancel_btn)

    main_layout.addWidget(self.mini_control_widget)
    self.mini_control_widget.hide()

# ========== 辅助方法 ==========
def _create_op_button(self, text, color):
    btn = QPushButton(text)
    btn.setCheckable(True)
    btn.setFixedHeight(50)
    btn.setStyleSheet(f"""
        QPushButton {{ background-color: white; color: {color}; font-size: 14px;
            font-weight: bold; border: 2px solid {color}; border-radius: 8px; }}
        QPushButton:hover {{ background-color: {color}20; }}
        QPushButton:checked {{ background-color: {color}; color: white; }}
    """)
    return btn

def _get_mini_btn_style(self, color):
    return f"""
        QPushButton {{ background-color: {color}; color: white; font-size: 18px;
            font-weight: bold; border: none; border-radius: 20px; }}
        QPushButton:hover {{ opacity: 0.8; }}
        QPushButton:disabled {{ background-color: #bdbdbd; }}
    """

def load_config(self):
    """加载配置到控件"""
    self.idle_min_spin.setValue(self.config.get("idle_threshold", 300) // 60)
    self.hour_spin.setValue(self.config.get("default_hours", 0))
    self.minute_spin.setValue(self.config.get("default_minutes", 30))
    self.second_spin.setValue(self.config.get("default_seconds", 0))

    # 恢复空闲操作选择
    if self.config.get("idle_action", "sleep") == "shutdown":
        self.idle_action_shutdown.setChecked(True)
    else:
        self.idle_action_sleep.setChecked(True)

def save_config(self):
    """保存配置"""
    self.config["idle_threshold"] = self.idle_min_spin.value() * 60
    self.config["idle_action"] = self.idle_action
    self.config["default_hours"] = self.hour_spin.value()
    self.config["default_minutes"] = self.minute_spin.value()
    self.config["default_seconds"] = self.second_spin.value()
    Config.save(self.config)

# ========== 操作选择 ==========
def on_operation_changed(self, operation_name):
    self.current_operation = operation_name
    for btn in [self.btn_shutdown, self.btn_restart, self.btn_sleep]:
        btn.setChecked(False)
    if operation_name == "关机":
        self.btn_shutdown.setChecked(True)
    elif operation_name == "重启":
        self.btn_restart.setChecked(True)
    else:
        self.btn_sleep.setChecked(True)
    self.setWindowTitle(f"智能关机助手 - 已选择: {operation_name}")

# ========== 模式切换 ==========
def on_mode_changed(self, mode):
    if mode == "倒计时":
        self.timer_widget.show()
        self.schedule_widget.hide()
    elif mode == "定时":
        self.timer_widget.hide()
        self.schedule_widget.show()
    else:
        self.timer_widget.hide()
        self.schedule_widget.hide()

# ========== 计算等待时间 ==========
def calculate_wait_seconds(self):
    mode = self.mode_combo.currentText()
    if mode == "立即执行":
        return 0
    elif mode == "倒计时":
        return (self.hour_spin.value() * 3600 + 
                self.minute_spin.value() * 60 + 
                self.second_spin.value())
    else:
        target = datetime.combine(
            self.date_edit.date().toPyDate(),
            self.time_edit.time().toPyTime()
        )
        now = datetime.now()
        if target < now:
            target += timedelta(days=1)
        return int((target - now).total_seconds())

# ========== 开始操作 ==========
def start_operation(self):
    try:
        mode = self.mode_combo.currentText()
        seconds = self.calculate_wait_seconds()

        if mode == "立即执行":
            self.execute_operation(self.current_operation)
        elif seconds <= 0:
            QMessageBox.warning(self, "提示", "请设置有效的时间!")
            return
        else:
            self.start_btn.setEnabled(False)
            self.pause_btn.setEnabled(True)
            self.cancel_btn.setEnabled(True)
            self.mini_pause_btn.setEnabled(True)
            self.mini_cancel_btn.setEnabled(True)

            self.shutdown_timer = ShutdownTimer(seconds, self.current_operation)
            self.shutdown_timer.countdown_signal.connect(self.update_countdown)
            self.shutdown_timer.shutdown_signal.connect(self.execute_operation)
            self.shutdown_timer.start()

            self.logger.info(f"开始倒计时: {seconds}秒, 操作: {self.current_operation}")
            self.minimize_to_corner()
    except Exception as e:
        self.logger.error(f"启动失败: {e}")
        QMessageBox.critical(self, "错误", f"启动失败: {str(e)}")

# ========== 小窗模式 ==========
def minimize_to_corner(self):
    if not self.is_minimized_mode:
        self.is_minimized_mode = True
        self.original_geometry = self.geometry()

        for widget in self.findChildren(QGroupBox):
            widget.hide()
        self.start_btn.hide()
        self.pause_btn.hide()
        self.cancel_btn.hide()

        self.mini_control_widget.show()
        self.setFixedSize(350, 200)

        self.countdown_label.setStyleSheet("""
            font-size: 40px; font-weight: bold; color: #2196F3;
            font-family: 'Consolas', monospace; padding: 10px;
            background-color: white; border-radius: 10px; border: 1px solid #e0e0e0;
        """)
        self.progress_bar.setFixedHeight(6)

        screen = QApplication.primaryScreen().geometry()
        self.move(screen.width() - 370, screen.height() - 240)
        self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
        self.show()

def restore_to_normal(self):
    if self.is_minimized_mode:
        self.is_minimized_mode = False

        for widget in self.findChildren(QGroupBox):
            widget.show()
        self.start_btn.show()
        self.pause_btn.show()
        self.cancel_btn.show()
        self.mini_control_widget.hide()

        self.setFixedSize(500, 820)
        if self.original_geometry:
            self.setGeometry(self.original_geometry)

        self.countdown_label.setStyleSheet("""
            font-size: 56px; font-weight: bold; color: #2196F3;
            font-family: 'Consolas', monospace; padding: 20px;
            background-color: white; border-radius: 15px; border: 2px solid #e0e0e0;
        """)
        self.progress_bar.setFixedHeight(8)

        self.setWindowFlags(Qt.Window)
        self.show()

def mouseDoubleClickEvent(self, event):
    if self.is_minimized_mode:
        self.restore_to_normal()
    elif self.shutdown_timer and self.shutdown_timer.isRunning():
        self.minimize_to_corner()
    event.accept()

# ========== 倒计时更新 ==========
def update_countdown(self, text, progress, time_desc):
    self.countdown_label.setText(text)
    self.progress_bar.setValue(progress)
    self.setWindowTitle(f"智能关机助手 - 剩余 {time_desc}")

# ========== 暂停/继续 ==========
def pause_operation(self):
    if self.shutdown_timer:
        if self.shutdown_timer.is_paused:
            self.shutdown_timer.resume()
            self.pause_btn.setText("暂停")
            self.mini_pause_btn.setText("⏸")
        else:
            self.shutdown_timer.pause()
            self.pause_btn.setText("继续")
            self.mini_pause_btn.setText("▶")

# ========== 取消操作 ==========
def cancel_operation(self):
    if self.shutdown_timer:
        self.shutdown_timer.stop()
        self.shutdown_timer = None

    self.countdown_label.setText("00:00:00")
    self.progress_bar.setValue(0)

    self.start_btn.setEnabled(True)
    self.pause_btn.setEnabled(False)
    self.pause_btn.setText("暂停")
    self.cancel_btn.setEnabled(False)
    self.mini_pause_btn.setEnabled(False)
    self.mini_pause_btn.setText("⏸")
    self.mini_cancel_btn.setEnabled(False)

    if self.is_minimized_mode:
        self.restore_to_normal()

    self.setWindowTitle("智能关机助手")
    WinAPI.cancel_shutdown()
    self.logger.info("取消操作")

# ========== 执行操作 ==========
def execute_operation(self, operation=None):
    op = operation or self.current_operation

    self.logger.info(f"执行操作: {op}")
    success = False

    try:
        if op == "关机":
            success = WinAPI.shutdown()
            if not success:
                os.system("shutdown /s /t 5")
                success = True
            self.setWindowTitle("智能关机助手 - 正在关机")
        elif op == "重启":
            success = WinAPI.restart()
            if not success:
                os.system("shutdown /r /t 5")
                success = True
            self.setWindowTitle("智能关机助手 - 正在重启")
        else:  # 睡眠
            success = WinAPI.sleep()
            if not success:
                os.system("rundll32.exe powrprof.dll,SetSuspendState 0,1,0")
                success = True
            self.setWindowTitle("智能关机助手 - 正在睡眠")

        if not success:
            raise RuntimeError("操作执行失败")

    except Exception as e:
        self.logger.error(f"执行操作失败: {e}")
        QMessageBox.critical(self, "错误", f"操作失败: {str(e)}")
        self.reset_ui()
        return

    QTimer.singleShot(3000, self.reset_ui)

def reset_ui(self):
    self.countdown_label.setText("00:00:00")
    self.progress_bar.setValue(0)
    self.start_btn.setEnabled(True)
    self.pause_btn.setEnabled(False)
    self.pause_btn.setText("暂停")
    self.cancel_btn.setEnabled(False)
    self.mini_pause_btn.setEnabled(False)
    self.mini_pause_btn.setText("⏸")
    self.mini_cancel_btn.setEnabled(False)
    self.setWindowTitle("智能关机助手")
    if self.is_minimized_mode:
        self.restore_to_normal()

# ========== 无人操作自动关机/睡眠 ==========
def on_idle_checkbox_changed(self, state):
    self.auto_idle_enabled = (state == Qt.Checked)
    self.idle_min_spin.setEnabled(self.auto_idle_enabled)
    self.idle_action_shutdown.setEnabled(self.auto_idle_enabled)
    self.idle_action_sleep.setEnabled(self.auto_idle_enabled)

    if self.auto_idle_enabled:
        self.save_config()
        self.start_idle_monitor()
        action_name = "关机" if self.idle_action == "shutdown" else "睡眠"
        self.logger.info(f"启用空闲监控,超时后执行: {action_name}")
    else:
        self.stop_idle_monitor()
        self.logger.info("禁用空闲监控")

def on_idle_threshold_changed(self, value):
    if self.auto_idle_enabled:
        self.idle_threshold = value * 60
        self.save_config()
        if self.idle_monitor:
            self.start_idle_monitor()

def on_idle_action_changed(self):
    """空闲操作选择变化"""
    if self.idle_action_shutdown.isChecked():
        self.idle_action = "shutdown"
    else:
        self.idle_action = "sleep"
    self.save_config()

    if self.auto_idle_enabled:
        action_name = "关机" if self.idle_action == "shutdown" else "睡眠"
        self.logger.info(f"空闲操作切换为: {action_name}")

def start_idle_monitor(self):
    self.stop_idle_monitor()
    self.idle_monitor = IdleMonitor(self.idle_threshold)
    self.idle_monitor.idle_timeout.connect(self.on_idle_timeout)
    self.idle_monitor.start()

def stop_idle_monitor(self):
    if self.idle_monitor:
        self.idle_monitor.stop()
        self.idle_monitor = None

def on_idle_timeout(self):
    """空闲超时处理"""
    if self.shutdown_timer and self.shutdown_timer.isRunning():
        return

    # 根据用户选择执行关机或睡眠
    if self.idle_action == "shutdown":
        self.logger.info("空闲超时,执行自动关机")
        self.execute_operation("关机")
    else:
        self.logger.info("空闲超时,执行自动睡眠")
        self.execute_operation("睡眠")

# ========== 系统托盘 ==========
def setup_tray_icon(self):
    self.tray_icon = QSystemTrayIcon(self)

    icon = self.style().standardIcon(QStyle.SP_ComputerIcon)
    self.tray_icon.setIcon(icon)
    self.tray_icon.setToolTip("智能关机助手")

    tray_menu = QMenu()

    show_action = QAction("显示窗口", self)
    show_action.triggered.connect(self.show_window)
    tray_menu.addAction(show_action)

    hide_action = QAction("隐藏窗口", self)
    hide_action.triggered.connect(self.hide_window)
    tray_menu.addAction(hide_action)

    tray_menu.addSeparator()

    exit_action = QAction("退出程序", self)
    exit_action.triggered.connect(self.exit_app)
    tray_menu.addAction(exit_action)

    self.tray_icon.setContextMenu(tray_menu)
    self.tray_icon.activated.connect(self.on_tray_icon_activated)
    self.tray_icon.show()

def on_tray_icon_activated(self, reason):
    if reason == QSystemTrayIcon.DoubleClick:
        self.show_window()

def show_window(self):
    self.showNormal()
    self.raise_()
    self.activateWindow()

def hide_window(self):
    self.hide()

def exit_app(self):
    self.logger.info("退出程序")
    self.stop_idle_monitor()
    if self.shutdown_timer:
        self.shutdown_timer.stop()
    WinAPI.cancel_shutdown()
    if self.tray_icon:
        self.tray_icon.hide()
    QApplication.quit()

# ========== 关闭事件 ==========
def closeEvent(self, event):
    event.ignore()
    self.hide()
    if self.tray_icon:
        self.tray_icon.showMessage(
            "智能关机助手",
            "程序已最小化到系统托盘,右键图标可退出",
            QSystemTrayIcon.Information,
            2000
        )

========== 程序入口 ==========

def main():
if platform.system() != "Windows":
print("错误:此程序仅支持Windows系统!")
sys.exit(1)

WinAPI.request_admin()

app = QApplication(sys.argv)
app.setApplicationName("智能关机助手")

try:
    ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("shutdown_helper")
except:
    pass

window = WindowsShutdownApp()
window.show()
sys.exit(app.exec_())

if name == "main":
main()

评论一下?

OωO
取消