Lekce 5 - Uložení objektů do CSV v Pythonu část 2
V minulé lekci, Uložení objektů do CSV v Pythonu, jsme načali databázi uživatelů pomocí CSV souborů.
Dnes do souboru CSV uložíme a aplikaci dokončíme.
Načtení uživatelů z CSV souboru
Uložení nám funguje, zbývá umět data opětovně načíst. Načteme
všechny řádky ze souboru a každý řádek rozdělíme metodou
split()
a následně do seznamu přidáme objekt s příslušnými
hodnotami. Před načtením si seznam vyprázdníme, aby v něm nebyli i
uživatelé načtení někdy dříve (kdyby se aplikace někdy
rozšiřovala).
def nacti(self): self.uzivatele = [] with open(self.soubor, "r", encoding="utf-8") as f: for s in f.readlines(): jmeno, vek, registrovan = s.strip().split(";") registrovan = datetime.datetime.strptime(registrovan, "%d.%m.%Y") self.pridejUzivatele(jmeno, vek, registrovan)
Třída Databaze
je tedy kompletní. Nyní se zaměříme na
formulářovou část.
Prezentační vrstva aplikace
Jako první si připravíme nové formulářové prvky. Aplikace je napsaná
v tkinter
, který je již součást Pythonu. Na vytvoření
formuláře jsem použil pygubu-designer
, který nainstalujete
pomocí pip install pygubu
a spustíte příkazem
pygubu-designer
. V pygubu-designeru si naklikáme formulář,
který poté propojíme s funkcemi. Formulář je uložen jako XML v
.ui
souboru.

Funkci, která se zavolá po kliknutí na tlačítko, v pygubu-designeru nastavíme po vybrání tlačítka v záložce General -> Specific -> command.

Funkci, která se zavolá po vybrání položky z ListBoxu, v pygubu-designeru po vybrání ListBoxu nastavíme v záložce Bindings.

Přidáme tlačítko "Načíst", dále ListBox
listUzivatelu
. Dále Entry
na jméno nového
uživatele, Entry
na jeho věk a Entry
na datum
registrace. K ovládacím prvkům přidáme nějaké labely. Tyto prvky můžeme
seskupit do Frame
. V dalším Frame
budou 3 labely na
detail uživatele, ty pojmenujeme jmenoLabel
, vekLabel
a registrovanLabel
. Další 3 labely přidáme jako jejich popisek.
Nakonec přidáme tlačítko "Přidat" k přidání uživatele. Pokud to bylo
moc rychlé, zde je obrázek výsledného formuláře:

V reálu by bylo přidání uživatelů pravděpodobně přítomné v samostatném formuláři, který by se zobrazoval jako dialog, ale nám to bude v tutoriálu stačit takto.
Nejdříve si musíme upravit třídu Aplikace
, abychom využili
náš naklikaný formulář (zdrojové kódy a gui.iu
jsou
případně ke stažení na konci článku, kdyby vám cokoli nešlo).
Formulář získáme načtením souboru, poté nastavíme rodiče hlavnímu
framu a nakonec napojíme obslužné funkce. To vše za nás v pozadí provede
pygubu
a my se tak o nic nestaráme. Třída Aplikace
nyní vypadá takto:
class Aplikace(): def __init__(self, master): self.master = master self.db = Databaze("uzivatele.csv") self.builder = pygubu.Builder() self.builder.add_from_file("gui.ui") self.builder.get_object("Frame_1", self.master) self.builder.connect_callbacks(self)
A inicializujeme ji takto:
root = tkinter.Tk() aplikace = Aplikace(root) root.mainloop()
Z obsluhy tlačítka "Uložit" odstraníme vytvoření testovacích
uživatelů. Samotné uložení nyní vložíme do
try
-except
bloku. Víme totiž, že
finally
(tedy with
blok v naší databázi) výjimky
nepohlcuje, což také chceme a budeme na ně reagovat ve formulářové
části, kam reakce logicky patří. Upozornění na chybu, tedy komunikace s
uživatelem, přímo ve třídě Databaze
by bylo špatně. Po
zachycení výjimky zobrazíme MessageBox
s chybou. Obslužná
metoda tlačítka bude tedy vypadat takto:
def tlacitkoNacistClicked(self): try: self.db.uloz() except: messagebox.showerror("Chyba", "Databázi se nepodařilo uložit, zkontrolujte přístupová práva k souboru.")
Obdobně upravíme metodu tlačítka "Načíst", pouze po načtení databáze
vložíme objekty do ListBox
u. Ten předtím vyprázdníme, aby
nám tam nezůstávali uživatelé z předešlého načtení. V reálu by se
načtení vykonalo asi automaticky po spuštění aplikace a uložení po
ukončení, pro názornost si to však ponecháme na tlačítkách. Metoda
tlačítka "Načíst" tedy vypadá takto:
def tlacitkoNacistClicked(self): try: self.db.nacti() except: messagebox.showerror("Chyba", "Databázi se nepodařilo načíst, soubor zřejmě neexisituje.") return listbox = self.builder.get_object("listUzivatelu") listbox.delete(0,tkinter.END) for u in self.db.VratVsechny(): listbox.insert(tkinter.END, u.jmeno)
Nyní zpracujeme kliknutí na položku v listUzivatelu
, které
provede zobrazení detailu vybraného uživatele do připravených labelů:
def ziskejUzivatele(self, evt): listbox = self.builder.get_object("listUzivatelu") i = listbox.curselection()[0] + 1 if len(listbox.curselection()) > 0 else None if len(self.db.uzivatele) == 0 or i == None: return jmeno_label = self.builder.get_object("jmenoLabel") vek_label = self.builder.get_object("vekLabel") registovan_label = self.builder.get_object("registrovanLabel") u = self.db.uzivatele[i-1] jmeno_label["text"] = u.jmeno vek_label["text"] = u.vek registovan_label["text"] = u.registrovan.strftime("%d.%m.%Y")
Kód jsme opodmínkovali pro případ, že by nebyl žádný uživatel vybrán (list by byl prázdný). Můžete si vyzkoušet, že vše funguje.
Poslední tlačítko bez metody je "Přidat" nového uživatele. Vytvoříme
si obslužnou metodu. Vložení bude velmi jednoduché, prvek ovšem musíme
přidat jak do databáze, tak do listUzivatelu
:
def tlacitkoPridatClicked(self): jmeno = self.builder.get_object("jmenoEntry").get() vek = self.builder.get_object("vekEntry").get() registrovan = self.builder.get_object("registrovanEntry").get() if jmeno == "" or vek == "" or registrovan == "": return registrovan = datetime.datetime.strptime(registrovan, "%d.%m.%Y") self.db.pridejUzivatele(jmeno, vek, registrovan) listbox = self.builder.get_object("listUzivatelu") listbox.delete(0,tkinter.END) for u in self.db.vratVsechny(): listbox.insert(tkinter.END, u.jmeno)
Zkusíme přidat nového uživatele:

Podobně bychom si mohli napsat i mazání uživatelů, ale to již nechám
na vás. Zbývá nám ještě ošetřit cestu k souboru, aby vedla do složky
AppData, nikoli do složky s programem. To umíme z tutoriálu Úvod do práce
se soubory v Pythonu. Získání cesty vykonáme v konstruktoru třídy
Aplikace
:
def __init__(self, master): self.master = master try: cesta = os.path.join(os.getenv("APPDATA"), "DatabazeUzivatelu") if not os.path.exists(cesta): os.mkdir(cesta) except: messagebox.showerror("Chyba", "Nepodařilo se vytvořit složku " + cesta + ", zkontrolujte prosím svá oprávnění.") self.db = Databaze(os.path.join(cesta, "uzivatele.csv")) self.builder = pygubu.Builder() self.builder.add_from_file("gui.ui") self.builder.get_object("Frame_1", self.master) self.builder.connect_callbacks(self)
A je to
Naše aplikace je téměř hotová, ještě se zamyslíme nad tím, co se
stane, když někdo do jména vloží středník. Aplikace se rozbije. Proto
budeme v metodě uloz()
středníky ze jména odstraňovat.
Kdybychom dělali aplikaci, kde bychom je potřebovali (což se nestává
příliš často), můžeme vybrat jiný zástupný znak. Pokud bychom chtěli
být dokonalí, vložíme takovou hodnotu se středníkem do uvozovek. Poté se
však již nejedná o jednoduché CSV a metoda split()
nám
přestane stačit. Dále by se to samozřejmě dalo řešit jiným formátem. My
si tedy středníky pouze odstraňme, přesněji je nahradíme mezerami změnou
jediného řádku v metodě uloz()
:
hodnoty = [u.jmeno.replace(";", " "), str(u.vek), u.registrovan.strftime("%d.%m.%Y")]
A jsme hotoví. Pokud vám něco nešlo úplně hladce, hotový projekt máte jako vždy v příloze i se zdrojovým kódem.
V následujícím cvičení, Řešené úlohy k 1.-5. lekci práce se soubory v Pythonu, si procvičíme nabyté zkušenosti z předchozích lekcí.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 302x (3.12 kB)
Aplikace je včetně zdrojových kódů v jazyce Python