Initial commit: Core Teletext Editor functionality
This commit is contained in:
152
src/teletext/ui.py
Normal file
152
src/teletext/ui.py
Normal file
@@ -0,0 +1,152 @@
|
||||
|
||||
import os
|
||||
from PyQt6.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QTreeWidget, QTreeWidgetItem, QFileDialog, QMenuBar, QMenu, QMessageBox)
|
||||
from PyQt6.QtGui import QAction, QKeyEvent
|
||||
from PyQt6.QtCore import Qt
|
||||
|
||||
from .io import load_t42, save_t42
|
||||
from .renderer import TeletextCanvas
|
||||
from .models import TeletextService, Page, Packet
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Teletext Editor")
|
||||
self.resize(1024, 768)
|
||||
|
||||
self.service = TeletextService()
|
||||
self.current_page: Page = None
|
||||
|
||||
# UI Components
|
||||
self.central_widget = QWidget()
|
||||
self.setCentralWidget(self.central_widget)
|
||||
|
||||
self.layout = QHBoxLayout(self.central_widget)
|
||||
|
||||
# Left Panel: Page Tree
|
||||
self.tree = QTreeWidget()
|
||||
self.tree.setHeaderLabel("Pages")
|
||||
self.tree.setFixedWidth(200)
|
||||
self.tree.itemClicked.connect(self.on_page_selected)
|
||||
self.layout.addWidget(self.tree)
|
||||
|
||||
# Center: Teletext Canvas
|
||||
self.canvas = TeletextCanvas()
|
||||
self.layout.addWidget(self.canvas, 1) # Expand
|
||||
|
||||
# Menus
|
||||
self.create_menus()
|
||||
|
||||
def create_menus(self):
|
||||
menu_bar = self.menuBar()
|
||||
|
||||
file_menu = menu_bar.addMenu("File")
|
||||
|
||||
open_action = QAction("Open T42...", self)
|
||||
open_action.triggered.connect(self.open_file)
|
||||
file_menu.addAction(open_action)
|
||||
|
||||
save_action = QAction("Save T42...", self)
|
||||
save_action.triggered.connect(self.save_file)
|
||||
file_menu.addAction(save_action)
|
||||
|
||||
exit_action = QAction("Exit", self)
|
||||
exit_action.triggered.connect(self.close)
|
||||
file_menu.addAction(exit_action)
|
||||
|
||||
view_menu = menu_bar.addMenu("View")
|
||||
|
||||
lang_menu = view_menu.addMenu("Language")
|
||||
langs = ["English", "German", "Swedish/Finnish", "Italian", "French", "Portuguese/Spanish", "Turkish", "Romania"]
|
||||
for i, lang in enumerate(langs):
|
||||
action = QAction(lang, self)
|
||||
action.setData(i)
|
||||
action.triggered.connect(self.set_language)
|
||||
lang_menu.addAction(action)
|
||||
|
||||
def set_language(self):
|
||||
action = self.sender()
|
||||
if action:
|
||||
idx = action.data()
|
||||
self.canvas.subset_idx = idx
|
||||
self.canvas.redraw()
|
||||
self.canvas.update()
|
||||
|
||||
def open_file(self):
|
||||
fname, _ = QFileDialog.getOpenFileName(self, "Open T42", "", "Teletext Files (*.t42);;All Files (*)")
|
||||
if fname:
|
||||
try:
|
||||
self.service = load_t42(fname)
|
||||
self.populate_tree()
|
||||
QMessageBox.information(self, "Loaded", f"Loaded {len(self.service.pages)} pages.")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Error", f"Failed to load file: {e}")
|
||||
|
||||
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}")
|
||||
|
||||
def populate_tree(self):
|
||||
self.tree.clear()
|
||||
|
||||
# Group by Magazine
|
||||
mags = {}
|
||||
for p in self.service.pages:
|
||||
if p.magazine not in mags:
|
||||
mags[p.magazine] = QTreeWidgetItem([f"Magazine {p.magazine}"])
|
||||
self.tree.addTopLevelItem(mags[p.magazine])
|
||||
|
||||
# Format: PPP-SS (Page-Subcode)
|
||||
# Create Item
|
||||
# Subcode is complicated to display "nicely" without decoding,
|
||||
# let's just show hex or raw for now if not standard 0000.
|
||||
|
||||
label = f"{p.page_number:02d} (Sub: {p.sub_code:04X})"
|
||||
item = QTreeWidgetItem([label])
|
||||
item.setData(0, Qt.ItemDataRole.UserRole, p)
|
||||
mags[p.magazine].addChild(item)
|
||||
|
||||
self.tree.expandAll()
|
||||
|
||||
def on_page_selected(self, item, column):
|
||||
page = item.data(0, Qt.ItemDataRole.UserRole)
|
||||
if isinstance(page, Page):
|
||||
self.current_page = page
|
||||
self.canvas.set_page(page)
|
||||
# Also set window focus to canvas or handle key events?
|
||||
self.canvas.setFocus()
|
||||
|
||||
# Input Handling (Editor Logic)
|
||||
def keyPressEvent(self, event: QKeyEvent):
|
||||
if not self.current_page:
|
||||
return
|
||||
|
||||
key = event.key()
|
||||
text = event.text()
|
||||
|
||||
# Navigation
|
||||
if key == Qt.Key.Key_Up:
|
||||
self.canvas.move_cursor(0, -1)
|
||||
elif key == Qt.Key.Key_Down:
|
||||
self.canvas.move_cursor(0, 1)
|
||||
elif key == Qt.Key.Key_Left:
|
||||
self.canvas.move_cursor(-1, 0)
|
||||
elif key == Qt.Key.Key_Right:
|
||||
self.canvas.move_cursor(1, 0)
|
||||
else:
|
||||
# Typing
|
||||
# Filter non-printable
|
||||
if text and len(text) == 1 and 32 <= ord(text) <= 126:
|
||||
self.canvas.handle_input(text)
|
||||
elif key == Qt.Key.Key_Backspace:
|
||||
# Move back and delete
|
||||
self.canvas.move_cursor(-1, 0)
|
||||
self.canvas.handle_input(' ')
|
||||
self.canvas.move_cursor(-1, 0) # Compensate for the auto-advance logic if any
|
||||
|
||||
Reference in New Issue
Block a user