833 lines
27 KiB
Python
833 lines
27 KiB
Python
"""
|
||
述职计时器 - 倒计时应用程序
|
||
使用 PySide6 构建的美观倒计时器
|
||
"""
|
||
|
||
import sys
|
||
import json
|
||
from pathlib import Path
|
||
from PySide6.QtWidgets import (
|
||
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||
QPushButton, QLabel, QDialog, QSpinBox, QCheckBox, QSlider, QFormLayout, QGroupBox
|
||
)
|
||
from PySide6.QtCore import QTimer, Qt, QPropertyAnimation, QEasingCurve, Property
|
||
from PySide6.QtGui import QFont
|
||
from PySide6.QtMultimedia import QSoundEffect, QAudioOutput, QMediaPlayer
|
||
from PySide6.QtCore import QUrl
|
||
|
||
|
||
class ConfigDialog(QDialog):
|
||
"""配置对话框"""
|
||
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self.setWindowTitle("其它设置")
|
||
self.setModal(True)
|
||
self.resize(400, 300)
|
||
|
||
layout = QFormLayout()
|
||
|
||
# 自定义倒计时时间(秒)
|
||
self.custom_time_spin = QSpinBox()
|
||
self.custom_time_spin.setRange(1, 3600)
|
||
self.custom_time_spin.setValue(300)
|
||
self.custom_time_spin.setSuffix(" 秒")
|
||
layout.addRow("自定义倒计时:", self.custom_time_spin)
|
||
|
||
# 提前告警时间(秒)
|
||
self.alert_time_spin = QSpinBox()
|
||
self.alert_time_spin.setRange(0, 600)
|
||
self.alert_time_spin.setValue(60)
|
||
self.alert_time_spin.setSuffix(" 秒")
|
||
layout.addRow("提前告警:", self.alert_time_spin)
|
||
|
||
# 置顶选项
|
||
self.topmost_checkbox = QCheckBox()
|
||
self.topmost_checkbox.setChecked(True)
|
||
layout.addRow("窗口置顶:", self.topmost_checkbox)
|
||
|
||
# 透明度滑块
|
||
self.opacity_slider = QSlider(Qt.Horizontal)
|
||
self.opacity_slider.setRange(10, 100)
|
||
self.opacity_slider.setValue(90)
|
||
self.opacity_slider.setTickPosition(QSlider.TicksBelow)
|
||
self.opacity_slider.setTickInterval(10)
|
||
self.opacity_label = QLabel("90%")
|
||
self.opacity_slider.valueChanged.connect(
|
||
lambda v: self.opacity_label.setText(f"{v}%")
|
||
)
|
||
|
||
opacity_layout = QHBoxLayout()
|
||
opacity_layout.addWidget(self.opacity_slider)
|
||
opacity_layout.addWidget(self.opacity_label)
|
||
layout.addRow("窗口透明度:", opacity_layout)
|
||
|
||
# 测试告警按钮
|
||
self.test_alert_button = QPushButton("测试告警声音")
|
||
self.test_alert_button.clicked.connect(self.test_alert_sound)
|
||
self.test_alert_button.setStyleSheet("""
|
||
QPushButton {
|
||
background-color: #3498db;
|
||
color: white;
|
||
border: none;
|
||
padding: 8px 16px;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
}
|
||
QPushButton:hover {
|
||
background-color: #2980b9;
|
||
}
|
||
""")
|
||
layout.addRow("", self.test_alert_button)
|
||
|
||
# 按钮
|
||
button_layout = QHBoxLayout()
|
||
|
||
self.ok_button = QPushButton("确定")
|
||
self.ok_button.clicked.connect(self.accept)
|
||
self.ok_button.setStyleSheet("""
|
||
QPushButton {
|
||
background-color: #4CAF50;
|
||
color: white;
|
||
border: none;
|
||
padding: 8px 16px;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
}
|
||
QPushButton:hover {
|
||
background-color: #45a049;
|
||
}
|
||
""")
|
||
|
||
self.cancel_button = QPushButton("取消")
|
||
self.cancel_button.clicked.connect(self.reject)
|
||
self.cancel_button.setStyleSheet("""
|
||
QPushButton {
|
||
background-color: #f44336;
|
||
color: white;
|
||
border: none;
|
||
padding: 8px 16px;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
}
|
||
QPushButton:hover {
|
||
background-color: #da190b;
|
||
}
|
||
""")
|
||
|
||
self.exit_button = QPushButton("退出")
|
||
self.exit_button.clicked.connect(self.exit_application)
|
||
self.exit_button.setStyleSheet("""
|
||
QPushButton {
|
||
background-color: #95a5a6;
|
||
color: white;
|
||
border: none;
|
||
padding: 8px 16px;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
}
|
||
QPushButton:hover {
|
||
background-color: #7f8c8d;
|
||
}
|
||
""")
|
||
|
||
button_layout.addWidget(self.ok_button)
|
||
button_layout.addWidget(self.cancel_button)
|
||
button_layout.addWidget(self.exit_button)
|
||
|
||
layout.addRow(button_layout)
|
||
|
||
self.setLayout(layout)
|
||
|
||
# 样式
|
||
self.setStyleSheet("""
|
||
QDialog {
|
||
background-color: #f5f5f5;
|
||
}
|
||
QLabel {
|
||
font-size: 13px;
|
||
color: #333;
|
||
}
|
||
QSpinBox {
|
||
padding: 5px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
font-size: 13px;
|
||
}
|
||
""")
|
||
|
||
def test_alert_sound(self):
|
||
"""测试告警声音"""
|
||
try:
|
||
QApplication.beep()
|
||
# 循环播放2次
|
||
QTimer.singleShot(500, QApplication.beep)
|
||
QTimer.singleShot(1000, QApplication.beep)
|
||
except Exception as e:
|
||
print(f"播放测试告警音失败: {e}")
|
||
|
||
def exit_application(self):
|
||
"""退出应用程序"""
|
||
self.accept() # 关闭对话框
|
||
# 退出整个应用程序
|
||
QApplication.quit()
|
||
|
||
|
||
class CountdownTimer(QMainWindow):
|
||
"""主倒计时器窗口"""
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
|
||
# 配置
|
||
self.config_file = Path.home() / ".countdown_timer_config.json"
|
||
self.load_config()
|
||
|
||
# 状态
|
||
self.remaining_seconds = 0
|
||
self.total_seconds = 0
|
||
self.is_running = False
|
||
self.alert_played = False
|
||
self.alert_loop_count = 0
|
||
|
||
# 定时器
|
||
self.timer = QTimer()
|
||
self.timer.timeout.connect(self.update_timer)
|
||
|
||
# 闪烁动画定时器
|
||
self.blink_timer = QTimer()
|
||
self.blink_timer.timeout.connect(self.toggle_blink)
|
||
self.blink_state = True
|
||
|
||
# 音频播放器(用于告警提示音)
|
||
self.media_player = None
|
||
|
||
self.init_ui()
|
||
self.apply_config()
|
||
|
||
# 鼠标位置跟踪(用于窗口移动)
|
||
self.drag_position = None
|
||
|
||
# 状态管理
|
||
self.current_state = "normal" # normal 或 mini
|
||
self.state_change_timer = None
|
||
|
||
def mousePressEvent(self, event):
|
||
"""鼠标按下事件 - 开始移动窗口"""
|
||
if event.button() == Qt.LeftButton:
|
||
self.drag_position = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
|
||
event.accept()
|
||
|
||
def mouseMoveEvent(self, event):
|
||
"""鼠标移动事件 - 移动窗口"""
|
||
if self.drag_position is not None and event.buttons() == Qt.LeftButton:
|
||
self.move(event.globalPosition().toPoint() - self.drag_position)
|
||
event.accept()
|
||
|
||
def mouseReleaseEvent(self, event):
|
||
"""鼠标释放事件 - 停止移动窗口"""
|
||
if event.button() == Qt.LeftButton:
|
||
self.drag_position = None
|
||
event.accept()
|
||
|
||
def load_config(self):
|
||
"""加载配置"""
|
||
default_config = {
|
||
"alert_seconds": 60,
|
||
"topmost": True,
|
||
"opacity": 90,
|
||
"custom_seconds": 300
|
||
}
|
||
|
||
if self.config_file.exists():
|
||
try:
|
||
with open(self.config_file, 'r', encoding='utf-8') as f:
|
||
self.config = json.load(f)
|
||
# 合并默认值
|
||
for key, value in default_config.items():
|
||
if key not in self.config:
|
||
self.config[key] = value
|
||
except:
|
||
self.config = default_config
|
||
else:
|
||
self.config = default_config
|
||
|
||
def save_config(self):
|
||
"""保存配置"""
|
||
try:
|
||
with open(self.config_file, 'w', encoding='utf-8') as f:
|
||
json.dump(self.config, f, ensure_ascii=False, indent=2)
|
||
except:
|
||
pass
|
||
|
||
def apply_config(self):
|
||
"""应用配置"""
|
||
# 透明度
|
||
self.setWindowOpacity(self.config["opacity"] / 100.0)
|
||
|
||
# 置顶
|
||
if self.config["topmost"]:
|
||
self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
|
||
else:
|
||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
|
||
|
||
self.show()
|
||
|
||
# 保存初始窗口位置(等待窗口显示完成)
|
||
QTimer.singleShot(100, self.save_initial_position)
|
||
|
||
def save_initial_position(self):
|
||
"""保存初始窗口位置"""
|
||
self.initial_pos = self.pos()
|
||
|
||
def init_ui(self):
|
||
"""初始化UI"""
|
||
# 设置无边框窗口
|
||
self.setWindowFlags(Qt.FramelessWindowHint)
|
||
# 设置最小窗口大小(初始状态)
|
||
self.setMinimumSize(250, 100)
|
||
# 设置初始窗口大小(倒计时模式)
|
||
self.resize(500, 300)
|
||
|
||
# 中心部件
|
||
central_widget = QWidget()
|
||
self.setCentralWidget(central_widget)
|
||
|
||
main_layout = QVBoxLayout()
|
||
main_layout.setSpacing(20)
|
||
main_layout.setContentsMargins(30, 30, 30, 30)
|
||
|
||
# 倒计时显示
|
||
self.time_label = QLabel("00:00")
|
||
self.time_label.setAlignment(Qt.AlignCenter)
|
||
self.time_label.setStyleSheet("""
|
||
QLabel {
|
||
font-size: 72px;
|
||
font-weight: bold;
|
||
color: #3498db;
|
||
font-family: 'Courier New', monospace;
|
||
background-color: #ecf0f1;
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
}
|
||
""")
|
||
main_layout.addWidget(self.time_label)
|
||
|
||
# 完成提示标签(初始隐藏)
|
||
self.finish_label = QLabel("时间已到")
|
||
self.finish_label.setAlignment(Qt.AlignCenter)
|
||
self.finish_label.setStyleSheet("""
|
||
QLabel {
|
||
font-size: 48px;
|
||
font-weight: bold;
|
||
color: #e74c3c;
|
||
background-color: #fee;
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
}
|
||
""")
|
||
self.finish_label.hide()
|
||
main_layout.addWidget(self.finish_label)
|
||
|
||
# 创建可折叠的按钮区域
|
||
self.button_group = QGroupBox()
|
||
self.button_group.setStyleSheet("""
|
||
QGroupBox {
|
||
border: 2px solid #bdc3c7;
|
||
border-radius: 8px;
|
||
margin-top: 10px;
|
||
padding-top: 10px;
|
||
background-color: #f8f9fa;
|
||
}
|
||
QGroupBox::title {
|
||
subcontrol-origin: margin;
|
||
left: 10px;
|
||
padding: 0 5px 0 5px;
|
||
}
|
||
""")
|
||
|
||
# 折叠/展开按钮
|
||
self.collapse_button = QPushButton("▼")
|
||
self.collapse_button.setFixedSize(30, 30)
|
||
self.collapse_button.setStyleSheet("""
|
||
QPushButton {
|
||
background-color: #3498db;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
}
|
||
QPushButton:hover {
|
||
background-color: #2980b9;
|
||
}
|
||
""")
|
||
self.collapse_button.clicked.connect(self.toggle_button_group)
|
||
|
||
# 按钮布局
|
||
button_layout = QHBoxLayout()
|
||
button_layout.setSpacing(15)
|
||
|
||
# 创建按钮
|
||
self.btn_5min = self.create_button("5分钟", lambda: self.start_countdown(300))
|
||
self.btn_6min = self.create_button("6分钟", lambda: self.start_countdown(360))
|
||
self.btn_other = self.create_button("其它", self.open_config, "#95a5a6")
|
||
|
||
button_layout.addWidget(self.btn_5min)
|
||
button_layout.addWidget(self.btn_6min)
|
||
button_layout.addWidget(self.btn_other)
|
||
|
||
# 控制按钮
|
||
control_layout = QHBoxLayout()
|
||
|
||
self.btn_pause = self.create_button("暂停", self.pause_countdown, "#f39c12")
|
||
self.btn_reset = self.create_button("重置", self.reset_countdown, "#e74c3c")
|
||
|
||
self.btn_pause.setEnabled(False)
|
||
self.btn_reset.setEnabled(False)
|
||
|
||
control_layout.addWidget(self.btn_pause)
|
||
control_layout.addWidget(self.btn_reset)
|
||
|
||
# 添加到按钮组
|
||
group_layout = QVBoxLayout()
|
||
group_layout.addLayout(button_layout)
|
||
group_layout.addLayout(control_layout)
|
||
self.button_group.setLayout(group_layout)
|
||
|
||
# 主布局添加折叠按钮和按钮组
|
||
main_layout.addWidget(self.collapse_button)
|
||
main_layout.addWidget(self.button_group)
|
||
|
||
# 默认折叠按钮区
|
||
self.button_group.hide()
|
||
self.collapse_button.setText("▶")
|
||
|
||
central_widget.setLayout(main_layout)
|
||
|
||
# 窗口样式
|
||
self.setStyleSheet("""
|
||
QMainWindow {
|
||
background-color: #ffffff;
|
||
}
|
||
""")
|
||
|
||
def create_button(self, text, callback, color="#3498db"):
|
||
"""创建样式化按钮"""
|
||
button = QPushButton(text)
|
||
button.clicked.connect(callback)
|
||
button.setMinimumHeight(20) # 减小按钮高度
|
||
button.setMinimumWidth(50) # 设置最小宽度
|
||
button.setStyleSheet(f"""
|
||
QPushButton {{
|
||
background-color: {color};
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
font-weight: bold;
|
||
padding: 2px 5px;
|
||
}}
|
||
QPushButton:hover {{
|
||
background-color: {self.darken_color(color)};
|
||
}}
|
||
QPushButton:pressed {{
|
||
background-color: {self.darken_color(color, 0.3)};
|
||
}}
|
||
QPushButton:disabled {{
|
||
background-color: #bdc3c7;
|
||
}}
|
||
""")
|
||
return button
|
||
|
||
def darken_color(self, hex_color, factor=0.15):
|
||
"""使颜色变暗"""
|
||
hex_color = hex_color.lstrip('#')
|
||
r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
|
||
r = int(r * (1 - factor))
|
||
g = int(g * (1 - factor))
|
||
b = int(b * (1 - factor))
|
||
return f"#{r:02x}{g:02x}{b:02x}"
|
||
|
||
def start_countdown(self, seconds):
|
||
"""开始倒计时"""
|
||
self.remaining_seconds = seconds
|
||
self.total_seconds = seconds
|
||
self.is_running = True
|
||
self.alert_played = False
|
||
self.alert_loop_count = 0
|
||
|
||
# 折叠按钮区域
|
||
self.button_group.hide()
|
||
self.collapse_button.setText("▶")
|
||
|
||
# 设置2秒后切换到微缩状态的定时器
|
||
if self.state_change_timer:
|
||
self.state_change_timer.stop()
|
||
self.state_change_timer = QTimer()
|
||
self.state_change_timer.timeout.connect(self.switch_to_mini_state)
|
||
self.state_change_timer.start(2000) # 2秒后切换
|
||
|
||
# 立即切换到微缩状态
|
||
self.switch_to_mini_state()
|
||
|
||
self.timer.start(1000)
|
||
self.update_display()
|
||
|
||
# 隐藏完成标签,显示倒计时
|
||
self.finish_label.hide()
|
||
self.time_label.show()
|
||
self.blink_timer.stop()
|
||
|
||
# 更新按钮状态
|
||
self.btn_pause.setEnabled(True)
|
||
self.btn_pause.setText("暂停")
|
||
self.btn_reset.setEnabled(True)
|
||
|
||
def pause_countdown(self):
|
||
"""暂停/继续倒计时"""
|
||
if self.is_running:
|
||
self.timer.stop()
|
||
self.is_running = False
|
||
self.btn_pause.setText("继续")
|
||
else:
|
||
self.timer.start(1000)
|
||
self.is_running = True
|
||
self.btn_pause.setText("暂停")
|
||
|
||
def reset_countdown(self):
|
||
"""重置倒计时"""
|
||
self.timer.stop()
|
||
self.blink_timer.stop()
|
||
self.is_running = False
|
||
self.remaining_seconds = 0
|
||
self.alert_played = False
|
||
self.alert_loop_count = 0
|
||
|
||
# 恢复原始窗口大小
|
||
self.resize(500, 300)
|
||
|
||
# 恢复窗口置顶状态
|
||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
|
||
self.show()
|
||
|
||
self.time_label.setText("00:00")
|
||
self.time_label.show()
|
||
self.finish_label.hide()
|
||
|
||
self.btn_pause.setEnabled(False)
|
||
self.btn_pause.setText("暂停")
|
||
self.btn_reset.setEnabled(False)
|
||
|
||
# 停止音频
|
||
if self.media_player:
|
||
self.media_player.stop()
|
||
|
||
def switch_to_mini_state(self):
|
||
"""切换到微缩状态"""
|
||
self.current_state = "mini"
|
||
|
||
# 调整窗口大小为微缩模式(200x80像素,增加高度以容纳倒计时)
|
||
self.resize(200, 80)
|
||
|
||
# 移动窗口位置到距离top 50px,距离右边200px
|
||
screen = QApplication.primaryScreen().geometry()
|
||
screen_width = screen.width()
|
||
screen_height = screen.height()
|
||
new_x = screen_width - 200 - 200 # 距离右边200px
|
||
new_y = 50 # 距离top 50px
|
||
self.move(new_x, new_y)
|
||
|
||
# 隐藏按钮区域
|
||
self.button_group.hide()
|
||
self.collapse_button.setText("▼")
|
||
|
||
# 调整布局边距以适应微缩状态
|
||
central_widget = self.centralWidget()
|
||
main_layout = central_widget.layout()
|
||
main_layout.setSpacing(5)
|
||
main_layout.setContentsMargins(10, 10, 10, 10)
|
||
|
||
# 调整倒计时标签大小以适应微缩状态
|
||
self.time_label.setStyleSheet("""
|
||
QLabel {
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
color: #3498db;
|
||
font-family: 'Courier New', monospace;
|
||
background-color: #ecf0f1;
|
||
border-radius: 3px;
|
||
padding: 2px;
|
||
}
|
||
""")
|
||
|
||
# 隐藏完成提示标签
|
||
self.finish_label.hide()
|
||
|
||
# 确保窗口在最前面
|
||
self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
|
||
self.show()
|
||
|
||
def switch_to_normal_state(self):
|
||
"""切换到正常状态"""
|
||
self.current_state = "normal"
|
||
|
||
# 取消2秒定时器(如果存在)
|
||
if self.state_change_timer and self.state_change_timer.isActive():
|
||
self.state_change_timer.stop()
|
||
|
||
# 恢复原始窗口大小
|
||
self.resize(500, 300)
|
||
|
||
# 恢复初始窗口位置
|
||
if hasattr(self, 'initial_pos') and self.initial_pos.x() >= 0 and self.initial_pos.y() >= 0:
|
||
self.move(self.initial_pos)
|
||
|
||
# 恢复窗口置顶状态
|
||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
|
||
self.show()
|
||
|
||
# 恢复布局边距
|
||
central_widget = self.centralWidget()
|
||
main_layout = central_widget.layout()
|
||
main_layout.setSpacing(20)
|
||
main_layout.setContentsMargins(30, 30, 30, 30)
|
||
|
||
# 恢复倒计时标签的正常样式
|
||
self.time_label.setStyleSheet("""
|
||
QLabel {
|
||
font-size: 72px;
|
||
font-weight: bold;
|
||
color: #3498db;
|
||
font-family: 'Courier New', monospace;
|
||
background-color: #ecf0f1;
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
}
|
||
""")
|
||
|
||
# 如果按钮区域之前是展开的,重新展开
|
||
if not self.button_group.isHidden():
|
||
self.button_group.hide()
|
||
self.collapse_button.setText("▶")
|
||
|
||
def update_timer(self):
|
||
"""更新计时器"""
|
||
if self.remaining_seconds > 0:
|
||
self.remaining_seconds -= 1
|
||
self.update_display()
|
||
|
||
# 检查是否需要播放告警
|
||
if (not self.alert_played and
|
||
self.remaining_seconds <= self.config["alert_seconds"] and
|
||
self.remaining_seconds > 0):
|
||
self.play_alert()
|
||
self.alert_played = True
|
||
else:
|
||
# 倒计时结束
|
||
self.timer.stop()
|
||
self.is_running = False
|
||
self.show_finish()
|
||
|
||
def update_display(self):
|
||
"""更新显示"""
|
||
minutes = self.remaining_seconds // 60
|
||
seconds = self.remaining_seconds % 60
|
||
self.time_label.setText(f"{minutes:02d}:{seconds:02d}")
|
||
|
||
# 根据剩余时间改变颜色
|
||
if self.remaining_seconds <= 10:
|
||
self.time_label.setStyleSheet("""
|
||
QLabel {
|
||
font-size: 72px;
|
||
font-weight: bold;
|
||
color: #e74c3c;
|
||
font-family: 'Courier New', monospace;
|
||
background-color: #fee;
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
}
|
||
""")
|
||
elif self.remaining_seconds <= self.config["alert_seconds"]:
|
||
self.time_label.setStyleSheet("""
|
||
QLabel {
|
||
font-size: 72px;
|
||
font-weight: bold;
|
||
color: #f39c12;
|
||
font-family: 'Courier New', monospace;
|
||
background-color: #fef5e7;
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
}
|
||
""")
|
||
else:
|
||
self.time_label.setStyleSheet("""
|
||
QLabel {
|
||
font-size: 72px;
|
||
font-weight: bold;
|
||
color: #3498db;
|
||
font-family: 'Courier New', monospace;
|
||
background-color: #ecf0f1;
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
}
|
||
""")
|
||
|
||
def play_alert(self):
|
||
"""播放告警提示音"""
|
||
# 使用系统提示音
|
||
# 注意:这里使用一个简单的实现
|
||
# 实际使用中,你可以替换为自定义音频文件
|
||
try:
|
||
# 尝试播放系统提示音
|
||
QApplication.beep()
|
||
|
||
# 设置循环播放2次(总共3次)
|
||
self.alert_loop_count = 0
|
||
self.alert_timer = QTimer()
|
||
self.alert_timer.timeout.connect(self.play_alert_loop)
|
||
self.alert_timer.start(1000) # 每秒播放一次
|
||
|
||
except Exception as e:
|
||
print(f"播放告警音失败: {e}")
|
||
|
||
def play_alert_loop(self):
|
||
"""循环播放告警"""
|
||
if self.alert_loop_count < 2:
|
||
QApplication.beep()
|
||
self.alert_loop_count += 1
|
||
else:
|
||
self.alert_timer.stop()
|
||
|
||
def show_finish(self):
|
||
"""显示完成提示"""
|
||
# 调整窗口大小为缩小模式(150x50像素)
|
||
self.resize(150, 50)
|
||
|
||
# 移动窗口位置到距离top 30px,距离右边边缘30px
|
||
screen = QApplication.primaryScreen().geometry()
|
||
screen_width = screen.width()
|
||
screen_height = screen.height()
|
||
new_x = screen_width - 150 - 30 # 距离右边30px
|
||
new_y = 30 # 距离top 30px
|
||
self.move(new_x, new_y)
|
||
|
||
# 确保窗口在最前面
|
||
self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
|
||
self.show()
|
||
|
||
self.time_label.hide()
|
||
self.finish_label.show()
|
||
|
||
# 开始闪烁动画
|
||
self.blink_state = True
|
||
self.blink_timer.start(500) # 每500ms切换一次
|
||
|
||
# 播放提示音
|
||
QApplication.beep()
|
||
|
||
self.btn_pause.setEnabled(False)
|
||
|
||
def toggle_blink(self):
|
||
"""切换闪烁状态"""
|
||
if self.blink_state:
|
||
self.finish_label.setStyleSheet("""
|
||
QLabel {
|
||
font-size: 48px;
|
||
font-weight: bold;
|
||
color: #e74c3c;
|
||
background-color: #fee;
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
}
|
||
""")
|
||
else:
|
||
self.finish_label.setStyleSheet("""
|
||
QLabel {
|
||
font-size: 52px;
|
||
font-weight: bold;
|
||
color: #c0392b;
|
||
background-color: #fadbd8;
|
||
border-radius: 10px;
|
||
padding: 25px;
|
||
}
|
||
""")
|
||
self.blink_state = not self.blink_state
|
||
|
||
def toggle_button_group(self):
|
||
"""切换按钮区域的折叠/展开状态"""
|
||
# 如果当前是微缩状态,点击箭头暂停倒计时并切换回正常状态
|
||
if self.current_state == "mini":
|
||
# 暂停倒计时
|
||
self.timer.stop()
|
||
self.is_running = False
|
||
self.btn_pause.setText("暂停")
|
||
|
||
# 切换回正常状态
|
||
self.switch_to_normal_state()
|
||
return
|
||
|
||
# 如果当前是正常状态
|
||
if self.button_group.isHidden():
|
||
self.button_group.show()
|
||
self.collapse_button.setText("▼")
|
||
|
||
# 如果正在运行倒计时,恢复原始窗口大小
|
||
if self.is_running:
|
||
self.switch_to_normal_state()
|
||
else:
|
||
self.button_group.hide()
|
||
self.collapse_button.setText("▶")
|
||
|
||
# 如果正在运行倒计时,暂停倒计时并切换到微缩状态
|
||
if self.is_running:
|
||
# 暂停倒计时
|
||
self.timer.stop()
|
||
self.is_running = False
|
||
|
||
# 切换到微缩状态
|
||
self.switch_to_mini_state()
|
||
|
||
# 按钮文本保持为"暂停",因为倒计时已经被暂停
|
||
self.btn_pause.setText("暂停")
|
||
|
||
def open_config(self):
|
||
"""打开配置对话框"""
|
||
dialog = ConfigDialog(self)
|
||
|
||
# 加载当前配置
|
||
dialog.custom_time_spin.setValue(self.config["custom_seconds"])
|
||
dialog.alert_time_spin.setValue(self.config["alert_seconds"])
|
||
dialog.topmost_checkbox.setChecked(self.config["topmost"])
|
||
dialog.opacity_slider.setValue(self.config["opacity"])
|
||
|
||
if dialog.exec() == QDialog.Accepted:
|
||
# 保存配置
|
||
self.config["custom_seconds"] = dialog.custom_time_spin.value()
|
||
self.config["alert_seconds"] = dialog.alert_time_spin.value()
|
||
self.config["topmost"] = dialog.topmost_checkbox.isChecked()
|
||
self.config["opacity"] = dialog.opacity_slider.value()
|
||
|
||
self.save_config()
|
||
self.apply_config()
|
||
|
||
# 启动自定义倒计时
|
||
self.start_countdown(self.config["custom_seconds"])
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
app = QApplication(sys.argv)
|
||
|
||
# 设置应用程序样式
|
||
app.setStyle("Fusion")
|
||
|
||
window = CountdownTimer()
|
||
window.show()
|
||
|
||
sys.exit(app.exec())
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|