diff --git a/src/video_subtitles/cli.py b/src/video_subtitles/cli.py index 7359fd1..78d0a17 100644 --- a/src/video_subtitles/cli.py +++ b/src/video_subtitles/cli.py @@ -42,7 +42,7 @@ def parse_args() -> argparse.Namespace: "--languages", type=parse_languages, help="Output languages as a comma-separated list.", - metavar="{en,es,fr,de,it,pt,ru,zh}", + metavar="{en,es,fr,de,it,ru,zh}", required=True, ) parser.add_argument( diff --git a/src/video_subtitles/gui.py b/src/video_subtitles/gui.py index a3f30b3..1f8fb84 100644 --- a/src/video_subtitles/gui.py +++ b/src/video_subtitles/gui.py @@ -11,6 +11,7 @@ from threading import Thread from PyQt6 import QtCore # type: ignore +from PyQt6.QtCore import pyqtSignal # type: ignore from PyQt6.QtWidgets import ( # type: ignore QApplication, QComboBox, @@ -19,6 +20,7 @@ QLineEdit, QMainWindow, QMessageBox, + QProgressBar, QPushButton, QVBoxLayout, QWidget, @@ -46,11 +48,15 @@ def open_folder(path): class MainWidget(QMainWindow): # pylint: disable=too-many-instance-attributes """Main widget.""" + progress_signal = pyqtSignal(bool) + def __init__(self, on_drop_callback): # pylint: disable=too-many-statements super().__init__() + self.setWindowTitle("Video Subtitle Generator") self.resize(720, 480) self.setAcceptDrops(True) + self.on_destroy = None deepl_api_key = settings.deepl_key() @@ -125,14 +131,27 @@ def __init__(self, on_drop_callback): # pylint: disable=too-many-statements self.label.setText("Drag and drop video file here for subtitles") self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + progress_bar_layout = QHBoxLayout() + # Create a progress bar + self.progress_bar = self.create_progress_bar() + self.progress_signal.connect(self.progress_bar.setVisible) + progress_bar_layout.addWidget(self.progress_bar) + # Add the header pane and label widget to the main layout main_layout.addWidget(header_pane) main_layout.addWidget(self.label) + main_layout.addLayout(progress_bar_layout) # Setting the alignment of the header pane to the top main_layout.setAlignment(header_pane, QtCore.Qt.AlignmentFlag.AlignTop) self.on_drop_callback = on_drop_callback + def closeEvent(self, event): + """Called when the window is closed.""" + if self.on_destroy: + self.on_destroy() + super().closeEvent(event) + def show_help_dialog(self): """Shows a dialog with the language codes.""" dialog = QMessageBox() @@ -143,6 +162,15 @@ def show_help_dialog(self): dialog.setText(text) dialog.exec() + def create_progress_bar(self): + """Creates a progress bar.""" + progress_bar = QProgressBar(self) + progress_bar.setMinimum(0) # Set minimum value of the progress bar + progress_bar.setMaximum(0) # Set maximum value of the progress bar + progress_bar.setValue(0) # Set the current value of the progress bar + progress_bar.setVisible(False) # Hide the progress bar by default + return progress_bar + def dragEnterEvent(self, event): """Drag and drop handler.""" if event.mimeData().hasUrls(): @@ -242,4 +270,17 @@ def _generate_subtitles( gui = MainWidget(callback) gui.show() + + def update_function(val: bool): + """Updates the progress bar.""" + data = update_function.__dict__ + data["last_value"] = data.get("last_value", False) + if val == data["last_value"]: + return + data["last_value"] = val + gui.progress_signal.emit(val) + + gui.on_destroy = thread_processor.stop + thread_processor.set_status_callback(update_function) + thread_processor.start() sys.exit(app.exec()) diff --git a/src/video_subtitles/thread_processor.py b/src/video_subtitles/thread_processor.py index 43dbb8d..d87d341 100644 --- a/src/video_subtitles/thread_processor.py +++ b/src/video_subtitles/thread_processor.py @@ -16,15 +16,22 @@ def __init__(self) -> None: self.pending_tasks: queue.Queue = queue.Queue() self.processing_task: threading.Thread | None = None self.event = threading.Event() - self.start() + self.update_status_cb = lambda _: None + + def set_status_callback(self, callback) -> None: + """Sets the status callback.""" + self.update_status_cb = callback def run(self) -> None: """Process each thread in the queue.""" while not self.event.wait(0.1): if self.processing_task is not None: + self.update_status_cb(True) if not self.processing_task.is_alive(): self.processing_task.join() self.processing_task = None + else: + self.update_status_cb(False) if self.processing_task is not None: continue if self.pending_tasks.empty():