|
@@ -0,0 +1,281 @@
|
|
|
|
|
+"""Main window for the countdown timer application."""
|
|
|
|
|
+
|
|
|
|
|
+from PyQt6.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
|
|
|
|
+ QPushButton, QListWidget, QGroupBox, QLabel,
|
|
|
|
|
+ QScrollArea, QFrame)
|
|
|
|
|
+from PyQt6.QtCore import QSettings, Qt, QTimer
|
|
|
|
|
+from PyQt6.QtGui import QFont
|
|
|
|
|
+from ui.settings_dialog import SettingsDialog
|
|
|
|
|
+from ui.timer_dialog import TimerDialog
|
|
|
|
|
+from ui.timer_widget import TimerWidget
|
|
|
|
|
+from ui.fullscreen_display import FullscreenDisplay
|
|
|
|
|
+from models.timer_model import Timer
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class MainWindow(QMainWindow):
|
|
|
|
|
+ def __init__(self):
|
|
|
|
|
+ super().__init__()
|
|
|
|
|
+ self.settings = QSettings()
|
|
|
|
|
+ self.timers = []
|
|
|
|
|
+ self.timer_widgets = []
|
|
|
|
|
+ self.fullscreen_display = None
|
|
|
|
|
+ self.init_ui()
|
|
|
|
|
+ self.load_timers()
|
|
|
|
|
+
|
|
|
|
|
+ def init_ui(self):
|
|
|
|
|
+ self.setWindowTitle("Countdown Timer Manager")
|
|
|
|
|
+ self.setMinimumSize(1000, 600)
|
|
|
|
|
+
|
|
|
|
|
+ # Central widget
|
|
|
|
|
+ central_widget = QWidget()
|
|
|
|
|
+ self.setCentralWidget(central_widget)
|
|
|
|
|
+ main_layout = QHBoxLayout(central_widget)
|
|
|
|
|
+
|
|
|
|
|
+ # Left panel - Timer management
|
|
|
|
|
+ left_panel = QWidget()
|
|
|
|
|
+ left_layout = QVBoxLayout(left_panel)
|
|
|
|
|
+ left_panel.setMaximumWidth(350)
|
|
|
|
|
+
|
|
|
|
|
+ # Timer list group
|
|
|
|
|
+ timer_group = QGroupBox("Timer Library")
|
|
|
|
|
+ timer_layout = QVBoxLayout()
|
|
|
|
|
+
|
|
|
|
|
+ self.timer_list = QListWidget()
|
|
|
|
|
+ timer_layout.addWidget(self.timer_list)
|
|
|
|
|
+
|
|
|
|
|
+ # Timer management buttons
|
|
|
|
|
+ timer_btn_layout = QVBoxLayout()
|
|
|
|
|
+ self.add_timer_btn = QPushButton("Add Timer")
|
|
|
|
|
+ self.edit_timer_btn = QPushButton("Edit Timer")
|
|
|
|
|
+ self.delete_timer_btn = QPushButton("Delete Timer")
|
|
|
|
|
+ self.start_timer_btn = QPushButton("Start Selected Timer")
|
|
|
|
|
+
|
|
|
|
|
+ self.add_timer_btn.clicked.connect(self.add_timer)
|
|
|
|
|
+ self.edit_timer_btn.clicked.connect(self.edit_timer)
|
|
|
|
|
+ self.delete_timer_btn.clicked.connect(self.delete_timer)
|
|
|
|
|
+ self.start_timer_btn.clicked.connect(self.start_timer)
|
|
|
|
|
+
|
|
|
|
|
+ timer_btn_layout.addWidget(self.add_timer_btn)
|
|
|
|
|
+ timer_btn_layout.addWidget(self.edit_timer_btn)
|
|
|
|
|
+ timer_btn_layout.addWidget(self.delete_timer_btn)
|
|
|
|
|
+ timer_btn_layout.addWidget(self.start_timer_btn)
|
|
|
|
|
+
|
|
|
|
|
+ timer_layout.addLayout(timer_btn_layout)
|
|
|
|
|
+ timer_group.setLayout(timer_layout)
|
|
|
|
|
+ left_layout.addWidget(timer_group)
|
|
|
|
|
+
|
|
|
|
|
+ # Global controls
|
|
|
|
|
+ controls_group = QGroupBox("Global Controls")
|
|
|
|
|
+ controls_layout = QVBoxLayout()
|
|
|
|
|
+
|
|
|
|
|
+ self.stop_all_btn = QPushButton("Stop All Timers")
|
|
|
|
|
+ self.pause_all_btn = QPushButton("Pause All Timers")
|
|
|
|
|
+ self.resume_all_btn = QPushButton("Resume All Timers")
|
|
|
|
|
+
|
|
|
|
|
+ self.stop_all_btn.clicked.connect(self.stop_all_timers)
|
|
|
|
|
+ self.pause_all_btn.clicked.connect(self.pause_all_timers)
|
|
|
|
|
+ self.resume_all_btn.clicked.connect(self.resume_all_timers)
|
|
|
|
|
+
|
|
|
|
|
+ controls_layout.addWidget(self.pause_all_btn)
|
|
|
|
|
+ controls_layout.addWidget(self.resume_all_btn)
|
|
|
|
|
+ controls_layout.addWidget(self.stop_all_btn)
|
|
|
|
|
+
|
|
|
|
|
+ controls_group.setLayout(controls_layout)
|
|
|
|
|
+ left_layout.addWidget(controls_group)
|
|
|
|
|
+
|
|
|
|
|
+ # Display mode group
|
|
|
|
|
+ display_group = QGroupBox("Display Mode")
|
|
|
|
|
+ display_layout = QVBoxLayout()
|
|
|
|
|
+
|
|
|
|
|
+ self.fullscreen_btn = QPushButton("Show Fullscreen on Monitor")
|
|
|
|
|
+ self.fullscreen_btn.clicked.connect(self.show_fullscreen_display)
|
|
|
|
|
+ display_layout.addWidget(self.fullscreen_btn)
|
|
|
|
|
+
|
|
|
|
|
+ display_group.setLayout(display_layout)
|
|
|
|
|
+ left_layout.addWidget(display_group)
|
|
|
|
|
+
|
|
|
|
|
+ # Settings button
|
|
|
|
|
+ settings_btn = QPushButton("Settings")
|
|
|
|
|
+ settings_btn.clicked.connect(self.open_settings)
|
|
|
|
|
+ left_layout.addWidget(settings_btn)
|
|
|
|
|
+
|
|
|
|
|
+ left_layout.addStretch()
|
|
|
|
|
+ main_layout.addWidget(left_panel)
|
|
|
|
|
+
|
|
|
|
|
+ # Right panel - Active timers display
|
|
|
|
|
+ right_panel = QWidget()
|
|
|
|
|
+ right_layout = QVBoxLayout(right_panel)
|
|
|
|
|
+
|
|
|
|
|
+ title_label = QLabel("Active Timers")
|
|
|
|
|
+ title_font = QFont("Arial", 16, QFont.Weight.Bold)
|
|
|
|
|
+ title_label.setFont(title_font)
|
|
|
|
|
+ title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
|
|
|
+ right_layout.addWidget(title_label)
|
|
|
|
|
+
|
|
|
|
|
+ # Scroll area for timers
|
|
|
|
|
+ scroll_area = QScrollArea()
|
|
|
|
|
+ scroll_area.setWidgetResizable(True)
|
|
|
|
|
+ scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
|
|
|
+
|
|
|
|
|
+ self.timers_container = QWidget()
|
|
|
|
|
+ self.timers_layout = QVBoxLayout(self.timers_container)
|
|
|
|
|
+ self.timers_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
|
|
|
|
+
|
|
|
|
|
+ scroll_area.setWidget(self.timers_container)
|
|
|
|
|
+ right_layout.addWidget(scroll_area)
|
|
|
|
|
+
|
|
|
|
|
+ main_layout.addWidget(right_panel, stretch=1)
|
|
|
|
|
+
|
|
|
|
|
+ # Status bar
|
|
|
|
|
+ self.statusBar().showMessage("Ready")
|
|
|
|
|
+
|
|
|
|
|
+ def add_timer(self):
|
|
|
|
|
+ dialog = TimerDialog(self)
|
|
|
|
|
+ if dialog.exec():
|
|
|
|
|
+ timer = dialog.get_timer()
|
|
|
|
|
+ self.timers.append(timer)
|
|
|
|
|
+ self.timer_list.addItem(timer.name)
|
|
|
|
|
+ self.save_timers()
|
|
|
|
|
+ self.statusBar().showMessage(f"Timer '{timer.name}' added", 3000)
|
|
|
|
|
+
|
|
|
|
|
+ def edit_timer(self):
|
|
|
|
|
+ current_row = self.timer_list.currentRow()
|
|
|
|
|
+ if current_row >= 0:
|
|
|
|
|
+ timer = self.timers[current_row]
|
|
|
|
|
+ dialog = TimerDialog(self, timer)
|
|
|
|
|
+ if dialog.exec():
|
|
|
|
|
+ updated_timer = dialog.get_timer()
|
|
|
|
|
+ self.timers[current_row] = updated_timer
|
|
|
|
|
+ self.timer_list.item(current_row).setText(updated_timer.name)
|
|
|
|
|
+ self.save_timers()
|
|
|
|
|
+ self.statusBar().showMessage(f"Timer '{updated_timer.name}' updated", 3000)
|
|
|
|
|
+
|
|
|
|
|
+ def delete_timer(self):
|
|
|
|
|
+ current_row = self.timer_list.currentRow()
|
|
|
|
|
+ if current_row >= 0:
|
|
|
|
|
+ timer_name = self.timers[current_row].name
|
|
|
|
|
+ del self.timers[current_row]
|
|
|
|
|
+ self.timer_list.takeItem(current_row)
|
|
|
|
|
+ self.save_timers()
|
|
|
|
|
+ self.statusBar().showMessage(f"Timer '{timer_name}' deleted", 3000)
|
|
|
|
|
+
|
|
|
|
|
+ def start_timer(self):
|
|
|
|
|
+ current_row = self.timer_list.currentRow()
|
|
|
|
|
+ if current_row >= 0:
|
|
|
|
|
+ timer = self.timers[current_row]
|
|
|
|
|
+
|
|
|
|
|
+ # Create timer widget
|
|
|
|
|
+ timer_widget = TimerWidget(timer)
|
|
|
|
|
+ timer_widget.finished.connect(lambda: self.remove_timer_widget(timer_widget))
|
|
|
|
|
+
|
|
|
|
|
+ self.timer_widgets.append(timer_widget)
|
|
|
|
|
+ self.timers_layout.addWidget(timer_widget)
|
|
|
|
|
+
|
|
|
|
|
+ # If fullscreen display is active, update it
|
|
|
|
|
+ if self.fullscreen_display and self.fullscreen_display.isVisible():
|
|
|
|
|
+ self.fullscreen_display.add_timer_widget(timer_widget)
|
|
|
|
|
+
|
|
|
|
|
+ self.statusBar().showMessage(f"Timer '{timer.name}' started", 3000)
|
|
|
|
|
+
|
|
|
|
|
+ def remove_timer_widget(self, timer_widget):
|
|
|
|
|
+ if timer_widget in self.timer_widgets:
|
|
|
|
|
+ self.timer_widgets.remove(timer_widget)
|
|
|
|
|
+ self.timers_layout.removeWidget(timer_widget)
|
|
|
|
|
+ timer_widget.deleteLater()
|
|
|
|
|
+
|
|
|
|
|
+ # Update fullscreen display if active
|
|
|
|
|
+ if self.fullscreen_display and self.fullscreen_display.isVisible():
|
|
|
|
|
+ self.fullscreen_display.remove_timer_widget(timer_widget)
|
|
|
|
|
+
|
|
|
|
|
+ self.statusBar().showMessage("Timer finished", 3000)
|
|
|
|
|
+
|
|
|
|
|
+ def show_fullscreen_display(self):
|
|
|
|
|
+ """Show all active timers on a fullscreen display."""
|
|
|
|
|
+ if not self.timer_widgets:
|
|
|
|
|
+ self.statusBar().showMessage("No timers running. Start a timer first.", 3000)
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ monitor_index = self.settings.value("monitor_index", 0, type=int)
|
|
|
|
|
+
|
|
|
|
|
+ if self.fullscreen_display is None:
|
|
|
|
|
+ self.fullscreen_display = FullscreenDisplay(monitor_index)
|
|
|
|
|
+ self.fullscreen_display.closed.connect(self.on_fullscreen_closed)
|
|
|
|
|
+
|
|
|
|
|
+ # Add all active timer widgets to fullscreen display
|
|
|
|
|
+ for timer_widget in self.timer_widgets:
|
|
|
|
|
+ self.fullscreen_display.add_timer_widget(timer_widget)
|
|
|
|
|
+
|
|
|
|
|
+ self.fullscreen_display.show()
|
|
|
|
|
+ self.statusBar().showMessage("Fullscreen display opened on selected monitor", 3000)
|
|
|
|
|
+
|
|
|
|
|
+ def on_fullscreen_closed(self):
|
|
|
|
|
+ """Handle fullscreen display being closed."""
|
|
|
|
|
+ self.fullscreen_display = None
|
|
|
|
|
+ self.statusBar().showMessage("Fullscreen display closed", 3000)
|
|
|
|
|
+
|
|
|
|
|
+ def stop_all_timers(self):
|
|
|
|
|
+ if not self.timer_widgets:
|
|
|
|
|
+ self.statusBar().showMessage("No timers running", 3000)
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ count = len(self.timer_widgets)
|
|
|
|
|
+ for timer_widget in list(self.timer_widgets):
|
|
|
|
|
+ timer_widget.stop_timer()
|
|
|
|
|
+
|
|
|
|
|
+ self.statusBar().showMessage(f"Stopped {count} timer(s)", 3000)
|
|
|
|
|
+
|
|
|
|
|
+ def pause_all_timers(self):
|
|
|
|
|
+ if not self.timer_widgets:
|
|
|
|
|
+ self.statusBar().showMessage("No timers running", 3000)
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ count = 0
|
|
|
|
|
+ for timer_widget in self.timer_widgets:
|
|
|
|
|
+ if not timer_widget.is_paused:
|
|
|
|
|
+ timer_widget.toggle_pause()
|
|
|
|
|
+ count += 1
|
|
|
|
|
+
|
|
|
|
|
+ self.statusBar().showMessage(f"Paused {count} timer(s)", 3000)
|
|
|
|
|
+
|
|
|
|
|
+ def resume_all_timers(self):
|
|
|
|
|
+ if not self.timer_widgets:
|
|
|
|
|
+ self.statusBar().showMessage("No timers running", 3000)
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ count = 0
|
|
|
|
|
+ for timer_widget in self.timer_widgets:
|
|
|
|
|
+ if timer_widget.is_paused:
|
|
|
|
|
+ timer_widget.toggle_pause()
|
|
|
|
|
+ count += 1
|
|
|
|
|
+
|
|
|
|
|
+ self.statusBar().showMessage(f"Resumed {count} timer(s)", 3000)
|
|
|
|
|
+
|
|
|
|
|
+ def open_settings(self):
|
|
|
|
|
+ dialog = SettingsDialog(self)
|
|
|
|
|
+ dialog.exec()
|
|
|
|
|
+
|
|
|
|
|
+ def save_timers(self):
|
|
|
|
|
+ self.settings.beginWriteArray("timers")
|
|
|
|
|
+ for i, timer in enumerate(self.timers):
|
|
|
|
|
+ self.settings.setArrayIndex(i)
|
|
|
|
|
+ self.settings.setValue("name", timer.name)
|
|
|
|
|
+ self.settings.setValue("duration", timer.duration)
|
|
|
|
|
+ self.settings.setValue("font_size", timer.font_size)
|
|
|
|
|
+ self.settings.setValue("bg_color", timer.bg_color)
|
|
|
|
|
+ self.settings.setValue("text_color", timer.text_color)
|
|
|
|
|
+ self.settings.endArray()
|
|
|
|
|
+
|
|
|
|
|
+ def load_timers(self):
|
|
|
|
|
+ size = self.settings.beginReadArray("timers")
|
|
|
|
|
+ for i in range(size):
|
|
|
|
|
+ self.settings.setArrayIndex(i)
|
|
|
|
|
+ timer = Timer(
|
|
|
|
|
+ name=self.settings.value("name", ""),
|
|
|
|
|
+ duration=self.settings.value("duration", 60, type=int),
|
|
|
|
|
+ font_size=self.settings.value("font_size", 120, type=int),
|
|
|
|
|
+ bg_color=self.settings.value("bg_color", "#000000"),
|
|
|
|
|
+ text_color=self.settings.value("text_color", "#FFFFFF")
|
|
|
|
|
+ )
|
|
|
|
|
+ self.timers.append(timer)
|
|
|
|
|
+ self.timer_list.addItem(timer.name)
|
|
|
|
|
+ self.settings.endArray()
|