From c55c1222f009220e265884bd5a4ef2f80dc1dec5 Mon Sep 17 00:00:00 2001 From: EKNr1 Date: Mon, 3 Feb 2025 14:08:19 +0100 Subject: [PATCH] Added a dock widget that shows background processes. --- wobuzz/gui.py | 8 +++- wobuzz/player/player.py | 4 ++ wobuzz/ui/library_dock.py | 6 --- wobuzz/ui/main_window.py | 5 ++ wobuzz/ui/process/__init__.py | 1 + wobuzz/ui/process/process.py | 41 ++++++++++++++++ wobuzz/ui/process/process_dock.py | 77 +++++++++++++++++++++++++++++++ 7 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 wobuzz/ui/process/__init__.py create mode 100644 wobuzz/ui/process/process.py create mode 100644 wobuzz/ui/process/process_dock.py diff --git a/wobuzz/gui.py b/wobuzz/gui.py index 5bcf85f..87d014b 100644 --- a/wobuzz/gui.py +++ b/wobuzz/gui.py @@ -17,7 +17,7 @@ class GUI: self.settings = self.window.settings self.track_control = self.window.track_control - self.window.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.app.library.main_library_dock) + self.window.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.app.library.main_library_dock) self.app.library.main_library_dock.setFeatures( QDockWidget.DockWidgetFeature.DockWidgetMovable | @@ -54,3 +54,9 @@ class GUI: self.track_control.on_track_change(previous_track, track) self.app.player.current_playlist.view.on_track_change(previous_track, track) + def on_background_job_start(self, job: str): + self.window.process_dock.job_started_signal.emit(job) + + def on_background_job_stop(self, job: str): + self.window.process_dock.on_background_job_stop(job) + diff --git a/wobuzz/player/player.py b/wobuzz/player/player.py index 24261a4..5c47a03 100644 --- a/wobuzz/player/player.py +++ b/wobuzz/player/player.py @@ -160,8 +160,12 @@ class Player: track = self.current_playlist.tracks[self.current_playlist.current_track_index + 1] if not track.cached: + self.app.gui.on_background_job_start("track_caching") + track.cache() + self.app.gui.on_background_job_stop("track_caching") + def cache_next_track(self): # function that creates a thread which will cache the next track caching_thread = threading.Thread(target=self.caching_thread_function) diff --git a/wobuzz/ui/library_dock.py b/wobuzz/ui/library_dock.py index 825d6d3..d4694f5 100644 --- a/wobuzz/ui/library_dock.py +++ b/wobuzz/ui/library_dock.py @@ -11,12 +11,6 @@ class LibraryDock(QDockWidget): self.library = library - self.setAllowedAreas( - Qt.DockWidgetArea.LeftDockWidgetArea | - Qt.DockWidgetArea.RightDockWidgetArea | - Qt.DockWidgetArea.BottomDockWidgetArea - ) - self.setAcceptDrops(True) self.library_widget = Library(library, self) diff --git a/wobuzz/ui/main_window.py b/wobuzz/ui/main_window.py index 553f2e7..4aaa7be 100644 --- a/wobuzz/ui/main_window.py +++ b/wobuzz/ui/main_window.py @@ -5,6 +5,7 @@ from PyQt6.QtGui import QIcon from PyQt6.QtWidgets import QMainWindow, QMenu from .track_control import TrackControl from .settings import Settings +from .process.process_dock import ProcessDock class MainWindow(QMainWindow): @@ -35,5 +36,9 @@ class MainWindow(QMainWindow): self.settings.hide() self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.settings) + self.process_dock = ProcessDock(app) + self.process_dock.hide() + self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.process_dock) + self.settings_action.triggered.connect(self.settings.show) diff --git a/wobuzz/ui/process/__init__.py b/wobuzz/ui/process/__init__.py new file mode 100644 index 0000000..a93a4bf --- /dev/null +++ b/wobuzz/ui/process/__init__.py @@ -0,0 +1 @@ +#!/usr/bin/python3 diff --git a/wobuzz/ui/process/process.py b/wobuzz/ui/process/process.py new file mode 100644 index 0000000..80e5fcf --- /dev/null +++ b/wobuzz/ui/process/process.py @@ -0,0 +1,41 @@ +#!/usr/bin/python3 + +from PyQt6.QtCore import Qt +from PyQt6.QtGui import QFont +from PyQt6.QtWidgets import QSizePolicy, QGroupBox, QLabel, QProgressBar, QVBoxLayout + + +class BackgroundProcess(QGroupBox): + normal_font = QFont() + normal_font.setBold(False) + bold_font = QFont() + bold_font.setBold(True) + + def __init__(self, title: str, parent=None, description: str="", steps: int=0): + super().__init__(title, parent) + + self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) + self.setAlignment(Qt.AlignmentFlag.AlignLeading | Qt.AlignmentFlag.AlignVCenter) + self.setFont(self.bold_font) + + self.layout = QVBoxLayout(self) + + self.description = QLabel(description, self) + self.description.setFont(self.normal_font) + self.layout.addWidget(self.description) + + self.progress_bar = QProgressBar(self) + self.progress_bar.setMaximum(steps) + self.layout.addWidget(self.progress_bar) + + self.setLayout(self.layout) + + def get_progress(self): + return 0 + + def set_range(self, maximum: int, minimum: int=0): + self.progress_bar.setRange(minimum, maximum) + + def update_progress(self): + self.progress_bar.setValue(self.get_progress()) + diff --git a/wobuzz/ui/process/process_dock.py b/wobuzz/ui/process/process_dock.py new file mode 100644 index 0000000..161d895 --- /dev/null +++ b/wobuzz/ui/process/process_dock.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 + +from PyQt6.QtCore import QTimer, pyqtSignal +from PyQt6.QtWidgets import QWidget, QDockWidget, QScrollArea, QVBoxLayout + +from .process import BackgroundProcess + +PROGRESS_UPDATE_RATE = 60 +PROGRESS_UPDATE_INTERVAL = 1000 // PROGRESS_UPDATE_RATE + + +class ProcessDock(QDockWidget): + # we need a signal for self.on_background_job_start() because PyQt6 doesn't allow some operations to be performed + # from a different thread + job_started_signal = pyqtSignal(str) + + def __init__(self, app, parent=None): + super().__init__(parent) + + self.app = app + + self.processes = {} + + self.setWindowTitle("Background Processes") + + self.scroll_area = QScrollArea(self) + self.scroll_area.setWidgetResizable(True) + + self.process_container = QWidget(self.scroll_area) + + self.process_layout = QVBoxLayout(self.process_container) + + # add expanding widget so the distance between processes will be equal + self.process_layout.addWidget(QWidget(self.process_container)) + + self.process_container.setLayout(self.process_layout) + + self.scroll_area.setWidget(self.process_container) + + self.setWidget(self.scroll_area) + + self.progress_update_timer = QTimer() + self.progress_update_timer.timeout.connect(self.update_processes) + self.progress_update_timer.start(PROGRESS_UPDATE_INTERVAL) + + self.job_started_signal.connect(self.on_background_job_start) + + # for i in range(8): + # self.add_process(BackgroundProcess(f"Kurwa x{i}", self.process_container, "Boooooooober!!!")) + # + # self.add_process(BackgroundProcess("ja perdole!", self.process_container, "Boooooooober!!!")) + + def add_process(self, name: str, process: BackgroundProcess): + if not name in self.processes: + self.processes[name] = process + self.process_layout.insertWidget(self.process_layout.count() - 1, process) + + def update_processes(self): + for process in self.processes.values(): + process.update_progress() + + def on_background_job_start(self, job): + match job: + case "track_caching": + self.add_process( + job, + BackgroundProcess( + "Loading Track", + self.process_container, + "Loading next track in the background so it starts immediately." + ) + ) + + def on_background_job_stop(self, job): + if job in self.processes: + self.processes.pop(job).deleteLater() +