timer_widget.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. """Timer widget for displaying in the main window."""
  2. from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFrame
  3. from PyQt6.QtCore import QTimer, Qt, pyqtSignal
  4. from PyQt6.QtGui import QFont
  5. class TimerWidget(QFrame):
  6. finished = pyqtSignal()
  7. time_updated = pyqtSignal(int) # Emits remaining seconds
  8. state_changed = pyqtSignal(bool) # Emits is_paused state
  9. def __init__(self, timer, is_mirror=False):
  10. super().__init__()
  11. self.timer_model = timer
  12. self.remaining_seconds = timer.duration
  13. self.is_paused = False
  14. self.is_mirror = is_mirror
  15. self.countdown_timer = None
  16. self.init_ui()
  17. if not is_mirror:
  18. self.start_countdown()
  19. def init_ui(self):
  20. # Frame styling
  21. self.setFrameStyle(QFrame.Shape.Box | QFrame.Shadow.Raised)
  22. self.setLineWidth(2)
  23. self.setStyleSheet(f"""
  24. QFrame {{
  25. background-color: {self.timer_model.bg_color};
  26. border: 3px solid {self.timer_model.text_color};
  27. border-radius: 10px;
  28. margin: 5px;
  29. }}
  30. """)
  31. # Main layout
  32. layout = QVBoxLayout(self)
  33. layout.setContentsMargins(20, 20, 20, 20)
  34. # Timer title
  35. self.title_label = QLabel(self.timer_model.name)
  36. self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
  37. self.title_label.setStyleSheet(f"color: {self.timer_model.text_color}; border: none;")
  38. title_font = QFont("Arial", 18, QFont.Weight.Bold)
  39. self.title_label.setFont(title_font)
  40. layout.addWidget(self.title_label)
  41. # Timer display
  42. self.time_label = QLabel(self.format_time(self.remaining_seconds))
  43. self.time_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
  44. self.time_label.setStyleSheet(f"color: {self.timer_model.text_color}; border: none;")
  45. time_font = QFont("Arial", 48, QFont.Weight.Bold)
  46. self.time_label.setFont(time_font)
  47. layout.addWidget(self.time_label)
  48. # Control buttons
  49. control_layout = QHBoxLayout()
  50. button_style = f"""
  51. QPushButton {{
  52. background-color: rgba(255, 255, 255, 0.2);
  53. color: {self.timer_model.text_color};
  54. border: 2px solid {self.timer_model.text_color};
  55. border-radius: 5px;
  56. padding: 10px 20px;
  57. font-size: 14px;
  58. font-weight: bold;
  59. }}
  60. QPushButton:hover {{
  61. background-color: rgba(255, 255, 255, 0.3);
  62. }}
  63. QPushButton:pressed {{
  64. background-color: rgba(255, 255, 255, 0.4);
  65. }}
  66. """
  67. self.pause_btn = QPushButton("Pause")
  68. self.pause_btn.setStyleSheet(button_style)
  69. self.pause_btn.clicked.connect(self.toggle_pause)
  70. control_layout.addWidget(self.pause_btn)
  71. self.restart_btn = QPushButton("Restart")
  72. self.restart_btn.setStyleSheet(button_style)
  73. self.restart_btn.clicked.connect(self.restart_timer)
  74. control_layout.addWidget(self.restart_btn)
  75. self.stop_btn = QPushButton("Stop")
  76. self.stop_btn.setStyleSheet(button_style)
  77. self.stop_btn.clicked.connect(self.stop_timer)
  78. control_layout.addWidget(self.stop_btn)
  79. layout.addLayout(control_layout)
  80. # Hide controls for mirror widgets
  81. if self.is_mirror:
  82. self.pause_btn.hide()
  83. self.restart_btn.hide()
  84. self.stop_btn.hide()
  85. # Set minimum size
  86. self.setMinimumHeight(200)
  87. def start_countdown(self):
  88. self.countdown_timer = QTimer(self)
  89. self.countdown_timer.timeout.connect(self.update_timer)
  90. self.countdown_timer.start(1000) # Update every second
  91. def update_timer(self):
  92. if not self.is_paused:
  93. self.remaining_seconds -= 1
  94. if self.remaining_seconds <= 0:
  95. self.countdown_timer.stop()
  96. self.time_label.setText("TIME'S UP!")
  97. self.pause_btn.setEnabled(False)
  98. self.time_updated.emit(0)
  99. QTimer.singleShot(3000, self.close_timer) # Close after 3 seconds
  100. else:
  101. self.time_label.setText(self.format_time(self.remaining_seconds))
  102. self.time_updated.emit(self.remaining_seconds)
  103. def format_time(self, seconds):
  104. hours = seconds // 3600
  105. minutes = (seconds % 3600) // 60
  106. secs = seconds % 60
  107. if hours > 0:
  108. return f"{hours:02d}:{minutes:02d}:{secs:02d}"
  109. else:
  110. return f"{minutes:02d}:{secs:02d}"
  111. def toggle_pause(self):
  112. """Toggle pause/resume state."""
  113. self.is_paused = not self.is_paused
  114. if self.is_paused:
  115. self.pause_btn.setText("Resume")
  116. else:
  117. self.pause_btn.setText("Pause")
  118. self.state_changed.emit(self.is_paused)
  119. def restart_timer(self):
  120. """Restart the timer from the beginning."""
  121. self.remaining_seconds = self.timer_model.duration
  122. self.time_label.setText(self.format_time(self.remaining_seconds))
  123. self.is_paused = False
  124. self.pause_btn.setText("Pause")
  125. self.pause_btn.setEnabled(True)
  126. if not self.countdown_timer.isActive():
  127. self.countdown_timer.start(1000)
  128. self.time_updated.emit(self.remaining_seconds)
  129. self.state_changed.emit(self.is_paused)
  130. def stop_timer(self):
  131. """Stop the timer and remove widget."""
  132. if self.countdown_timer and self.countdown_timer.isActive():
  133. self.countdown_timer.stop()
  134. self.close_timer()
  135. def close_timer(self):
  136. """Emit finished signal to remove this widget."""
  137. self.finished.emit()
  138. def sync_with(self, source_widget):
  139. """Sync this mirror widget with the source widget."""
  140. self.remaining_seconds = source_widget.remaining_seconds
  141. self.is_paused = source_widget.is_paused
  142. self.time_label.setText(self.format_time(self.remaining_seconds))
  143. def update_display(self, remaining_seconds):
  144. """Update the display with new time (for mirror widgets)."""
  145. self.remaining_seconds = remaining_seconds
  146. if remaining_seconds <= 0:
  147. self.time_label.setText("TIME'S UP!")
  148. else:
  149. self.time_label.setText(self.format_time(remaining_seconds))
  150. def update_state(self, is_paused):
  151. """Update the paused state (for mirror widgets)."""
  152. self.is_paused = is_paused