Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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.

Návrh Python formuláře v pygubu-designer - Soubory a práce s nimi v Pythonu

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.

Command v pygubu designeru pro Python - Soubory a práce s nimi v Pythonu

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.

Bindings v pygubu designeru pro Python - Soubory a práce s nimi v Pythonu

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:

Formulář programu pro práci s CSV soubory v Pythonu - Soubory a práce s nimi v Pythonu

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

Přidání nového uživatele v CSV databázi v Pythonu - Soubory a práce s nimi v Pythonu

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 267x (3.12 kB)
Aplikace je včetně zdrojových kódů v jazyce Python

 

Předchozí článek
Uložení objektů do CSV v Pythonu
Všechny články v sekci
Soubory a práce s nimi v Pythonu
Přeskočit článek
(nedoporučujeme)
Řešené úlohy k 1.-5. lekci práce se soubory v Pythonu
Článek pro vás napsal MQ .
Avatar
Uživatelské hodnocení:
57 hlasů
Používám hlavně Python a zajímám se o Deep Learning a vše kolem.
Aktivity