def on_resize(): buf.resize(app.screen.w, app.screen.h) for x in range(buf.w): for y in range(buf.h): col = Color.rgb(0, 0, 0) if (x + y) % 2 else Color.rgb( 0.1, 0.1, 0.1) buf.put_char(" ", x, y, bg=col)
def _parse_style(style): s = style.copy() if "fg" in s: s["fg"] = Color(s["fg"]) if "bg" in s: s["bg"] = Color(s["bg"]) return s
def frame_gameover(): dark = Color.rgb(0.2,0.2,0.2) app.screen.clear() app.screen.blit(game_buffer, 0, ui_height) print_hcenter(app.screen, "Game over!", y=3, fg=Color.rgb(1,1,0), bg=dark) print_hcenter(app.screen, "Press enter to restart", y=4, fg=Color.rgb(0.75,0.75,0.75), bg=dark) print_hcenter(app.screen, "Press escape to exit", y=5, fg=Color.rgb(0.75,0.75,0.75), bg=dark) app.screen.update()
def on_resize(): app.screen.clear() app.screen.fill(0, 0, app.screen.w, app.screen.h, bg=Color(0, 255, 0)) app.screen.fill(1, 1, app.screen.w - 2, app.screen.h - 2, bg=Color(0, 0, 0)) app.screen.print("{} x {}".format(app.screen.w, app.screen.h), 2, 2) app.screen.update()
def main(): # example of manually controlling terminal b = detect_backend() b.enter_alt_buffer() b.cursor_pos = (0, 0) b.fg = Color.rgb(0, 1, 0) b.write("Hello world from {}\n".format(b.terminal_name)) b.cursor_pos = (0, 0) b.fg = Color.rgb(1, 0, 0) b.write("Hello\n") cols, rows = b.size b.write("Screen size: {}x{} hello\n".format(cols, rows)) b.flush() sleep(1) b.exit_alt_buffer()
def on_frame(): dx = app.mouse_x - app.mouse_px dy = app.mouse_y - app.mouse_py l = math.sqrt(dx**2 + dy**2) d = math.atan2(dy, dx) j = min(45, int(l)) * 2 for i in range(j): f = i / j ll = l * random.uniform(0.25, 0.5) dd = d + random.uniform(-0.2, 0.2) vx = ll * math.cos(dd) vy = ll * math.sin(dd) app.particles.append( Particle(app.mouse_px + dx * f, app.mouse_py + dy * f, vx, vy)) app.screen.clear() for i, p in enumerate(app.particles): col = Color.hsl(i / len(app.particles), 1.0, 0.5) app.screen.print(" ", int(p.x), int(p.y), bg=col) p.update() if p.x < 0 or p.y < 0 or p.x >= app.screen.w or p.y >= app.screen.h: app.particles.remove(p) app.screen.update() app.mouse_px = app.mouse_x app.mouse_py = app.mouse_y
def main(): app = App(framerate=float("inf")) colors = [Color.rgb(r,g,b) for r in (0, 1) for g in (0, 1) for b in (0, 1)] pixels = [PixelData(char="▄", fg=col1, bg=col2) for col1 in colors for col2 in colors] t0 = perf_counter() n_frames = 0 n_updates = 0 update_time = 0 @app.on("frame") def on_frame(): nonlocal n_frames, n_updates, update_time for x in range(app.screen.w): for y in range(app.screen.h): app.screen[x,y].set(choice(pixels)) app.screen.update() n_frames += 1 n_updates += app.screen._update_count update_time += app.screen._update_duration app.run() updates_per_ms = n_updates / update_time / 1000 updates_per_ms_real = n_updates / (perf_counter() - t0) / 1000 print("Updates per ms (updating): {:.2f}".format(updates_per_ms)) print("Updates per ms (real time): {:.2f}".format(updates_per_ms_real)) print("Average update time ms: {:.2f}".format(update_time / n_frames * 1000)) print("Average potential FPS: {:.2f}".format(n_frames / update_time))
def on_frame(): app.screen.clear() app.screen.cursor_pos = (c_x, c_y) P.update(buf, w, h) P.draw(app.screen, buf, w, h) if show_help: app.screen.print(helptext, 1, 1, fg=Color.rgb(1, 1, 1)) app.screen.update()
def log(s): global scroll_y for line in splitlines_print(s): b.print(line, fg=Color.hsl(time() * 0.2, 0.5, 0.4)) b.print_pos = (0, b.print_pos[1] + 1) b.extend_to(0, b.print_pos[1] + 1) if autoscroll: scroll_y = b.print_pos[1] - a.screen.h
def do_box(buffer): if drag_start is None or mouse_pos is None: return x, y, w, h = corners_to_box(*drag_start, *mouse_pos) # draw_box(buffer, x, y, w, h, chars=BOX_CHARS_DOUBLE, fg=Color.hsl(perf_counter(), 1.0, 0.5)) draw_frame(buffer, x, y, w, h, chars=FRAME_CHARS_DOUBLE, fg=Color.hsl(perf_counter(), 1.0, 0.5))
def frame_game(): nonlocal control_x, control_y nonlocal score, hiscore nonlocal mode # handle input if control_x != 0 and not worm.intersecting(worm.x + control_x, worm.y): worm.vx = control_x worm.vy = 0 control_x = 0 if control_y != 0 and not worm.intersecting(worm.x, worm.y + control_y): worm.vy = control_y worm.vx = 0 control_y = 0 if worm.intersecting(food_x, food_y): score += 1 worm.length += 1 hiscore = max(hiscore, score) save_hiscore(hiscore) move_food() game_buffer.clear() game_buffer.print("░░", food_x * 2, food_y) try: worm.update(game_buffer) except GameOver: mode = MODE_GAMEOVER multiply_buffer(game_buffer, 0.4) return # draw app.screen.clear() app.screen.print("~TermWorm~", 0, 0) app.screen.print("Score: {}".format(score), 0, 1, fg=Color.rgb(.7,.7,.7)) app.screen.print(" Hi: {}".format(hiscore), fg=Color.rgb(.7,.7,0)) draw_box(app.screen, 0, 2, app.screen.w, 1, chars=BOX_CHARS_DOUBLE) app.screen.blit(game_buffer, 0, ui_height) app.screen.update()
def main(): backend = App().backend backend.enter_alt_buffer() colors = [ Color.rgb(1, 0, 0), Color.rgb(1, 1, 0), Color.rgb(0, 1, 0), Color.rgb(0, 1, 1), Color.rgb(0, 0, 1), Color.rgb(1, 0, 1) ] t0 = perf_counter() frames = 0 pos = 0 while frames < 10000: backend.bg = colors[frames % len(colors)] backend.write(" ") backend.flush() frames += 1 t1 = perf_counter() backend.exit_alt_buffer() backend.flush() tpf = (t1 - t0) / frames print("Terminal: {}\n".format(backend.terminal_name)) print("Avg time per frame: {:.4f}\n".format(tpf)) print("Avg framerate: {:.2f}\n".format(1 / tpf))
def on_frame(): for x, p in enumerate(cols): # update colors for i in range(int(p.length) + 1): col = Color.hsl(HUE / 360, 1, (1 - i / p.length)**2) a.screen.at(x, int(p.pos - i + p.speed), clip=True).fg = col # render new characters for i in range(int(p.speed) + 1): pos = p.pos + i a.screen.print(" ", x, int(pos - p.length), fg=Color.rgb(0, 0, 0)) a.screen.print(randchar(), x, int(pos)) # randomly change a character a.screen.print(randchar(), x, int(p.pos + p.speed - randint(1, int(p.length)))) # move particle p.pos += p.speed if p.pos - p.length >= a.screen.h: cols[x] = Particle() a.screen.update()
def interact(place): nonlocal show_help show_help = False x = c_x y = c_y * 2 if x >= 0 and x < w and y >= 0 and y < h: if place: buf[y * w + x] = P(color=Color.hsl(perf_counter() * 0.25, 1, 0.5), **props["sand"]) else: buf[y * w + x] = P(**props["air"])
def on_frame(): w = app.screen.w h = app.screen.h colormap = [Color.rgb(0,0,0) for x in range(w) for y in range(h * 2)] dx = app.mouse_x - app.mouse_px dy = app.mouse_y - app.mouse_py l = math.sqrt(dx ** 2 + dy ** 2) d = math.atan2(dy, dx) j = min(25, int(l)) * 6 for i in range(j): f = i / j ll = l* random.uniform(0.25, 0.5) dd = d + random.uniform(-0.25, 0.25) vx = ll * math.cos(dd) vy = ll * math.sin(dd) app.particles.append(Particle(app.mouse_px + dx * f, app.mouse_py + dy * f, vx, vy)) app.screen.clear() for i, p in enumerate(app.particles): col = Color.hsl(i/len(app.particles), 1.0, 0.5) steps = math.hypot(p.vx, p.vy) for step in range(int(steps + 1)): f = step / steps px = int(p.x - p.vx * f) py = int(p.y - p.vy * f) if px >= 0 and py >= 0 and px < w and py < h * 2: colormap[py * w + px] += RED p.update() if p.x < 0 or p.y < 0 or p.x >= w or p.y >= h * 2: app.particles.remove(p) draw_colormap_2x(app.screen, colormap, 0, 0, w=w, h=h * 2) app.screen.update() app.mouse_px = app.mouse_x app.mouse_py = app.mouse_y
def update(self, buffer): self.old += [(self.x, self.y)] if len(self.old) > self.length: self.old.pop(0) for i, (x, y) in enumerate(self.old): col = Color.rgb(1,1,0) buffer.print("██", x * 2, y, fg=col) if self.intersecting(self.x + self.vx, self.y + self.vy): raise GameOver() self.x += self.vx self.y += self.vy self.wrap(buffer)
def on_frame(): app.screen.clear() # remove everything from the screen text = "Hello world, from termpixels!" for i, c in enumerate(text): f = i / len(text) color = Color.hsl(f + time(), 1, 0.5) # create a color from a hue value x = app.screen.w // 2 - len( text) // 2 # horizontally center the text offset = sin(time() * 3 + f * 5) * 2 # some arbitrary math y = round(app.screen.h / 2 + offset) # vertical center with an offset app.screen.print(c, x + i, y, fg=color) # draw the text to the screen buffer app.screen.update() # commit the changes to the screen
def on_frame(self): if self.need_resize: self.do_resize() for x in range(self.screen.w): for y in range(1, self.screen.h*2): i = y*self.screen.w + x self.propagate(i) self.screen.clear() self.screen.print(TITLE, self.screen.w // 2 - TITLE_W // 2, 2, fg=Color.rgb(1,1,1), bg=Color.rgb(0,0,0)) for x in range(self.screen.w): for y in range(0, self.screen.h): i = y*2*self.screen.w + x top = min(36, self.heightmap[i]) bot = min(36, self.heightmap[i+self.screen.w]) if top > 0 or bot > 0: self.screen.print("▄", x, y, bg=COLOR_MAP[top], fg=COLOR_MAP[bot]) self.screen.update()
def redraw(app): white = Color.rgb(1, 1, 1) gray = Color.rgb(0.5, 0.5, 0.5) yellow = Color.rgb(1, 1, 0) app.screen.clear(bg=Color(0, 0, 0)) app.screen.fill(1, 1, app.screen.w - 2, app.screen.h - 2, bg=Color.rgb(0.2, 0.2, 0.2)) app.screen.print("Termpixels version {}\n".format(termpixels.__version__), 2, 2, fg=white) app.screen.print("Detected terminal: ", x=2, fg=gray) app.screen.print(app.backend.terminal_name, fg=yellow) app.screen.print("\n") app.screen.print("Color support: ", x=2, fg=gray) app.screen.print(app.backend.color_mode, fg=yellow) app.screen.print("\n") steps = app.screen.w - 4 for x in range(steps): app.screen.print(" ", x=x + 2, bg=Color.hsl(x / steps, 1, 0.5)) app.screen.print("\n") app.screen.print("Last keypress: ", x=2, fg=gray) app.screen.print(repr(app.key), fg=white) app.screen.print("\n") app.screen.print("Last render time: ", x=2, fg=gray) app.screen.print("{:.1f}ms".format(app.screen._update_duration * 1000), fg=white) app.screen.print("\n") if app.mouse: app.screen.print(" ", app.mouse.x, app.mouse.y, bg=white) app.screen.update()
from termpixels import App, Color if __name__ == "__main__": # it's possible to not "start" an App and just use its terminal backend. term = App().backend term.fg = Color.rgb(1, 0, 1) term.write("Hello world\n") term.flush() term.fg = Color.rgb(1, 1, 0) term.flush() print("print() works too; just flush first.")
import sys from termpixels import App, Color from time import perf_counter AQUA = Color.rgb(0, 1, 1) def main(): app = App(framerate=float("inf")) app.t0 = perf_counter() app.frames = 0 app.pos = 0 @app.on("frame") def on_frame(): app.pos = (app.pos + 1) % (app.screen.w * app.screen.h) x = app.pos % app.screen.w y = app.pos // app.screen.w app.screen.clear() app.screen.print(" " * 5, x, y, bg=AQUA) app.screen.update() app.frames += 1 app.t1 = perf_counter() @app.on("after_stop") def on_after_stop(): tpf = (app.t1 - app.t0) / app.frames print("Terminal: {} ({}x{})\n".format(app.backend.terminal_name, app.screen.w, app.screen.h)) print("Avg time per frame: {:.4f}\n".format(tpf)) print("Avg framerate: {:.2f}\n".format(1 / tpf))
import math import time from termpixels import App, Color, Buffer from termpixels.drawing import draw_box, draw_hline, draw_spinner, draw_progress, draw_colormap, draw_colormap_2x from termpixels.drawing import BOX_CHARS_LIGHT_DOUBLE_TOP, SPINNER_PIPE, SPINNER_MOON, SPINNER_BAR, SPINNER_CLOCK, SPINNER_DOTS, PROGRESS_SMOOTH from termpixels.util import wrap_text WHITE = Color.rgb(1, 1, 1) GREY = Color.rgb(0.5, 0.5, 0.5) YELLOW = Color.rgb(0.5, 0.5, 0) BRIGHT_YELLOW = Color.rgb(1, 1, 0) GREEN = Color.rgb(0, 0.5, 0) LIPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nec erat quis turpis ultrices eleifend id et urna. Praesent ultricies orci fermentum, placerat eros id, scelerisque mi. Aenean lobortis pellentesque diam, vel auctor felis semper in. Aliquam cursus diam sit amet lorem faucibus, eget sagittis eros bibendum. Maecenas dignissim libero." # a frightening hand-drawn smiley face SMILEY = list(map( lambda b: [None, YELLOW, GREEN, GREY][b], [0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 0, 1, 3, 3, 3, 1, 0, 0, 0, 1, 1, 1, 0, 0] )) def main(): app = App() inner_buffer = Buffer(0, 0)
import random import math from termpixels import App, Color from termpixels.drawing import draw_colormap_2x RED = Color.rgb(0.25,0.1,0.05) class Particle: def __init__(self, x, y, vx = 0, vy = 0): self.x = x self.y = y self.vx = vx self.vy = vy def update(self): self.x += self.vx self.y += self.vy self.vy += 0.1 def main(): app = App(mouse=True, framerate=60) app.mouse_x = 0 app.mouse_y = 0 app.mouse_px = 0 app.mouse_py = 0 app.particles = [] @app.on("mouse") def on_mouse(m): app.mouse_x = m.x
if "name" in node and node["name"] == name: return node return None return find(layout[ROOT]) def _merge(source, target): result = target.copy() for k, v in source.items(): result[k] = v return result STYLE_DEFAULTS = { "bg": Color.rgb(0.2, 0.2, 0.2), "fg": Color.rgb(0.8, 0.8, 0.8), "h-align": "left", "v-align": "top" } def _parse_style(style): s = style.copy() if "fg" in s: s["fg"] = Color(s["fg"]) if "bg" in s: s["bg"] = Color(s["bg"]) return s
from termpixels import App, Color from unicodedata import category COL_FG = Color.rgb(0.8, 0.8, 0.8) COL_CTRL = Color.rgb(0.8, 0.0, 0.8) COL_NUM = Color.rgb(0.8, 0.4, 0.0) COL_PUNC = Color.rgb(0.0, 0.4, 0.8) COL_SEP = Color.rgb(0.2, 0.0, 0.2) def main(): app = App(mouse=True) in_buffer = [] @app.input.on("raw_input") def on_raw(data): in_buffer.append(data) def print_escape(line, x, y): app.screen.print_pos = (x, y) for ch in line: fg = COL_FG bg = None cat = category(ch) if "C" in cat: fg = COL_CTRL elif "N" in cat: fg = COL_NUM elif "P" in cat:
def on_start(): app.buffer = Buffer(16, 4) app.buffer.clear(bg=Color.rgb(0.2, 0, 0), fg=Color.rgb(1, 0.5, 0.5)) app.buffer.print("Hello world")
from termpixels.app import LegacyApp from random import randint # http://fabiensanglard.net/doom_fire_psx/ TITLE = u""" ▄▄▄▄▀ ▄███▄ █▄▄▄▄ █▀▄▀█ █ ▄▄ ▄█ ▄ ▄███▄ █ ▄▄▄▄▄ ▀▀▀ █ █▀ ▀ █ ▄▀ █ █ █ █ █ ██ ▀▄ █ █▀ ▀ █ █ ▀▄ █ ██▄▄ █▀▀▌ █ ▄ █ █▀▀▀ ██ █ ▀ ██▄▄ █ ▄ ▀▀▀▀▄ █ █▄ ▄▀ █ █ █ █ █ ▐█ ▄ █ █▄ ▄▀ ███▄ ▀▄▄▄▄▀ ▀ ▀███▀ █ █ █ ▐ █ ▀▄ ▀███▀ ▀ ▀ ▀ ▀ ▀ """ TITLE_W = max(len(l) for l in TITLE.splitlines()) COLOR_MAP = [Color(i[0], i[1], i[2]) for i in [ (0x07,0x07,0x07), (0x1F,0x07,0x07), (0x2F,0x0F,0x07), (0x47,0x0F,0x07), (0x57,0x17,0x07), (0x67,0x1F,0x07), (0x77,0x1F,0x07), (0x8F,0x27,0x07), (0x9F,0x2F,0x07), (0xAF,0x3F,0x07), (0xBF,0x47,0x07), (0xC7,0x47,0x07), (0xDF,0x4F,0x07), (0xDF,0x57,0x07), (0xDF,0x57,0x07), (0xD7,0x5F,0x07), (0xD7,0x5F,0x07), (0xD7,0x67,0x0F), (0xCF,0x6F,0x0F), (0xCF,0x77,0x0F), (0xCF,0x7F,0x0F), (0xCF,0x87,0x17), (0xC7,0x87,0x17), (0xC7,0x8F,0x17), (0xC7,0x97,0x1F), (0xBF,0x9F,0x1F), (0xBF,0x9F,0x1F), (0xBF,0xA7,0x27), (0xBF,0xA7,0x27), (0xBF,0xAF,0x2F), (0xB7,0xAF,0x2F), (0xB7,0xB7,0x2F), (0xB7,0xB7,0x37), (0xCF,0xCF,0x6F), (0xDF,0xDF,0x9F), (0xEF,0xEF,0xC7), (0xFF,0xFF,0xFF)]] class DoomTerm(LegacyApp): def __init__(self): super().__init__(framerate=24)
from termpixels import App, Color, Buffer, SparseBuffer a = App() b = Buffer(16, 8) sb = SparseBuffer(16, 8) b.clear(bg=Color.rgb(0.5, 0, 0)) b.print("Hello Buffer!") sb.clear(bg=Color.rgb(0, 0.5, 0)) sb.print("Hello Sparse!") @a.on("frame") def frame(): a.screen.clear() a.screen.blit(b, 1, 1) a.screen.blit(sb, 1 + b.w + 1, 1) a.screen.print_pos = (1, 1 + b.h + 1) a.screen.print(f"Pixels in buffer: {b.w * b.h}\n") a.screen.print(f"Pixels in sparse buffer: {sb._pixel_count}\n") a.screen.update() if __name__ == "__main__": a.run()
from termpixels import App, Color from random import random from time import perf_counter helptext = """Use mouse or arrow keys to move cursor. Use left/right click or Z/X to add/remove sand.""" props = { "air": { "color": Color.rgb(0, 0, 0), "falling": False, "density": 1, "sliding": False }, "sand": { "falling": True, "density": 2, "sliding": True } } class P: def __init__(self, *, color, falling, density, sliding): self.color = color self.falling = falling self.density = density self.sliding = sliding @staticmethod def update(buf, w, h):
def start(): global cols cols = [Particle() for x in range(a.screen.w)] a.screen.clear(fg=Color.rgb(0, 0, 0))