Sławomir Kwiatkowski

by: Sławomir Kwiatkowski

2024/07/09

Car manager CRUD app - part III

Content description:
In the current post I am adding search box methods (for focus-in, focus-out and to handle text entered into the search box).
I define methods that display windows for creating a new car and for displaying repairs.


 

The methods for handling the search field are: on_entry_in(), on_entry_out() and on_entry_return().

The on_entry_in() method removes the text in the search box and turns it black.

     
def on_entry_in(self, event):
    self.search_entry.config(fg='black')
    self.search_variable.set('')

The on_entry_out() method shows the 'Search car by VRN' text greyed out in the search box.

     
def on_entry_out(self, event):
    self.search_entry.config(fg='grey')
    self.search_variable.set('Search car by VRN')

In the on_entry_return() method, I check whether something has been entered in the search field and if so, I search for a car by the given registration number and enter its data into the Treeview widget.

     
def on_entry_return(self, event):
    vrn = self.search_variable.get()
    if vrn == '':
        self.show_cars()
    else:
        car = self.helper.search_by_vrn(vrn)
        self.car_tview.delete(*self.car_tview.get_children())
        if car:
            car = list(car)
            car.insert(0, 1)
            self.car_tview.insert('', 'end', values=car)

The add_car() method creates a new window in which you can enter data about the new vehicle..

     
def add_car(self):
    add_car = NewCarWindow(self.root, self)

The show repairs() method displays the repair window for the selected car.

     
def show_repairs(self):
    car = self.__get_car_from_selection()
    if car:
        repairs = RepairsWindow(self.root, car)

The NewCarWindow class in the new_car.py file describes a simple form for entering data about a new vehicle.

During form validation, it is checked whether all fields have been completed. Then, using the add_car() method of the Helper class, a new vehicle is created in the database.

     
import tkinter as tk
from sql_helper import Helper


class NewCarWindow():
    def __init__(self, root, parent):
        self.top_level = tk.Toplevel(root)
        self.helper = Helper()
        self.parent = parent
        x = self.top_level.winfo_screenwidth()
        y = self.top_level.winfo_screenheight()
        geometry = '+{}+{}'.format(int((x / 2) - 100),
                                   int((y / 2) - 100))
        size = self.top_level.geometry(geometry)
        self.top_level.title('Add car')
        self.top_level.grab_set()
        label_make = tk.Label(self.top_level, text='Make: ',
                              font=12, padx=20, pady=10).grid(row=0, column=0)
        label_model = tk.Label(self.top_level, text='Model: ',
                               font=12, padx=20, pady=10).grid(row=1, column=0)
        label_year = tk.Label(self.top_level, text='Year: ',
                              font=12, padx=20, pady=10).grid(row=2, column=0)
        label_vrn = tk.Label(self.top_level, text='VRN: ',
                             font=12, padx=20, pady=10).grid(row=3, column=0)
        label_vin = tk.Label(self.top_level, text='VIN: ',
                             font=12, padx=20, pady=10).grid(row=4, column=0)
        self.make_sv = tk.StringVar()
        self.model_sv = tk.StringVar()
        self.year_sv = tk.StringVar()
        self.vrn_sv = tk.StringVar()
        self.vin_sv = tk.StringVar()
        self.info_sv = tk.StringVar()

        entry_make = tk.Entry(
            self.top_level, text=self.make_sv)
        entry_make.focus_set()
        entry_make.grid(row=0, column=1)
        entry_model = tk.Entry(
            self.top_level, text=self.model_sv).grid(row=1, column=1)
        entry_year = tk.Entry(
            self.top_level, text=self.year_sv).grid(row=2, column=1)
        entry_vrn = tk.Entry(
            self.top_level, text=self.vrn_sv).grid(row=3, column=1)
        entry_vin = tk.Entry(
            self.top_level, text=self.vin_sv).grid(row=4, column=1)

        info_label = tk.Label(self.top_level, textvariable=self.info_sv,
                              font=12, padx=10, pady=10, fg='red').grid(row=5, column=0, columnspan=2)
        save_button = tk.Button(self.top_level, text='Save',
                                command=self.save_new_car)
        save_button.bind('<Return>', self.save_new_car)
        save_button.grid(row=6, column=1, sticky='W', padx=10)
        cancel_button = tk.Button(self.top_level, text='Cancel',
                                  command=self.top_level.destroy)
        cancel_button.grid(row=6, column=1, sticky='W', padx=70)
        cancel_button.bind('<Return>', self.top_level.destroy)

    def save_new_car(self, event=None):
        if self.make_sv.get() and self.model_sv.get() and self.year_sv.get() and self.vrn_sv.get() and self.vin_sv.get():
            self.helper.add_car(self.make_sv.get(),
                                self.model_sv.get(),
                                self.year_sv.get(),
                                self.vrn_sv.get(),
                                self.vin_sv.get())
            self.top_level.destroy()
            self.parent.show_cars()
        else:
            self.info_sv.set('Please fill in all entry fields')

The RepairsWindow class of the repairs.py file describes a window displaying repairs for a given car. You can also add a new repair note using the add_repair method of this class.

     
import tkinter as tk
from tkinter import ttk
from sql_helper import Helper
from date_picker import DatePicker


class RepairsWindow():
    def __init__(self, root, car):
        self.top_level = tk.Toplevel(root)
        self.root = root
        self.top_level.title('Repairs')
        self.top_level.grab_set()
        self.car = car
        self.helper = Helper()

        x = self.top_level.winfo_screenwidth()
        y = self.top_level.winfo_screenheight()
        geometry = '+{}+{}'.format(int((x / 2) - 150),
                                   int((y / 2) - 150))
        size = self.top_level.geometry(geometry)

        toolbox_frame = tk.Frame(self.top_level)
        toolbox_frame.grid(column=0, row=0, sticky='W')
        self.add_repair_img = tk.PhotoImage(file='Resources/add_repair.gif')
        add_repair_button = tk.Button(
            toolbox_frame, image=self.add_repair_img, command=self.add_repair)
        add_repair_button.grid(column=0, row=0, sticky='W')
        add_repair_button.bind('<Return>', self.add_repair)
        add_repair_button.bind('<KP_Enter>', self.add_repair)
        if car.sold:
            add_repair_button.config(state='disabled')

        col_headers = ('No', 'Date', 'Description')
        self.repairs_tv = ttk.Treeview(self.top_level, columns=col_headers,
                                       show='headings', selectmode='none')
        self.repairs_tv.tag_configure('c1', background='ivory2')
        self.repairs_tv.tag_configure('c2', background='ivory3')
        for i, col in enumerate(col_headers):
            self.repairs_tv.heading(col, text=col)
            self.repairs_tv.column(col, anchor='center')
            if i == 0:
                self.repairs_tv.column(col, width=50, stretch='NO')
        self.repairs_tv.grid(column=0, row=2,  sticky='NSWE')

        scrollbar = tk.Scrollbar(self.top_level, command=self.repairs_tv.yview)
        scrollbar.grid(column=1, row=2, sticky='NS')

        self.show_repairs()

    def add_repair(self, event=None):
        self.add_repair_frame = tk.Frame(self.top_level)
        self.add_repair_frame.grid(
            column=0, row=1, pady=20, sticky='WE')

        date_label = tk.Label(self.add_repair_frame,
                              text='Date:').grid(column=0, row=2)
        self.date_sv = tk.StringVar()
        self.date_entry = tk.Entry(self.add_repair_frame,
                                   text=self.date_sv)
        self.date_entry.focus_set()
        self.date_entry.grid(column=1, row=2, sticky='W')

        self.cal_img = tk.PhotoImage(file='Resources/calendar.gif')
        show_cal_btn = tk.Button(self.add_repair_frame, image=self.cal_img,
                                 command=self.show_cal, relief='flat').grid(column=1, row=2, sticky='W', padx=170)

        description_label = tk.Label(self.add_repair_frame,
                                     text='Description:').grid(column=0, row=3)
        self.description_sv = tk.StringVar()
        self.description_entry = tk.Entry(self.add_repair_frame,
                                          text=self.description_sv)
        self.description_entry.grid(column=1, row=3, ipadx=200)
        save_button = tk.Button(self.add_repair_frame, text='Save',
                                command=self.save_repair)
        save_button.grid(column=1, row=4, pady=10, sticky='E')
        save_button.bind('<Return>', self.save_repair)
        save_button.bind('<KP_Enter>', self.save_repair)
        cancel_button = tk.Button(self.add_repair_frame, text='Cancel',
                                  command=self.cancel_repair)
        cancel_button.grid(column=1, row=4, sticky='E', padx=60)
        cancel_button.bind('<Return>', self.cancel_repair)
        cancel_button.bind('<KP_Enter>', self.cancel_repair)

    def cancel_repair(self, event=None):
        self.add_repair_frame.grid_remove()

    def save_repair(self, event=None):
        if self.date_sv.get() and self.description_sv.get():
            self.helper.add_repair(self.car, self.date_sv.get(),
                                   self.description_sv.get())
            self.show_repairs()

            self.add_repair_frame.grid_remove()

    def show_repairs(self):
        repairs = self.helper.show_repairs(self.car)
        self.repairs_tv.delete(*self.repairs_tv.get_children())
        for i, repair in enumerate(repairs, start=1):
            repair = (i, repair[0], repair[2])
            if i % 2:
                self.repairs_tv.insert('', 'end', values=repair, tag='c1')
            else:
                self.repairs_tv.insert('', 'end', values=repair, tag='c2')

    def show_cal(self, event=None):
        date_picker = DatePicker(self.top_level, self.date_entry, '%d-%m-%Y')
        self.description_entry.focus_set()



In the next post I will describe my DatePicker class for Tkinter.