diff --git a/src/teletext/io.py b/src/teletext/io.py index 9092f3b..f2f6d81 100644 --- a/src/teletext/io.py +++ b/src/teletext/io.py @@ -1,10 +1,15 @@ import os -from typing import List +from typing import List, Callable, Optional from .models import Packet, Page, TeletextService -def load_t42(file_path: str) -> TeletextService: +def load_t42(file_path: str, progress_callback: Optional[Callable[[int, int], None]] = None) -> TeletextService: service = TeletextService() + total_bytes = os.path.getsize(file_path) + # Each packet is 42 bytes + total_packets = total_bytes // 42 + processed_packets = 0 + with open(file_path, 'rb') as f: while True: chunk = f.read(42) @@ -13,6 +18,10 @@ def load_t42(file_path: str) -> TeletextService: if len(chunk) < 42: # Should not happen in a valid T42 stream, or we just ignore incomplete tail break + + processed_packets += 1 + if progress_callback and processed_packets % 100 == 0: + progress_callback(processed_packets, total_packets) packet = Packet(chunk) service.all_packets.append(packet) @@ -114,9 +123,16 @@ def decode_packet_header(b1, b2): row = (d2 << 1) | ((d1 >> 3) & 1) return mag, row -def save_t42(file_path: str, service: TeletextService): +def save_t42(file_path: str, service: TeletextService, progress_callback: Optional[Callable[[int, int], None]] = None): + total_packets = len(service.all_packets) + processed = 0 + with open(file_path, 'wb') as f: for packet in service.all_packets: + processed += 1 + if progress_callback and processed % 100 == 0: + progress_callback(processed, total_packets) + # Check if we can reuse the original header (preserving parity/integrity) use_original_header = False diff --git a/src/teletext/ui.py b/src/teletext/ui.py index ee934ec..4dd973b 100644 --- a/src/teletext/ui.py +++ b/src/teletext/ui.py @@ -1,6 +1,9 @@ -from PyQt6.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, - QListWidget, QListWidgetItem, QComboBox, QLabel, QLineEdit, QPushButton, QFileDialog, QMenuBar, QMenu, QMessageBox) +from PyQt6.QtWidgets import ( + QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QListWidget, QListWidgetItem, QComboBox, QLabel, QLineEdit, QPushButton, + QFileDialog, QMenuBar, QMenu, QMessageBox, QStatusBar, QProgressBar, QApplication +) # ... (imports remain) @@ -10,6 +13,8 @@ from PyQt6.QtCore import Qt from .io import load_t42, save_t42 from .renderer import TeletextCanvas +import sys +import os from .models import TeletextService, Page, Packet class MainWindow(QMainWindow): @@ -170,6 +175,7 @@ class MainWindow(QMainWindow): # Reset everything self.service = TeletextService() self.current_page = None + self.current_file_path = None self.populate_list() self.subpage_combo.clear() self.page_groups = {} @@ -178,25 +184,48 @@ class MainWindow(QMainWindow): # Maybe reset text of hex input self.hex_input.clear() QMessageBox.information(self, "Closed", "File closed.") + self.status_label.setText("Ready") def open_file(self): fname, _ = QFileDialog.getOpenFileName(self, "Open T42", "", "Teletext Files (*.t42);;All Files (*)") if fname: try: - self.service = load_t42(fname) + self.progress_bar.setVisible(True) + self.status_label.setText(f"Loading {os.path.basename(fname)}...") + self.progress_bar.setValue(0) + + self.service = load_t42(fname, progress_callback=self.update_progress) + self.current_file_path = fname self.populate_list() - QMessageBox.information(self, "Loaded", f"Loaded {len(self.service.pages)} pages.") + + self.progress_bar.setVisible(False) + self.status_label.setText(f"Loaded {len(self.service.pages)} pages from {os.path.basename(fname)}") except Exception as e: + self.progress_bar.setVisible(False) QMessageBox.critical(self, "Error", f"Failed to load file: {e}") + self.status_label.setText("Error loading file") def save_file(self): - fname, _ = QFileDialog.getSaveFileName(self, "Save T42", "", "Teletext Files (*.t42);;All Files (*)") - if fname: - try: - save_t42(fname, self.service) - QMessageBox.information(self, "Saved", "File saved successfully.") - except Exception as e: - QMessageBox.critical(self, "Error", f"Failed to save file: {e}") + if not self.current_file_path: + # Logic for "Save As" if path not known, but for T42 we usually overwrite or ask. + # To keep it simple, ask every time or track path. + # Let's ask. + fname, _ = QFileDialog.getSaveFileName(self, "Save T42", "", "Teletext Files (*.t42)") + if not fname: return + self.current_file_path = fname + + try: + self.progress_bar.setVisible(True) + self.status_label.setText(f"Saving {os.path.basename(self.current_file_path)}...") + + save_t42(self.current_file_path, self.service, progress_callback=self.update_progress) + + self.progress_bar.setVisible(False) + self.status_label.setText(f"Saved {len(self.service.pages)} pages to {os.path.basename(self.current_file_path)}") + except Exception as e: + self.progress_bar.setVisible(False) + QMessageBox.critical(self, "Error", f"Failed to save file: {e}") + self.status_label.setText("Error saving file") def populate_list(self): self.page_list.clear()