5. diel - Uloženie objektov do CSV v Pythone časť 2
V predchádzajúcej lekcii, Uloženie objektov do CSV v Pythone, sme načali databázu používateľov pomocou CSV súborov.
V dnešnej lekcii Pythonu dokončíme našu objektovú formulárovú aplikáciu s databázou používateľov s použitím textových súborov vo formáte CSV.
Načítanie používateľov z CSV súboru
Uloženie nám funguje, zostáva nám 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
načítaní skôr aj používatelia (keby sa aplikácia niekedy
rozširovala):
def load(self): self.users = [] with open(self.file, "r", encoding="utf-8") as f: for s in f.readlines(): name, age, registered = s.strip().split(";") registered = datetime.datetime.strptime(registered, "%m/%d/%Y") self.addUser(name, age, registered)
Trieda Database 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ť Pythonu. Na vytvorenie
formulára som použil pygubu-designer, ktorý nainštalujete
pomocou pip install pygubu-designer 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 Appearance > command > Callback::

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 Load, ďalej ListBox
userList. Ďalej Entry na meno nového použí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
používateľa, tie pomenujeme nameLabel, ageLabel a
registeredLabel. Ďalšie 3 labely pridáme ako ich popis. Nakoniec
pridáme tlačidlo Add na pridanie používateľa. Ak to bolo veľmi
rýchle, tu je obrázok výsledného formulára:

V reáli by bolo pridanie používateľov pravdepodobne prítomné v samostatnom formulári, ktorý by sa zobrazoval ako dialóg, ale nám to bude v lekcii stačiť takto.
Najskôr si musíme upraviť triedu Application, aby sme
využili náš naklikaný formulár (zdrojové kódy a gui.ui 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
Application teraz vyzerá takto:
class Application(): def __init__(self, master): self.master = master self.db = Database("users.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() application = Application(root) root.mainloop()
Z obsluhy tlačidla Save odstránime vytvorenie testovacích
používateľov. Samotné uloženie teraz vložíme do bloku try
– except. Vieme totiž, že finally (teda blok
with 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 používateľom, priamo v triede
Database by bolo zle. Po zachytení výnimky zobrazíme
MessageBox s chybou. Obslužná metóda tlačidla bude teda
vyzerať takto:
def saveButtonClicked(self): try: self.db.save() except: messagebox.showerror("Error", "Failed to save the database, please check file permissions.")
Obdobne upravíme metódu tlačidla Load, 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 necháme na tlačidlách. Metóda
tlačidla Load teda vyzerá takto:
def loadButtonClicked(self): try: self.db.load() except: messagebox.showerror("Error", "Failed to load the database, the file probably does not exist.") return listbox = self.builder.get_object("userList") listbox.delete(0,tkinter.END) for u in self.db.returnAll(): listbox.insert(tkinter.END, u.name)
Teraz spracujeme kliknutie na položku v userList, ktoré
vykoná zobrazenie detailu vybraného používateľa do pripravených
labelov:
def getUsers(self, evt): listbox = self.builder.get_object("userList") i = listbox.curselection()[0] + 1 if len(listbox.curselection()) > 0 else None if len(self.db.users) == 0 or i == None: return name_label = self.builder.get_object("nameLabel") age_label = self.builder.get_object("ageLabel") registered_label = self.builder.get_object("registeredLabel") u = self.db.users[i-1] name_label["text"] = u.name age_label["text"] = u.age registered_label["text"] = u.registered.strftime("%m/%d/%Y")
Kód sme opodmienkovali pre prípad, že by nebol žiadny používateľ vybraný (list by bol prázdny). Môžete si vyskúšať, že všetko funguje.
Posledné tlačidlo bez metódy je Add nového použí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 userList:
def addButtonClicked(self): name = self.builder.get_object("nameEntry").get() age = self.builder.get_object("ageEntry").get() registered = self.builder.get_object("registeredEntry").get() if name == "" or age == "" or registered == "": return registered = datetime.datetime.strptime(registered, "%m/%d/%Y") self.db.addUser(name, age, registered) listbox = self.builder.get_object("userList") listbox.delete(0,tkinter.END) for u in self.db.returnAll(): listbox.insert(tkinter.END, u.name)
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 lekcie Úvod do práce so
súbormi v Pythone. Získanie cesty vykonáme v konštruktore triedy
Application:
def __init__(self, master): self.master = master try: path = os.path.join(os.getenv("APPDATA"), "UserDatabase") if not os.path.exists(path): os.mkdir(path) except: messagebox.showerror("Error", "Failed to create folder " + path + ", please check your permissions.") self.db = Database(os.path.join(path, "users.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 save() 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 s bodkočiarkou do úvodzoviek. Potom však už nejde o
jednoduché CSV a metóda split() nám už nebude 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 jedného riadku v
metóde save():
values = [u.name.replace(";", " "), str(u.age), u.registered.strftime("%%m/%d/%Y")]
A sme hotoví. Ak 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. lekcii 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é 41x (4.28 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Python
