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