Tyto stránky již nejsou udržovány. Obsah je postupně přesouván/aktualizován na adrese chytrosti.marrek.cz.
156/429
Obsah:
Vytvořte aplikaci pro hlídání narozenin a svátků.
#!/usr/bin/python
# -*- coding: utf8 -*-
from Tkinter import *
##################################################
### Definice funkcí 
##################################################
### GUI
okno = Tk()
##################################################
## Nekonečná smyčka
okno.mainloop()`--> stáhnout

Jsou zde použity widgety LabelFrame, Label, Entry, Button, ListBox spolu se správcem rozmístění grid.
okno = Tk()
okno.config(padx=10, pady=10)
okno.title("Hlídač")
### Datum
LFdatum=LabelFrame(okno, text='Datum', padx=10, pady=10 )
LFdatum.grid( sticky=W+E, )
denVar = StringVar()
mesicVar = StringVar()
rokVar = StringVar()
LFdatum.grid_columnconfigure(0, weight=1)
LFdatum.grid_columnconfigure(1, weight=1)
LFdatum.grid_columnconfigure(2, weight=1)
LFdatum.grid_columnconfigure(3, weight=1)
lbl = Label(LFdatum, text=u"Den").grid(row=0,column=0)
SBden = Spinbox(LFdatum, from_=1, to=31, width=5, textvar=denVar)
SBden.grid(row=1,column=0, sticky=W+E, padx=3)
lbl = Label(LFdatum, text=u"Měsíc").grid(row=0,column=1)
SBmesic = Spinbox(LFdatum, from_=1, to=12, width=5, textvar=mesicVar)
SBmesic.grid(row=1,column=1, sticky=W+E, padx=3)
lbl = Label(LFdatum, text=u"Rok").grid(row=0,column=2)
SBrok = Spinbox(LFdatum, from_=1900, to=datetime.date.today().year, width=5, textvar=rokVar)
SBrok.grid(row=1,column=2, sticky=W+E, padx=3)
Button(LFdatum, text='Dnes', command=dnes).grid(row=1, column=3, sticky=W+E, padx=10)
### Jmeniny
LFjmeniny = LabelFrame(okno, text='Jmeniny', padx=10, pady=10)
LFjmeniny.grid( sticky=N+S+W+E)
LBLjmeniny =  Label(LFjmeniny, text='karel')
LBLjmeniny.grid( )
### Narozeniny
LFnarozeniny = LabelFrame(okno, text='Narozeniny', padx=10, pady=10)
LFnarozeniny.grid(row=3, column=0, )
LBnarozeniny=Listbox(LFnarozeniny)
LBnarozeniny.grid(row=0, column=0, columnspan=2, sticky=W+E )
jmenoVar = StringVar()
jmenoVar.set('Jméno')
Ejmeno=Entry(LFnarozeniny,textvar=jmenoVar)
Ejmeno.grid(row=1, column=0)
poznamkaVar = StringVar()
poznamkaVar.set('poznámka')
Epoznamka=Entry(LFnarozeniny, textvar=poznamkaVar)
Epoznamka.grid(row=1, column=1)
Button(LFnarozeniny, text='(+) Přidej\nzáznam narozenin', command=pridej).grid(row=2, column=0, sticky=W+E)
Button(LFnarozeniny, text='(-) Odeber\nvybranou osobu', command=odeber).grid(row=2, column=1, sticky=W+E)`--> stáhnout
Jednoduchou práci s datem a časem umožňuje modul datetime. Funkce dnes nastaví aktuální datum
def dnes():
    # dnešní datum
    dnes=datetime.date.today()
    denVar.set( dnes.day )
    mesicVar.set( dnes.month )
    rokVar.set( dnes.year )`--> stáhnout
Načtení souboru s jmenin zajišťuje funkce nactiJmeniny. Funkce čte soubor
jmena.txt. Nejprve vyhledá mezeru. Text před mezerou je datum. Text za
mezerou je jméno. Datum se rozdělí na den a měsíc podle tečky. Proměnná jmena
je slovnik ve kterém jsou obsaženy slovníky. Jako klíče se
používá měsíc a den.
Ve funkci jsou dvě vnořené sekce try:, aby bylo možné rozlišit na kterém
řádku došlo k chybě a aby byl ignorován právě jen ten jeden řádek, na kterém je
chyba.
def nactiJmeniny():
    jmena={}
    try :
        n = 0   # počítadlo řádků
        f = open('jmena.txt','r')
        while 1:
            n += 1
            radek = f.readline()
            if radek == '':
                break
            try:
                m = radek.find(' ') # mezera
                datum = radek[:m]
                jmeno = radek[m:].strip()
                ( den,mesic ) = datum.split('.')
                (den, mesic ) = map(int, (den,mesic) )
                if not jmena.has_key(mesic):
                    jmena[mesic]={}
                if not jmena[mesic].has_key(den):
                    jmena[mesic][den] = jmeno
            except:
                sys.stderr.write('jmena.txt: na řádku {} je chyba\n'.format(n)) 
        f.close()
    except:
        sys.stderr.write('jmena.txt: na řádku je chyba\n')
    return jmena`--> stáhnout
Dále je třeba pří každé změně data načíst příslušný svátek. Proto
zaregistrujeme funkci zmenaData, která se bude volat vždy při změně hodnot
Spinboxů.
Funkce změna data musí dále ošetřit různé počty dnů v jednotlivých měsících.
Proto se na jejím začátku nejprve správně nastaví meze hodnot Spinboxů.
(config(to=...))
def zmenaData(*args):
    ### kontrola a nastavení počtu dnů v měsíci
    # přestupný rok
    rok=int( rokVar.get() )
    unor = 29 if rok % 4 == 0 else 28
    mesic=int( mesicVar.get() )
    if mesic==1 or mesic==3 or mesic==5 or mesic==7 or mesic==8 or mesic==10 or mesic==12:
        SBden.config(to=31)
    elif mesic == 2 :
        SBden.config(to=unor)
        if int(denVar.get())>unor:
            denVar.set(unor)
    else:
        SBden.config(to=30)
        if int(denVar.get())>30:
            denVar.set(30)
    den=int( denVar.get() )
    # vyhlednání narozenin
    try: 
        LBLjmeniny.config(text=jmena[mesic][den])
    except: 
        LBLjmeniny.config(text='CHYBA')
        print "Nelze najít jmeniny -> den:{}, měsíc:{}".format(den,mesic)
...
...
####################################################
### události
denVar.trace('w',zmenaData)
mesicVar.trace('w',zmenaData)
rokVar.trace('w',zmenaData)`--> stáhnout
Dále je vhodné pro větší komfort ovládání umožnit změnu data pomocí kolečka
myši. V Linuxu se kolečko myši chová jako 4. a 5. tlačítko. Na Window nebo Mac je
nutné použít událost <MouseWheel>.
def rolovani(event):
    if event.num == 4:
        event.widget.invoke("buttonup")
    if event.num == 5:
        event.widget.invoke("buttondown")
...
...
####################################################
### události
# rolování kolečkem myši
SBden.bind('<Button-4>', rolovani)
SBden.bind('<Button-5>', rolovani)
SBmesic.bind('<Button-4>', rolovani)
SBmesic.bind('<Button-5>', rolovani)
SBrok.bind('<Button-4>', rolovani)
SBrok.bind('<Button-5>', rolovani)`--> stáhnout
Narozeniny je nutné ze souboru nejen načíst ale také je tam při změně uložit.
Vytvoříme proto dvě funkce nactiNarozeniny a ulozNarozeniny.
Narozeniny se ukládají do datové struktury založené na slovnících.
rok, jmeno a poznamkaPro tyto údaje:
3.11.1979  Jan Klk: bratr
12.4.1974  Petr Holoubek: známí
12.4.1985 Jan Novák: neznámí
12.4.1979 Tonda Neuman: uhlobaron
13.4.1984  Jaroslv Smykl: hodinář
... budou data vypadat takto
{
 11:
   {
    3: 
       [
        {
         'rok': 1979, 
         'jmeno': 'Jan Klk', 
         'poznamka': 'bratr'
        }
       ]
    }, 
 4: 
   {
    12:
       [
        {
         'rok': 1974, 
         'jmeno': 'Petr Holoubek',
         'poznamka': 'známí'
        },
        {
         'rok': 1985, 
         'jmeno': 'Jan Novák', 
         'poznamka': 'neznámí'
        }, 
        {
         'rok': 1979, 
         'jmeno': 'Tonda Neuman', 
         'poznamka': 'uhlobaron'
        }
       ], 
    13: 
       [
        {
         'rok': 1984,
         'jmeno': 'Jaroslv Smykl', 
         'poznamka': 'hodinář'
        }
       ]
    }
}`--> stáhnout
Seznam osob pro 12.4. dostaneme jako narozeniny[4][12]. Ke jménu třetí osoby (index 2)
v pořadí se dostaneme narozeniny[4][12][2]['jmeno']
def nactiNarozeniny():
    narozeniny={}
    try :
        n = 0   # počítadlo řádků
        f = open('narozeniny.txt','r')
        while 1:
            n += 1
            radek = f.readline()
            if radek == '':
                break
            try: 
                m = radek.find(' ') # mezera
                datum = radek[:m]
                clovek = radek[m:].strip()
                ( den,mesic,rok ) = datum.split('.')
                (den, mesic,rok ) = map(int, (den,mesic,rok) )
                (jmeno, poznamka) = clovek.split(':')
                jmeno = jmeno.strip()
                poznamka = poznamka.strip()
                if not narozeniny.has_key(mesic):
                    narozeniny[mesic]={}
                if not narozeniny[mesic].has_key(den):
                    narozeniny[mesic][den] = []
                narozeniny[mesic][den].append( {} )
                narozeniny[mesic][den][-1]['rok'] = rok
                narozeniny[mesic][den][-1]['jmeno'] = jmeno
                narozeniny[mesic][den][-1]['poznamka'] = poznamka
            except:
                sys.stderr.write('narozeniny.txt: na řádku {} je chyba\n'.format(n))
        f.close()
    except:
        sys.stderr.write('narozeniny.txt: nedaří se číst soubor\n')
    return narozeniny 
`--> stáhnout
Nyní do funkce zmenaData přidáme několik řádků pro přidávání narozenin
    ### Narozeniny
    global narozeniny
    LBnarozeniny.delete(0, END)  # vymažu všechny záznamy
    if narozeniny.has_key(mesic) and narozeniny[mesic].has_key(den):
        for osoba in narozeniny[mesic][den] : 
            vek = datetime.date.today().year - osoba['rok']
            zaznam= "{} let | {} | {}".format(vek, osoba['jmeno'], osoba['poznamka'])
            LBnarozeniny.insert(END,zaznam)
`--> stáhnout
Ukládání do souboru narozenin bude vypadat asi následovně: (Nebude se samozřejmě zapisovat na stdout, ale do příslušného souboru...)
def ulozNarozeniny():
    for mesic in sorted(narozeniny.keys()): 
        for den in sorted(narozeniny[mesic].keys()): 
            for osoba in narozeniny[mesic][den]:
                sys.stdout.write('{den}.{mesic}.{rok} {jmeno}: {poznamka}\n'.format(
                    **dict({'den':den, 'mesic':mesic}, **osoba) ))`--> stáhnout
Nyní je již vše nachystáno, aby mohlo fungovat i přidání a odebírání osob: Důležité je, že je třeba přidávat a odebírat z
def pridej():
    den=int( denVar.get() )
    mesic=int( mesicVar.get() )
    rok=int( rokVar.get() )
    jmeno = jmenoVar.get().encode('UTF8')
    poznamka = poznamkaVar.get().encode('UTF8')
    # přidání do seznamu
    vek = datetime.date.today().year - rok
    zaznam= "{} let | {} | {}".format(vek, jmeno, poznamka)
    LBnarozeniny.insert(END,zaznam)
    # přidání do souboru
    global narozeniny
    if not narozeniny.has_key(mesic):
        narozeniny[mesic]={}
    if not narozeniny[mesic].has_key(den):
        narozeniny[mesic][den] = []
    narozeniny[mesic][den].append( {} )
    narozeniny[mesic][den][-1]['rok'] = rok
    narozeniny[mesic][den][-1]['jmeno'] = jmeno
    narozeniny[mesic][den][-1]['poznamka'] = poznamka
    ulozNarozeniny()
def odeber():
    den=int( denVar.get() )
    mesic=int( mesicVar.get() )
    rok=int( rokVar.get() )
    jmeno = jmenoVar.get().encode('UTF8')
    poznamka = poznamkaVar.get().encode('UTF8')
    # odeber ze seznamu
    LBnarozeniny.delete(LBnarozeniny.index(ACTIVE))
    # odeber ze souboru 
    global narozeniny
    if narozeniny.has_key(mesic) and narozeniny[mesic].has_key(den):
        narozeniny[mesic][den].pop( LBnarozeniny.index(ACTIVE) )
    ulozNarozeniny()`--> stáhnout
Chyby v práci se souborem jsou již ošetřeny. Zbývá ještě ošetřit vložení
nečíselných znaků do políček data. Já přidal do funkce zmenaData
následující kód. Je samozřejmě zapotřebí udělat to stejné i pro měsíc a den:
    try :
        rok=int( rokVar.get() )
    except :
        SBrok.delete( SBrok.index(INSERT) - 1 )
        rok=int( rokVar.get() )`--> stáhnout