Obsah
1. Tvorba aplikací a her s textovým uživatelským rozhraním s využitím knihovny Blessed (dokončení)
2. Prohlížeč obrázků pro terminál?
3. Využití „plného“ znaku ve funkci pixelu
4. Využití mezery s modifikovanou barvou pozadí
5. Použití znaků Unicode na terminálu
7. Čtení kláves v režimu cbreak
8. Rozpoznání jména a kódu stisknutých kláves
9. Sekvence znaků poslaná po stisku klávesy
12. Pohyb kurzoru po ploše terminálu
13. Ukázka skriptu přesunujícího kurzor
14. Výpočet umístění textu s řídicími znaky, obnovení původní pozice kurzoru
17. Dodatek 1: klávesy rozeznávané knihovnou Blessed
18. Dodatek 2: příklad nastavení xtermu
19. Repositář s demonstračními příklady
1. Tvorba aplikací a her s textovým uživatelským rozhraním s využitím knihovny Blessed (dokončení)
V úvodním článku o knihovně Blessed jsme si popsali pouze základní funkce této potenciálně užitečné knihovny, které je možné použít pro změnu (vylepšení) zobrazení údajů na terminálu. To je však ve skutečnosti pouze relativně malá (ovšem pochopitelně důležitá) část celkové funkcionality. Aby bylo možné vytvářet plnohodnotné celoobrazovkové aplikace, je nutné umět přesunout textový kurzor na libovolnou pozici na terminálu (sloupec, řádek), reagovat na stisk klávesy (a nikoli čekat na potvrzení Enterem, což je implicitní režim práce s terminálem), popř. reagovat na změnu velikosti okna terminálu. Tyto funkce budou popsány v dnešní a současně i poslední části mikroseriálku o knihovně Blessed.
Obrázek 1: Rogue, aneb prapředek klasických roguelike her – průzkum prvního patra dungeonu. Tato hra využívá některých možností poskytovaných textovými terminály.
Tyto funkce poskytované knihovnou Blessed jsou dostatečné pro vývoj většiny aplikací pracujících v terminálu. Mezi takové aplikace patří především textové editory, dále například celoobrazovkové debuggery (příkladem může být PuDB, což je debugger pro Python), nástroje typu Midnight Commander a dokonce i některé hry. U her se na chvíli zastavme: vytvářet lze hry typu Rogue/Nethack, pochopitelně textovky, strategické hry i některé hry akční. Ovšem zde poměrně záhy narazíme na problematickou práci s klávesnicí, neboť nelze (alespoň ne jednoduše) reagovat na stisk i puštění klávesy, u kláves se posílá informace o automatickém opakování atd. Možnosti knihovny Blessed jsou v tomto ohledu poněkud limitovány – nejedná se tedy v žádném případě o „SDL pro konzoli“.
Obrázek 2: Pohled na textové uživatelské rozhraní debuggeru PuDB s pěti regiony.
2. Prohlížeč obrázků pro terminál?
Minule jsme si kromě dalších informací řekli, že terminály většinou podporují osm, šestnáct, 256, popř. 224 barev (xterm pak 4096 barev). Knihovna Blessed dokáže pracovat s celým barvovým spektrem, tedy s oněmi více než šestnácti miliony barvových odstínů (záleží pak na konkrétním terminálu, zda dokáže všechny tyto barvy zobrazit nebo například provede převod na nejbližší barvu z palety 4096 odstínů). Pro převod vlastní barvy (reprezentované většinou v barvovém prostoru RGB) na příslušný řídicí kód (escape sekvenci) slouží funkce nazvaná color_rgb.
Obrázek 3: Kingdom of Kroz II – úvodní obrazovka s ANSI artem (starší hra pro IBM PC).
Pokusme se nyní tuto vlastnost moderních terminálů využít a vytvořit primitivní prohlížeč obrázků. Namísto bitmapy s jednotlivými pixely použijeme textový terminál, přičemž funkci pixelu (obrazového elementu) převezme celý znak – buď vykreslíme určitou barvou znak „box“ z Unicode (viz též tento přehledový článek), nebo vykreslíme mezeru, ovšem se změněnou barvou pozadí. Nejdříve získáme testovací obrázek, který po stažení zmenšíme na rozměry 128×128 pixelů. O obě tyto operace se postará následující jednoduchý skript:
"""Retrieve test image, resize it, and store under new filename.""" from PIL import Image import urllib.request url = "https://homepages.cae.wisc.edu/~ece533/images/fruits.png" original_filename = "fruits_.png" resized_filename = "fruits.png" urllib.request.urlretrieve(url, original_filename) img = Image.open(original_filename) resized = img.resize((128, 128), Image.BILINEAR) resized.save(resized_filename)
Obrázek 4: Originální obrázek s rozlišením 512×512 pixelů.
Obrázek 5: Obrázek po zmenšení na 128×128 pixelů.
3. Využití „plného“ znaku ve funkci pixelu
První verze prohlížeče obrázků využívá „plný“ znak, tj. znak, který by měl mít ve všech znakových sadách tvar vyplněného obdélníku. Jedná se o následující znak Unicode. Program pracuje jednoduše:
- Vnutí terminálu režim true color (to je nutné například pro xterm)
- Načte rastrový obrázek s využitím funkce Image.open z knihovny PIL/Pillow
- Postupně projde všemi sudými řádky obrázku (toto je trik, protože znaky mají většinou poměr výška:šířka roven 2:1)
- Projde všemi pixely na řádku
- Přečte barvové složky pixelů, převede je s využitím metody Terminal.color_rgb na sekvenci řídicích znaků a ty následně vypíše
- Nový řádek je pochopitelně zajištěn zavoláním print()
Úplný zdrojový kód vypadá následovně:
from PIL import Image import blessed terminal = blessed.Terminal() terminal.number_of_colors = 1 << 24 filename = "fruits.png" img = Image.open(filename) for j in range(0, img.height, 2): for i in range(img.width): red, green, blue = img.getpixel((i, j)) print(f"{terminal.color_rgb(red, green, blue)}█", end="") print() print() print(f"{terminal.normal}DONE")
Pokud má váš terminál minimálně 129 znaků na řádku, měli byste uvidět náhled na obrázek:
Obrázek 6: První část obrázku zobrazená na terminálu.
Obrázek 7: Druhá část obrázku zobrazená na terminálu.
4. Využití mezery s modifikovanou barvou pozadí
Může se stát, že font nastavený pro použití v terminálu výše zmíněný znak „full block“ nezobrazí korektně. V takovém případě je možné prohlížeč přepsat, a to takovým způsobem, že se bude vykreslovat pouze mezera, ovšem s modifikovaným pozadím. Změnu pozadí na základě barvových složek red, green, blue zajišťuje metoda Terminal.on_color_rgb. Skript je upraven takto:
from PIL import Image import blessed terminal = blessed.Terminal() terminal.number_of_colors = 1 << 24 filename = "fruits.png" img = Image.open(filename) for j in range(0, img.height, 2): for i in range(img.width): red, green, blue = img.getpixel((i, j)) print(f"{terminal.on_color_rgb(red, green, blue)}.", end="") print() print() print(f"{terminal.normal}DONE")
Obrázek 8: První část obrázku zobrazená na terminálu.
Obrázek 9: Druhá část obrázku zobrazená na terminálu.
5. Použití znaků Unicode na terminálu
Již v předchozích dvou kapitolách jsme se krátce zmínili o Unicode. Pokud je nainstalován a především nastaven korektní font, je Unicode podporován většinou moderních emulátorů terminálu (i když například v případě rxvt je nutné použít jeho variantu urxvt). Pro otestování korektnosti vykreslování Unicode znaků doporučuji použít dokument uložený na adrese https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-demo.txt, jenž obsahuje různé části Unicode, včetně různých abeced, znaků pro pseudografiku atd. Na základě tohoto souboru je možné si vytvořit testovací program, který se spustí v terminálu a zjistí se tak jeho skutečné možnosti:
Zdrojový kód vypadá takto:
# Based on famous UTF-8-demo.txt created by: # Markus Kuhn [ˈmaʳkʊs kuːn] <http://www.cl.cam.ac.uk/~mgk25/> — 2002-07-25 import blessed terminal = blessed.Terminal() print(""" STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑ ▉ ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳ ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳ ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳ ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳ ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎ ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏ ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█ ▝▀▘▙▄▟ ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫ ⎪⎢⎜│a²+b³ ⎟⎥⎪ ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β), ⎪⎢⎜│───── ⎟⎥⎪ ⎪⎢⎜⎷ c₈ ⎟⎥⎪ ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⎨⎢⎜ ⎟⎥⎬ ⎪⎢⎜ ∞ ⎟⎥⎪ ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫), ⎪⎢⎜ ⎲ ⎟⎥⎪ ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪ 2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm ⎩⎣⎝i=1 ⎠⎦⎭ """)
Příklad spuštění na mé variantě terminálu ukazuje, že ne všechny znaky se skutečně zobrazily korektně:
Obrázek 10: Výsledek testu zobrazení Unicode znaků.
6. Režim terminálu
Terminál může pracovat v několika režimech. Na příkazové řádce se používá takzvaný cooked mode, v němž jsou významy některých kláves interpretovány a zpracovány ještě předtím, než jsou poslány do programu. Zcela typickým příkladem je zápis „Hix(Backspace)!“, který není v cooked režimu poslán do aplikace v této podobě – aplikace totiž dostane na vstupu pouze řetězec „Hi!“, protože klávesa Backspace je mezitím interpretována a zpracována. Pro běžné vstupy a základní interaktivitu se jedná o užitečný režim, který ovšem přestane být výhodný ve chvíli, kdy je nutné reagovat (a to ihned) na stisky všech kláves. V takovém případě je nutné terminál přepnout do režimu raw nebo do režimu cbreak, jenž je někdy známý pod jménem rare (což je odvozeno od „half cooked“). V knihovně Blessed se toto přepnutí provádí metodou Terminal.cbreak():
Help on function cbreak in module blessed.terminal: cbreak(self) Allow each keystroke to be read immediately after it is pressed. This is a context manager for :func:`tty.setcbreak`. This context manager activates 'rare' mode, the opposite of 'cooked' mode: On entry, :func:`tty.setcbreak` mode is activated disabling line-buffering of keyboard input and turning off automatic echo of input as output. ...
Obrázek 11: Hra DoomRL – hlavní menu (co mi to připomíná…).
Většinou se setkáme s tím, že se režim cbreak zapíná pouze pro určitou část programu. Využívá se zde kontextu a zápis tedy vypadá následovně:
import blessed terminal = blessed.Terminal() with terminal.cbreak(): ... ... ...
Obrázek 12: Hra DoomRL – první úroveň.
7. Čtení kláves v režimu cbreak
Ve třídě Terminal nalezneme i funkci nazvanou inkey, kterou je možné použít pro čtení kódu stisknuté klávesy v režimu cbreak:
Help on function inkey in module blessed.terminal: inkey(self, timeout=None, esc_delay=0.35) Read and return the next keyboard event within given timeout. Generally, this should be used inside the :meth:`raw` context manager.
Podívejme se nyní na příklad použití této funkce v kontextu (režimu) cbreak. Celý program, který dokáže jednotlivé klávesy číst a zobrazovat je, může vypadat následovně:
import blessed terminal = blessed.Terminal() with terminal.cbreak(): while True: key = terminal.inkey() print(key)
Příklad po spuštění tohoto skriptu (po stisku různých kláves):
$ python3 inkey_1.py + 2 3 q w P Q R
Skript lze ukončit klávesovou zkratkou Ctrl+C. Proto také používáme režim cbreak a nikoli raw, protože v režimu cbreak je tato zkratka správně rozpoznána a zpracována:
Traceback (most recent call last): File "inkey_1.py", line 7, in key = terminal.inkey() File "/home/ptisnovs/.local/lib/python3.9/site-packages/blessed/terminal.py", line 1385, in inkey while not ks and self.kbhit(timeout=_time_left(stime, timeout)): File "/home/ptisnovs/.local/lib/python3.9/site-packages/blessed/terminal.py", line 1203, in kbhit ready_r, _, _ = select.select(check_r, [], [], timeout) KeyboardInterrupt
8. Rozpoznání jména a kódu stisknutých kláves
Hodnota, která je vrácena metodou Terminal.inkey, není typu „znak“, ale typu blessed.keyboard.Keystroke:
class Keystroke(builtins.str) | A unicode-derived class for describing a single keystroke. | | A class instance describes a single keystroke received on input, | which may contain multiple characters as a multibyte sequence, | which is indicated by properties :attr:`is_sequence` returning | ``True``. | | When the string is a known sequence, :attr:`code` matches terminal | class attributes for comparison, such as ``term.KEY_LEFT``. | | The string-name of the sequence, such as ``u'KEY_LEFT'`` is accessed | by property :attr:`name`, and is used by the :meth:`__repr__` method | to display a human-readable form of the Keystroke this class | instance represents. It may otherwise by joined, split, or evaluated | just as as any other unicode string.
Jedná se tedy o plnohodnotné objekty, které obsahují několik užitečných atributů. Především se jedná o jméno stisknuté klávesy a taktéž o její numerický kód. Jména a kódy všech podporovaných kláves jsou uvedeny v prvním dodatku v sedmnácté kapitole.
Samozřejmě si vše vyzkoušíme na následujícím jednoduchém skriptu:
import blessed terminal = blessed.Terminal() with terminal.cbreak(): while True: key = terminal.inkey() print(key.name, key.code, key)
Ukázka použití (včetně speciálních kláves):
KEY_F1 265 P KEY_F12 276 KEY_LEFT 260 KEY_RIGHT 261 KEY_ENTER 343 KEY_BACKSPACE 263 KEY_TAB 512 None None a None None A KEY_F24 288
Povšimněte si, že:
- U běžných ASCII kláves se nevrací ani jméno klávesy ani její numerický kód. Navíc je možné použít Shift a rozlišit tak mezi klávesou „a“ a „A“ atd.
- Funkční klávesy F1 atd. jsou (většinou) zpracovány korektně.
- Další klávesy typu Backspace, Enter a kurzorové šipky jsou taktéž zpracovány korektně.
- Při stisku Shift+Fx se typicky vrací klávesa Fy, kde y je zvýšeno o 12 (na PC). Například Shift+F12 vrací jméno a kód odpovídající klávese F24.
9. Sekvence znaků poslaná po stisku klávesy
Některé kombinace kláves, například Ctrl+End apod., nemají vlastní jméno ani kód. Namísto toho knihovna Blessed tuto kombinaci zaznamená jako sekvenci řídicích znaků, které aplikace může (pokud je to skutečně zapotřebí) zpracovat a následně použít, ovšem s tím, že se nebude jednat o plně přenositelný kód (což je mimochodem jeden z důvodů, proč má například Midnight Commander dialog pro „učení kláves“ a dokáže tedy zaznamenat i různé méně stabilní kombinace kláves).
Obrázek 13: Dialog Midnight Commanderu, ve kterém je možné zaznamenat sekvenci řídicích znaků po stisku neznámé klávesové kombinace.
Sekvenci řídicích znaků lze ve skriptu získat a zobrazit například takto:
import blessed terminal = blessed.Terminal() with terminal.cbreak(): while True: key = terminal.inkey() if key.is_sequence: print("got sequence: {0}.".format(key.name, key.code, (str(key)))) else: print(key.name, key.code, key)
Výsledky pro známé klávesy:
None None a None None A got sequence: KEY_F1. got sequence: KEY_F12. got sequence: KEY_F24. got sequence: KEY_LEFT. got sequence: KEY_RIGHT. got sequence: KEY_UP. got sequence: KEY_DOWN. got sequence: KEY_ESCAPE.
Výsledky pro neznámé kombinace:
Ctrl+Up:
got sequence: KEY_ESCAPE. None None [ None None 1 None None ; None None 5 None None A
Ctrl+Home:
got sequence: KEY_ESCAPE. None None [ None None 1 None None ; None None 5 None None H
10. Neblokující čtení klávesy
Pro některé interaktivní aplikace, které například potřebují zobrazit průběžně se měnící grafy, příchozí zprávy atd. (a pochopitelně i pro hry) je někdy nutné nečekat na stisk klávesy po nekonečně dlouhou dobu. Knihovna Blessed sice programátorům nenabízí přístup do plnohodnotné smyčky událostí (event loop), ale přesto je možné alespoň specifikovat maximální dobu, po kterou se bude na stisk klávesy čekat. Příkladem může být tento skript, který čeká jen přibližně 300 milisekund a pokud klávesa stisknuta není, lze tento stav jednoznačně programově detekovat:
import blessed terminal = blessed.Terminal() with terminal.cbreak(): while True: key = terminal.inkey(timeout=0.3) if str(key) == "": print("Nothing... try again") elif key.is_sequence: print("got sequence: {0}.".format(key.name, key.code, (str(key)))) else: print(key.name, key.code, key)
Příklad výstupu:
$ python3 inkey_4.py Nothing... try again Nothing... try again got sequence: KEY_ENTER. None None d None None f None None d None None s Nothing... try again got sequence: KEY_F1. Nothing... try again
Obrázek 14: ADOM ve verzi pro DOS – podrobnější měnitelné vlastnosti hrdiny.
11. „Raw“ režim terminálu
V šesté kapitole jsme se zmínili o třech režimech terminálu – „cooked“, „raw“ a „cbreak/rare“. V prvním režimu je vstup zpracován až po stisku klávesy Enter, ve druhém režimu lze okamžitě číst stisky kláves a reagovat na ně, ovšem s tím, že některé klávesové zkratky, například Ctrl+C jsou zachyceny a zpracovány systémem (takže lze aplikaci ukončit právě s využitím Ctrl+C). A konečně v režimu „raw“ může program zachytit stisky všech kláves, a to i například již zmíněné Ctrl+C. To má své přednosti i zápory. Předností je možnost využít tyto klávesové zkratky například v textových editorech, záporem to, že se o ukončení aplikace musíme postarat sami. Jak to provést nám ukazuje tento skript:
import blessed terminal = blessed.Terminal() with terminal.raw(): while True: key = terminal.inkey() if key == 'q': break print(key)
Tento skript lze ukončit stiskem klávesy q.
Obrázek 15: Angband pro Linux – podrobnější charakteristiky hrdiny.
12. Pohyb kurzoru po ploše terminálu
Na terminál se nemůžeme dívat jako na obdobu rastrového obrázku v klasickém framebufferu. Největší rozdíl spočívá v tom, že zobrazení znaku na určitém místě terminálu se provádí nepřímo – přesunem textového kurzoru na potřebné místo, které je následováno vytištěním příslušného znaku (typicky bez odřádkování). Samotné vytištění znaku je snadné, protože lze použít standardní funkci print, ovšem pro přesun kurzoru muselo být do knihovny Blessed přidáno několik metod objektu typu Terminal:
# | Metoda | Stručný popis metody |
---|---|---|
1 | move_xy(x, y) | přesun kurzoru na souřadnice [x, y] |
2 | move_x(x) | horizontální přesun kurzoru |
3 | move_y(y) | vertikální přesun kurzoru |
4 | home | přesun kurzoru na souřadnice [0, 0] |
5 | move_up | posun kurzoru o řádek nahoru |
6 | move_up(y) | posun kurzoru o y řádků nahoru |
7 | move_down | posun kurzoru o řádek dolů |
8 | move_down(y) | posun kurzoru o y řádků dolů |
9 | move_left | posun kurzoru o znak doleva |
10 | move_left(x) | posun kurzoru o x znaků doleva |
11 | move_right | posun kurzoru o znak doprava |
12 | move_right(x) | posun kurzoru o x znaků doprava |
Obrázek 16: Úvodní animace legendární hry Dwarf Fortress – vstup do herního světa. Tato hra ve skutečnosti terminál pouze emuluje (tedy emuluje emulátor terminálu).
13. Ukázka skriptu přesunujícího kurzor
Následující (do co největší míry zjednodušený) skript vykreslí okolo terminálu rámeček. Kvůli jednoduchosti není terminál vymazán, pro rámeček se nepoužívají znaky Unicode a rohy nejsou vykresleny korektně:
import blessed terminal = blessed.Terminal() with terminal.cbreak(): for x in range(terminal.width): print(f"{terminal.move_xy(x, 0)}-", end="", flush=True) print(f"{terminal.move_xy(x, terminal.height-1)}-", end="", flush=True) for y in range(terminal.height): print(f"{terminal.move_xy(0, y)}|", end="", flush=True) print(f"{terminal.move_xy(terminal.width-1, y)}|", end="", flush=True) terminal.inkey()
Obrázek 17: Testovací aréna hry Dwarf Fortress.
14. Výpočet umístění textu s řídicími znaky, obnovení původní pozice kurzoru
V případě, že je zapotřebí zobrazit například vycentrovaný text, který ovšem obsahuje i příkazy (řídicí znaky) pro změnu stylu či barvy, je možné pro tento účel použít jednu ze čtyř funkcí poskytovaných přímo knihovnou Blessed. Tyto funkce zajistí, že se vypočítá korektní umístění řetězce i s přihlédnutím k řídicím znakům (které mají při zobrazení nulovou šířku):
# | Metoda | Stručný popis |
---|---|---|
1 | center(text, width=None, fillchar=' ') | vycentrování textu na zadanou šířku |
2 | ljust(text, width=None, fillchar=' ') | úprava textu takovým způsobem, aby byl zarovnán doprava na zadanou šířku |
3 | rjust(text, width=None, fillchar=' ') | úprava textu takovým způsobem, aby byl zarovnán doprava na zadanou šířku |
4 | wrap(text, width=None, …) | vrací seznam řádků získaných zarovnáním textu na zadanou šířku |
Další užitečnou metodou je metoda nazvaná Terminal.location, která vytvoří nový kontext, v němž se zapamatuje původní pozice textového kurzoru:
with terminal.location(nastavení umístění kurzoru): ... ... ...
V dalším demonstračním příkladu je ukázáno jak použití metody location společně s vycentrováním textu na zadanou šířku:
import blessed terminal = blessed.Terminal() with terminal.cbreak(): for x in range(terminal.width): print(f"{terminal.move_xy(x, 0)}-", end="", flush=True) print(f"{terminal.move_xy(x, terminal.height-1)}-", end="", flush=True) for y in range(terminal.height-1): print(f"{terminal.move_xy(0, y)}|", end="", flush=True) print(f"{terminal.move_xy(terminal.width-1, y)}|", end="", flush=True) with terminal.location(y=terminal.height // 2): print(terminal.center(terminal.bold("Press any key to exit..."))) terminal.inkey()
Obrázek 18: Různé ikony představující trpaslíky ve hře Dwarf Fortress (použito v režimu fortress).
15. Přeformátování textu
Podívejme se ještě na metodu Terminal.wrap zmíněnou v předchozí kapitole. Tuto metodu je možné použít pro zalomení textu do zvolené šířky. Vše si otestujeme interaktivně přímo v REPLu programovacího jazyka Python:
>>> import blessed >>> terminal = blessed.Terminal() >>> t = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. " >>> lines = terminal.wrap(t, width=40) >>> for line in lines: ... print(line) ... Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Totéž, ovšem s textem, který obsahuje řídicí znaky pro změnu barvy a stylu písma:
>>> t = f"{terminal.red}Lorem ipsum {terminal.yellow}dolor{terminal.normal} sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. {terminal.bold}Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat{terminal.normal}. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. " >>> li; >>> lnes = terminal.wrap(t, width=40) >>> for line in lines: ... print(line) ... Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Obrázek 19: Vytváření historie herního světa v Dward Fortress.
16. Chybějící funkcionalita
V knihovně Blessed některé funkce nenalezneme. Především se jedná o sadu textových ovládacích prvků (widgetů), což by však nemuselo vadit, protože pro tyto účely již existují jiné knihovny (prompt_toolkit atd.). Nenalezneme zde ani funkce či metody, které na ploše terminálů vytváří vlastní (relativně nezávislá) okna, což je opět řešeno dalšími balíčky (a nejedná se o zcela triviální problematiku, zejména při použití překrývajících se oken). A pro tvorbu interaktivnějších her chybí především obdoba funkce delay, kterou je nutné získat z jiných balíčků určených pro jazyk Python.
Obrázek 20: Pro tvorbu rozhraní ve stylu TurboVision je vhodnější použít jiný nástroj, než knihovnu Blessed.
17. Dodatek 1: klávesy rozeznávané knihovnou Blessed
V tomto dodatku jsou vypsána jména a číselné kódy všech kláves rozpoznávaných knihovnou Blessed. Mnoho kláves na vaší klávesnici nenajdete, protože se jedná o dnes již historické pozůstatky po starších terminálech a počítačích (ostatně jestli chce navštívit lokální počítačové muzeum, podívejte se do databáze termcap a terminfo):
Jméno | Kód | Jméno | Kód |
---|---|---|---|
KEY_BACKSPACE | 263 | KEY_FIND | 362 |
KEY_BEGIN | 354 | KEY_HELP | 363 |
KEY_BTAB | 353 | KEY_HOME | 262 |
KEY_C1 | 351 | KEY_IL | 329 |
KEY_C3 | 352 | KEY_INSERT | 331 |
KEY_CANCEL | 355 | KEY_KP0 | 520 |
KEY_CATAB | 342 | KEY_KP1 | 521 |
KEY_CENTER | 350 | KEY_KP2 | 522 |
KEY_CLEAR | 333 | KEY_KP3 | 523 |
KEY_CLOSE | 356 | KEY_KP4 | 524 |
KEY_COMMAND | 357 | KEY_KP5 | 525 |
KEY_COPY | 358 | KEY_KP6 | 526 |
KEY_CREATE | 359 | KEY_KP7 | 527 |
KEY_CTAB | 341 | KEY_KP8 | 528 |
KEY_DELETE | 330 | KEY_KP9 | 529 |
KEY_DL | 328 | KEY_KP_ADD | 514 |
KEY_DOWN | 258 | KEY_KP_DECIMAL | 517 |
KEY_EIC | 332 | KEY_KP_DIVIDE | 518 |
KEY_END | 360 | KEY_KP_EQUAL | 519 |
KEY_ENTER | 343 | KEY_KP_MULTIPLY | 513 |
KEY_EOL | 335 | KEY_KP_SEPARATOR | 515 |
KEY_EOS | 334 | KEY_KP_SUBTRACT | 516 |
KEY_ESCAPE | 361 | KEY_LEFT | 260 |
KEY_F0 | 264 | KEY_LL | 347 |
KEY_F1 | 265 | KEY_MARK | 364 |
KEY_F2 | 266 | KEY_MAX | 511 |
KEY_F3 | 267 | KEY_MESSAGE | 365 |
KEY_F4 | 268 | KEY_MIN | 257 |
KEY_F5 | 269 | KEY_MOUSE | 409 |
KEY_F6 | 270 | KEY_MOVE | 366 |
KEY_F7 | 271 | KEY_NEXT | 367 |
KEY_F8 | 272 | KEY_OPEN | 368 |
KEY_F9 | 273 | KEY_OPTIONS | 369 |
KEY_F10 | 274 | KEY_PGDOWN | 338 |
KEY_F11 | 275 | KEY_PGUP | 339 |
KEY_F12 | 276 | KEY_PREVIOUS | 370 |
KEY_F13 | 277 | KEY_PRINT | 346 |
KEY_F14 | 278 | KEY_REDO | 371 |
KEY_F15 | 279 | KEY_REFERENCE | 372 |
KEY_F16 | 280 | KEY_REFRESH | 373 |
KEY_F17 | 281 | KEY_REPLACE | 374 |
KEY_F18 | 282 | KEY_RESET | 345 |
KEY_F19 | 283 | KEY_RESIZE | 410 |
KEY_F20 | 284 | KEY_RESTART | 375 |
KEY_F21 | 285 | KEY_RESUME | 376 |
KEY_F22 | 286 | KEY_RIGHT | 261 |
KEY_F23 | 287 | KEY_SAVE | 377 |
KEY_SBEG | 378 | KEY_SCANCEL | 379 |
KEY_SCOMMAND | 380 | KEY_SCOPY | 381 |
KEY_SCREATE | 382 | KEY_SDC | 383 |
KEY_SDL | 384 | KEY_SDOWN | 336 |
KEY_SELECT | 385 | KEY_SEND | 386 |
KEY_SEOL | 387 | KEY_SEXIT | 388 |
KEY_SFIND | 389 | KEY_SHELP | 390 |
KEY_SHOME | 391 | KEY_SIC | 392 |
KEY_SLEFT | 393 | KEY_SMESSAGE | 394 |
KEY_SMOVE | 395 | KEY_SNEXT | 396 |
KEY_SOPTIONS | 397 | KEY_SPREVIOUS | 398 |
KEY_SPRINT | 399 | KEY_SREDO | 400 |
KEY_SREPLACE | 401 | KEY_SRESET | 344 |
KEY_SRIGHT | 402 | KEY_SRSUME | 403 |
KEY_SSAVE | 404 | KEY_SSUSPEND | 405 |
KEY_STAB | 340 | KEY_SUNDO | 406 |
KEY_SUP | 337 | KEY_SUSPEND | 407 |
KEY_TAB | 512 | KEY_UNDO | 408 |
KEY_UP | 259 | KEY_UP_LEFT | 348 |
KEY_UP_RIGHT | 349 |
18. Dodatek 2: příklad nastavení xtermu
V dodatku je ukázáno praktické nastavení xtermu (což ani zdaleka není primitivní terminál – jen své vlastnosti před uživateli dobře skrývá) v souboru .Xresources. Povšimněte si přemapování logických barev na barvy fyzické:
xterm*eightBitControl: false xterm*eightBitInput: false xterm*eightBitOutput: true xterm*utf8: 1 xterm*background: gray90 xterm*foreground: Black xterm*cursorColor: rgb:ff/00/00 xterm*saveLines: 1000 xterm*color0: black xterm*color1: red3 xterm*color2: green3 xterm*color3: brown xterm*color4: blue3 xterm*color5: magenta3 xterm*color6: cyan4 xterm*color7: gray50 xterm*color8: gray40 xterm*color9: red xterm*color10: green3 xterm*color11: Goldenrod xterm*color12: DodgerBlue1 xterm*color13: magenta2 xterm*color14: cyan3 xterm*color15: white xterm*colorUL: yellow xterm*colorBD: white xterm*scrollBar: false xterm*rightScrollBar: true xterm*font: -*-terminus-bold-r-*-*-*-240-*-*-*-*-iso10646-1 xterm*boldFont: -*-terminus-bold-r-*-*-*-240-*-*-*-*-iso10646-1 xterm*geometry: 80x25 XTerm*fullscreen: never XTerm.omitTranslation: fullscreen Xterm.sessionMgt: false
Tento soubor se zpracovává nástrojem xrdb, a to následovně:
$ xrdb ~/.Xresources
Nebo častěji:
$ xrdb -merge ~/.Xresources
19. Repositář s demonstračními příklady
Zdrojové kódy všech minule i dnes popsaných demonstračních příkladů určených pro Python 3 byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má velikost zhruba několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- blessed na PyPi
https://pypi.org/project/blessed/ - blessed na GitHubu
https://github.com/jquast/blessed - Blessed documentation!
https://blessed.readthedocs.io/en/latest/ - termbox-go na GitHubu
https://github.com/nsf/termbox-go - termui na GitHubu
https://github.com/gizak/termui - blessed na GitHubu
https://github.com/chjj/blessed - blessed-contrib na GitHubu
https://github.com/yaronn/blessed-contrib - tui-rs na GitHubu
https://github.com/fdehau/tui-rs - Operace s framebufferem na Raspberry Pi
https://www.root.cz/clanky/operace-s-framebufferem-na-raspberry-pi/ - Framebuffer na Raspberry Pi: vykreslování složitějších objektů
https://www.root.cz/clanky/framebuffer-na-raspberry-pi-vykreslovani-slozitejsich-objektu/ - 256 COLORS – CHEAT SHEET
https://jonasjacek.github.io/colors/ - Terminfo (Wikipedia)
https://en.wikipedia.org/wiki/Terminfo - Termcap (Wikipedia)
https://en.wikipedia.org/wiki/Termcap - Python 3's f-Strings: An Improved String Formatting Syntax (Guide)
https://realpython.com/python-f-strings/ - Top 20 Best ASCII Games on Linux System
https://www.ubuntupit.com/best-ascii-games-on-linux/ - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - IBM 2741
https://en.wikipedia.org/wiki/IBM_2741 - Terminal mode
https://en.wikipedia.org/wiki/Terminal_mode - Box-drawing character
https://en.wikipedia.org/wiki/Box-drawing_character - Angrenost – brána do světa J.R.R.Tolkiena (nejedná se o popis hry)
http://www.angrenost.cz/ - Angband na rephial.org
http://rephial.org/ - Angband – stránka s možností downloadu hry
http://angband.oook.cz/download.php - Angband a její klony (varianty)
http://angband.oook.cz/variants.php - Další seznam klonů hry Angband (podrobnější)
http://roguebasin.roguelikedevelopment.org/index.php?title=List_of_Angband_variants - Angband (pevnost ve Středozemi)
http://en.wikipedia.org/wiki/Angband - Angband (hra)
http://en.wikipedia.org/wiki/Angband_(video_game) - Doom, the Roguelike
http://doomwiki.org/wiki/DoomRL - Roguelike evolution
http://roguebasin.roguelikedevelopment.org/index.php?title=Tree_of_roguelike_evolution - Roguelike (Wikipedia)
http://en.wikipedia.org/wiki/Roguelike - Brogue Home Page
https://sites.google.com/site/broguegame/ - Brogue (Roguelike wiki)
http://roguebasin.roguelikedevelopment.org/index.php?title=Brogue - Zangband.org
http://www.zangband.org/ - Dungeon crawl (Wikipedia)
http://en.wikipedia.org/wiki/Dungeon_crawl - FULL BLOCK (znak Unicode)
https://www.unicodepedia.com/unicode/block-elements/2588/full-block/ - Unicode.org
https://www.unicode.org/main.html - Screenshot ze hry Brogue
https://drive.google.com/file/d/1-_bjBA2rewDnleV87cu4PiytXzVWkWkT/view