[Python] 纯文本查看 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import json
import os
import logging
import time
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QFrame, QSystemTrayIcon, QMenu, QAction,
QMessageBox, QSpinBox, QComboBox, QDialog, QListWidget, QListWidgetItem,
QFormLayout, QLineEdit, QTextEdit, QDialogButtonBox, QScrollArea,
QSizePolicy
)
from PyQt5.QtCore import Qt, QTimer, QRect, pyqtSignal
from PyQt5.QtGui import QFont, QIcon, QPainter, QBrush, QPen, QColor, QPixmap
# ====================== 日志配置 ======================
def setup_logging():
"""配置日志:追加模式,写入 log.txt"""
log_file = "log.txt"
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file, mode='a', encoding='utf-8'),
]
)
logging.info("========== 程序启动 ==========")
# ======================= 默认配置 =======================
DEFAULT_INTERVAL = 30 * 60
DEFAULT_REMINDERS = [
{
"id": "water",
"title": "💧 喝水",
"message": "站起来倒杯水,补充水分吧!",
"interval": 30 * 60,
"mode": "single",
"loop_count": 3
},
{
"id": "break",
"title": "🚶 休息",
"message": "久坐伤身,起来活动5分钟!",
"interval": 45 * 60,
"mode": "single",
"loop_count": 3
},
{
"id": "eye",
"title": "👀 眼保健操",
"message": "闭上眼睛,做一套眼保健操。",
"interval": 90 * 60,
"mode": "single",
"loop_count": 3
}
]
CONFIG_FILE = "config.json"
# =========================================================
class ReminderItem:
def __init__(self, data):
self.id = data.get("id", self.generate_id())
self.title = data.get("title", "新提醒")
self.message = data.get("message", "")
self.interval = data.get("interval", DEFAULT_INTERVAL)
self.mode = data.get("mode", "single")
self.loop_count = data.get("loop_count", 3)
def to_dict(self):
return {
"id": self.id,
"title": self.title,
"message": self.message,
"interval": self.interval,
"mode": self.mode,
"loop_count": self.loop_count
}
@staticmethod
def generate_id():
import time
return f"reminder_{int(time.time()*1000)}"
class ConfigManager:
@staticmethod
def load():
try:
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
data = json.load(f)
if isinstance(data, dict) and "water" in data:
# 旧版格式迁移
new_list = []
for key, val in data.items():
new_list.append({
"id": key,
"title": val.get("title", key),
"message": val.get("message", ""),
"interval": DEFAULT_INTERVAL,
"mode": "single",
"loop_count": 3
})
logging.info("从旧版配置迁移数据")
items = [ReminderItem(item) for item in new_list]
ConfigManager.save(items)
return items
elif isinstance(data, list):
logging.info(f"加载配置成功,共 {len(data)} 个提醒项")
return [ReminderItem(item) for item in data]
else:
logging.warning("配置格式未知,使用默认配置并重新生成")
items = [ReminderItem(item) for item in DEFAULT_REMINDERS]
ConfigManager.save(items)
return items
else:
logging.info("配置文件不存在,创建默认配置")
items = [ReminderItem(item) for item in DEFAULT_REMINDERS]
ConfigManager.save(items)
return items
except Exception as e:
logging.exception(f"加载配置时发生异常: {e}")
items = [ReminderItem(item) for item in DEFAULT_REMINDERS]
ConfigManager.save(items)
return items
@staticmethod
def save(items):
try:
data = [item.to_dict() for item in items]
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=4)
logging.info(f"配置保存成功,共 {len(items)} 个提醒项")
return True
except Exception as e:
logging.exception(f"保存配置时发生异常: {e}")
return False
class NewReminderDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("新建提醒")
self.setMinimumWidth(350)
layout = QFormLayout(self)
self.title_edit = QLineEdit()
self.title_edit.setPlaceholderText("输入标题,如 '💧 喝水'")
self.msg_edit = QTextEdit()
self.msg_edit.setPlaceholderText("输入提醒消息")
self.msg_edit.setMaximumHeight(80)
layout.addRow("标题:", self.title_edit)
layout.addRow("消息:", self.msg_edit)
btn_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
btn_box.accepted.connect(self.accept)
btn_box.rejected.connect(self.reject)
layout.addRow(btn_box)
def get_data(self):
return {
"title": self.title_edit.text().strip(),
"message": self.msg_edit.toPlainText().strip()
}
class EditReminderDialog(QDialog):
def __init__(self, title, message, parent=None):
super().__init__(parent)
self.setWindowTitle("编辑提醒")
self.setMinimumWidth(350)
layout = QFormLayout(self)
self.title_edit = QLineEdit(title)
self.msg_edit = QTextEdit(message)
self.msg_edit.setMaximumHeight(80)
layout.addRow("标题:", self.title_edit)
layout.addRow("消息:", self.msg_edit)
btn_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
btn_box.accepted.connect(self.accept)
btn_box.rejected.connect(self.reject)
layout.addRow(btn_box)
def get_data(self):
return {
"title": self.title_edit.text().strip(),
"message": self.msg_edit.toPlainText().strip()
}
class SettingsDialog(QDialog):
def __init__(self, config_items, parent=None):
super().__init__(parent)
self.config_items = config_items
self.setWindowTitle("提醒项管理")
self.setMinimumSize(450, 400)
self.setModal(True)
layout = QVBoxLayout(self)
self.list_widget = QListWidget()
self.refresh_list()
layout.addWidget(self.list_widget)
btn_layout = QHBoxLayout()
self.new_btn = QPushButton("➕ 新建")
self.new_btn.clicked.connect(self.new_reminder)
self.edit_btn = QPushButton("✏️ 编辑")
self.edit_btn.clicked.connect(self.edit_reminder)
self.delete_btn = QPushButton("🗑️ 删除")
self.delete_btn.clicked.connect(self.delete_reminder)
btn_layout.addWidget(self.new_btn)
btn_layout.addWidget(self.edit_btn)
btn_layout.addWidget(self.delete_btn)
btn_layout.addStretch()
btn_box = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel)
btn_box.accepted.connect(self.save_and_close)
btn_box.rejected.connect(self.reject)
layout.addLayout(btn_layout)
layout.addWidget(btn_box)
self.list_widget.itemDoubleClicked.connect(self.edit_reminder)
def refresh_list(self):
self.list_widget.clear()
for item in self.config_items:
self.list_widget.addItem(f"{item.title} ({item.id})")
def new_reminder(self):
dialog = NewReminderDialog(self)
if dialog.exec_() == QDialog.Accepted:
data = dialog.get_data()
if not data["title"]:
QMessageBox.warning(self, "错误", "标题不能为空")
return
new_item = ReminderItem({
"id": ReminderItem.generate_id(),
"title": data["title"],
"message": data["message"],
"interval": DEFAULT_INTERVAL,
"mode": "single",
"loop_count": 3
})
self.config_items.append(new_item)
self.refresh_list()
logging.info(f"新建提醒项: {new_item.title}")
def edit_reminder(self):
current = self.list_widget.currentRow()
if current < 0:
QMessageBox.warning(self, "提示", "请先选择一个提醒项")
return
item = self.config_items[current]
dialog = EditReminderDialog(item.title, item.message, self)
if dialog.exec_() == QDialog.Accepted:
data = dialog.get_data()
if not data["title"]:
QMessageBox.warning(self, "错误", "标题不能为空")
return
old_title = item.title
item.title = data["title"]
item.message = data["message"]
self.refresh_list()
logging.info(f"编辑提醒项: {old_title} -> {item.title}")
def delete_reminder(self):
if len(self.config_items) <= 1:
QMessageBox.warning(self, "提示", "至少保留一个提醒项")
return
current = self.list_widget.currentRow()
if current < 0:
QMessageBox.warning(self, "提示", "请先选择一个提醒项")
return
reply = QMessageBox.question(self, "确认删除", f"确定要删除“{self.config_items[current].title}”吗?",
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
deleted = self.config_items.pop(current)
self.refresh_list()
logging.info(f"删除提醒项: {deleted.title}")
def save_and_close(self):
if ConfigManager.save(self.config_items):
logging.info("设置窗口保存配置")
self.accept()
else:
QMessageBox.warning(self, "保存失败", "无法写入配置文件")
class TimerCard(QFrame):
def __init__(self, reminder_item, parent=None):
super().__init__(parent)
self.item = reminder_item
self.remaining = reminder_item.interval
self.running = False
self.parent_window = parent
self.mode = reminder_item.mode
self.loop_total = reminder_item.loop_count
self.loop_remaining = reminder_item.loop_count
self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
self.setObjectName("timerCard")
self.setMinimumWidth(240)
layout = QVBoxLayout(self)
layout.setSpacing(6)
layout.setContentsMargins(12, 12, 12, 12)
self.title_label = QLabel(reminder_item.title)
self.title_label.setAlignment(Qt.AlignCenter)
self.title_label.setObjectName("cardTitle")
layout.addWidget(self.title_label)
self.time_label = QLabel(self.format_time(self.remaining))
self.time_label.setAlignment(Qt.AlignCenter)
self.time_label.setObjectName("timeDisplay")
layout.addWidget(self.time_label)
btn_layout = QHBoxLayout()
btn_layout.setSpacing(6)
self.start_btn = QPushButton("▶ 开始")
self.start_btn.setMinimumWidth(70)
self.start_btn.clicked.connect(self.start_timer)
self.pause_btn = QPushButton("⏸ 暂停")
self.pause_btn.setMinimumWidth(70)
self.pause_btn.clicked.connect(self.pause_timer)
self.pause_btn.setEnabled(False)
self.reset_btn = QPushButton("🔄 重置")
self.reset_btn.setMinimumWidth(70)
self.reset_btn.clicked.connect(self.reset_timer)
btn_layout.addWidget(self.start_btn)
btn_layout.addWidget(self.pause_btn)
btn_layout.addWidget(self.reset_btn)
layout.addLayout(btn_layout)
interval_layout = QHBoxLayout()
interval_layout.setSpacing(4)
interval_label = QLabel("间隔(分钟):")
interval_label.setStyleSheet("font-size: 9pt; color: #2c3e50;")
self.interval_spin = QSpinBox()
self.interval_spin.setRange(1, 999)
self.interval_spin.setValue(reminder_item.interval // 60)
self.interval_spin.setMinimumWidth(60)
self.interval_spin.setStyleSheet("font-size: 9pt;")
self.update_interval_btn = QPushButton("更新")
self.update_interval_btn.setMinimumWidth(50)
self.update_interval_btn.clicked.connect(self.update_interval)
self.update_interval_btn.setStyleSheet("""
QPushButton { background-color: #3498db; color: white; font-size: 9pt; border-radius: 4px; padding: 2px 6px; }
QPushButton:hover { background-color: #2980b9; }
""")
interval_layout.addWidget(interval_label)
interval_layout.addWidget(self.interval_spin)
interval_layout.addWidget(self.update_interval_btn)
interval_layout.addStretch()
layout.addLayout(interval_layout)
mode_layout = QHBoxLayout()
mode_layout.setSpacing(4)
mode_label = QLabel("模式:")
mode_label.setStyleSheet("font-size: 9pt; color: #2c3e50;")
self.mode_combo = QComboBox()
self.mode_combo.addItems(["单一", "循环", "无限"])
self.mode_combo.setCurrentIndex(["single","loop","infinite"].index(self.mode))
self.mode_combo.currentIndexChanged.connect(self.on_mode_changed)
self.mode_combo.setMinimumWidth(70)
self.mode_combo.setStyleSheet("font-size: 9pt;")
self.loop_label = QLabel("次数:")
self.loop_label.setStyleSheet("font-size: 9pt; color: #2c3e50;")
self.loop_spin = QSpinBox()
self.loop_spin.setRange(1, 99)
self.loop_spin.setValue(self.loop_total)
self.loop_spin.setMinimumWidth(50)
self.loop_spin.setStyleSheet("font-size: 9pt;")
self.loop_spin.valueChanged.connect(self.on_loop_count_changed)
mode_layout.addWidget(mode_label)
mode_layout.addWidget(self.mode_combo)
mode_layout.addWidget(self.loop_label)
mode_layout.addWidget(self.loop_spin)
mode_layout.addStretch()
layout.addLayout(mode_layout)
self.apply_mode_to_ui()
self.setStyleSheet("""
QFrame#timerCard {
background-color: rgba(255, 255, 255, 0.85);
border-radius: 12px;
padding: 8px;
}
QLabel#cardTitle {
font-size: 14pt;
font-weight: bold;
color: #2c3e50;
}
QLabel#timeDisplay {
font-size: 24pt;
font-weight: bold;
color: #2980b9;
font-family: 'Consolas', monospace;
}
QPushButton {
background-color: #ecf0f1;
border: none;
border-radius: 6px;
padding: 4px 6px;
font-size: 9pt;
font-weight: bold;
color: #2c3e50;
}
QPushButton:hover { background-color: #d0d7db; }
QPushButton:pressed { background-color: #b0b8be; }
QPushButton:disabled { color: #b0b8be; background-color: #e0e4e8; }
QComboBox, QSpinBox { background-color: white; border: 1px solid #ccc; border-radius: 4px; padding: 1px 3px; font-size: 9pt; }
""")
def format_time(self, seconds):
if seconds < 0: seconds = 0
mins, secs = divmod(int(seconds), 60)
return f"{mins:02d}:{secs:02d}"
def update_time(self):
if self.running and self.remaining > 0:
self.remaining -= 1
self.time_label.setText(self.format_time(self.remaining))
if self.remaining <= 0:
self.on_timeout()
def on_timeout(self):
title = self.item.title
message = self.item.message
self.parent_window.show_notification(title, message)
logging.info(f"提醒触发: {title} - {message}")
if self.mode == "single":
self.running = False
self.start_btn.setEnabled(True)
self.pause_btn.setEnabled(False)
elif self.mode == "loop":
self.loop_remaining -= 1
self.loop_spin.setValue(self.loop_remaining)
if self.loop_remaining > 0:
self.remaining = self.item.interval
self.time_label.setText(self.format_time(self.remaining))
self.running = True
self.start_btn.setEnabled(False)
self.pause_btn.setEnabled(True)
else:
self.running = False
self.start_btn.setEnabled(True)
self.pause_btn.setEnabled(False)
else: # infinite
self.remaining = self.item.interval
self.time_label.setText(self.format_time(self.remaining))
self.running = True
self.start_btn.setEnabled(False)
self.pause_btn.setEnabled(True)
def start_timer(self):
if self.remaining <= 0:
self.remaining = self.item.interval
self.time_label.setText(self.format_time(self.remaining))
if self.mode == "loop" and self.loop_remaining <= 0:
self.loop_remaining = self.loop_total
self.loop_spin.setValue(self.loop_remaining)
self.running = True
self.start_btn.setEnabled(False)
self.pause_btn.setEnabled(True)
logging.info(f"开始计时: {self.item.title}")
def pause_timer(self):
self.running = False
self.start_btn.setEnabled(True)
self.pause_btn.setEnabled(False)
logging.info(f"暂停计时: {self.item.title}")
def reset_timer(self):
self.remaining = self.item.interval
self.time_label.setText(self.format_time(self.remaining))
self.running = False
self.start_btn.setEnabled(True)
self.pause_btn.setEnabled(False)
if self.mode == "loop":
self.loop_remaining = self.loop_total
self.loop_spin.setValue(self.loop_remaining)
logging.info(f"重置计时: {self.item.title}")
def on_mode_changed(self, index):
mode_text = self.mode_combo.currentText()
if mode_text == "单一":
self.mode = "single"
self.loop_label.setEnabled(False)
self.loop_spin.setEnabled(False)
elif mode_text == "循环":
self.mode = "loop"
self.loop_label.setEnabled(True)
self.loop_spin.setEnabled(True)
self.loop_total = self.loop_spin.value()
self.loop_remaining = self.loop_total
else:
self.mode = "infinite"
self.loop_label.setEnabled(False)
self.loop_spin.setEnabled(False)
self.item.mode = self.mode
self.item.loop_count = self.loop_total
self.reset_timer()
logging.info(f"{self.item.title} 切换模式为 {mode_text}")
def on_loop_count_changed(self, value):
if self.mode == "loop":
self.loop_total = value
self.item.loop_count = value
if not self.running:
self.loop_remaining = value
def update_interval(self):
new_minutes = self.interval_spin.value()
new_interval = new_minutes * 60
if new_interval != self.item.interval:
self.item.interval = new_interval
self.reset_timer()
ConfigManager.save(self.parent_window.config_items)
logging.info(f"{self.item.title} 间隔更新为 {new_minutes} 分钟")
if self.parent_window.tray_icon:
self.parent_window.tray_icon.showMessage(
"间隔已更新",
f"{self.item.title} 间隔改为 {new_minutes} 分钟",
QSystemTrayIcon.Information,
1500
)
def apply_mode_to_ui(self):
if self.mode == "single":
self.loop_label.setEnabled(False)
self.loop_spin.setEnabled(False)
elif self.mode == "loop":
self.loop_label.setEnabled(True)
self.loop_spin.setEnabled(True)
else:
self.loop_label.setEnabled(False)
self.loop_spin.setEnabled(False)
def update_title(self, new_title):
self.title_label.setText(new_title)
class HealthReminder(QMainWindow):
def __init__(self):
super().__init__()
logging.info("初始化主窗口")
self.config_items = ConfigManager.load()
self.card_widgets = []
self.setWindowTitle("定时守护")
self.setWindowIcon(self.create_icon())
self.resize(1000, 600)
self.setMinimumSize(800, 400)
screen_rect = QApplication.primaryScreen().availableGeometry()
center = screen_rect.center()
self.move(center.x() - self.width()//2, center.y() - self.height()//2)
self.tray_icon = None
self.init_tray()
central = QWidget()
self.setCentralWidget(central)
main_layout = QVBoxLayout(central)
main_layout.setContentsMargins(15, 10, 15, 10)
main_layout.setSpacing(10)
top_layout = QHBoxLayout()
title_label = QLabel("⏱ 定时守护")
title_label.setStyleSheet("font-size: 18pt; font-weight: bold; color: white;")
top_layout.addWidget(title_label)
top_layout.addStretch()
main_layout.addLayout(top_layout)
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setStyleSheet("QScrollArea { border: none; background: transparent; }")
self.card_container = QWidget()
self.card_container.setStyleSheet("background: transparent;")
self.card_layout = QHBoxLayout(self.card_container)
self.card_layout.setSpacing(15)
self.card_layout.setContentsMargins(5, 5, 5, 5)
self.card_layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
scroll.setWidget(self.card_container)
main_layout.addWidget(scroll, 1)
bottom_layout = QHBoxLayout()
bottom_layout.setSpacing(10)
self.reset_all_btn = QPushButton("🔄 重置所有")
self.reset_all_btn.setMinimumWidth(100)
self.reset_all_btn.clicked.connect(self.reset_all)
self.start_all_btn = QPushButton("▶ 一键开始所有")
self.start_all_btn.setMinimumWidth(100)
self.start_all_btn.clicked.connect(self.start_all)
bottom_layout.addWidget(self.reset_all_btn)
bottom_layout.addWidget(self.start_all_btn)
bottom_layout.addStretch()
self.settings_btn = QPushButton("⚙️ 管理提醒")
self.settings_btn.setMinimumWidth(100)
self.settings_btn.clicked.connect(self.open_settings)
self.settings_btn.setStyleSheet("background-color: #8e44ad; color: white;")
bottom_layout.addWidget(self.settings_btn)
self.hide_btn = QPushButton("⏹ 隐藏到后台")
self.hide_btn.setMinimumWidth(100)
self.hide_btn.clicked.connect(self.hide_to_tray)
self.hide_btn.setStyleSheet("background-color: #3498db; color: white;")
bottom_layout.addWidget(self.hide_btn)
main_layout.addLayout(bottom_layout)
self.setStyleSheet("""
QMainWindow {
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
stop:0 #74ebd5, stop:1 #9face6);
}
QPushButton {
background-color: #ecf0f1;
border: none;
border-radius: 8px;
padding: 6px 12px;
font-size: 10pt;
font-weight: bold;
color: #2c3e50;
}
QPushButton:hover { background-color: #d0d7db; }
QPushButton:pressed { background-color: #b0b8be; }
QPushButton#resetAllBtn { background-color: #e74c3c; color: white; }
QPushButton#resetAllBtn:hover { background-color: #c0392b; }
QPushButton#startAllBtn { background-color: #27ae60; color: white; }
QPushButton#startAllBtn:hover { background-color: #229954; }
""")
self.reset_all_btn.setObjectName("resetAllBtn")
self.start_all_btn.setObjectName("startAllBtn")
self.rebuild_cards()
self.timer = QTimer(self)
self.timer.timeout.connect(self.update_all_timers)
self.timer.start(1000)
logging.info("主窗口初始化完成")
def rebuild_cards(self):
for card in self.card_widgets:
self.card_layout.removeWidget(card)
card.deleteLater()
self.card_widgets.clear()
for item in self.config_items:
card = TimerCard(item, self)
self.card_layout.addWidget(card)
self.card_widgets.append(card)
logging.info(f"重建卡片,共 {len(self.card_widgets)} 个")
def open_settings(self):
dialog = SettingsDialog(self.config_items, self)
if dialog.exec_() == QDialog.Accepted:
self.config_items = ConfigManager.load()
self.rebuild_cards()
if self.tray_icon:
self.tray_icon.showMessage("设置已更新", "提醒项已修改", QSystemTrayIcon.Information, 1500)
logging.info("设置窗口关闭,配置已更新")
def create_icon(self):
pixmap = QPixmap(64, 64)
pixmap.fill(Qt.transparent)
painter = QPainter(pixmap)
painter.setRenderHint(QPainter.Antialiasing)
painter.setBrush(QBrush(QColor("#2196F3")))
painter.setPen(QPen(QColor("#0D47A1"), 2))
painter.drawEllipse(8, 8, 48, 48)
painter.setPen(QColor("white"))
painter.setFont(QFont("Segoe UI Emoji", 24))
painter.drawText(QRect(8, 8, 48, 48), Qt.AlignCenter, "⏱")
painter.end()
return QIcon(pixmap)
def init_tray(self):
if not QSystemTrayIcon.isSystemTrayAvailable():
logging.warning("系统托盘不可用")
return
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(self.create_icon())
self.tray_icon.setToolTip("定时守护")
tray_menu = QMenu()
show_action = QAction("显示主窗口", self)
show_action.triggered.connect(self.show_window)
tray_menu.addAction(show_action)
reset_action = QAction("重置所有计时器", self)
reset_action.triggered.connect(self.reset_all)
tray_menu.addAction(reset_action)
start_action = QAction("一键开始所有", self)
start_action.triggered.connect(self.start_all)
tray_menu.addAction(start_action)
tray_menu.addSeparator()
quit_action = QAction("退出", self)
quit_action.triggered.connect(self.quit_app)
tray_menu.addAction(quit_action)
self.tray_icon.setContextMenu(tray_menu)
self.tray_icon.activated.connect(self.tray_activated)
self.tray_icon.show()
logging.info("系统托盘已初始化")
def tray_activated(self, reason):
if reason == QSystemTrayIcon.DoubleClick:
self.show_window()
def show_window(self):
self.showNormal()
self.raise_()
self.activateWindow()
logging.info("显示主窗口")
def hide_to_tray(self):
self.hide()
if self.tray_icon and self.tray_icon.isVisible():
self.tray_icon.showMessage("定时守护", "程序已隐藏到后台,双击图标恢复", QSystemTrayIcon.Information, 2000)
logging.info("隐藏到系统托盘")
def closeEvent(self, event):
self.quit_app()
event.accept()
def quit_app(self):
logging.info("程序退出")
self.timer.stop()
if self.tray_icon:
self.tray_icon.hide()
QApplication.quit()
def show_notification(self, title, message):
if self.tray_icon and self.tray_icon.isVisible():
self.tray_icon.showMessage(title, message, QSystemTrayIcon.Information, 5000)
else:
QMessageBox.information(self, title, message)
def update_all_timers(self):
for card in self.card_widgets:
card.update_time()
def reset_all(self):
for card in self.card_widgets:
card.reset_timer()
if self.tray_icon:
self.tray_icon.showMessage("重置成功", "所有计时器已重置并暂停", QSystemTrayIcon.Information, 1000)
logging.info("重置所有计时器")
def start_all(self):
for card in self.card_widgets:
if not card.running:
card.start_timer()
if self.tray_icon:
self.tray_icon.showMessage("已启动", "所有计时器已开始倒计时", QSystemTrayIcon.Information, 1000)
logging.info("一键启动所有计时器")
def main():
setup_logging()
try:
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False)
window = HealthReminder()
window.show()
sys.exit(app.exec_())
except Exception as e:
logging.exception(f"程序运行异常: {e}")
raise
if __name__ == "__main__":
main()