| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- """Timer widget for displaying in the main window."""
- from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFrame
- from PyQt6.QtCore import QTimer, Qt, pyqtSignal
- from PyQt6.QtGui import QFont
- class TimerWidget(QFrame):
- finished = pyqtSignal()
- time_updated = pyqtSignal(int) # Emits remaining seconds
- state_changed = pyqtSignal(bool) # Emits is_paused state
-
- def __init__(self, timer, is_mirror=False):
- super().__init__()
- self.timer_model = timer
- self.remaining_seconds = timer.duration
- self.is_paused = False
- self.is_mirror = is_mirror
- self.countdown_timer = None
- self.init_ui()
- if not is_mirror:
- self.start_countdown()
-
- def init_ui(self):
- # Frame styling
- self.setFrameStyle(QFrame.Shape.Box | QFrame.Shadow.Raised)
- self.setLineWidth(2)
- self.setStyleSheet(f"""
- QFrame {{
- background-color: {self.timer_model.bg_color};
- border: 3px solid {self.timer_model.text_color};
- border-radius: 10px;
- margin: 5px;
- }}
- """)
-
- # Main layout
- layout = QVBoxLayout(self)
- layout.setContentsMargins(20, 20, 20, 20)
-
- # Timer title
- self.title_label = QLabel(self.timer_model.name)
- self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- self.title_label.setStyleSheet(f"color: {self.timer_model.text_color}; border: none;")
-
- title_font = QFont("Arial", 18, QFont.Weight.Bold)
- self.title_label.setFont(title_font)
-
- layout.addWidget(self.title_label)
-
- # Timer display
- self.time_label = QLabel(self.format_time(self.remaining_seconds))
- self.time_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- self.time_label.setStyleSheet(f"color: {self.timer_model.text_color}; border: none;")
-
- time_font = QFont("Arial", 48, QFont.Weight.Bold)
- self.time_label.setFont(time_font)
-
- layout.addWidget(self.time_label)
-
- # Control buttons
- control_layout = QHBoxLayout()
-
- button_style = f"""
- QPushButton {{
- background-color: rgba(255, 255, 255, 0.2);
- color: {self.timer_model.text_color};
- border: 2px solid {self.timer_model.text_color};
- border-radius: 5px;
- padding: 10px 20px;
- font-size: 14px;
- font-weight: bold;
- }}
- QPushButton:hover {{
- background-color: rgba(255, 255, 255, 0.3);
- }}
- QPushButton:pressed {{
- background-color: rgba(255, 255, 255, 0.4);
- }}
- """
-
- self.pause_btn = QPushButton("Pause")
- self.pause_btn.setStyleSheet(button_style)
- self.pause_btn.clicked.connect(self.toggle_pause)
- control_layout.addWidget(self.pause_btn)
-
- self.restart_btn = QPushButton("Restart")
- self.restart_btn.setStyleSheet(button_style)
- self.restart_btn.clicked.connect(self.restart_timer)
- control_layout.addWidget(self.restart_btn)
-
- self.stop_btn = QPushButton("Stop")
- self.stop_btn.setStyleSheet(button_style)
- self.stop_btn.clicked.connect(self.stop_timer)
- control_layout.addWidget(self.stop_btn)
-
- layout.addLayout(control_layout)
-
- # Hide controls for mirror widgets
- if self.is_mirror:
- self.pause_btn.hide()
- self.restart_btn.hide()
- self.stop_btn.hide()
-
- # Set minimum size
- self.setMinimumHeight(200)
-
- def start_countdown(self):
- self.countdown_timer = QTimer(self)
- self.countdown_timer.timeout.connect(self.update_timer)
- self.countdown_timer.start(1000) # Update every second
-
- def update_timer(self):
- if not self.is_paused:
- self.remaining_seconds -= 1
-
- if self.remaining_seconds <= 0:
- self.countdown_timer.stop()
- self.time_label.setText("TIME'S UP!")
- self.pause_btn.setEnabled(False)
- self.time_updated.emit(0)
- QTimer.singleShot(3000, self.close_timer) # Close after 3 seconds
- else:
- self.time_label.setText(self.format_time(self.remaining_seconds))
- self.time_updated.emit(self.remaining_seconds)
-
- def format_time(self, seconds):
- hours = seconds // 3600
- minutes = (seconds % 3600) // 60
- secs = seconds % 60
-
- if hours > 0:
- return f"{hours:02d}:{minutes:02d}:{secs:02d}"
- else:
- return f"{minutes:02d}:{secs:02d}"
-
- def toggle_pause(self):
- """Toggle pause/resume state."""
- self.is_paused = not self.is_paused
- if self.is_paused:
- self.pause_btn.setText("Resume")
- else:
- self.pause_btn.setText("Pause")
- self.state_changed.emit(self.is_paused)
-
- def restart_timer(self):
- """Restart the timer from the beginning."""
- self.remaining_seconds = self.timer_model.duration
- self.time_label.setText(self.format_time(self.remaining_seconds))
- self.is_paused = False
- self.pause_btn.setText("Pause")
- self.pause_btn.setEnabled(True)
-
- if not self.countdown_timer.isActive():
- self.countdown_timer.start(1000)
-
- self.time_updated.emit(self.remaining_seconds)
- self.state_changed.emit(self.is_paused)
-
- def stop_timer(self):
- """Stop the timer and remove widget."""
- if self.countdown_timer and self.countdown_timer.isActive():
- self.countdown_timer.stop()
- self.close_timer()
-
- def close_timer(self):
- """Emit finished signal to remove this widget."""
- self.finished.emit()
-
- def sync_with(self, source_widget):
- """Sync this mirror widget with the source widget."""
- self.remaining_seconds = source_widget.remaining_seconds
- self.is_paused = source_widget.is_paused
- self.time_label.setText(self.format_time(self.remaining_seconds))
-
- def update_display(self, remaining_seconds):
- """Update the display with new time (for mirror widgets)."""
- self.remaining_seconds = remaining_seconds
- if remaining_seconds <= 0:
- self.time_label.setText("TIME'S UP!")
- else:
- self.time_label.setText(self.format_time(remaining_seconds))
-
- def update_state(self, is_paused):
- """Update the paused state (for mirror widgets)."""
- self.is_paused = is_paused
|