Tyto stránky již nejsou udržovány. Obsah je postupně přesouván/aktualizován na adrese chytrosti.marrek.cz.
156/424
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 poznamka
Pro 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