From fa333348239119aa10c1cd82f1dddaafb015bb78 Mon Sep 17 00:00:00 2001 From: Stu Leak Date: Mon, 3 Nov 2025 10:29:05 -0500 Subject: [PATCH] =?UTF-8?q?v0.1.1=20=E2=80=94=20structural=20renderer=20wi?= =?UTF-8?q?th=20safe=20area=20and=2040x24=20grid=20baseline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/fonts/EuropeanTeletext.ttf | Bin 0 -> 24216 bytes assets/fonts/EuropeanTeletextNuevo.ttf | Bin 0 -> 24736 bytes assets/fonts/Modeseven.ttf | Bin 0 -> 28360 bytes config/config.json | 16 +++ main.py | 23 ++-- .../config_manager.cpython-313.pyc | Bin 0 -> 2002 bytes .../telefact_renderer.cpython-313.pyc | Bin 5747 -> 7354 bytes src/config_manager.py | 22 ++++ src/telefact_renderer.py | 119 ++++++++++++------ 9 files changed, 132 insertions(+), 48 deletions(-) create mode 100644 assets/fonts/EuropeanTeletext.ttf create mode 100644 assets/fonts/EuropeanTeletextNuevo.ttf create mode 100755 assets/fonts/Modeseven.ttf create mode 100644 config/config.json create mode 100644 src/__pycache__/config_manager.cpython-313.pyc create mode 100644 src/config_manager.py diff --git a/assets/fonts/EuropeanTeletext.ttf b/assets/fonts/EuropeanTeletext.ttf new file mode 100644 index 0000000000000000000000000000000000000000..64391841875f6832ef3b70ab10858d1997b0100e GIT binary patch literal 24216 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

guM~q7;8F1Ci!j}*D6md(y=ym#Zj^CgEdi> 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 literal 0 HcmV?d00001 diff --git a/assets/fonts/EuropeanTeletextNuevo.ttf b/assets/fonts/EuropeanTeletextNuevo.ttf new file mode 100644 index 0000000000000000000000000000000000000000..99ea3853ee23d05c0f2e80c068c493600db9c35e GIT binary patch literal 24736 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 literal 0 HcmV?d00001 diff --git a/config/config.json b/config/config.json new file mode 100644 index 0000000..edf17f5 --- /dev/null +++ b/config/config.json @@ -0,0 +1,16 @@ +{ + "Mode": "Broadcaster", + "ScreenWidth": 800, + "ScreenHeight": 600, + "Font": { + "Name": "Modeseven", + "Path": "assets/fonts/Modeseven.ttf", + "Size": 18 + }, + "ShowGrid": false, + "Colours": { + "Header": "blue", + "Footer": "red", + "Background": "black" + } +} diff --git a/main.py b/main.py index 0d179e0..060dd01 100644 --- a/main.py +++ b/main.py @@ -1,22 +1,23 @@ -""" -Telefact — minimal runner -- Opens a 4:3 800×600 window -- Renders header (blue band) and footer (red band) -- No formatter yet; that’s next. -""" - import tkinter as tk +from src.config_manager import ConfigManager from src.telefact_renderer import TelefactRenderer from src.core.telefact_frame import TelefactFrame def main(): + config = ConfigManager().config + root = tk.Tk() - root.title("Telefact — Broadcaster Prototype") + root.title(f"Telefact — Broadcaster ({config['Mode']})") + + renderer = TelefactRenderer( + root, + width=config["ScreenWidth"], + height=config["ScreenHeight"], + show_grid=config.get("ShowGrid", False), + font_path=config["Font"]["Path"] + ) - renderer = TelefactRenderer(root, width=800, height=600, show_grid=False) frame = TelefactFrame() - - # put a couple test glyphs so you can confirm alignment quickly for i, ch in enumerate("TELEFACT"): frame.set_cell(2 + i, 2, ch, "yellow") for i, ch in enumerate("BROADCASTER BASE"): diff --git a/src/__pycache__/config_manager.cpython-313.pyc b/src/__pycache__/config_manager.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d9a63ad34e9379dab421480979617468797c35cb GIT binary patch literal 2002 zcmaJ>O>ERw5T0jyf3nGD$wvG%rSJ$5=}O5iv`t0QfJ6~UsAVB*MMVmdYi#eviDReF zUML^|r>dz`1&UHQqMn)?N3OZ_oIuqelS8HU!mTAmNJy1BaRPYa=ibzvsk_d%%J=Q-XaIu|kBQQbYS4q)1(-N4-UWpY-Ea=*Ch`7f#cVJWAc8evr?*hZFj5Uyly)dcfEkuy{2o9@Q{hu zsb)|gJi5rjtmPV>X}gV5Hk3R+R0wrp+WOk|1HT25uG1Kx>IJ81`6(eh5h~uS<%Y_X z?;$4$ElPq;AF_Ik8$kyYRKc)&%gvKN8T>Di-Ga43*Zh^aEAuzzZrnFl_mx-nl~)Uu zl|p5$cyRg9u}9|W@#@O)>hiI3%YEmc6vvi3#@6y(xBFI$y(`7u2PYr)KhBRl($~6o z-y8m6`2N`4(N{8bezKY$!QX42@>trt>%^$MG@AP~m#G?WHwrae1gE1YEKnQaxRthR ztqaBELi(V@t_{_!pebCg^T3-Obgavk!&}|Jr>(04+$uN9y#KW42L0RX9onCH?>d(715c0LIGPShJN&jY|}(4BjCocrFuoQT@xW}p_WZN-i+f+Rn*k5 z%t1}lmsAenn*JXsr(-B*uI>Lq*n!1$_bN9gb+=*p{E}`=W39Pq`_tu+T@gjxPjn-D zPG=~wFm1c0UzQ={wU8j{H3KxTBKb@HfICUG9$uPJp}wxf7k4`0Asq5I@BhSmGD7 zOpPU|lBFCyp{_n-;S-wEv>DHAIwW^!+E-28i7f?9tJ}g49NV>A56!Y42$9DCQ40!A zQVChOQ`7uF4{SrzgFx7qnt|nO+9E_SX~B%{SP-MX{~oe$;BQGv9ei-|CE)NPtEBip z9a3uWWmZldZmlF=gfP`t&~qLI5JyNN07j9+>}V_hr+kh$fYu zQDoU`z}Sa_jq}0M?HZDR17ts00dBF%Q@p?e>peeQa1RF&_QkweXA$7`;rvEN+8^A$ zTvZQ;q-A7VMas#}|0uZuux`!`4O?HWS<3oB-F7KqIs196*l5Q$JC zaT0%;qZ})FYJt*nnm;8_tB5?|wC$9g+8J#CGy8L>xrUXLGAYM2F`}g73RS4M zXLvf9))X3cbru>`Q5O4p9xm9{%|PG+c>lIJtCni!K)$q6cJ$xK?zq*L?Q?ffiN#Pjpo6twM8 zwKzyHUbxHk42M4y=5%2)lh&|2o{VAXHHS4w<%`N?`>r0A3dG+ znL$X5RcOKb;9%IHTPIR-Y+4s4QnQL~p-No0Uz8I{T9b8af+|W{7h>~rTDQ$BsZ?f8 zx6Vx^HAT0dKXT^8@bHP4vED{rU8rpS9*EmyoWOAG3in%SvsM2C+r^dERJ)uCsI|CF{u}1O<;h25uYhhDxtNgt<>6S zjJ;?pAO(Q4Jwa%vtl&UeJRr@)q*a8)AB zrgW){w3cX-8nN~lc26W@qS+d0GpSP9Mn*W$Tio9g2U?5!T56T-W9?=wf(W@SRUV_{ zK(qL3JQH3e=bKhofq>ei?a2mvfaA#pcii*d z^{#VSu{Rs+#inoS8<$dxsXOs|$-BvHeK)kZbCufsbxX~Q_$gbiMCKi&sVx`S@!V<& zy0GgX9mG?U^Z0I^zH$1ArzPuYS^o5~XZMzk1RIu;|Cj_;uBLwJwZ+$RwZWx<#erO_ z__VtI)|DGqmitx*RtE0;INQ{>9{*zc^XUhBvu~VxRDJ%r+h!=wR}rW8mivbLi6fMC zgdRH@VFl_Mmd-7nyL0kU;LYp8KR7pG(ZDFEqRrBDtO(rDh>*#k-vKs!8$iH3W%6A@ z#;_Ypm4G2qgm_a5jy7ikPEpTQ0*?7_Xev5{lBU1WqX_k-9;I)aV~{u;Vj4mNfPxC} zUCH@G%n+lbm`J>28L>zL{8>l%{=#ShQt_;_B-utRRb)SJepw`=`}+9y^7RD0W%AAu zu8K&m{gzS>$vwhLo<2)E*-6HV4x^GIvlAKiOeC#j=;$OlWN9Un9P}xICGZ>~nv&r$xpR*0L^!VzKUkWo%@rmnDe$bdAo3_uHrNi~QFJ16Cf$<+L1YbQn!mv@XM z)03GC;u$~>C0>eZ5l9Y-mv@cL$uxaIJeHYF#l>_+6A@;_*sQ8$EGT5M)y>B~=YTR-H*fpm|Bt9TT#uq#^1xAa5_uS^(Ti>5k!8>I9`3s#|9$ zMD)7z#HE-rgTZ##O80?a2@DI-73$GBMMcwN$Vm62ST8DVdKuP=*n0*9)aQ_0BfoP8 z{;6g)xDs4#S!r2wK55vOZP=IR-QFK@Pn%m--(7k4N%P)p^WJ}s{NlsUK77)7DBF4H ze)3W0dykq=ez`N-d}hI!=dJ#RTp+Y`a`EKSnZ+|t0v*{vM=sQI$FUY(|G@+2m*3wQ zzOZHC>*}{$#9zM=>R(T;k8RW+dF(ry3+>zpc5e7O|7ass!KWVoMxbZCVcoqkaBidK z{A17iIp2=u`L*2})x8^z-al{oNvLt@%Howx!ucEiGZ4yKp#000g9LU!6OP(fw-Fk+ zf8_rDjl&l<>VNpe7tQ*je*rmQ{*O8Uv;O(p$6Cm5I$u8~kbepJfPN?hjx|{xhIphK zIi#B`NVm9-bC!pDs*k;8dH9wUXx##?GgcnDpxrW5I{*Zr8i_!;J&pP*p&AQ4LA0HG z#z8W!<*>b&yZc!Br^WZ8Nb!cM3bQwg6X4&$;k1b7kLrELiiw$K~+!Zs#Up*Ji)(_jOM z;?<%g8L8kMMn8~vxI$F$0%cfR0sBXfo8h8ldDAA6gb=n~4jiZQoG6QkJt;*@q~>R) zx+n(aGz?i-&~0)$HU+RDAi%J&3NGBqRB9;fpr=so9A>yJv>P(rZGddFD9*w)+6BC@ zy<~V4H;=xL8Je`tUDCOE6&(_T8q*|Q(Krk+%C@Tl<`kI*-%k zf7$Wq@P~N|af;s(-sxht6Dv0e+#Ys3{~KCwld&QXwzK>ggBT{mTuh^=CGTAT=8@3gzsi}kr7q6jlg;8dWMTbTObQ_^aCh{!XDj9<#a-!JCViC&kLsp z;s?b5y4O$uM@!ot} zyPfOpU+n&Tced@o{hbxGe~a1;WVcS@qu&cosEFx%;7Gp8hgQOGsdxuqS8!OL?Wxki zg&6x42NulFv0Vei(#4}0@xIK3K9vm9f{5|gctxz|B>~ZX%x~%*;VL}~m4On#XqtaP zzQO@3lYa-~Eugi+(HdcpF~UYW3#&hKVnm2QM87?mPaWo6D-N<}!DYY@)nMEe@{-@Sx!<_(=+{3xEhIrR*66 zcbs`l0{~!R1r$Kk&;@p$t*S%o^|K7(-uyhm#^wIUj-7BhH?QmMSw}lK&+DHowBy}> zIlel*GX2yaT54KsS{_~}Ege`q zuypXJ2k~}*j|9ioCcX;vZ`w&H{M}UI7 zL@wWhIT~xta2Z`XWdMwpPj_S1ff)nfcBE=BYr~8!K7umaz_2Yj1KpE90}CD=8hVCu z5U+k8ilJ}-ABON+1rI~6X!K$xKAS??6ODc{E2j#a>S%N_NmUJfK{^wSQg)5who^N5 zUfkdwm`UkE9RAs%TfhitAL!Pt+UyM2P%n;yxfSLxn026GP{a$0IIq`5qpAk~Wr;;) z4NPcaR#Vhy^fMzeHm-Q+$SG#{KCk8>`#S<}LG=we_-$;-wY25L_FO~LbAOlh;5Svi zTgnY3Zv`4`!UwyGl!N#Kc_&gX0v9PaQXb;3ThJEWc`w#r%Y(~-#e?~3tofk!2h^9J z;C$A7u$k?5Pvr@a_xH2nLH`!k^1C@}?*a@8ie+^Ri@dEulg;W{j^zmyYZg|lgBqya zhptdO(6+GH^0}>5`Q}<{%ktSgfnqKGCoGtWuq6mr>Go(eo{7Om;PpD!nuA+0$-E1uSxK00*gYbL%FIT p{uT*VeeQQQ@z?Cv1J`2<`>#*kXxSuAj{j3g{|hSVvaA3A literal 5747 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 tuple: + if font_path and os.path.exists(font_path): + try: + font_name = os.path.splitext(os.path.basename(font_path))[0] + try: + self.root.tk.call( + "font", "create", font_name, "-family", font_name, "-size", font_size + ) + except tk.TclError: + pass + print(f"[info] Loaded Telefact font: {font_name}") + return (font_name, font_size) + except Exception as e: + print(f"[warn] Could not load custom font ({e}). Falling back to Courier New.") + elif font_path: + print(f"[warn] Font path not found: {font_path}. Using Courier New.") + return ("Courier New", font_size, "bold") + + # ---------------------------------------------------------------------- def _gx(self, col: int) -> int: - return col * self.cell_w + return self.margin_x + col * self.cell_w def _gy(self, row: int) -> int: - return row * self.cell_h - - def _fill_row(self, row: int, color: str) -> None: - self.canvas.create_rectangle( - self._gx(0), self._gy(row), self._gx(self.cols), self._gy(row + 1), - fill=PALETTE.get(color, color), width=0 - ) + return self.margin_y + row * self.cell_h def _draw_char(self, col: int, row: int, char: str, color: str) -> None: + """Draw a single glyph.""" x = self._gx(col) + 2 y = self._gy(row) + self.cell_h // 2 self.canvas.create_text( - x, y, anchor="w", text=char, - font=self.font, fill=PALETTE.get(color, color) + x, y, anchor="w", text=char, font=self.font, fill=PALETTE.get(color, color) ) def _draw_grid(self) -> None: + """Debug overlay for exact cell spacing.""" for c in range(self.cols + 1): x = self._gx(c) - self.canvas.create_line(x, 0, x, self.h, fill="#202020") + self.canvas.create_line( + x, self.margin_y, x, self.margin_y + self.h, fill=self.colors["Grid"] + ) for r in range(self.rows + 1): y = self._gy(r) - self.canvas.create_line(0, y, self.w, y, fill="#202020") + self.canvas.create_line( + self.margin_x, y, self.margin_x + self.w, y, fill=self.colors["Grid"] + ) - # ----- public API ----- + # ---------------------------------------------------------------------- def render(self, frame: TelefactFrame) -> None: - """Paint the whole frame to the canvas.""" + """Render just the text grid; no header/footer bars.""" self.canvas.delete("all") - # background - self.canvas.create_rectangle(0, 0, self.w, self.h, - fill=PALETTE["black"], width=0) + # Fill total background + self.canvas.create_rectangle( + 0, 0, self.width, self.height, + fill=PALETTE.get(self.colors["Background"], self.colors["Background"]), + width=0, + ) - # header/footer colour bands - for r in frame.header_region(): - self._fill_row(r, "blue") - for r in frame.footer_region(): - self._fill_row(r, "red") - - # draw text cells + # Draw Teletext cells for row in range(frame.rows): for col in range(frame.cols): ch, fg = frame.grid[row][col]