A pygame-powered ANSI terminal emulator framework for building text-based games and apps.
Project description
PixTerm
A pygame-powered ANSI terminal emulator framework for building text-based games and TUI applications.
PixTerm renders a fixed-size character grid (120 × 40 by default) using bitmap BDF fonts and a fully parsed ANSI escape code pipeline — giving you the look of a classic terminal with the full power of Python and pygame underneath.
Features
- ANSI escape code rendering — foreground/background colours (16-colour palette), bold, cursor movement
- Bitmap BDF fonts — bundled Terminus fonts in sizes 12–24 px (normal, bold, variable-width variants)
- Screen manager — stack-based screen system with
on_entry/render/update/handle_event/on_exitlifecycle - Theme system — swap colour palettes at runtime; ships with
basic_black,basic_white, and more - Preferences — persistent JSON preferences stored in
~/.pixterm/preferences.json - Hot-reloader — optional watchdog-based module reloader for rapid development
Installation
pip install pixterm
Requires Python 3.10+ and pygame 2.6+.
Quick Start
import pygame
from pixterm import Game, Screen, Preferences
class HelloScreen(Screen):
_text: str
def on_entry(self):
self._text = "Hello, PixTerm!"
def on_exit(self):
pass
def render(self):
self.cli.write(f"\x1b[2;2H\x1b[93m{self._text}\x1b[0m")
def update(self, dt: float):
pass
def handle_event(self, event: pygame.event.Event):
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
self.game.stop()
game = Game(title="Hello World", target_fps=60)
prefs = Preferences.load()
prefs.apply_theme(game.cli) # apply saved colour theme
game.add_screen("hello", HelloScreen)
game.switch_screen("hello") # can also use game.current_screen = "hello"
game.run()
Core Concepts
Game
The main entry point. Creates the pygame window, owns the CLI, ScreenManager, and main loop.
game = Game(title="My Game", target_fps=60)
game.run()
CLI
The character-grid terminal emulator. Write ANSI-escaped strings to any position:
# Absolute cursor position then coloured text
cli.write("\x1b[5;10H\x1b[32mGreen text\x1b[0m")
# Allows for writing text at specific grid coordinates:
cli.write("\x1b[31mRed text at (15, 20)\x1b[0m", 15, 20) # x=15, y=20
The grid is 120 columns × 40 rows (STANDARD_WIDTH × STANDARD_HEIGHT),
you can also use game.width and game.height to get the current dimensions
in characters (which may differ if you change the font size).
Screen
Base class for all application views. Override the lifecycle methods, it's
recommended not to override the constructor due to .cli and .game references not
being set until initialisation, use on_entry instead for setup.
from pixterm import Screen
class GameScreen(Screen):
def on_entry(self):
"""Called once when this screen becomes active."""
def render(self):
"""Called every frame — redraw everything here."""
def update(self, dt: float):
"""Called every frame with elapsed seconds since last frame."""
def handle_event(self, event):
"""Receives every pygame event while this screen is active."""
def on_exit(self):
"""Called once when leaving this screen."""
BDFFont
Bitmap font loader. Terminus fonts (12–24 px) are bundled and loaded automatically:
from pixterm import BDFFont
font = BDFFont.get_font(16) # 16 px normal weight
font_bold = BDFFont.get_font(16, bold=True)
Preferences
Singleton for persistent user settings (theme, etc.):
from pixterm import Preferences
prefs = Preferences.load() # load from ~/.pixterm/preferences.json
prefs.theme_name = "basic_white"
prefs.save()
prefs.apply_theme(game.cli)
Themes
Built-in colour themes:
| Name | Description |
|---|---|
basic_black |
Default dark terminal (standard ANSI colours) |
basic_white |
Light background variant |
catppuccin_mocha |
Based on the popular Catppuccin Mocha palette |
catppuccin_latte |
Based on the popular Catppuccin Latte palette |
solarized_dark |
Based on the classic Solarized Dark palette |
solarized_light |
Based on the classic Solarized Light palette |
gruvbox_dark |
Based on the popular Gruvbox Dark palette |
dracula |
Based on the popular Dracula palette |
pastel_pink |
Custom pastel pink palette |
orange_sunset |
Custom orange sunset palette |
from pixterm import get_theme_names, get_theme
print(get_theme_names()) # ['basic_black', 'basic_white', ...]
theme = get_theme("basic_black")
Font Sizes
Bundled Terminus BDF fonts:
| Size (px) | Normal | Bold | Variable |
|---|---|---|---|
| 12 | ✓ | ✓ | — |
| 14 | ✓ | ✓ | ✓ |
| 16 | ✓ | ✓ | ✓ |
| 18 | ✓ | ✓ | — |
| 20 | ✓ | ✓ | — |
| 22 | ✓ | ✓ | — |
| 24 | ✓ | ✓ | — |
Project Layout
pixterm/
├── __init__.py # Public API
├── cli.py # ANSI terminal emulator + TerminalCell grid
├── font_renderer.py # BDF font loader and glyph renderer
├── game.py # Main game loop + pygame initialisation
├── screen.py # Screen base class
├── screen_manager.py # Screen registration + switching
├── preferences.py # Persistent user preferences (singleton)
├── themes.py # Colour theme definitions
├── hotreloader.py # Optional watchdog-based hot-reloader
└── fonts/ # Bundled Terminus BDF bitmap fonts
└── ter-u*.bdf
Licence
MIT — see LICENSE.
Author's Notes on Artificial Intelligence
This project was developed with the assistance of AI. The AI was used to generate code snippets, suggest improvements, and help with debugging. The AI's contributions were reviewed and edited by the author to ensure they fit the project's requirements and coding style.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pixterm-0.2.1.tar.gz.
File metadata
- Download URL: pixterm-0.2.1.tar.gz
- Upload date:
- Size: 292.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80dfbdf8c7c9e45c45c0e9af3997c061cdf448e01b851e68effec7960a27e1ab
|
|
| MD5 |
e9b952eb66ada2f9922f88bb720f50f1
|
|
| BLAKE2b-256 |
afa19ed9162cad22ffd4b868bc1b26ab558cc602fb1cc8dd7981b6f457a802ca
|
File details
Details for the file pixterm-0.2.1-py3-none-any.whl.
File metadata
- Download URL: pixterm-0.2.1-py3-none-any.whl
- Upload date:
- Size: 311.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e6c32b0ce6b3218dc7e6dbd4473b84fce27e1d524a49aba5bfe9d0c9bf6330a
|
|
| MD5 |
88cee850fb36009e95163c0d44ccad6f
|
|
| BLAKE2b-256 |
a4fd1e2f23e1f739a87fd67217d524567611c63fb077e7c70088d78cd83bb116
|