Sqlite and Python – part 3.2

I am goint to reorganize the code to use tkinter and sqlite using different windows made with tkinter for our program made with sqlite3 module in Python programming language to create a database… We are going to understand how to split the code into 4 different classes, so that we can have a clear understanding of who does what, when we will add other features to the program.

The new code

Last time we explained the menu window code you can see below:

import sqlite3 as lite
from sqlite3 import Error
import tkinter as tk
from glob import glob

class App:
    # window
    def __init__(self, root):
        self.root = root
        self.root.geometry("200x200")
        self.root['bg'] = "cyan"
        tk.Button(self.root, text = "Create a database",
            command= lambda: self.new_window(Win1)).pack()
        tk.Button(self.root, text = "Create a table",
            command= lambda: self.new_window(Win2)).pack()
    
    def new_window(self, _class):
        self.new = tk.Toplevel(self.root)
        _class(self.new)

I deleted the new_button function, as you can see in the video, because I thought it was nice but a little redundant, so, I prefer a little less code, even if it is a little less readable at first glance, but with the practice we won’t notice too much difference. If you want, you can always leave it like it was. See the code below to notice the difference and choose the best for you, it’s a matter of taste, after all.

        self.butnew("Create Database", Win1)
        self.butnew("Create Table", Win2)

    def butnew(self, text, _class):
        tk.Button(self.root, text = text, command= lambda: self.new_window(_class)).pack()

Now it is:

        tk.Button(self.root, text = "Create a database",
            command= lambda: self.new_window(Win1)).pack()
        tk.Button(self.root, text = "Create a table",
            command= lambda: self.new_window(Win2)).pack()

2 lines (splitted in 2) instead of 4 lines, wich one do you prefer? I like the first neat version, but the second avoids a method, so… let’s go on.

The menu window

The mother of the new windows (super class)

In this class I want to put the common code that will be inherited by the other windows. For example we will put the listbox with the list of the database file that we create here, because we will use in window 1 and window 2 (Win1 and Win2 classes).

class Win:
    fields = [] # To create the records field in the databases table
    def listbox(self):
        self.lab_lb = tk.Label(self.root, text="Database in the folder")
        self.lab_lb.pack()
        self.lb = tk.Listbox(self.root) # create a listbox
        self.lb.pack()                  # make it visible
        for file in glob("*.db"):       # insert all the db file in the listbox
            self.lb.insert(tk.END, file)

So, this class does nothing except contain the common attributes and methods of the windows.

Win1

Let’s see the Win1, to create the database:

class Win1(Win):
    def __init__(self, root):
        self.root = root
        self.root.geometry("400x300")
        self.root.title("Create Database")
        self.widgets_db() # widgets to create the new db
        self.listbox()

    def widgets_db(self):
        """Widgets to create a new database

            - a label "Create a db" ........ self.l
            - an entry ..................... self.e 
            - a button ..................... self.b
        
        """
        self.l = tk.Label(self.root, text="Create a db [insert the name]")
        self.l.pack()
        self.db = tk.StringVar()
        self.e = tk.Entry(self.root, textvariable = self.db)
        self.e.pack()
        self.b = tk.Button(self.root, text="Create DB", command= lambda: self.mk_db())
        self.b.pack()

As you can see, this uses the listbox method of the super class Win and adds her own widgets to create the database (a label, an entry and a button).

The clicking the button creates the database and insert its name in the listbox with the method mk_db

We also need a method to create the database, that is the following one mk_db and it is used only in this window, so we add it to the Win1 class:

    def mk_db(self):
        db = self.e.get()
        if db.endswith(".db"):
            pass
        else:
            db = db + ".db"
        try:
            conn = lite.connect(db)
            if db in self.lb.get(0, tk.END):
                pass
            else:
                self.lb.insert(tk.END, db)
            return conn
        except Error as e:
            print(e)
        finally:
            self.db.set("")
            conn.close()

Whatch the video to see how it works. There are a lot of lines, but it does a few basic things.

The window to create databases

Win2

This is the second window. Until now it just has the listbox with the list of the database. So we do not need to write again the code for the listbox, we just need to call the method that has been inherited.

class Win2(Win):
    def __init__(self, root):
        self.root = root
        self.root.geometry("400x500")
        self.root.title("Create Tables")
        self.listbox()

There it is, just a listbox.

The second window, inclomplete until now, to create tables

At the end, out of the classes there is the code to start it all, making the main window and istanciating the App class.

root = tk.Tk()
app = App(root)
root.mainloop()

The video with the live coding of the app until now

The whole code ’til now

This is the re-roganized code until now… we will continue in the next post.

import sqlite3 as lite
from sqlite3 import Error
import tkinter as tk
from glob import glob

class App:
    # window
    def __init__(self, root):
        self.root = root
        self.root.geometry("200x200")
        self.root['bg'] = "cyan"
        tk.Button(self.root, text = "Create a database",
            command= lambda: self.new_window(Win1)).pack()
        tk.Button(self.root, text = "Create a table",
            command= lambda: self.new_window(Win2)).pack()
    
    def new_window(self, _class):
        self.new = tk.Toplevel(self.root)
        _class(self.new)


class Win:
    fields = [] # To create the records field in the databases table
    def listbox(self):
        self.lab_lb = tk.Label(self.root, text="Database in the folder")
        self.lab_lb.pack()
        self.lb = tk.Listbox(self.root) # create a listbox
        self.lb.pack()                  # make it visible
        for file in glob("*.db"):       # insert all the db file in the listbox
            self.lb.insert(tk.END, file)

class Win1(Win):
    def __init__(self, root):
        self.root = root
        self.root.geometry("400x300")
        self.root.title("Create Database")
        self.widgets_db() # widgets to create the new db
        self.listbox()

    def widgets_db(self):
        """Widgets to create a new database

            - a label "Create a db" ........ self.l
            - an entry ..................... self.e 
            - a button ..................... self.b
        
        """
        self.l = tk.Label(self.root, text="Create a db [insert the name]")
        self.l.pack()
        self.db = tk.StringVar()
        self.e = tk.Entry(self.root, textvariable = self.db)
        self.e.pack()
        self.b = tk.Button(self.root, text="Create DB", command= lambda: self.mk_db())
        self.b.pack()

    def mk_db(self):
        db = self.e.get()
        if db.endswith(".db"):
            pass
        else:
            db = db + ".db"
        try:
            conn = lite.connect(db)
            if db in self.lb.get(0, tk.END):
                pass
            else:
                self.lb.insert(tk.END, db)
            return conn
        except Error as e:
            print(e)
        finally:
            self.db.set("")
            conn.close()

class Win2(Win):
    def __init__(self, root):
        self.root = root
        self.root.geometry("400x500")
        self.root.title("Create Tables")
        self.listbox()

root = tk.Tk()
app = App(root)
root.mainloop()

To be continued…

 

Published by pythonprogramming

Started with basic on the spectrum, loved javascript in the 90ies and python in the 2000, now I am back with python, still making some javascript stuff when needed.