Hlavní navigace

Tvorba textového uživatelského rozhraní s knihovnou prompt_toolkit: ovládací prvky (widgety)

Pavel Tišnovský

Pátá část miniseriálu o tvorbě aplikací s textovým uživatelským rozhraním založeným na knihovně prompt_toolkit je věnována popisu všech zbývajících ovládacích prvků TUI, a to samozřejmě včetně menu a nástrojových pruhů.

Doba čtení: 32 minut

11. Klávesové zkratky, využití podmenu

12. Další vlastnosti prvku typu TextArea

13. Vyvolání menu klávesou F10

14. Zalamování řádků na obrazovce

15. Zvýraznění syntaxe v textovém poli

16. Prvky SearchToolbar a SystemToolbar

17. Doplnění: konfigurace emulátoru terminálu Xterm

18. Otestování nového nastavení terminálu Xterm

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Tvorba textového uživatelského rozhraní s knihovnou prompt_toolkit: ovládací prvky (widgety)

V dnešní části miniseriálu o tvorbě textového uživatelského rozhraní (TUI) s využitím programovacího jazyka Python a knihovny prompt_toolkit tematicky navážeme na část předchozí, v níž jsme si ukázali způsob použití základních prvků TUI. Připomeňme si, že kromě kontejnerů a prvků pro vstup/výstup se jednalo o widgety Label, Button a TextArea, které byly doplněny o pomocné prvky pojmenované Frame a Bar. Umístění jednotlivých ovládacích prvků na ploše terminálu/konzole bylo zajištěno s využitím správců rozložení a taktéž tříd HSplit a VSplit, které dokázaly jednotlivé prvky sdružit a umístit je buď vedle sebe nebo pod sebe (s automaticky nastavenými rozměry).

Obrázek 1: Textové uživatelské rozhraní se čtveřicí tlačítek a pasivní textovou plochou.

Dnes si popíšeme další ovládací prvky, s jejichž využitím je možné vytvořit téměř plnohodnotné textové uživatelské rozhraní. Jedná se zejména o zaškrtávací tlačítka, přepínací tlačítka, pomocné pasivní prvky (horizontální a vertikální oddělovač), ale například i o hlavní menu, rozbalovací menu atd. Dále si podrobněji popíšeme i textové pole, s nímž jsme se sice již setkali, ovšem prozatím jen ve stručnosti – zdaleka jsme si neukázali všechny možnosti, které nám tento pokročilý ovládací prvek nabízí.

Obrázek 2: Textové uživatelské rozhraní testovací aplikace s vlastním nastaveným stylem.

Poznámka: na rozdíl od předchozích čtyř dílů seriálu, v nichž byly všechny screenshoty pořízeny s terminálem, který měl nastavené černé pozadí, budou dnešní příklady používat pozadí světlé, aby bylo patrné, jak si s tímto odlišným nastavením aplikace založené na knihovně prompt_toolkit poradí.

2. Ovládací prvek typu Checkbox

První nový ovládací prvek (widget), se kterým se v dnešním článku setkáme, se jmenuje Checkbox neboli zaškrtávací tlačítko. Jedná se skutečně o variantu tlačítka, které si pamatuje svůj stav (zaškrtnuto/nezaškrtnuto) a tento stav je na tlačítku vizuálně zvýrazněn. Ukažme si nyní ten prakticky nejjednodušší možný příklad, v němž je zaškrtávací tlačítko použito. Bude se jednat o jediný prvek textového uživatelského rozhraní, který je přímo předán správci rozložení (Layout). Zaškrtávací tlačítko, jakožto jediný prvek TUI, automaticky získá zaměření (fokus), což znamená, že jeho stav bude možné měnit klávesami [Enter], [Space] atd. Aplikace se ukončí klávesou [Esc] (je na ní navázána callback funkce pojmenovaná exit_clicked):

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
from prompt_toolkit import Application
from prompt_toolkit.layout import Layout
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.application.current import get_app
from prompt_toolkit.widgets import Checkbox
 
 
def exit_clicked():
    get_app().exit()
 
 
checkbox = Checkbox('Checkbox')
 
layout = Layout(checkbox)
 
# napojení na klávesové zkratky
key_bindings = KeyBindings()
 
 
@key_bindings.add('escape')
def on_escape_press(event):
    """Callback funkce volaná při stisku klávesy Esc."""
    event.app.exit()
 
 
def main():
    # vytvoření aplikace s textovým uživatelským rozhraním
    application = Application(layout=layout,
                              key_bindings=key_bindings,
                              full_screen=True)
 
    # spuštění aplikace
    application.run()
 
 
if __name__ == '__main__':
    main()

Obrázek 3: Výchozí stav zaškrtávacího tlačítka ihned po spuštění aplikace.

Obrázek 4: Změněný stav zaškrtávacího tlačítka (akcí uživatele).

3. Reálné použití zaškrtávacích tlačítek Checkbox

Ve druhém příkladu je ukázáno, jakým způsobem se mohou zaškrtávací tlačítka použít v reálných aplikacích. Nejprve je v demonstračním skriptu vytvořena sada tří na sobě nezávislých zaškrtávacích tlačítek, dále dvě běžná tlačítka s příslušnou obsluhou události (handler) a následně jsou všechny tyto prvky seskupeny pod sebe s využitím správce rozvržení HSplit:

checkbox1 = Checkbox('Checkbox 1')
checkbox2 = Checkbox('Checkbox 2')
checkbox3 = Checkbox('Checkbox 3')
button1 = Button('Show status', handler=show_status)
button2 = Button('Exit', handler=exit_clicked)
 
buttons = HSplit([checkbox1,
                  checkbox2,
                  checkbox3,
                  button1,
                  button2])

Dále v příkladu vytvoříme textové pole, které nebude uživatelem měnitelné (tj. uživatel do něho nebude moci zapisovat), neboť nebude možné získat zaměření (fokus). Sada již seskupených tlačítek a textové pole se do TUI umístí vedle sebe, tentokrát s použitím správce rozvržení VSplit:

text_area = TextArea(focusable=False)
 
# správce rozvržení
root = VSplit([Box(Frame(buttons, style="bg:#ansiblue #ansiwhite"), padding=2),
               Box(Frame(text_area, title="Events"), padding=2)])
 
layout = Layout(root)

Po stisku běžného tlačítka [Show status] se zavolá callback funkce show(), jejímž úkolem bude zobrazit stav všech tří zaškrtávacích tlačítek:

def show_status():
    print_checkbox_status(checkbox1, 1)
    print_checkbox_status(checkbox2, 2)
    print_checkbox_status(checkbox3, 3)

Pro přečtení stavu zaškrtávacího tlačítka postačuje získat hodnotu atributu checked a do textového pole se stav zapíše změnou jeho atributu text (jedná se o běžný Unicode řetězec), takže výpis stavu vybraného zaškrtávacího tlačítka obslouží tato pomocná funkce:

def print_checkbox_status(checkbox, n):
    checked = "checked" if checkbox.checked else "unchecked"
    msg = "Checkbox {n} {s}\n".format(n=n, s=checked)
    text_area.text += msg

Podívejme se nyní na chování takto upraveného příkladu, které je zobrazeno na následujících dvou screenshotech terminálu:

Obrázek 5: Demonstrační příklad ihned po svém spuštění.

Obrázek 6: Změna stavu zaškrtávacích tlačítek s následným výpisem aktuálního stavu.

Zdrojový kód dnešního druhého demonstračního příkladu vypadá následovně:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
from prompt_toolkit import Application
from prompt_toolkit.layout import Layout, HSplit, VSplit
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.application.current import get_app
from prompt_toolkit.widgets import *
 
 
def exit_clicked():
    get_app().exit()
 
 
def print_checkbox_status(checkbox, n):
    checked = "checked" if checkbox.checked else "unchecked"
    msg = "Checkbox {n} {s}\n".format(n=n, s=checked)
    text_area.text += msg
 
 
def print_hr():
    text_area.text += "-" * 40
    text_area.text += "\n"
 
 
def show_status():
    print_checkbox_status(checkbox1, 1)
    print_checkbox_status(checkbox2, 2)
    print_checkbox_status(checkbox3, 3)
    print_hr()
 
 
checkbox1 = Checkbox('Checkbox 1')
checkbox2 = Checkbox('Checkbox 2')
checkbox3 = Checkbox('Checkbox 3')
button1 = Button('Show status', handler=show_status)
button2 = Button('Exit', handler=exit_clicked)
 
buttons = HSplit([checkbox1,
                  checkbox2,
                  checkbox3,
                  button1,
                  button2])
 
text_area = TextArea(focusable=False)
 
# správce rozvržení
root = VSplit([Box(Frame(buttons, style="bg:#ansiblue #ansiwhite"), padding=2),
               Box(Frame(text_area, title="Events"), padding=2)])
 
layout = Layout(root)
 
# napojení na klávesové zkratky
key_bindings = KeyBindings()
key_bindings.add('up')(focus_previous)
key_bindings.add('down')(focus_next)
 
 
@key_bindings.add('escape')
def on_escape_press(event):
    """Callback funkce volaná při stisku klávesy Esc."""
    print("\n\n[escape]\n\n")
    event.app.exit()
 
 
def main():
    # vytvoření aplikace s textovým uživatelským rozhraním
    application = Application(layout=layout,
                              key_bindings=key_bindings,
                              full_screen=True)
 
    # spuštění aplikace
    application.run()
 
 
if __name__ == '__main__':
    main()

4. Pomocný prvek HorizontalLine

Již z předchozí části tohoto seriálu víme, že se prvky textového uživatelského rozhraní dělí na aktivní a pasivní. Aktivní prvky dokážou nějakým způsobem reagovat na akce prováděné uživatelem (výběr, kliknutí myší pokud je myš povolena, stisk klávesy), zatímco prvky pasivní nikoli. Mezi nám již známé pasivní prvky patří Box a Frame, ovšem při tvorbě složitějších obrazovek nebo dialogů může přijít vhod i prvek nazvaný HorizontalLine, jehož funkce je zřejmá již z jeho názvu. Jedná se vlastně o zjednodušenou podobu prvku Frame, ovšem HorizontalLine je tvořen vždy jediným řádkem a používá jediný znak s kódem 0×2500. Šířka se upraví na základě velikosti ostatních prvků ve skupině.

Horizontální oddělovač se vytvoří jednoduše:

hl = HorizontalLine()

Po vytvoření oddělovače ho můžeme vložit s ostatními prvky do jedné skupiny:

buttons = HSplit([checkbox1,
                  checkbox2,
                  checkbox3,
                  hl,
                  button1,
                  hl,
                  button2])

Výsledek by měl vypadat takto:

Obrázek 7: Aplikace s celoobrazovkovým TUI, v němž je v levé části použit horizontální oddělovač.

Zdrojový kód dnešního třetího demonstračního příkladu, v němž je mezi ostatní prvky (tlačítka, výběrová tlačítka) vložen oddělovač, vypadá následovně:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
from prompt_toolkit import Application
from prompt_toolkit.layout import Layout, HSplit, VSplit
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.application.current import get_app
from prompt_toolkit.widgets import *
 
 
def exit_clicked():
    get_app().exit()
 
 
def print_checkbox_status(checkbox, n):
    checked = "checked" if checkbox.checked else "unchecked"
    msg = "Checkbox {n} {s}\n".format(n=n, s=checked)
    text_area.text += msg
 
 
def print_hr():
    text_area.text += "-" * 40
    text_area.text += "\n"
 
 
def show_status():
    print_checkbox_status(checkbox1, 1)
    print_checkbox_status(checkbox2, 2)
    print_checkbox_status(checkbox3, 3)
    print_hr()
 
 
checkbox1 = Checkbox('Checkbox 1')
checkbox2 = Checkbox('Checkbox 2')
checkbox3 = Checkbox('Checkbox 3')
hl = HorizontalLine()
button1 = Button('Show status', handler=show_status)
button2 = Button('Exit', handler=exit_clicked)
 
buttons = HSplit([checkbox1,
                  checkbox2,
                  checkbox3,
                  hl,
                  button1,
                  hl,
                  button2])
 
text_area = TextArea(focusable=False)
 
# správce rozvržení
root = VSplit([Box(Frame(buttons, style="bg:#ansiblue #ansiwhite"), padding=2),
               Box(Frame(text_area, title="Events"), padding=2)])
 
layout = Layout(root)
 
# napojení na klávesové zkratky
key_bindings = KeyBindings()
key_bindings.add('up')(focus_previous)
key_bindings.add('down')(focus_next)
 
 
@key_bindings.add('escape')
def on_escape_press(event):
    """Callback funkce volaná při stisku klávesy Esc."""
    print("\n\n[escape]\n\n")
    event.app.exit()
 
 
def main():
    # vytvoření aplikace s textovým uživatelským rozhraním
    application = Application(layout=layout,
                              key_bindings=key_bindings,
                              full_screen=True)
 
    # spuštění aplikace
    application.run()
 
 
if __name__ == '__main__':
    main()

5. Pomocný prvek VerticalLine

Vážené čtenáře pravděpodobně již napadlo, že když v knihovně prompt_toolkit existuje pasivní prvek nazvaný HorizontalLine, bude pravděpodobně existovat i prvek VerticalLine určený pro vizuální oddělení ovládacích prvků, které leží vedle sebe. Skutečně tomu tak je a použití vertikálního oddělovače je ve skutečnosti velmi snadné. V následujícím úryvku kódu je použit vertikální oddělovač vložený mezi dva prvky Box, které obsahují viditelný rámec Frame, přičemž v levém rámci je skupina tlačítek a v rámci pravém pasivní textové pole (na které nelze získat fokus):

root = VSplit([Box(Frame(buttons, style="bg:#ansiblue #ansiwhite"), padding=2),
               VerticalLine(),
               Box(Frame(text_area, title="Events"), padding=2)])

Obrázek 8: Aplikace s celoobrazovkovým TUI, v němž je mezi oběma částmi použit vertikální oddělovač.

Opět následuje výpis zdrojového kódu dnešního v pořadí již čtvrtého demonstračního příkladu, v němž je použit jak horizontální, tak i vertikální oddělovač:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
from prompt_toolkit import Application
from prompt_toolkit.layout import Layout, HSplit, VSplit
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.application.current import get_app
from prompt_toolkit.widgets import *
 
 
def exit_clicked():
    get_app().exit()
 
 
def print_checkbox_status(checkbox, n):
    checked = "checked" if checkbox.checked else "unchecked"
    msg = "Checkbox {n} {s}\n".format(n=n, s=checked)
    text_area.text += msg
 
 
def print_hr():
    text_area.text += "-" * 40
    text_area.text += "\n"
 
 
def show_status():
    print_checkbox_status(checkbox1, 1)
    print_checkbox_status(checkbox2, 2)
    print_checkbox_status(checkbox3, 3)
    print_hr()
 
 
checkbox1 = Checkbox('Checkbox 1')
checkbox2 = Checkbox('Checkbox 2')
checkbox3 = Checkbox('Checkbox 3')
hl = HorizontalLine()
button1 = Button('Show status', handler=show_status)
button2 = Button('Exit', handler=exit_clicked)
 
buttons = HSplit([checkbox1,
                  checkbox2,
                  checkbox3,
                  hl,
                  button1,
                  hl,
                  button2])
 
text_area = TextArea(focusable=False)
 
# správce rozvržení
root = VSplit([Box(Frame(buttons, style="bg:#ansiblue #ansiwhite"), padding=2),
               VerticalLine(),
               Box(Frame(text_area, title="Events"), padding=2)])
 
layout = Layout(root)
 
# napojení na klávesové zkratky
key_bindings = KeyBindings()
key_bindings.add('up')(focus_previous)
key_bindings.add('down')(focus_next)
 
 
@key_bindings.add('escape')
def on_escape_press(event):
    """Callback funkce volaná při stisku klávesy Esc."""
    print("\n\n[escape]\n\n")
    event.app.exit()
 
 
def main():
    # vytvoření aplikace s textovým uživatelským rozhraním
    application = Application(layout=layout,
                              key_bindings=key_bindings,
                              full_screen=True)
 
    # spuštění aplikace
    application.run()
 
 
if __name__ == '__main__':
    main()

6. Skupina přepínacích tlačítek (RadioList)

Dalšími ovládacími prvky, s nimiž se setkáme v mnoha aplikacích vybavených GUI či TUI, jsou přepínací tlačítka. Existují dva způsoby, jak přepínací tlačítka vytvořit – buď se jedná o samostatné prvky, které jsou sdruženy do skupin na základě nějaké identifikace (číslo/jméno skupiny apod.), nebo se celá skupina přepínacích tlačítek vytváří jako jediný prvek uživatelského rozhraní. V knihovně prompt_toolkit je použit druhý způsob, protože skupina přepínacích tlačítek je tvořena objektem typu RadioList. Tomuto objektu se v konstruktoru předává seznam dvojic hodnota+text, kde hodnota může být libovolná (ideálně jednoznačná) a text bude odpovídat popisu přepínacího tlačítka na TUI:

radiolist = RadioList([('python_val', 'Python'),
                       ('java_val', 'Java'),
                       ('clojure_val', 'Clojure'),
                       ('perl_val', 'Perl')])

Obrázek 9: Skupina přepínacích tlačítek.

Aktuálně vybrané tlačítko, resp. přesněji řečeno hodnota přiřazená tlačítku, se získá jednoduše:

selected = radiolist.current_value

Opět se podívejme na rozsáhlejší demonstrační příklad, v němž je kromě již výše popsaných zaškrtávacích tlačítek použita i skupina tlačítek přepínacích společně s kódem určeným pro zjištění aktuální hodnoty přepínače:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
from prompt_toolkit import Application
from prompt_toolkit.layout import Layout, HSplit, VSplit
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.application.current import get_app
from prompt_toolkit.widgets import *
 
 
def exit_clicked():
    get_app().exit()
 
 
def print_checkbox_status(checkbox, n):
    checked = "checked" if checkbox.checked else "unchecked"
    msg = "Checkbox {n} {s}\n".format(n=n, s=checked)
    text_area.text += msg
 
 
def print_radiolist_status(radiolist):
    text_area.text += "Radiolist: {v}\n".format(v=radiolist.current_value)
 
 
def print_hr():
    text_area.text += "-" * 40
    text_area.text += "\n"
 
 
def show_status():
    print_checkbox_status(checkbox1, 1)
    print_checkbox_status(checkbox2, 2)
    print_checkbox_status(checkbox3, 3)
    print_radiolist_status(radiolist)
    print_hr()
 
 
radiolist = RadioList([('python_val', 'Python'),
                       ('java_val', 'Java'),
                       ('clojure_val', 'Clojure'),
                       ('perl_val', 'Perl')])
 
checkbox1 = Checkbox('Checkbox 1')
checkbox2 = Checkbox('Checkbox 2')
checkbox3 = Checkbox('Checkbox 3')
hl = HorizontalLine()
button1 = Button('Show status', handler=show_status)
button2 = Button('Exit', handler=exit_clicked)
 
buttons = HSplit([radiolist,
                  hl,
                  checkbox1,
                  checkbox2,
                  checkbox3,
                  hl,
                  button1,
                  hl,
                  button2])
 
text_area = TextArea(focusable=False)
 
# správce rozvržení
root = VSplit([Box(Frame(buttons, style="bg:#ansiblue #ansiwhite"), padding=2),
               Box(Frame(text_area, title="Events"), padding=2)])
 
layout = Layout(root)
 
# napojení na klávesové zkratky
key_bindings = KeyBindings()
key_bindings.add('s-tab')(focus_previous)
key_bindings.add('tab')(focus_next)
 
 
@key_bindings.add('escape')
def on_escape_press(event):
    """Callback funkce volaná při stisku klávesy Esc."""
    print("\n\n[escape]\n\n")
    event.app.exit()
 
 
def main():
    # vytvoření aplikace s textovým uživatelským rozhraním
    application = Application(layout=layout,
                              key_bindings=key_bindings,
                              full_screen=True)
 
    # spuštění aplikace
    application.run()
 
 
if __name__ == '__main__':
    main()

7. Systém menu

Nedílnou součástí poměrně velkého množství aplikací je systém menu. Menu se používají především ze tří důvodů – kromě toho, že jsou na jejich použití uživatelé zvyklí, jsou menu nenáročná na prostor plochy terminálu (hlavní menu zabírá pouze jediný řádek) a položky menu mohou v případě potřeby obsahovat další podmenu, opět bez dalších nároků na prostor terminálu (položky jsou ve výchozím stavu skryté). Menu je samozřejmě podporováno i v knihovně prompt_toolkit, ovšem již na tomto místě je dobré upozornit na to, že tato podpora nedosahuje takových možností, jako je tomu v mnoha knihovnách pro tvorbu grafického uživatelského rozhraní (například je problematické zvýraznit aktivní znak nebo přiřadit položce menu klávesovou zkratku). S některými dalšími omezeními menu se ostatně setkáme i v navazujících kapitolách.

Obrázek 10: Typická aplikace s plnohodnotným menu v TUI – Midnight Commander.

Poznámka: při tvorbě menu, ale i při použití dalších prvků TUI, máte úplnou volnost v deklaraci jejich chování. Ovšem pro zajištění větší konzistence (look and feel) je vhodné se snažit dodržovat například CUA. Týká se to zejména použití klávesové zkratky F10 pro vyvolání menu.

Obrázek 11: Další aplikací, v níž se používá plnohodnotné menu v TUI, je například textový editor Jed.

Obrázek 12: Menu do třetice, tentokrát ve webovém prohlížeči Links.

8. Vytvoření hlavního menu pro aplikaci s TUI

Nejprve se seznámíme s tvorbou hlavního menu. Koncept, na kterém je hlavní menu postaveno, je snadný – jedná se totiž o kontejner, který může obsahovat další ovládací prvky textového uživatelského rozhraní a samozřejmě taktéž obsahuje řádek s hlavním menu. Nejdříve je nutné vytvořit jednotlivé položky hlavního menu, které jsou představovány objekty typu MenuItem. V tom nejjednodušším případě se těmto objektům do konstruktoru předávají pouze nápisy zobrazené uživateli:

file_menu = MenuItem("File")
edit_menu = MenuItem("Edit")
format_menu = MenuItem("Format")
view_menu = MenuItem("View")
help_menu = MenuItem("Help")

Všechny položky, které mají tvořit hlavní menu, vložíme do jednoho seznamu:

main_menu = [file_menu,
             edit_menu,
             format_menu,
             view_menu,
             help_menu]

A následně již můžeme vytvořit kontejner, který bude obsahovat libovolný další prvek TUI (zde konkrétně textovou plochu, ovšem může se jednat o jakýkoli prvek nebo kontejner s větším množstvím prvků) a položky hlavního menu:

menu = MenuContainer(text_area, menu_items=main_menu)

Tento kontejner se následně zobrazí přímo na ploše terminálu (nemusíme tedy využívat kontejnery HSplit a VSplit):

layout = Layout(menu)

Následně již jen stačí nakonfigurovat klávesové zkratky a použít nového správce rozložení při konstrukci instance třídy Application:

application = Application(layout=layout,
                          key_bindings=key_bindings,
                          full_screen=True)

Obrázek 13: Hlavní menu zobrazené v naší jednoduché testovací aplikaci.

Úplný zdrojový kód bude vypadat následovně:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
from prompt_toolkit import Application
from prompt_toolkit.layout import Layout, HSplit, VSplit
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.application.current import get_app
from prompt_toolkit.widgets import *
 
 
text_area = TextArea(focusable=False)
 
file_menu = MenuItem("File")
edit_menu = MenuItem("Edit")
format_menu = MenuItem("Format")
view_menu = MenuItem("View")
help_menu = MenuItem("Help")
 
main_menu = [file_menu,
             edit_menu,
             format_menu,
             view_menu,
             help_menu]
 
menu = MenuContainer(text_area, menu_items=main_menu)
 
 
layout = Layout(menu)
 
# napojení na klávesové zkratky
key_bindings = KeyBindings()
key_bindings.add('s-tab')(focus_previous)
key_bindings.add('tab')(focus_next)
 
 
@key_bindings.add('escape')
def on_escape_press(event):
    """Callback funkce volaná při stisku klávesy Esc."""
    print("\n\n[escape]\n\n")
    event.app.exit()
 
 
def main():
    # vytvoření aplikace s textovým uživatelským rozhraním
    application = Application(layout=layout,
                              key_bindings=key_bindings,
                              full_screen=True)
 
    # spuštění aplikace
    application.run()
 
 
if __name__ == '__main__':
    main()

9. Rozbalovací menu navázaná na hlavní menu

Pouze jednořádkové (a tím pádem i jednoúrovňové) hlavní menu je sice možné v některých případech skutečně využít (ostatně podobným způsobem bylo koncipováno rozhraní slavné aplikace Lotus 1–2–3), ovšem u většiny aplikací je menu přece jen složitější, protože každá položka hlavního menu může obsahovat další rozbalitelné seznamy. I tato rozbalovací menu jsou samozřejmě knihovnou prompt_toolkit podporována. U každé položky menu typu MenuItem je totiž možné specifikovat i celé podmenu, a to následujícím způsobem:

file_menu = MenuItem("File", children=[MenuItem("New"),
                                       MenuItem("Open"),
                                       MenuItem("Save"),
                                       MenuItem("Exit")])

Do menu lze přidávat prázdné řádky a vizuálně tak oddělit jednotlivé položky. Jen je nutné s využitím nepovinného parametru disabled zabezpečit, aby byl prázdný řádek přeskakován při výběru položek pomocí klávesnice:

file_menu = MenuItem("File", children=[MenuItem("New"),
                                       MenuItem("Open"),
                                       MenuItem("Save"),
                                       MenuItem(disabled=True),
                                       MenuItem("Exit")])

K jednotlivým položkám menu je možné nakonfigurovat callback funkce volané při výběru dané položky (klasicky klávesou Enter, popř. myší, pokud je použití myši nakonfigurováno):

file_menu = MenuItem("File", children=[MenuItem("New", handler=on_new_selected),
                                       MenuItem("Open", handler=on_open_selected),
                                       MenuItem("Save", handler=on_save_selected),
                                       MenuItem(disabled=True),
                                       MenuItem("Exit", handler=on_exit_selected)])

Jednotlivé callback funkce jsou při výběru položky menu zavolány bez parametrů, takže by jejich hlavičky mohly vypadat následovně:

def on_new_selected():
    message("'New' menu item selected")

Obrázek 14: Hlavní menu a rozbalovací menu.

V úplném zdrojovém kódu demonstračního příkladu je vytvořeno hlavní menu s pěti položkami, přičemž každá položka obsahuje vlastní rozbalovací menu. Celková struktura menu by měla připomínat jeden pokročilý a mnoha zajímavými vlastnostmi obdařený textový editor:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
from prompt_toolkit import Application
from prompt_toolkit.layout import Layout, HSplit, VSplit
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.application.current import get_app
from prompt_toolkit.widgets import *
 
 
def message(msg):
    text_area.text += msg + "\n"
 
 
def on_new_selected():
    message("'New' menu item selected")
 
 
def on_open_selected():
    message("'Open' menu item selected")
 
 
def on_save_selected():
    message("'Save' menu item selected")
 
 
def on_exit_selected():
    get_app().exit()
 
 
text_area = TextArea(focusable=False)
 
file_menu = MenuItem("File", children=[MenuItem("New", handler=on_new_selected),
                                       MenuItem("Open", handler=on_open_selected),
                                       MenuItem("Save", handler=on_save_selected),
                                       MenuItem(disabled=True),
                                       MenuItem("Exit", handler=on_exit_selected)])
 
edit_menu = MenuItem("Edit", children=[MenuItem("Cut"),
                                       MenuItem("Copy"),
                                       MenuItem("Paste"),
                                       MenuItem(disabled=True),
                                       MenuItem("Select all")])
 
format_menu = MenuItem("Format", children=[MenuItem("Word wrap")])
 
view_menu = MenuItem("View", children=[MenuItem("Status bar"),
                                       MenuItem("Line numbers")])
 
help_menu = MenuItem("Help", children=[MenuItem("Content"),
                                       MenuItem(disabled=True),
                                       MenuItem("About")])
 
main_menu = [file_menu,
             edit_menu,
             format_menu,
             view_menu,
             help_menu]
 
menu = MenuContainer(text_area, menu_items=main_menu)
 
 
layout = Layout(menu)
 
# napojení na klávesové zkratky
key_bindings = KeyBindings()
key_bindings.add('s-tab')(focus_previous)
key_bindings.add('tab')(focus_next)
 
 
@key_bindings.add('escape')
def on_escape_press(event):
    """Callback funkce volaná při stisku klávesy Esc."""
    print("\n\n[escape]\n\n")
    event.app.exit()
 
 
def main():
    # vytvoření aplikace s textovým uživatelským rozhraním
    application = Application(layout=layout,
                              key_bindings=key_bindings,
                              full_screen=True)
 
    # spuštění aplikace
    application.run()
 
 
if __name__ == '__main__':
    main()

10. Horizontální oddělovač v menu

V předchozím demonstračním příkladu byly do jednotlivých rozbalovacích menu přidány volné řádky pro vizuální oddělení jednotlivých skupin souvisejících příkazů:

file_menu = MenuItem("File", children=[MenuItem("New", handler=on_new_selected),
                                       MenuItem("Open", handler=on_open_selected),
                                       MenuItem("Save", handler=on_save_selected),
                                       MenuItem(disabled=True),
                                       MenuItem("Exit", handler=on_exit_selected)])

Ve skutečnosti je však možné použít ještě jeden zajímavý trik – vložit do menu oddělovač ve formě horizontální úsečky. Tuto možnost sice nenaleznete v oficiální dokumentaci, ale je implementována a dnes se jedná o stabilní součást API. Oddělovač se přidává jako nová položka menu s textem „-“ a opět je vhodné tuto položku zakázat, aby si ji uživatel nemohl vybrat:

file_menu = MenuItem("File", children=[MenuItem("New", handler=on_new_selected),
                                       MenuItem("Open", handler=on_open_selected),
                                       MenuItem("Save", handler=on_save_selected),
                                       MenuItem("-", disabled=True),
                                       MenuItem("Exit", handler=on_exit_selected)])

Obrázek 15: Horizontální oddělovače v rozbalovacím menu.

Takto upravený demonstrační příklad naleznete na adrese https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui25_hline_in_me­nu.py.

11. Klávesové zkratky, využití podmenu

V dobře navržených aplikacích je možné jednotlivé příkazy buď přímo vyvolat z menu jejich výběrem, nebo je alternativně možné použít klávesové zkratky. Tato druhá možnost sice není přímo třídou MenuItem podporována, to nám však nemusí vadit, protože můžeme klávesovou zkratku přiřadit přímo callback funkci, která se bude volat jak při stisku příslušné klávesové zkratky, tak i při výběru položky z menu. Musíme pouze zařídit jednu maličkost – při volání callback funkce z menu se funkci nepředává žádný parametr, kdežto při stisku klávesové zkratky se předá objekt typu event. V Pythonu můžeme problematiku nepovinného parametru řešit například následujícím způsobem:

@key_bindings.add('c-n')
def on_new_selected(event=None):
    message("'New' menu item selected")
 
 
@key_bindings.add('c-o')
def on_open_selected(event=None):
    message("'Open' menu item selected")
 
 
@key_bindings.add('c-s')
def on_save_selected(event=None):
    message("'Save' menu item selected")
 
 
@key_bindings.add('c-x')
def on_exit_selected(event=None):
    get_app().exit()
Poznámka: klávesové zkratky zapisované stylem „c-n“ ve skutečnosti znamenají stisk kombinace kláves [Ctrl+N]. Podobným způsobem se zapisují i kombinace s klávesou Shift („s-x“) atd.

Pro úplnost je vhodné do vlastních položek přidat informaci o klávesových zkratkách. Díky použití TUI není problém se zarovnáváním ani s proporcionálními znaky, takže lze vše vyřešit značně jednoduše (nepoužívejte \t atd.; sami si vyzkoušejte, že se nejedná o funkční řešení). V úryvku kódu, který je zobrazen pod tímto odstavcem, je navíc celé menu vytvořeno jako jediná datová struktura (seznam objektů typu MenuItem s případnými podmenu), tj. nemusíme zde používat žádné pomocné proměnné tak, jako tomu bylo v předchozích příkladech:

main_menu = [MenuItem("File", children=[MenuItem("New   <Ctrl-N>", handler=on_new_selected),
                                        MenuItem("Open  <Ctrl-O>", handler=on_open_selected),
                                        MenuItem("Save  <Ctrl-S>", handler=on_save_selected),
                                        MenuItem("-", disabled=True),
                                        MenuItem("Exit  <Ctrl-X>", handler=on_exit_selected)]),
             ...
             ...
             ...
             MenuItem("Help", children=[MenuItem("Content"),
                                        MenuItem("-", disabled=True),
                                        MenuItem("About")])]

Obrázek 16: Menu s klávesovými zkratkami.

Opět si ukažme úplný zdrojový kód tohoto příkladu:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
from prompt_toolkit import Application
from prompt_toolkit.layout import Layout, HSplit, VSplit
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.application.current import get_app
from prompt_toolkit.widgets import *
 
 
# napojení na klávesové zkratky
key_bindings = KeyBindings()
key_bindings.add('s-tab')(focus_previous)
key_bindings.add('tab')(focus_next)
 
 
def message(msg):
    text_area.text += msg + "\n"
 
 
@key_bindings.add('c-n')
def on_new_selected(event=None):
    message("'New' menu item selected")
 
 
@key_bindings.add('c-o')
def on_open_selected(event=None):
    message("'Open' menu item selected")
 
 
@key_bindings.add('c-s')
def on_save_selected(event=None):
    message("'Save' menu item selected")
 
 
@key_bindings.add('c-x')
def on_exit_selected(event=None):
    get_app().exit()
 
 
text_area = TextArea(focusable=False)
 
main_menu = [MenuItem("File", children=[MenuItem("New   ", handler=on_new_selected),
                                        MenuItem("Open  ", handler=on_open_selected),
                                        MenuItem("Save  ", handler=on_save_selected),
                                        MenuItem("-", disabled=True),
                                        MenuItem("Exit  ", handler=on_exit_selected)]),
             MenuItem("Edit", children=[MenuItem("Cut"),
                                        MenuItem("Copy"),
                                        MenuItem("Paste"),
                                        MenuItem("-", disabled=True),
                                        MenuItem("Select all")]),
             MenuItem("Format", children=[MenuItem("Word wrap")]),
             MenuItem("View", children=[MenuItem("Status bar", children=[MenuItem("Enabled"),
                                                                         MenuItem("Disabled")]),
                                        MenuItem("Line numbers", children=[MenuItem("Yes"),
                                                                           MenuItem("No")])]),
             MenuItem("Help", children=[MenuItem("Content"),
                                        MenuItem("-", disabled=True),
                                        MenuItem("About")])]
 
menu = MenuContainer(text_area, menu_items=main_menu)
 
layout = Layout(menu)
 
 
@key_bindings.add('escape')
def on_escape_press(event):
    """Callback funkce volaná při stisku klávesy Esc."""
    print("\n\n[escape]\n\n")
    event.app.exit()
 
 
def main():
    # vytvoření aplikace s textovým uživatelským rozhraním
    application = Application(layout=layout,
                              key_bindings=key_bindings,
                              full_screen=True)
 
    # spuštění aplikace
    application.run()
 
 
if __name__ == '__main__':
    main()

Obrázek 17: Submenu.

12. Další vlastnosti prvku typu TextArea

Vraťme se ještě na chvíli k ovládacímu prvku nazvanému TextArea. Ten jsme prozatím používali pouze pro účely zobrazení nějakých zpráv uživateli, ovšem ve skutečnosti jsou možnosti nabízené tímto prvkem podstatně větší, neboť se vlastně jedná o implementaci dobře použitelného textového editoru. Ukažme si použití textového pole pro vstup údajů. Nejjednodušší aplikace, která tuto funkcionalitu zajišťuje, může vypadat následovně:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
from prompt_toolkit import Application
from prompt_toolkit.layout.containers import Window
from prompt_toolkit.layout.controls import FormattedTextControl
from prompt_toolkit.layout import Layout, HSplit, VSplit
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.application.current import get_app
from prompt_toolkit.widgets import *
 
 
# napojení na klávesové zkratky
key_bindings = KeyBindings()
key_bindings.add('s-tab')(focus_previous)
key_bindings.add('tab')(focus_next)
 
 
@key_bindings.add('escape')
def on_exit_selected(event=None):
    """Callback funkce volaná při stisku klávesy Esc."""
    get_app().exit()
 
 
text_area = TextArea(focusable=True)
 
file_menu = MenuItem("File", children=[MenuItem("Exit", handler=on_exit_selected)])
menu = MenuContainer(text_area, menu_items=[file_menu])
 
layout = Layout(menu, focused_element=text_area)
 
 
def main():
    # vytvoření aplikace s textovým uživatelským rozhraním
    application = Application(layout=layout,
                              key_bindings=key_bindings,
                              full_screen=True)
 
    # spuštění aplikace
    application.run()
 
 
if __name__ == '__main__':
    main()

Obrázek 18: Textové pole v aplikaci s menu.

Poznámka: prozatím se do menu dostanete stiskem klávesy [Tab] nebo [Shift+Tab].

13. Vyvolání menu klávesou F10

Pro vyvolání menu klávesou F10 (nebo samozřejmě jinou vhodnou klávesou) postačuje do aplikace přidat následující callback funkci, která po stisku vybrané klávesy změní zaměření (fokus) na objekt menu.window (pozor, zde nestačí zapsat jen menu – opět se jedná o informaci, která v oficiální dokumentaci prozatím chybí):

@key_bindings.add('f10')
def on_f10_pressed(event=None):
    """Callback funkce volaná při stisku klávesy F10."""
    get_app().layout.focus(menu.window)

Zdrojový kód takto upraveného příkladu naleznete na adrese https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui28_f10_for_me­nu.py.

Obrázek 19: Textové pole v aplikaci s menu se zakázaným zalamováním řádků.

14. Zalamování řádků na obrazovce

Pokud se má textové pole použít pro vstup běžného textu s automatickým zalamováním řádků, je vhodné ponechat nastavení parametrů multiline a wrap_lines, popř. i parametr scrollbar. Implicitně je zalamování řádků zapnuté, ale samozřejmě ho můžeme vypnout – potom se budou tvořit „nekonečné“ řádky, což může být vhodné při psaní programátorských editorů:

text_area = TextArea(focusable=True, multiline=True, wrap_lines=False)

15. Zvýraznění syntaxe v textovém poli

Již několikrát jsme se setkali s použitím Lexeru z knihovny Pygments (které se věnujeme v samostatném miniseriálu). Lexer lze samozřejmě nastavit i pro vstupní textové pole, a to poměrně jednoduše. Zkusme si například vytvořit jednoduchý editor pro editaci zdrojového kódu naprogramovaného v Pythonu:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
from pygments import lex
from pygments.token import Token
from pygments.lexers import PythonLexer
 
from prompt_toolkit import Application
from prompt_toolkit.layout.containers import Window
from prompt_toolkit.layout.controls import FormattedTextControl
from prompt_toolkit.layout import Layout, HSplit, VSplit
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.application.current import get_app
from prompt_toolkit.widgets import *
from prompt_toolkit.lexers import PygmentsLexer
 
 
# napojení na klávesové zkratky
key_bindings = KeyBindings()
key_bindings.add('s-tab')(focus_previous)
key_bindings.add('tab')(focus_next)
 
 
@key_bindings.add('escape')
def on_exit_selected(event=None):
    """Callback funkce volaná při stisku klávesy Esc."""
    get_app().exit()
 
 
@key_bindings.add('f10')
def on_f10_pressed(event=None):
    """Callback funkce volaná při stisku klávesy F10."""
    get_app().layout.focus(menu.window)
 
 
lexer = PygmentsLexer(PythonLexer)
text_area = TextArea(focusable=True, multiline=True, wrap_lines=False, lexer=lexer)
 
file_menu = MenuItem("File", children=[MenuItem("Exit", handler=on_exit_selected)])
menu = MenuContainer(text_area, menu_items=[file_menu])
 
layout = Layout(menu, focused_element=text_area)
 
 
def main():
    # vytvoření aplikace s textovým uživatelským rozhraním
    application = Application(layout=layout,
                              key_bindings=key_bindings,
                              full_screen=True)
 
    # spuštění aplikace
    application.run()
 
 
if __name__ == '__main__':
    main()

Obrázek 20: Editor se zvýrazněním syntaxe naprogramovaný na třech řádcích.

16. Prvky SearchToolbar a SystemToolbar

Kromě výše popsaných ovládacích prvků, hlavního menu a vstupně/výstupního textového pole nabízí knihovna prompt_toolkit uživatelům i programátorům i další užitečné prvky. Jedná se například o SearchToolbar či o SystemToolbar. Jejich použití si popíšeme příště.

17. Doplnění: konfigurace emulátoru terminálu Xterm

Na závěr dnešního článku si jako malé doplnění ukažme konfiguraci terminálu Xterm takovým způsobem, aby se v něm používalo světlé pozadí a aby byl tento terminál připravený pro práci s Unicode. Vzhledem k tomu, že Xterm podporuje zobrazení 256 barev z plné barvové palety, je možné barvy popředí, pozadí i kurzoru zadávat buď jménem (standardní jména známá X) nebo zápisem rgb:rr/gg/bb. Taktéž je možné přenastavit barvy s indexy 0 až 15 a upravit si tak chování aplikací (ty se typicky o konkrétní barvy nestarají, ale používají standardní paletu, kterou si tak můžeme přemapovat). Poslední volba slouží k zákazu zpracování klávesové zkratky [Alt+Enter], protože se tato zkratka používá v dalších aplikacích, například v Midnight Commanderu, takže není vhodné, aby ji emulátor terminálu sám zpracovával:

xterm*eightBitControl: false
xterm*eightBitInput:   false
xterm*eightBitOutput:  true
xterm*utf8:            1
xterm*background:  White
xterm*foreground:  Black
xterm*cursorColor: rgb:ff/00/00
xterm*saveLines: 1000
xterm*color0: black
xterm*color1: red3
xterm*color2: green3
xterm*color3: yellow3
xterm*color4: DodgerBlue1
xterm*color5: magenta3
xterm*color6: cyan3
xterm*color7: gray90
xterm*color8: gray50
xterm*color9: red
xterm*color10: green
xterm*color11: yellow
xterm*color12: blue
xterm*color13: magenta
xterm*color14: cyan
xterm*color15: white
xterm*colorUL: yellow
xterm*colorBD: white
xterm*rightScrollBar: true
xterm*font:     -*-terminus-bold-r-*-*-*-200-*-*-*-*-iso10646-1
xterm*boldFont:     -*-terminus-bold-r-*-*-*-200-*-*-*-*-iso10646-1
xterm*geometry: 80x25
XTerm*fullscreen: never
XTerm.omitTranslation: fullscreen

Alternativní barevné nastavení, které se více přibližuje standardní paletě:

xterm*background:  gray90
xterm*foreground:  Black
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

Nastavení jsou uložena v souboru ~/.Xresources a po každé úpravě je nutné je přidat do databáze systému X příkazem:

$ xrdb ~/.Xresources

Pokud navíc budete chtít namísto primárního výběrového bufferu (PRIMARY) použít klasickou schránku (CLIPBOARD), lze to samozřejmě zařídit:

XTerm*selectToClipboard: true
Poznámka: prompt_toolkit samozřejmě není žádným způsobem navázaný právě na Xterm, ovšem tento emulátor mi osobně nejlépe zapadá do minimalisticky nakonfigurovaného desktopu s Fluxboxem, feh atd..

18. Otestování nového nastavení terminálu Xterm

Pro usnadnění testování nových barvových schémat (což je vlastně nová barvová paleta pro indexy 0..15) je možné využít pomocný skript xterm_colors, který automaticky upraví databázi používanou systémem X, spustí nový emulátor terminálu a v tomto terminálu spustí druhý skript pro výpis všech šestnácti standardních barev:

xrdb .Xresources
xterm -e ./colortest

Samotný pomocný skript colortest vypadá následovně:

echo -e "\033[0mNC (No color)"
echo -e "\033[1;37mWHITE\t\033[0;30mBLACK"
echo -e "\033[0;34mBLUE\t\033[1;34mLIGHT_BLUE"
echo -e "\033[0;32mGREEN\t\033[1;32mLIGHT_GREEN"
echo -e "\033[0;36mCYAN\t\033[1;36mLIGHT_CYAN"
echo -e "\033[0;31mRED\t\033[1;31mLIGHT_RED"
echo -e "\033[0;35mPURPLE\t\033[1;35mLIGHT_PURPLE"
echo -e "\033[0;33mYELLOW\t\033[1;33mLIGHT_YELLOW"
echo -e "\033[1;30mGRAY\t\033[0;37mLIGHT_GRAY"
# reset colors
echo -e "\033[0m----------------------------------------"
read

Volání read na konci zajistí, že nově spuštěný emulátor terminálu nebude ihned uzavřen:

19. Repositář s demonstračními příklady

Všechny dnes popisované demonstrační příklady byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/pre­sentations. Příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý (dnes již poměrně rozsáhlý) repositář:

# Příklad Popis Odkaz
1 tui18_basic_checkbox.py minimální příklad na použití zaškrtávacího tlačítka https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui18_basic_chec­kbox.py
2 tui19_checkbox_real_usage.py použití zaškrtávacích tlačítek v reálných aplikacích https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui19_chec­kbox_real_usage.py
3 tui20_checkbox_and_hline.py horizontální oddělovací čára https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui20_chec­kbox_and_hline.py
4 tui21_checkbox_and_hline_and_vline.py horizontální i vertikální oddělovací čáry https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui21_chec­kbox_and_hline_and_vline.py
5 tui22_radiolist.py sada přepínacích tlačítek https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui22_radi­olist.py
6 tui23_toplevel_menu.py hlavní menu https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui23_tople­vel_menu.py
7 tui24_basic_structured_menu.py rozbalovací menu https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui24_basic_struc­tured_menu.py
8 tui25_hline_in_menu.py horizontální oddělovač v menu https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui25_hline_in_me­nu.py
9 tui26_keyboard_binding_submenu.py klávesové zkratky pro menu https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui26_keybo­ard_binding_submenu.py
10 tui27_text_area.py použití textového pole https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui27_text_a­rea.py
11 tui28_f10_for_menu.py klávesa F10 vyvolá menu https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui28_f10_for_me­nu.py
12 tui29_nowrap_lines.py zákaz zalamování řádků https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui29_nowrap_li­nes.py
13 tui30_python_lexer.py obarvení syntaxe v textovém poli https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui30_python_le­xer.py
14 tui31_basic_lexer.py obarvení syntaxe v textovém poli https://github.com/tisnik/pre­sentations/blob/master/prom­pt_toolkit/tui/tui31_basic_le­xer.py

20. Odkazy na Internetu

  1. Správce rozvržení (Layout)
    https://python-prompt-toolkit.readthedocs.io/en/mas­ter/pages/reference.html#la­yout
  2. Popis všech ovládacích prvků (Widgetů)
    https://python-prompt-toolkit.readthedocs.io/en/mas­ter/pages/reference.html#mo­dule-prompt_toolkit.widgets
  3. Prvek TUI MenuContainer
    https://python-prompt-toolkit.readthedocs.io/en/mas­ter/pages/reference.html#prom­pt_toolkit.widgets.MenuCon­tainer
  4. Prvek TUI RadioList
    https://python-prompt-toolkit.readthedocs.io/en/mas­ter/pages/reference.html#prom­pt_toolkit.widgets.RadioList
  5. Třída Application
    https://python-prompt-toolkit.readthedocs.io/en/mas­ter/pages/reference.html#mo­dule-prompt_toolkit.application
  6. Třída KeyBindings
    https://python-prompt-toolkit.readthedocs.io/en/mas­ter/pages/reference.html#prom­pt_toolkit.key_binding.Ke­yBindings
  7. UTF-8 encoded sample plain-text file
    http://www.cl.cam.ac.uk/~mgk25/uc­s/examples/UTF-8-demo.txt
  8. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  9. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  10. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  11. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  12. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  13. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  14. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  15. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  16. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  17. Editline Library (libedit)
    http://thrysoee.dk/editline/
  18. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  19. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  20. WinEditLine
    http://mingweditline.sourceforge.net/
  21. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  22. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  23. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  24. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  25. history(3) – Linux man page
    https://linux.die.net/man/3/history
  26. vi(1) – Linux man page
    https://linux.die.net/man/1/vi
  27. emacs(1) – Linux man page
    https://linux.die.net/man/1/emacs
  28. Pygments – Python syntax highlighter
    http://pygments.org/
  29. Write your own lexer
    http://pygments.org/docs/le­xerdevelopment/
  30. Jazyky podporované knihovnou Pygments
    http://pygments.org/languages/
  31. Pygments FAQ
    http://pygments.org/faq/
  32. TUI – Text User Interface
    https://en.wikipedia.org/wiki/Text-based_user_interface
  33. PuDB: výkonný debugger pro Python s retro uživatelským rozhraním (nástroj s plnohodnotným TUI)
    https://www.root.cz/clanky/pudb-vykonny-debugger-pro-python-s-retro-uzivatelskym-rozhranim/
  34. Historie vývoje textových editorů: krkolomná cesta k moderním textovým procesorům
    https://www.root.cz/clanky/historie-vyvoje-textovych-editoru-krkolomna-cesta-k-modernim-textovym-procesorum/
  35. Rosetta Code
    http://rosettacode.org/wi­ki/Rosetta_Code
  36. Mandelbrot set: Sinclair ZX81 BASIC
    http://rosettacode.org/wi­ki/Mandelbrot_set#Sinclair_ZX81_BA­SIC
  37. Nástroj Dialog
    http://invisible-island.net/dialog/
  38. Projekt Zenity
    https://wiki.gnome.org/Pro­jects/Zenity
  39. Xterm256 color names for console Vim
    http://vim.wikia.com/wiki/Xter­m256_color_names_for_conso­le_Vim
  40. Hidden gems of xterm
    https://lukas.zapletalovi­.com/2013/07/hidden-gems-of-xterm.html
Našli jste v článku chybu?