v0.1.3 — Centered Teletext frame, added full footer with color band, and finalized header alignment.
This commit is contained in:
parent
64f0e74105
commit
211ebfcd01
22
main.py
22
main.py
|
|
@ -1,13 +1,12 @@
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# File: /home/stu/Projects/Local REPO/telefact/main.py
|
# File: /telefact/main.py
|
||||||
# Description:
|
# Description:
|
||||||
# Entry point for the Telefact Broadcaster mode.
|
# Entry point for the Telefact Broadcaster mode.
|
||||||
# Initializes the Tkinter window, loads configuration,
|
# Initializes the Tkinter window, loads configuration,
|
||||||
# and renders the Teletext grid with dynamic header.
|
# and renders the Teletext grid with dynamic header/footer.
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
|
||||||
from src.config_manager import ConfigManager
|
from src.config_manager import ConfigManager
|
||||||
from src.telefact_renderer import TelefactRenderer
|
from src.telefact_renderer import TelefactRenderer
|
||||||
from src.core.telefact_frame import TelefactFrame
|
from src.core.telefact_frame import TelefactFrame
|
||||||
|
|
@ -31,34 +30,33 @@ def main():
|
||||||
height=config["ScreenHeight"],
|
height=config["ScreenHeight"],
|
||||||
show_grid=config.get("ShowGrid", False),
|
show_grid=config.get("ShowGrid", False),
|
||||||
font_path=config["Font"]["Path"],
|
font_path=config["Font"]["Path"],
|
||||||
|
font_size=config["Font"]["Size"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- Prepare frame ---
|
# --- Prepare frame ---
|
||||||
frame = TelefactFrame()
|
frame = TelefactFrame()
|
||||||
|
|
||||||
# --- Initialize header ---
|
# --- Header ---
|
||||||
header = TelefactHeader(frame, config)
|
header = TelefactHeader(frame, config)
|
||||||
header.render()
|
header.render()
|
||||||
header.update_time(root, renderer)
|
header.update_time(root, renderer)
|
||||||
|
|
||||||
# --- Initialize footer ---
|
# --- Body content ---
|
||||||
footer = TelefactFooter(frame, config)
|
|
||||||
footer.render_test_footer()
|
|
||||||
|
|
||||||
# --- Body & footer test content ---
|
|
||||||
formatter = TelefactFormatter(frame)
|
formatter = TelefactFormatter(frame)
|
||||||
formatter.add_body_line(1, "Welcome to the Telefact Broadcaster base.", align="center", color="white")
|
formatter.add_body_line(1, "Welcome to the Telefact Broadcaster base.", align="center", color="white")
|
||||||
formatter.add_body_line(3, "Press Q or ESC to exit.", align="center", color="cyan")
|
formatter.add_body_line(3, "Press Q or ESC to exit.", align="center", color="cyan")
|
||||||
formatter.set_footer("PAGE 100 TELEFACT", align="center", color="green")
|
|
||||||
|
|
||||||
# --- Render frame ---
|
# --- Footer (replaces old PAGE 100 TELEFACT) ---
|
||||||
|
footer = TelefactFooter(frame, config)
|
||||||
|
footer.set_footer("Telefact: The world at your fingertips", align="left", fg="yellow", bg="blue")
|
||||||
|
|
||||||
|
# --- Render everything ---
|
||||||
renderer.render(frame)
|
renderer.render(frame)
|
||||||
|
|
||||||
# --- Bind exit keys ---
|
# --- Bind exit keys ---
|
||||||
root.bind("<Escape>", lambda e: root.quit())
|
root.bind("<Escape>", lambda e: root.quit())
|
||||||
root.bind("q", lambda e: root.quit())
|
root.bind("q", lambda e: root.quit())
|
||||||
|
|
||||||
# --- Start loop ---
|
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
BIN
src/core/__pycache__/telefact_footer.cpython-313.pyc
Normal file
BIN
src/core/__pycache__/telefact_footer.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -2,8 +2,7 @@
|
||||||
# File: /home/stu/Projects/Local REPO/telefact/src/core/telefact_footer.py
|
# File: /home/stu/Projects/Local REPO/telefact/src/core/telefact_footer.py
|
||||||
# Description:
|
# Description:
|
||||||
# Dynamic Telefact footer renderer for broadcaster mode.
|
# Dynamic Telefact footer renderer for broadcaster mode.
|
||||||
# Draws a single bottom-row text band with background color
|
# Draws a single bottom-row text band with full background fill.
|
||||||
# and per-page contextual support.
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
from src.core.telefact_frame import TelefactFrame
|
from src.core.telefact_frame import TelefactFrame
|
||||||
|
|
@ -22,24 +21,24 @@ class TelefactFooter:
|
||||||
self.frame = frame
|
self.frame = frame
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
# default colors from global config
|
# Default colors
|
||||||
colours = config.get("Colours", {})
|
colours = config.get("Colours", {})
|
||||||
self.default_bg = colours.get("Footer", "blue")
|
self.default_bg = colours.get("Footer", "blue")
|
||||||
self.default_fg = colours.get("TextPrimary", "white")
|
self.default_fg = colours.get("TextPrimary", "white")
|
||||||
|
|
||||||
# reserved footer row (last visible line)
|
# Reserved row (bottom row = line 24)
|
||||||
self.row = frame.rows - 1
|
self.row = frame.rows - 1
|
||||||
|
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
def clear_footer(self, bg: str | None = None):
|
def clear_footer(self, bg: str | None = None):
|
||||||
"""Clears the footer line with background fill."""
|
"""Clears the footer row with solid background color."""
|
||||||
bg_color = bg or self.default_bg
|
bg_color = bg or self.default_bg
|
||||||
for c in range(self.frame.cols):
|
for c in range(self.frame.cols):
|
||||||
self.frame.set_cell(c, self.row, " ", self.default_fg, bg_color)
|
self.frame.set_cell(c, self.row, " ", self.default_fg, bg_color)
|
||||||
|
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
def set_footer(self, text: str, align: str = "left", fg: str | None = None, bg: str | None = None):
|
def set_footer(self, text: str, align: str = "left", fg: str | None = None, bg: str | None = None):
|
||||||
"""Draw a footer message with color and alignment control."""
|
"""Draw footer message with proper background fill."""
|
||||||
fg_color = fg or self.default_fg
|
fg_color = fg or self.default_fg
|
||||||
bg_color = bg or self.default_bg
|
bg_color = bg or self.default_bg
|
||||||
self.clear_footer(bg_color)
|
self.clear_footer(bg_color)
|
||||||
|
|
@ -47,25 +46,26 @@ class TelefactFooter:
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
text_len = len(text)
|
text_len = len(text)
|
||||||
|
|
||||||
# --- Alignment handling ---
|
# Alignment
|
||||||
if align == "center":
|
if align == "center":
|
||||||
start_col = (self.frame.cols - text_len) // 2
|
start_col = (self.frame.cols - text_len) // 2
|
||||||
elif align == "right":
|
elif align == "right":
|
||||||
start_col = max(self.frame.cols - text_len - 1, 0)
|
start_col = max(self.frame.cols - text_len - 1, 0)
|
||||||
else:
|
else:
|
||||||
start_col = 1
|
start_col = 1 # left padding
|
||||||
|
|
||||||
# --- Background fill ---
|
# Draw each character with full background
|
||||||
for c in range(self.frame.cols):
|
|
||||||
self.frame.set_cell(c, self.row, " ", fg_color, bg_color)
|
|
||||||
|
|
||||||
# --- Text overlay ---
|
|
||||||
for i, ch in enumerate(text):
|
for i, ch in enumerate(text):
|
||||||
pos = start_col + i
|
pos = start_col + i
|
||||||
if 0 <= pos < self.frame.cols:
|
if 0 <= pos < self.frame.cols:
|
||||||
self.frame.set_cell(pos, self.row, ch, fg_color)
|
self.frame.set_cell(pos, self.row, ch, fg_color, bg_color)
|
||||||
|
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
def render_test_footer(self):
|
def render_test_footer(self):
|
||||||
"""Temporary demo for testing footer display."""
|
"""Temporary demo for testing footer display."""
|
||||||
self.set_footer("Telefact: The world at your fingertips", align="left", fg="yellow", bg="blue")
|
self.set_footer(
|
||||||
|
"Telefact: The world at your fingertips",
|
||||||
|
align="left",
|
||||||
|
fg="yellow",
|
||||||
|
bg="blue",
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# File: /home/stu/Projects/Local REPO/telefact/src/core/telefact_frame.py
|
# File: /telefact/src/core/telefact_frame.py
|
||||||
# Description:
|
# Description:
|
||||||
# Telefact Frame (model)
|
# Telefact Frame (model)
|
||||||
# Defines a 40×24 grid and logical regions for Teletext layout.
|
# Defines a 40×24 grid and logical regions for Teletext layout.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# File: /home/stu/Projects/Local REPO/telefact/src/core/telefact_header.py
|
# File: /telefact/src/core/telefact_header.py
|
||||||
# Description:
|
# Description:
|
||||||
# Dynamic Telefact header renderer for broadcaster mode.
|
# Dynamic Telefact header renderer for broadcaster mode.
|
||||||
# Displays page numbers, centered service name block with
|
# Displays page numbers, centered service name block with
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# File: /home/stu/Projects/Local REPO/telefact/src/telefact_renderer.py
|
# File: /telefact/src/telefact_renderer.py
|
||||||
# Description:
|
# Description:
|
||||||
# Telefact Renderer (Tkinter)
|
# Telefact Renderer (Tkinter)
|
||||||
# Renders a 40×24 Telefact grid with full foreground/background
|
# Renders a 40×24 Telefact grid with full foreground/background
|
||||||
# colour support, aligned inside a CRT-style safe area margin.
|
# colour support, perfectly centered inside the window.
|
||||||
# No header/footer bars — layout calibration only.
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
@ -44,20 +43,33 @@ class TelefactRenderer:
|
||||||
self.height = height
|
self.height = height
|
||||||
self.show_grid = show_grid
|
self.show_grid = show_grid
|
||||||
|
|
||||||
# Teletext dimensions
|
# Teletext logical dimensions
|
||||||
self.cols, self.rows = 40, 24
|
self.cols, self.rows = 40, 24
|
||||||
|
|
||||||
# Safe area margins (simulate CRT overscan)
|
# --- Dynamic centered layout ---
|
||||||
self.margin_x = 24
|
# Define safe area target margins
|
||||||
self.margin_y = 24
|
safe_margin_x = 24
|
||||||
self.inner_w = width - self.margin_x * 2
|
safe_margin_y = 24
|
||||||
self.inner_h = height - self.margin_y * 2
|
|
||||||
|
|
||||||
# Per-cell dimensions
|
# Compute total drawable region
|
||||||
self.cell_w = self.inner_w // self.cols
|
self.inner_w = width - safe_margin_x * 2
|
||||||
self.cell_h = self.inner_h // self.rows
|
self.inner_h = height - safe_margin_y * 2
|
||||||
self.w = self.inner_w
|
|
||||||
self.h = self.inner_h
|
# Compute exact per-cell dimensions (float precision)
|
||||||
|
self.cell_w = self.inner_w / self.cols
|
||||||
|
self.cell_h = self.inner_h / self.rows
|
||||||
|
|
||||||
|
# Compute corrected total grid size (may differ slightly due to float math)
|
||||||
|
total_grid_w = self.cell_w * self.cols
|
||||||
|
total_grid_h = self.cell_h * self.rows
|
||||||
|
|
||||||
|
# --- Center grid on screen ---
|
||||||
|
self.margin_x = (width - total_grid_w) / 2
|
||||||
|
self.margin_y = (height - total_grid_h) / 2
|
||||||
|
|
||||||
|
# Cache total grid extents
|
||||||
|
self.w = total_grid_w
|
||||||
|
self.h = total_grid_h
|
||||||
|
|
||||||
# Base colours
|
# Base colours
|
||||||
self.colors = {
|
self.colors = {
|
||||||
|
|
@ -101,12 +113,12 @@ class TelefactRenderer:
|
||||||
return ("Courier New", font_size, "bold")
|
return ("Courier New", font_size, "bold")
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def _gx(self, col: int) -> int:
|
def _gx(self, col: int) -> float:
|
||||||
"""Grid X coordinate."""
|
"""Grid X coordinate (precise, centered)."""
|
||||||
return self.margin_x + col * self.cell_w
|
return self.margin_x + col * self.cell_w
|
||||||
|
|
||||||
def _gy(self, row: int) -> int:
|
def _gy(self, row: int) -> float:
|
||||||
"""Grid Y coordinate."""
|
"""Grid Y coordinate (precise, centered)."""
|
||||||
return self.margin_y + row * self.cell_h
|
return self.margin_y + row * self.cell_h
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
@ -115,21 +127,18 @@ class TelefactRenderer:
|
||||||
x = self._gx(col)
|
x = self._gx(col)
|
||||||
y = self._gy(row)
|
y = self._gy(row)
|
||||||
|
|
||||||
# Draw background block
|
# Background rectangle
|
||||||
self.canvas.create_rectangle(
|
self.canvas.create_rectangle(
|
||||||
x,
|
x, y, x + self.cell_w, y + self.cell_h,
|
||||||
y,
|
|
||||||
x + self.cell_w,
|
|
||||||
y + self.cell_h,
|
|
||||||
fill=PALETTE.get(bg, bg),
|
fill=PALETTE.get(bg, bg),
|
||||||
outline=PALETTE.get(bg, bg),
|
outline=PALETTE.get(bg, bg)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Draw text glyph
|
# Foreground glyph
|
||||||
if char.strip():
|
if char.strip():
|
||||||
self.canvas.create_text(
|
self.canvas.create_text(
|
||||||
x + 2,
|
x + 2,
|
||||||
y + self.cell_h // 2,
|
y + self.cell_h / 2,
|
||||||
anchor="w",
|
anchor="w",
|
||||||
text=char,
|
text=char,
|
||||||
font=self.font,
|
font=self.font,
|
||||||
|
|
@ -138,7 +147,7 @@ class TelefactRenderer:
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def _draw_grid(self) -> None:
|
def _draw_grid(self) -> None:
|
||||||
"""Optional debug overlay for cell alignment."""
|
"""Optional grid overlay for visual debugging."""
|
||||||
for c in range(self.cols + 1):
|
for c in range(self.cols + 1):
|
||||||
x = self._gx(c)
|
x = self._gx(c)
|
||||||
self.canvas.create_line(
|
self.canvas.create_line(
|
||||||
|
|
@ -152,20 +161,17 @@ class TelefactRenderer:
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def render(self, frame: TelefactFrame) -> None:
|
def render(self, frame: TelefactFrame) -> None:
|
||||||
"""Renders the full Telefact grid (foreground + background)."""
|
"""Renders the entire 40×24 Telefact grid."""
|
||||||
self.canvas.delete("all")
|
self.canvas.delete("all")
|
||||||
|
|
||||||
# Fill background
|
# Fill full canvas background
|
||||||
self.canvas.create_rectangle(
|
self.canvas.create_rectangle(
|
||||||
0,
|
0, 0, self.width, self.height,
|
||||||
0,
|
|
||||||
self.width,
|
|
||||||
self.height,
|
|
||||||
fill=PALETTE.get(self.colors["Background"], self.colors["Background"]),
|
fill=PALETTE.get(self.colors["Background"], self.colors["Background"]),
|
||||||
width=0,
|
width=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Draw all cells (bg first, then text)
|
# Draw text grid (each cell: bg + fg)
|
||||||
for row in range(frame.rows):
|
for row in range(frame.rows):
|
||||||
for col in range(frame.cols):
|
for col in range(frame.cols):
|
||||||
ch, fg, bg = frame.get_cell(col, row)
|
ch, fg, bg = frame.get_cell(col, row)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user