Sqlite3 & Tkinter – Part II

After some days from the first post about sqlite, we are back at the code writing to finish the app we wanted to. Tkinter is one of the topic that I often write about in this blog, as you could be aware of searching into my posts, and I always try to write about it’s features making practical examples, rather than talking in abstract of his widgets and stuffs. This three tools together can do a lot of good things, I think. Let’ go into more details about it through a little app to create databases with sqlite3.

This second post about our little application to use sqlite3 database with python and tkinter. The tkinter module is a built-in one of python that allows us to create graphic user interfaces. We want to create this GUI to:

  • create a sqlite database
  • create a table
  • insert data in the table
  • do other stuffs

In the first post and video(python and sqlite with tkinter) (go here to read and watch if you do not have done it yet) we created a window to generate databases with nothing inside.

This is how the windows appeared. It does what it looks like it does.

Create tables with sqlite and tkinter

Now we will add tables through the interface. Before we start we will include the function create into the class to create the window called Win.

Let’s do this (the most relevant part of this code is conn = lite.connect(db) that opens / creates the database.

def create(obj):
	db = obj.e.get()
	if db[-3] == ".db":
		pass
	else:
		db = db + ".db"
	try:
		conn = lite.connect(db)
		return conn
	except Error as e:
		print(e)
	finally:
		conn.close()
		obj.lb.insert(tk.END, db)
		obj.db.set("")

The function above will be in included in the Win class (that we will call now App). The function will be a method of App and we will change the name from create to mk_db. Instead of ‘obj’ we will put self as it’s in the class. There are other changes explained in the video.

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

This are other changes, consequential to the decisions we took previously:

We change the name of ‘db’ (StringVar object) in ‘e_string_var’, for more intuitive stuff:

		self.e_string_var = tk.StringVar()
		self.e = tk.Entry(self.root, textvariable=self.e_string_var)
		self.e.pack()

and also the button need some changes:

		self.b = tk.Button(self.root, text="Create DB", command= lambda: self.mk_db())
		self.b.pack()

So, nothing dramatic until know. We just moved some lines of code into the main class and changed some names.

There is a last thing. We put the creation of the Tk() object outside of the App, for reason that will be declared later. This is the complete code at the end.

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
		# entry containing the db name
		
		self.l = tk.Label(self.root, text="Create a db [insert the name]")
		self.l.pack()

		self.e_string_var = tk.StringVar()
		self.e = tk.Entry(self.root, textvariable=self.e_string_var)
		self.e.pack()

		self.b = tk.Button(self.root, text="Create DB", command= lambda: self.mk_db())
		self.b.pack()

		self.lb = tk.Listbox(self.root)
		self.lb.pack()
		self.show_db()

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


	def show_db(self):
		for file in glob("*.db"):
			self.lb.insert(tk.END, file)

		

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

Functionality are the same. Nothing changed. Time to create tables.

The mk_tb method to create tables, finally

To create the fields we use this

	def mk_fl(self):
		self.fields.append(self.efl.get())
		self.vfl.set("")

The fields will go into a list called self.fields. In the mk_tb method to create the table, this fields list will become a string.

Here is the function for the table

	def mk_tb(self, dbn, tbn):
		self.conn = lite.connect(dbn.get())
		self.cur = self.conn.cursor()
		self.fields = "".join(self.fields)
		self.cur.execute("""create table {} (
		{});""".format(tbn, self.fields))
		self.fields = []
		self.conn.close()

Simple:

  • open the database
  • start the cursor()
  • join the list of fields in a string
  • execute the string with the sql command
  • close the database

When this function is called there are two arguments:

  • the name of the database (to open it with lite.connect(dbn)
  • the name of the table (that will go in the string with the commands

The fields will be taken from the attribute self.fields, that was a list, transformed in string with join. We don’t need to pass them.

The widgets

Now we just need the widgets to insert the name of the database, of the table and to define the fields.

The fields have a label (name) and then the type of fields (text or integer, float etc) and must be followed by a comma (we could add some code to avoid forgetting it).

The label and Entry for the db name

		# label and Entry for Database name
		self.ldbname = tk.Label(self.root, text="Insert Database name")
		self.ldbname.pack()
		self.dbn = tk.StringVar()
		self.edb = tk.Entry(self.root, textvariable = self.dbn)
		self.edb.pack()

The label and Entry for the table name

		self.ltbname = tk.Label(self.root, text="Insert Table name")
		self.ltbname.pack()
		self.tbn = tk.StringVar()
		self.etb = tk.Entry(self.root, textvariable = self.tbn)
		self.etb.pack()

The label, the entry and the butto for the fields

You create a fields like

name text,

and then click the button “Create fields”…

Repeat for all the fields.

		# FIELDS - vfl is the StringVar, efl is the Entry
		self.lflname = tk.Label(self.root, text="Insert Fields name and type\n followeb by a comma, one by one,\nclicking once for each field.")
		self.lflname.pack()
		self.vfl = tk.StringVar()
		self.efl = tk.Entry(self.root, textvariable = self.vfl)
		self.efl.pack()
		self.bfl = tk.Button(self.root, text="Create Field", command= lambda: self.mk_fl())
		self.bfl.pack()

 

When you have finished inserting the fields, you click “Create the table”.

The button to create the table

		self.btb = tk.Button(self.root, text="Create Table", command= lambda: self.mk_tb(self.dbn, self.tbn))
		self.btb.pack()

The whole code until creation of the tables

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



class App:
	# window
	def __init__(self, root):

		self.fields = []


		self.root = root
		# entry containing the db name
		
		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()

		self.lb = tk.Listbox(self.root)
		self.lb.pack()
		self.show_db()

		# label and Entry for Database name
		self.ldbname = tk.Label(self.root, text="Insert Database name")
		self.ldbname.pack()
		self.dbn = tk.StringVar()
		self.edb = tk.Entry(self.root, textvariable = self.dbn)
		self.edb.pack()
 
		self.ltbname = tk.Label(self.root, text="Insert Table name")
		self.ltbname.pack()
		self.tbn = tk.StringVar()
		self.etb = tk.Entry(self.root, textvariable = self.tbn)
		self.etb.pack()

		# FIELDS - vfl is the StringVar, efl is the Entry
		self.lflname = tk.Label(self.root, text="Insert Fields name and type\n followeb by a comma, one by one,\nclicking once for each field.")
		self.lflname.pack()
		self.vfl = tk.StringVar()
		self.efl = tk.Entry(self.root, textvariable = self.vfl)
		self.efl.pack()
		self.bfl = tk.Button(self.root, text="Create Field", command= lambda: self.mk_fl())
		self.bfl.pack()

		self.btb = tk.Button(self.root, text="Create Table", command= lambda: self.mk_tb(self.dbn, self.tbn))
		self.btb.pack()


	def show_db(self):
		for file in glob("*.db"):
			self.lb.insert(tk.END, file)

	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()

	def mk_tb(self, dbn, tbn):
		self.conn = lite.connect(dbn.get())
		self.cur = self.conn.cursor()
		self.fields = "".join(self.fields)
		self.cur.execute("""create table {} (
		{});""".format(tbn, self.fields))
		self.fields = []
		self.conn.close()


	def mk_fl(self):
		self.fields.append(self.efl.get())
		self.vfl.set("")



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

The window

Some restyiling to the code

Nothing is changed in this code from above, apart some methods to add readability.

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



class App:
    # window
    def __init__(self, root):

        self.fields = []
        self.root = root
        self.label()
        self.entry()
        self.button()
        self.listbox()
        self.db_name_widgets()
        self.tb_name_widgets()
        self.fields_widgets()
        self.btn_create_table()

    def label(self):
        self.l = tk.Label(self.root, text="Create a db [insert the name]")
        self.l.pack()

    def entry(self):
        self.db = tk.StringVar()
        self.e = tk.Entry(self.root, textvariable=self.db)
        self.e.pack()

    def button(self):
        self.b = tk.Button(self.root, text="Create DB", command= lambda: self.mk_db())
        self.b.pack()

    def listbox(self):
        self.lb = tk.Listbox(self.root)
        self.lb.pack()
        self.show_db()

    def db_name_widgets(self):
        # label and Entry for Database name
        self.ldbname = tk.Label(self.root, text="Insert Database name")
        self.ldbname.pack()
        self.dbn = tk.StringVar()
        self.edb = tk.Entry(self.root, textvariable = self.dbn)
        self.edb.pack()

    def tb_name_widgets(self):
        self.ltbname = tk.Label(self.root, text="Insert Table name")
        self.ltbname.pack()
        self.tbn = tk.StringVar()
        self.etb = tk.Entry(self.root, textvariable = self.tbn)
        self.etb.pack()

    def fields_widgets(self):
        self.lflname = tk.Label(self.root, text="Insert Fields name and type\n followeb by a comma, one by one,\nclicking once for each field.")
        self.lflname.pack()
        self.vfl = tk.StringVar()
        self.efl = tk.Entry(self.root, textvariable = self.vfl)
        self.efl.pack()
        self.bfl = tk.Button(self.root, text="Create Field", command= lambda: self.mk_fl())
        self.bfl.pack()

    def btn_create_table(self):
        self.btb = tk.Button(self.root, text="Create Table", command= lambda: self.mk_tb(self.dbn, self.tbn))
        self.btb.pack()

    def show_db(self):
        for file in glob("*.db"):
            self.lb.insert(tk.END, file)

    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()

    def mk_tb(self, dbn, tbn):
        self.conn = lite.connect(dbn.get())
        self.cur = self.conn.cursor()
        self.fields = "".join(self.fields)
        self.cur.execute("""create table {} (
        {});""".format(tbn, self.fields))
        self.fields = []
        self.conn.close()

    def mk_fl(self):
        self.fields.append(self.efl.get())
        self.vfl.set("")



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

 

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.