Mikuláš je tu! Získaj 90 % extra kreditov ZADARMO s promo kódom CERTIK90 pri nákupe od 1 199 kreditov. Len do nedele 7. 12. 2025! Zisti viac:
NOVINKA: Najžiadanejšie rekvalifikačné kurzy teraz s 50% zľavou + kurz AI ZADARMO. Nečakaj, táto ponuka dlho nevydrží! Zisti viac:

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:

Návrh Python formulára v pygubu-designer - Práca so súbormi v Pythone

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::

Command v pygubu-designeri pre Python - Práca so súbormi v Pythone

Funkciu, ktorá sa zavolá po vybraní položky z ListBoxu, v pygube-designeri po vybraní ListBoxu nastavíme v záložke Bindings:

Bindings v pygubu-designeri pre Python - Práca so súbormi v Pythone

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:

Formulár programu pre prácu s CSV súbormi v Pythonu - Práca so súbormi v Pythone

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 tryexcept. 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:

Pridanie nového používateľa v CSV databáze v Pythonu - Práca so súbormi v Pythone

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

 

Predchádzajúci článok
Uloženie objektov do CSV v Pythone
Všetky články v sekcii
Práca so súbormi v Pythone
Preskočiť článok
(neodporúčame)
Riešené úlohy k 1.-5. lekcii práce so súbormi v Pythone
Článok pre vás napísal MQ .
Avatar
Užívateľské hodnotenie:
76 hlasov
Používám hlavně Python a zajímám se o Deep Learning a vše kolem.
Aktivity