GUI do programu CRUD cz.1

W poprzednim wpisie opisałem prosty program CRUD, który modyfikował bazę danych (w tym przypadku SQLite). Do utworzonego wcześniej programu utworzę gui w bibliotece tkinter. Interfejs będzie rozwijany stopniowo, dzięki czemu łatwiej będzie wytłumaczyć zastosowane metody.

Kod programu można pobrać tutaj.

Utworzone we wcześniejszym wpisie pliki nieznacznie zmodyfikuję tzn. do pliku opisującego klasę Car() dopiszę metodę klasy, która tworzy instancję, gdy jako argument podano listę. Ponadto w klasie Helper() metoda del_car() oprócz usuwania auta z bazy danych, usuwa również wszystkie dane o wpisanych naprawach związanych z tym autem. W produkcyjnej bazie danych lepszym rozwiązaniem byłoby zapewne oznaczenie auta jako “skasowanego” zamiast faktycznego kasowania z bazy.

Kod dopisanych fragmentów wygląda następująco:

class Car():
    @classmethod
    def from_list(cls, list):
        make, model, year, vrn, vin, sold = list
        return cls(make, model, year, vrn, vin, sold)

class Helper():
    def del_car(self, car):
        with self.conn:
            self.c.execute("SELECT ROWID FROM cars WHERE vin=:vin",
                           {'vin': car.vin})
            car_id = self.c.fetchone()
            self.c.execute("DELETE FROM repairs WHERE car=?",
                           (car_id))
            self.c.execute("DELETE FROM cars WHERE vin=:vin", {'vin': car.vin})

Kod pliku car_manager.py zawierający klasę CarManager() opisującą graficzny interfejs.

import tkinter as tk
from tkinter import messagebox, ttk
from sql_helper import Helper
from car import Car

class CarManager(tk.Frame):
    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.helper = Helper()
        root.title('Car manager')
        root.iconphoto(True, tk.PhotoImage(file='Resources/car_logo.png'))
        root.bind('<Control-x>', self.close_app)
        root.columnconfigure(1, weight=1)
        root.rowconfigure(2, weight=1)
        self.create_widgets()
        self.show_cars()


if __name__ == '__main__':
    root = tk.Tk()
    cm = CarManager(root)
    cm.mainloop()

Podczas tworzenia egzemplarza klasy CarManager tworzę instancje klasy pomocniczej Helper, ustawiam nazwę tworzonego okna na ‘Car Manager’, dodaję ikonkę programu z katalogu Resources. Oprócz tego ustawiam skrót klawiszowy do zakończenia programu (Ctrl+x). Następnie ustawiany jest sposób w jaki mają być skalowane widgety podczas zmiany rozmiaru okna. Ostatnie dwa wiersze metody __init__() uruchamiają metody odpowiedzialne za utworzenie wszystkich widgetów okna głównego oraz za wyświetlenie danych z bazy danych o zapisanych autach.

Kod metody close_app() uruchamianej podczas naciśnięcia skrótu (Ctrl+x):

    def close_app(self, event):
        result = tk.messagebox.askyesno('Exit', 'Close application?')
        if result:
            self.root.destroy()

Jeśli rezultat jest wartością True tzn. użytkownik wybrał przycisk potwierdzający zamknięcie aplikacji to okno główne aplikacji jest zamykane.

Kod metody create_widgets() odpowiedzialnej za wyświetlenie elementów interfejsu:

def create_widgets(self):
        # create menu
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)
        file_menu = tk.Menu(menubar, tearoff=0)
        file_menu.add_command(label='Exit', command=exit, accelerator="Ctrl+x")
        menubar.add_cascade(label="File", menu=file_menu)

        # create toolbar
        tbar_frame = tk.Frame(root, height=10)
        tbar_frame.grid(row=0, column=0)

        self.add_car_img = tk.PhotoImage(file='Resources/add_car.gif')
        self.remove_car_img = tk.PhotoImage(file='Resources/remove_car.gif')
        self.repairs_img = tk.PhotoImage(file='Resources/repairs.gif')
        self.sold_img = tk.PhotoImage(file='Resources/sold.gif')

        add_car_button = tk.Button(tbar_frame, image=self.add_car_img,
                                   command=self.add_car).grid(row=0, column=0, sticky='W')
        del_car_button = tk.Button(tbar_frame, image=self.remove_car_img,
                                   command=self.del_car).grid(row=0, column=0, sticky='W', padx=30)
        repairs_button = tk.Button(tbar_frame, image=self.repairs_img,
                                   command=self.show_repairs).grid(row=0, column=0, sticky='W', padx=60)
        sold_button = tk.Button(tbar_frame, image=self.sold_img,
                                command=self.sell_car).grid(row=0, column=0, sticky='W', padx=90)

        # create search entry
        self.search_variable = tk.StringVar()
        self.search_entry = tk.Entry(
            root, textvariable=self.search_variable)
        self.search_entry.config(fg='grey')
        self.search_variable.set("Search car by VRN")
        self.search_entry.bind('<FocusIn>', self.on_entry_in)
        self.search_entry.bind('<FocusOut>', self.on_entry_out)
        self.search_entry.bind('<Return>', self.on_entry_return)
        self.search_entry.bind('<KP_Enter>', self.on_entry_return)
        self.search_entry.grid(row=0, column=1, sticky='E', ipadx=20)

        # create TreeView with Scrollbar
        col_headers = ('No', 'Make', 'Model', 'Year', 'VRN', 'VIN', 'Sold')
        self.car_tview = ttk.Treeview(self.root, columns=col_headers,
                                      show='headings', selectmode='browse')
        self.car_tview.columnconfigure(0, weight=1)
        self.car_tview.rowconfigure(0, weight=1)
        # set column headers
        for i, col in enumerate(col_headers):
            self.car_tview.heading(col, text=col)
            self.car_tview.column(col, anchor='center')
            if i == 0:
                self.car_tview.column(col, width=50, stretch='NO')
        self.car_tview.grid(row=2, column=0, columnspan=2, sticky='NSWE')

        scrollbar = tk.Scrollbar(self.root, command=self.car_tview.yview)
        scrollbar.grid(column=3, row=2, sticky='NS')

W metodzie create_widgets() kolejno tworzone jest menu programu, następnie pasek narzędziowy zawierające przyciski dodawania auta do bazy, usuwania auta, dodawania danych o naprawach auta oraz oznaczania sprzedaży auta. Następnie tworzone jest pole wyszukiwania auta po numerze rejestracyjnym, do którego podpięte są metody, które reagują na zdarzenia uzyskania/stracenia focusa oraz zatwierdzania danych przez wciśnięcie klawisza ENTER na: klawiaturze numerycznej (<KP_Enter>) lub podstawowej (<Return>). Kolejne linie definiują widget TreeView, w którym będą przedstawiane kolejne wiersze zawierające dane o autach oraz pasek przewijania, jeśli dane nie mieszczą się na stronie.

Po zmianach główne okno programu powinno wyglądać następująco:

Car Manager 1
Początkowe okno programu. Na razie bez danych…

>>część druga<<

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *