Initial commit: 述职计时器
This commit is contained in:
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
dist/
|
||||
*.spec
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
832
countdown.py
Normal file
832
countdown.py
Normal file
@@ -0,0 +1,832 @@
|
||||
"""
|
||||
述职计时器 - 倒计时应用程序
|
||||
使用 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()
|
||||
18
要求.txt
Normal file
18
要求.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
目的:写一个python语言的,pyside6库作为界面的应用程序。
|
||||
功能:倒计时显示和提醒。
|
||||
界面需求:整体要美观大方。
|
||||
1. 窗口无标题栏,可以通过点击并拖动任何位置移动窗口
|
||||
2. 倒计时显示区域:显示分和秒的倒计时,字体大且醒目
|
||||
3. 界面分为2个状态:
|
||||
a. 正常状态:显示时间,点击箭头,显示按钮区域,再点击箭头,按钮区域折叠
|
||||
b. 微缩状态:点击"5分钟"后,延迟2秒,界面改为微缩状态,只有时间区域和向下箭头
|
||||
4. 微缩状态详情:
|
||||
- 窗口长为200px,高为60px
|
||||
- 距离top为50px,距离屏幕右边留出空白200px的距离
|
||||
- 点击向下箭头,回到正常状态的位置和大小
|
||||
5. 快捷按钮:第一个按钮是"5分钟",第二个按钮是"6分钟",第三个按钮是"其它"
|
||||
6. 其它按钮功能:打开配置页面,可以设置自定义倒计时时间、提前告警时间、窗口置顶选项、透明度设置
|
||||
7. 告警功能:倒计时结束前设置的秒数时播放告警提示音,循环3次
|
||||
8. 倒计时结束后:显示"时间已到"文字,带有跳动和闪动效果
|
||||
9. 配置对话框:增加退出按钮,只有通过此按钮才能退出程序
|
||||
10. 测试要求:使用pyqt-test库作为gui程序的测试工具。
|
||||
Reference in New Issue
Block a user