O čem si budeme povídat?
Naše programy zatím používaly pouze statická data. Taková data jsme mohli, pokud jsme to potřebovali, prozkoumat ještě před spuštěním programu, takže jsme program mohli napsat tak, aby jim vyhovoval. Ale většina programů taková není. Většina programů očekává, že budou řízeny uživatelem. Přinejmenším očekávají, že jim uživatel sdělí jaký soubor mají otevřít, upravit jeho obsah a podobně. Jiné programy si v kritických místech od uživatele vyžádají vstup dat. A právě to se v programování nazývá uživatelským rozhraním. Návrhem a budováním uživatelského rozhraní se v komerčních programech zabývají specialisté, kteří byli školeni v oblastech styku člověka se strojem (human-machine interaction) a ergonomie. Běžný programátor je takového luxusu ušetřen, takže se musí řídit citem a musí pečlivě zvažovat, jakým způsobem budou uživatelé program používat.
Nejzákladnějším rysem uživatelského rozhraní je zobrazování výstupu. S
jeho nejprimitivnější formou jsme se již seznámili v podobě pythonovského
příkazu print
(a také v podobě funkce write()
jazyka JavaScript a v podobě dialogu MsgBox
z jazyka VBScript).
Další krok v návrhu uživatelského rozhraní spočívá v přímém získávání
vstupu od uživatele. Nejjednodušší způsob, jak to zařídit, spočívá
v tom, že se program za běhu na vstupní údaj zeptá. Druhý nejjednodušší
způsob vyžaduje od uživatele, aby potřebné informace zadal při spuštění
programu (jako
parametry programu). A konečně se dostáváme i ke grafickému
uživatelskému rozhraní (GUI = Graphical User Interface [grafikl júzr
interfejs]) s různými vstupními poli a dalšími prvky. V této části se
podíváme na první dvě metody. S programováním grafického uživatelského
rozhraní se seznámíme v této učebnici až mnohem později, protože jde o
problematiku výrazně složitější.
Při využití interaktivního režimu systému Python v okně IDLE nebo v terminálovém okně operačního systému si nyní ukážeme, jak lze od uživatele získat data. Poté si ukážeme, jak lze totéž provést přímo z programu.
Poznámka překladatele: Při pokusech s českými texty v interaktivním režimu můžete narazit na problémy. Při pokusech proto doporučuji nepoužívat řetězce v kódování Unicode (viz předchozí odkaz na podrobnější poznámku). V interaktivním režimu se raději úplně vyhněte používání českých znaků. Pokud ovšem kód uložíte do souboru a dodržíte pravidla pro práci s řetězci v kódování Unicode, měly by programy korektně fungovat.
Jak můžete pozorovat, raw_input()
jednoduše zobrazí zadanou
výzvu — v našem případě "Něco napište: "
— a zachytí vše, co uživatel napíše jako odpověď. Příkaz
print
pak tuto odpověď zobrazí. Odpověď bychom ale mohli místo
zobrazení přiřadit do proměnné:
>>> odpoved = raw_input(u"Jak se jmenuješ? ") >>> print u"Tak ty se jmenuješ %s! Jsem rád, že jsem tě poznal." % odpoved
Příkaz raw_input()
má bratrance jménem input()
.
Rozdíl spočívá v tom, že raw_input()
sesbírá uživatelem napsané
znaky a chápe je jako textový řetězec, zatímco input()
se z nich
bude snažit vytvořit číslo. Pokud například uživatel napíše znaky
'1
', '2
' a '3
', pak
input
tyto tři znaky přečte a převede je na číslo
123.
Použijme nyní příkaz input()
k tomu, aby uživatel rozhodl,
která tabulka násobků se má tisknout:
nasobitel = input(u"Vyberte hodnotu násobitele: ") for j in range(1, 13): print "%d x %d = %d" % (j, nasobitel, j * nasobitel)
Použití příkazu input
je naneštěstí velmi záludné. Je to dáno
tím, že input()
se nesnaží vyhodnocovat jen čísla, ale snaží se
na libovolný vstup pohlížet jako na kód v jazyce Python a snaží se jej
provést. To znamená, že by znalý uživatel se zákeřnými úmysly mohl napsat
příkaz jazyka Python, který by například vymazal nějaký soubor na vašem PC! Z
tohoto důvodu bude lepší, když se budete držet příkazu
raw_input()
a výsledný řetězec si budete převádět na potřebný
datový typ použitím zabudovaných konverzních funkcí jazyka Python. Je to v
podstatě velmi snadné:
>>> nasobitel = int(raw_input(u"Vyberte hodnotu násobitele: ")) >>> for j in range(1, 13): ... print "%d x %d = %d" % (j, nasobitel, j * nasobitel)
Vidíte? Volání raw_input()
jsme jednoduše obalili voláním
int()
. Efekt je stejný, jako kdybychom použili
input()
, ale je to mnohem bezpečnější. Existují i jiné konverzní
funkce, takže uživatelský vstup můžete stejně dobře převádět na reálná čísla a
na další typy.
Takže co kdybychom si to vyzkoušeli v opravdovém programu? Vzpomínáte si na příklady realizující adresář lidí, které využívaly strukturu typu slovník? Zabývali jsme se jimi v rámci tématu Data, datové typy a proměnné. Podívejme se na tento příklad znovu, v situaci, kdy již známe cykly a umíme číst vstup od uživatele.
# Vytvoříme prázdný slovník používaný jako adresář osob. adresy = {} # Načítáme jeho položky, dokud není zadán prázdný řetězec. print jmeno = raw_input(u'Zadejte jméno (nic => konec): ') while jmeno != '': polozka = raw_input(u'Zadejte ulici, město, telefon (nic => konec): ') adresy[jmeno] = polozka jmeno = raw_input(u'Zadejte jméno (nic => konec): ') # Teď se budeme ptát, co se má zobrazit. jmeno = raw_input(u'Které jméno se má zobrazit? (nic = konec): ') while jmeno != '': print jmeno + ':', adresy[jmeno] jmeno = raw_input(u'Které jméno se má zobrazit? (nic = konec): ')
Prozatím je to náš nejrozsáhlejší program. Uživatelské rozhraní je sice
trochu neučesané, ale funguje. V dalších tématech uvidíme, jak je můžeme
vylepšit. U tohoto programu se zmiňme o jedné věci, a sice o boolovském
testu v příkazu cyklu while
. Podle jeho výsledku se určuje, zda
chce uživatel ukončit činnost (zda se má ukončit provádění
cyklu). Povšimněte si také, že datovou část uchováváme v podobě
jednoho řetězce, zatímco v příkladu v části Data, datové typy a proměnné jsme
informaci uchovávali v samostatných buňkách. Zatím jsme se totiž nezabývali
tím, jak bychom mohli řetězec rozdělit na několik částí. Dostaneme se k tomu
v pozdějších tématech. S programem implementujícím adresář osob se dále v
učebnici ještě setkáme. Postupně jej budeme upravovat do užitečnější podoby.
V jazyce VBScript se vstup zadávaný uživatelem čte příkazem
InputBox
. Můžeme psát:
<script type="text/vbscript"> Dim vstup vstup = InputBox("Zadejte své jméno") MsgBox ("Zadali jste: " & vstup) </script>
Funkce InputBox()
zobrazí dialogové okno s textem výzvy a
s polem pro zadávání vstupu. Zadaný obsah tohoto pole získáme jako
návratovou hodnotu funkce. Při volání můžeme funkci navíc předat další
parametry, jako je například řetězec zobrazovaný v titulku okna. Pokud
uživatel stiskne tlačítko Storno (Cancel) vrací funkce prázdný řetězec
nezávisle na tom, co bylo zadáno do vstupního pole.
Následující příklad ukazuje implementaci adresáře osob v jazyce VBScript.
<script type="text/vbscript"> Dim adresy, jmeno, polozka ' Vytvoříme proměnné. Set adresy = CreateObject("Scripting.Dictionary") jmeno = InputBox("Zadejte jméno", "Položka adresáře osob") While jmeno <> "" polozka = InputBox("Zadejte detaily - ulice město, telefon", "Položka adresáře osob") adresy.Add jmeno, polozka ' Přidej klíč a detaily. jmeno = InputBox("Zadejte jméno", "Položka adresáře osob") Wend ' Teď se budeme ptát, co se má zobrazit. jmeno = InputBox("Zadejte jméno", "Zobrazení údaje z adresáře osob") While jmeno <> "" MsgBox(jmeno & " - " & adresy.Item(jmeno)) jmeno = InputBox("Zadejte jméno", "Zobrazení údaje z adresáře osob") Wend </script>
Základní struktura progamu se zcela shoduje s Pythonovskou verzí, až na
pár řádků navíc. VBScript vyžaduje, aby byly proměnné předem
deklarovány příkazem Dim
. Každý příkaz cyklu musí být navíc
ukončen příkazem Wend
.
JavaScript pro nás předtavuje určitou výzvu, protože jde o jazyk, který
se používá především pro webovské prohlížeče. Jako takový nemá žádný
speciální příkaz pro vstup. Místo toho můžeme číst z HTML elementu
form
. V prohlížeči Internet Explorer můžeme případně použít
technologii Active Scripting firmy Microsoft a nechat si zobrazit dialog
InputBox stejně, jako jsme to udělali v jazyce VBScript. Abychom se
seznámili s různými možnostmi, ukáži zde techniku využívající HTML
formuláře. Pokud vám pojem HTML formulář nic neříká, můžete nahlédnout do
referenční příručky HTML (viz norma
HTML 4.01) nebo do nějaké učebnice, která jej vysvětluje. Můžete
také jednoduše okopírovat to, co uvádím dále. Doufám, že vysvětlování
významu ani není třeba. Slibuji, že se to budu snažit načrtnout co
nejjednodušším způsobem.
Základem struktury našeho příkladu v HTML bude javascriptový kód vložený do funkce. Zatím jsme se s tím ještě nesetkali. Prozatím můžete detaily kolem definice funkce ignorovat.
<script type="text/javascript"> function mujProgram(){ alert("Získali jsme hodnotu " + document.formular.pole.value); } </script> <form name='formular'> <p>Zadej hodnotu a potom klikni myší mimo vstupní pole</p> <input type='text' name='pole' onChange='mujProgram()'> </form>
Program se skládá z jediného řádku, který zobrazí dialog
alert
(velmi se podobá MsgBox()
v jazyce
VBScript). V něm se zobrazuje hodnota získaná ze vstupního pole. Ve
formuláři je zobrazen vyzývací text (uzavřený do párových značek <p> a
</p>) a vstupní pole. V kontextu dokumentu document
má
formulář jméno formular
. Vstupní pole jsme pojmenovali
jednoduše pole
. Na jeho hodnotu, která byla vložena uživatelem,
se tedy můžeme v javascriptovém programu odkázat takto:
document.formular.pole.value
Příklad implementace našeho adresáře osob v JavaScript zde ukazovat nebudu. Vzhledem k použití v HTML bude vše o něco složitější a zvýší se i četnost používání funkcí. S příkladem tedy chci počkat až do doby, kdy potřebnou problematiku probereme v samostatných tématech.
Poznámka: Slovem stdin se v počítačovém žargonu označuje standardní vstupní zařízení (obvykle klávesnice). Slovo stdout se vztahuje ke standardnímu výstupnímu zařízení (obvykle k obrazovce). V diskusích o programování se s termíny stdin a stdout setkáváme poměrně často. Abychom mohli využít kód pro práci se soubory, je to zařízeno tak, že se stdin a stdout chovají jako soubory.
V systému Python jsou stdin a stdout součástí modulu
sys
a jmenují se sys.stdin
a
sys.stdout
. Funkce raw_input()
používá automaticky
stdin, příkaz print
používá zase stdout. Ze
standardního vstupu můžeme číst a na standardní výstup můžeme zapisovat také
přímo. Má to určité výhody ve smyslu jemnějšího řízení vstupu a výstupu.
Uveďme si příklad čtení jednoho řádku ze standardního
vstupu:
import sys print "Zadej hodnotu: ", # čárka zabrání přechodu na další řádek hodnota = sys.stdin.readline() # explicitní použití stdin print hodnota
Funkčnost se téměř shoduje s chováním příkazu:
print raw_input("Zadej hodnotu: ")
Výhodou explicitního použití stdin je to, že standardní vstup můžeme svázat se skutečným souborem, takže program svůj vstup nebude číst z klávesnice, ale z daného souboru. Tento obrat je užitečný při realizaci dlouhých testů programu, kdy místo ručního vkládání vstupních údajů v okamžiku, kdy je program požaduje, necháme vstup přečíst z předem připraveného souboru. (Další výhodou je v takovém případě skutečnost, že test můžeme spouštět opakovaně, přičemž jsme si jisti, že vstup bude pokaždé stejný, takže by tomu měl odpovídat stejný výstup. Tuto techniku opakovaného spouštění dřívějších testů, které mají ověřit, že se nic nepokazilo, programátoři nazývají regresní testování.)
Na závěr si ukažme příklad přímého výstupu na standardní výstupní
zařízení sys.stdout
. Tento výstup může být rovněž přesměrován
do fyzického
souboru. Funkčnost příkazu print
se přibližně shoduje s
funkčností následujícího zápisu:
sys.stdout.write("Ahoj, vy tam!\n") # \n = nový řádek
V praxi tento obrat použijeme hlavně v případech, kdy chceme obejít
vlastnost příkazu print
, který vždy vkládá mezi výstupní
hodnoty alespoň mezeru. Při použití stdout
se tomu můžeme
vyhnout. Srovnejte obsah dvou řádků na výstupu následujícího příkladu:
import sys for polozka in ['jedna', 'je', 1]: print polozka, # čárka potlačí přechod na nový řádek print for polozka in ['jedna', 'je', str(1)]: # musíme explicitně převést na řetězec sys.stdout.write(polozka) # zcela bez mezer!
Poznámka překladatele — výstup by měl vypadat takto:
jedna je 1 jednaje1
Pokud předem víme, jak budou data vypadat, pak stejného výsledku můžeme
samozřejmě dosáhnout i použitím formátovacího řetězce. Ale pokud to nevíme,
pak je jednodušší jednoduše posílat vše na stdout
, než abychom
se snažili o generování složitého formátovacího řetězce za běhu
programu.
Takže jak vlastně můžeme přesměrovat stdin a stdout z a do souborů? Můžeme toho dosáhnout přímo z našeho programu, když použijeme běžné pythonovské techniky pro práci se soubory (budeme se tím zabývat za chvíli). Ale nejjednodušší způsob spočívá ve využití vlastností operačního systému.
Vyzkoušejte si, jak funguje jeden z příkazů operačního systému, když na příkazovém řádku předepíšeme přesměrování:
C:\> dir C:\> dir > dir.txt
První z příkazů vypíše obsah adresáře na obrazovku, druhý jej zapíše do
souboru. Použitím znaku '>
' programu říkáme, že chceme
stdout
přesměrovat do souboru dir.txt
.
Totéž bychom mohli udělat s našim pythonovským programem:
$ python mujprogram.py > vysledek.txt
Poznámka překladatele: Vyzývací znak
'$
' v uvedeném příkladu napovídá, že jde o použití v unixovém
systému (například v systému Linux). V systému MS Windows bychom to
udělali úplně stejně.
Uvedený zápis vede ke spuštění mujprogram.py
, ale jeho
výstup by se místo na obrazovce objevil v souboru vysledek.txt
.
Jeho obsah si poté můžeme prohlédnout prostřednictvím nějakého textového
editoru.
K přesměrování stdin
z nějakého souboru jednoduše místo
znaku '>
' použijeme znak '<
'. Následuje úplný
příklad. Nejdříve vytvoříme soubor opisvstupu.py
a vložíme do
něj následující zdrojový text:
import sys vstup = sys.stdin.readline() while vstup.strip() != '': print vstup vstup = sys.stdin.readline()
Poznámka: Metoda strip()
odsekne znak,
který reprezentuje přechod na nový řádek. Ten je součástí načítaného řádku
ze stdin. Příkaz raw_input()
by to udělal za vás.
Poznámka překladatele: Metoda strip()
ve skutečnosti odstraňuje všechny bílé znaky (whitespaces) ze
začátku i z konce řetězce. To znamená, že odstraní všechny úvodní a koncové
mezery, tabulátory a znaky přechodu na nový řádek. Při použití metody
readline()
ovšem můžeme získat řetězec, ve kterém se znak
přechodu na nový řádek vyskytuje jenom jednou a vždy zcela na konci.
Teď si to můžeme vyzkoušet spuštěním z příkazového řádku:
$ python opisvstupu.py
Výsledkem by měl být program, který vše opisuje na výstup, dokud nezadáte prázdný řádek.
Teď si vytvořte jednoduchý textový soubor nazvaný vstup.txt
,
který bude obsahovat pár řádků textu. Spusťte program znovu a přesměrujte do
něj vstup ze souboru vstup.txt
:
$ python opisvstupu.py < vstup.txt
Python opisuje na výstup vše, co našel v souboru vstup.txt
.
Vzpomínáte si, že jsme si řekli, že příkaz print
a standardní
funkce raw_input()
ve skutečnosti vnitřně pracují se stdin a
stdout? To znamená, že v příkladu opisvstupu.py
můžeme práci se
stdin nahradit voláním raw_input()
následovně:
vstup = raw_input() while vstup != '': print vstup vstup = raw_input()
... což je ve většině případů mnohem jednodušší.
Postupným přesměrováním vstupu z více různých souborů můžeme rychle a snadno otestovat chování našich programů v různých situacích (například při zadávání špatných hodnot nebo údajů špatného typu). Můžeme tak učinit opakovatelným a spolehlivým způsobem. Techniku přesměrování můžeme použít také při zpracování velkých objemů dat z připravených souborů. Přitom se při používání stejného programu nezbavujeme možnosti ručního vstupu dat v případech, kdy je objem dat malý. Mechanismus přesměrování stdin a stdout představuje pro programátora velmi užitečný trik. Zkoušejte a uvidíte sami, jaká další použití se vám podaří najít.
V systému Windows se vyskytuje známá chyba, která se projevuje při
přesměrování vstupu. Pokud svůj program spustíte prostým uvedením jména
skriptu, místo abyste před něj explicitně napsali python
, nebudou
Windows vypisovat výsledky v konzolovém okně! Na webové stránce firmy Microsoft naleznete popis zásahu do
registry, který tuto chybu opravuje. Ani tento popis však není zcela korektní.
Musíte se dívat pod HKEY_CURRENT_USER
a ne pod
HKEY_LOCAL_MACHINE
, jak vám radí zmíněný dokument. Pokud
pracujete s přesměrováním vstupu nebo výstupu, pak raději uveďte přímé
spuštění programu python
. [Děkuji Timu Graberovi, který si
uvedeného problému všimnul, a Timu Petersovi, který mi prozradil onu opravu
zásahem do registry.]
Další typ vstupu představuje vstup zadaný z příkazového řádku. Dejme tomu, že nějak takto spustíte svůj textový editor z příkazového řádku:
$ EDIT Foo.txt
Operační systém spustí program nazvaný EDIT a předá mu jméno souboru,
který se má editovat — v našem případě Foo.txt
. Ale jak
si editor přečte jméno souboru?
Ve většině jazyků systém poskytuje pole nebo seznam řetězců, které
obsahují slova z příkazového řádku. Takže první prvek bude obsahovat jméno
příkazu, druhý prvek bude obsahovat první argument, atd. Někdy máme k
dispozici další magickou proměnnou (často je pojmenována argc
z
anglického argument count — počet argumentů), ve které je
uložen počet prvků uvedeného seznamu.
V systému Python je zmíněný seznam součástí modulu sys
a
nazývá se argv
(z anglického arg
ument
v
alues, čili hodnoty argumentů). V Pythonu nepotřebujeme znát
hodnotu argc
, protože počet předaných argumentů můžeme
zjistit jako délku (počet prvků) seznamu argv
pomocí standardní
funkce len()
. Většinou nepotřebujeme dělat ani to, protože
průchod celým seznamem můžeme předepsat pythonovským cyklem for
takto:
import sys for polozka in sys.argv: print polozka print u'První argument byl:', sys.argv[1]
Poznamenejme, že to bude fungovat jen v případě, kdy uvedený kód uložíte
do souboru (dejme tomu args.py
) a spustíme jej z příkazového
řádku operačního systému, například takto:
C:\Python\Projekty> python args.py 1 23 fred args.py 1 23 fred První argument byl: 1 C:\Python\Projekty>
Tyto jazyky jsou určeny pro tvorbu webových stránek, takže možnost načítání
parametrů zadaných na příkazovém řádku u nich nepřipadá v úvahu. Pokud bychom
je používali v prostředí Microsoft Windows Script
Host, situace by se změnila. WHS nabízí možnost extrakce těchto argumentů
z objektu WshArguments
, který je naplněn v době běhu.
A to je k tématu uživatelského vstupu opravdu vše, co budeme v této učebnici potřebovat. Je to sice velmi primitivní, ale přesto s tím vystačíte při psaní užitečných programů. V počátcích systému Unix nebo u prvních osobních počítačů představovala tato podoba interakce s uživatelem jedinou dostupnou možnost. V programech s grafickým uživatelským rozhraním můžeme samozřejmě vstup načítat také. K tomu, jak se to dělá, se ale v této učebnici dostaneme mnohem později.
Zapamatujte si
input
, pro čtení znaků či
řetězců použijeme raw_input
.input
tak raw_input
mohou zobrazit
řetězec s výzvou pro uživatele.argv
, který v systému Python můžeme importovat z modulu
sys
. První prvek seznamu obsahuje jméno programu.Pokud vás napadne, co by se dalo na překladu této kapitoly vylepšit, zašlete e-mail odklepnutím Tím budou do dopisu automaticky vloženy informace o tomto HTML dokumentu.
$Id: cztutinput.html,v 1.13 2005/09/03 13:07:56 petr Exp $