5. diel - Uloženie objektov do CSV v Pythone časť 2
V minulej lekcii, Uloženie objektov do CSV v Pythone , sme načali databázu užívateľov pomocou CSV súborov.
Dnes do súboru CSV uložíme a aplikáciu dokončíme.
Načítanie užívateľov z CSV súboru
Uloženie nám funguje, zostáva vedieť dáta opätovne načítať.
Načítame všetky riadky zo súboru a každý riadok rozdelíme metódou
split()
a následne do zoznamu pridáme objekt s príslušnými
hodnotami. Pred načítaním si zoznam vyprázdnime, aby v ňom neboli aj
používatelia načítaní niekedy skôr (keby sa aplikácia niekedy
rozširovala).
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)
Trieda Databaze
je teda kompletná. Teraz sa zameriame na
formulárovú časť.
Prezentačná vrstva aplikácie
Ako prvé si pripravíme nové formulárové prvky. Aplikácia je napísaná
v tkinter
, ktorý je už súčasť Pythona. Na vytvorenie
formulára som použil pygubu-designer
, ktorý nainštalujete
pomocou pip install pygubu
a spustíte príkazom
pygubu-designer
. V pygubu-designeri si naklikáme formulár, ktorý
potom prepojíme s funkciami. Formulár je uložený ako XML v .ui
súbore.
Funkciu, ktorá sa zavolá po kliknutí na tlačidlo, v pygube-designeri nastavíme po vybraní tlačidla v záložke General -> Specific -> command.
Funkciu, ktorá sa zavolá po vybraní položky z ListBoxu, v pygube-designeri po vybraní ListBoxu nastavíme v záložke Bindings.
Pridáme tlačidlo "Načítať", ďalej ListBox
listUzivatelu
. Ďalej Entry
na meno nového
užívateľa, Entry
na jeho vek a Entry
na dátum
registrácie. K ovládacím prvkom pridáme nejaké labely. Tieto prvky môžeme
zoskupiť do Frame
. V ďalšom Frame
budú 3 labely na
detail užívateľa, tie pomenujeme jmenoLabel
,
vekLabel
a registrovanLabel
. Ďalšie 3 labely
pridáme ako ich popis. Nakoniec pridáme tlačidlo "Pridať" na pridanie
užívateľa. Pokiaľ to bolo veľmi rýchle, tu je obrázok výsledného
formulára:
V reáli by bolo pridanie užívateľov pravdepodobne prítomné v samostatnom formulári, ktorý by sa zobrazoval ako dialóg, ale nám to bude v tutoriáli stačiť takto.
Najskôr si musíme upraviť triedu Aplikace
, aby sme využili
náš naklikaný formulár (zdrojové kódy a gui.iu
sú prípadne
na stiahnutie na konci článku, keby vám čokoľvek nešlo). Formulár
získame načítaním súboru, potom nastavíme rodičov hlavnému framu a
nakoniec napojíme obslužné funkcie. To všetko za nás v pozadí prevedie
pygubu
a my sa tak o nič nestaráme. Trieda Aplikace
teraz vyzerá 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 ju takto:
root = tkinter.Tk() aplikace = Aplikace(root) root.mainloop()
Z obsluhy tlačidla "Uložiť" odstránime vytvorenie testovacích
užívateľov. Samotné uloženie teraz vložíme do try
–
except
bloku. Vieme totiž, že finally
(teda
with
blok v našej databáze) výnimky nepohlcuje, čo tiež chceme
a budeme na ne reagovať vo formulárovej časti, kam reakcia logicky patrí.
Upozornenie na chybu, teda komunikácia s užívateľom, priamo v triede
Databaze
by bolo zle. Po zachytení výnimky zobrazíme
MessageBox
s chybou. Obslužná metóda tlačidla bude teda
vyzerať 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.")
Obdobne upravíme metódu tlačidla "Načítať", iba po načítaní
databázy vložíme objekty do ListBox
. Ten predtým vyprázdnime,
aby nám tam nezostávali používatelia z predošlého načítania. V reáli by
sa načítanie vykonalo asi automaticky po spustení aplikácie a uložení po
ukončení, pre názornosť si to však ponecháme na tlačidlách. Metóda
tlačidla "Načítať" teda vyzerá 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)
Teraz spracujeme kliknutie na položku v listUzivatelu
, ktoré
vykoná zobrazenie detailu vybraného užívateľa do pripravených labelov:
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 sme opodmienkovali pre prípad, že by nebol žiadny užívateľ vybraný (list by bol prázdny). Môžete si vyskúšať, že všetko funguje.
Posledné tlačidlo bez metódy je "Pridať" nového užívateľa. Vytvoríme
si obslužnú metódu. Vloženie bude veľmi jednoduché, prvok však musíme
pridať ako do databázy, 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)
Skúsime pridať nového používateľa:
Podobne by sme si mohli napísať aj mazanie používateľov, ale to už
nechám na vás. Zostáva nám ešte ošetriť cestu k súboru, aby viedla do
zložky AppData, nie do zložky s programom. To vieme z tutoriálu Úvod do práce
so súbormi v Pythone. Získanie cesty vykonáme v konštruktore triedy
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ša aplikácia je takmer hotová, ešte sa zamyslíme nad tým, čo sa
stane, keď niekto do mena vloží bodkočiarku. Aplikácia sa rozbije. Preto
budeme v metóde uloz()
bodkočiarky z mena odstraňovať. Keby sme
robili aplikáciu, kde by sme ich potrebovali (čo sa nestáva príliš často),
môžeme vybrať iný zástupný znak. Ak by sme chceli byť dokonalí,
vložíme takú hodnotu so bodkočiarkou do úvodzoviek. Potom však už nejde o
jednoduché CSV a metóda split()
nám prestane stačiť. Ďalej by
sa to samozrejme dalo riešiť iným formátom. My si teda bodkočiarky iba
odstráňme, presnejšie ich nahradíme medzerami zmenou jediného riadku v
metóde uloz()
:
hodnoty = [u.jmeno.replace(";", " "), str(u.vek), u.registrovan.strftime("%d.%m.%Y")]
A sme hotoví. Pokiaľ vám niečo nešlo úplne hladko, hotový projekt máte ako vždy v prílohe aj so zdrojovým kódom.
V nasledujúcom cvičení, Riešené úlohy k 1.-5. lekciu práce so súbormi v Pythone, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 285x (3.12 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Python