Es muy posible que necesitemos en algunas situaciones acceder a una base de datos de SQLite desde una aplicación con una interfaz visual.
Implementaremos el mismo problema que resolvimos cuando trabajamos con el gestor de base de datos MySQL.
Desarrollar una aplicación visual con la librería tkinter que permita implementar los algoritmos de carga de artículos, consulta por código y listado completo.
Trabajaremos con la base de datos 'bd1.db' que creamos en el concepto anterior.
Las interfaz visual para la carga debe ser:
Las interfaz visual para la consulta:
Y finalmente la interfaz para el listado completo:
Para trabajar un poco más ordenado en la resolución de este problema lo dividiremos en dos módulos 'formularioarticulos.py' y 'articulos.py'.
El primer paso será crear una carpeta llamada 'proyecto4' y dentro de esta crearemos los dos módulos:
El módulo 'formularioarticulos.py' contiene toda la lógica de presentación de datos y hace uso del otro módulo para el acceso a la base de datos SQLite.
import tkinter as tk from tkinter import ttk from tkinter import messagebox as mb from tkinter import scrolledtext as st import articulos class FormularioArticulos: def __init__(self): self.articulo1=articulos.Articulos() self.ventana1=tk.Tk() self.ventana1.title("Mantenimiento de artículos") self.cuaderno1 = ttk.Notebook(self.ventana1) self.carga_articulos() self.consulta_por_codigo() self.listado_completo() self.cuaderno1.grid(column=0, row=0, padx=10, pady=10) self.ventana1.mainloop() def carga_articulos(self): self.pagina1 = ttk.Frame(self.cuaderno1) self.cuaderno1.add(self.pagina1, text="Carga de artículos") self.labelframe1=ttk.LabelFrame(self.pagina1, text="Artículo") self.labelframe1.grid(column=0, row=0, padx=5, pady=10) self.label1=ttk.Label(self.labelframe1, text="Descripción:") self.label1.grid(column=0, row=0, padx=4, pady=4) self.descripcioncarga=tk.StringVar() self.entrydescripcion=ttk.Entry(self.labelframe1, textvariable=self.descripcioncarga) self.entrydescripcion.grid(column=1, row=0, padx=4, pady=4) self.label2=ttk.Label(self.labelframe1, text="Precio:") self.label2.grid(column=0, row=1, padx=4, pady=4) self.preciocarga=tk.StringVar() self.entryprecio=ttk.Entry(self.labelframe1, textvariable=self.preciocarga) self.entryprecio.grid(column=1, row=1, padx=4, pady=4) self.boton1=ttk.Button(self.labelframe1, text="Confirmar", command=self.agregar) self.boton1.grid(column=1, row=2, padx=4, pady=4) def agregar(self): datos=(self.descripcioncarga.get(), self.preciocarga.get()) self.articulo1.alta(datos) mb.showinfo("Información", "Los datos fueron cargados") self.descripcioncarga.set("") self.preciocarga.set("") def consulta_por_codigo(self): self.pagina2 = ttk.Frame(self.cuaderno1) self.cuaderno1.add(self.pagina2, text="Consulta por código") self.labelframe2=ttk.LabelFrame(self.pagina2, text="Artículo") self.labelframe2.grid(column=0, row=0, padx=5, pady=10) self.label1=ttk.Label(self.labelframe2, text="Código:") self.label1.grid(column=0, row=0, padx=4, pady=4) self.codigo=tk.StringVar() self.entrycodigo=ttk.Entry(self.labelframe2, textvariable=self.codigo) self.entrycodigo.grid(column=1, row=0, padx=4, pady=4) self.label2=ttk.Label(self.labelframe2, text="Descripción:") self.label2.grid(column=0, row=1, padx=4, pady=4) self.descripcion=tk.StringVar() self.entrydescripcion=ttk.Entry(self.labelframe2, textvariable=self.descripcion, state="readonly") self.entrydescripcion.grid(column=1, row=1, padx=4, pady=4) self.label3=ttk.Label(self.labelframe2, text="Precio:") self.label3.grid(column=0, row=2, padx=4, pady=4) self.precio=tk.StringVar() self.entryprecio=ttk.Entry(self.labelframe2, textvariable=self.precio, state="readonly") self.entryprecio.grid(column=1, row=2, padx=4, pady=4) self.boton1=ttk.Button(self.labelframe2, text="Consultar", command=self.consultar) self.boton1.grid(column=1, row=3, padx=4, pady=4) def consultar(self): datos=(self.codigo.get(), ) respuesta=self.articulo1.consulta(datos) if len(respuesta)>0: self.descripcion.set(respuesta[0][0]) self.precio.set(respuesta[0][1]) else: self.descripcion.set('') self.precio.set('') mb.showinfo("Información", "No existe un artículo con dicho código") def listado_completo(self): self.pagina3 = ttk.Frame(self.cuaderno1) self.cuaderno1.add(self.pagina3, text="Listado completo") self.labelframe3=ttk.LabelFrame(self.pagina3, text="Artículo") self.labelframe3.grid(column=0, row=0, padx=5, pady=10) self.boton1=ttk.Button(self.labelframe3, text="Listado completo", command=self.listar) self.boton1.grid(column=0, row=0, padx=4, pady=4) self.scrolledtext1=st.ScrolledText(self.labelframe3, width=30, height=10) self.scrolledtext1.grid(column=0,row=1, padx=10, pady=10) def listar(self): respuesta=self.articulo1.recuperar_todos() self.scrolledtext1.delete("1.0", tk.END) for fila in respuesta: self.scrolledtext1.insert(tk.END, "código:"+str(fila[0])+"\ndescripción:"+fila[1]+"\nprecio:"+str(fila[2])+"\n\n") aplicacion1=FormularioArticulos()
El módulo 'articulos.py' contiene toda la lógica de acceso a SQLite.
import sqlite3 class Articulos: def abrir(self): conexion=sqlite3.connect("c:/programaspython/bd1.db") return conexion def alta(self, datos): cone=self.abrir() cursor=cone.cursor() sql="insert into articulos(descripcion, precio) values (?,?)" cursor.execute(sql, datos) cone.commit() cone.close() def consulta(self, datos): try: cone=self.abrir() cursor=cone.cursor() sql="select descripcion, precio from articulos where codigo=?" cursor.execute(sql, datos) return cursor.fetchall() finally: cone.close() def recuperar_todos(self): try: cone=self.abrir() cursor=cone.cursor() sql="select codigo, descripcion, precio from articulos" cursor.execute(sql) return cursor.fetchall() finally: cone.close()
Tener en cuenta que el módulo principal se encuentra en el archivo 'formularioarticulos.py' y es el que debemos ejecutar:
Cuando al programa lo ejecutemos desde la línea de comandos fuera del editor VS Code debemos recordar de llamar al módulo principal:
Analicemos un poco el código del módulo 'formularioarticulos.py', lo primero que hacemos es importar los módulos necesarios para implementar la interfaz visual:
import tkinter as tk from tkinter import ttk from tkinter import messagebox as mb from tkinter import scrolledtext as st
Otro import fundamental es el módulo 'articulos.py' donde tenemos implementada la clase 'Articulos' que es la que se comunica con la base de datos SQLite:
import articulos
La clase visual la hemos llamado 'FormularioArticulos' y en el método __init__ creamos un objeto de la clase 'Articulos' que se encuentra en el otro módulo:
class FormularioArticulos: def __init__(self): self.articulo1=articulos.Articulos()
También en el método __init__ llamamos a una serie de métodos para crear cada una de las páginas del objeto de la clase 'Notebook':
self.ventana1=tk.Tk() self.ventana1.title("Mantenimiento de artículos") self.cuaderno1 = ttk.Notebook(self.ventana1) self.carga_articulos() self.consulta_por_codigo() self.listado_completo() self.cuaderno1.grid(column=0, row=0, padx=10, pady=10) self.ventana1.mainloop()
Cuando desde la pestaña "Carga de artículos" se presiona el botón "Confirmar" lo primero que hacemos es crear una tupla con los dos datos ingresados en los controles "Entry":
def agregar(self): datos=(self.descripcioncarga.get(), self.preciocarga.get())
Luego llamamos al método alta del objeto 'articulo1' y le pasamos la tupla con los datos a añadir:
self.articulo1.alta(datos) mb.showinfo("Información", "Los datos fueron cargados") self.descripcioncarga.set("") self.preciocarga.set("")
En este momento nos conviene analizar el método 'alta' de la clase 'Articulos' que se encuentra en el otro módulo:
def alta(self, datos): cone=self.abrir() cursor=cone.cursor() sql="insert into articulos(descripcion, precio) values (?,?)" cursor.execute(sql, datos) cone.commit() cone.close()
En el método 'alta' abrimos la conexión con el SQLite, creamos un cursor y llamamos seguidamente al método execute pasando un string con el comando SQL 'insert' y los datos a almacenar. Llamamos al método commit de la conexión y finalmente cerramos la conexión.
El método para abrir la conexión es:
def abrir(self): conexion=sqlite3.connect("c:/programaspython/bd1.db") return conexion
Es importante especificar el path donde se encuentra el archivo 'bd1.db' si nuestro programa en Python se encuentra en otra carpeta.
Cuando se presiona el botón "Consultar" se ejecuta el método siguiente:
def consultar(self): datos=(self.codigo.get(), ) respuesta=self.articulo1.consulta(datos) if len(respuesta)>0: self.descripcion.set(respuesta[0][0]) self.precio.set(respuesta[0][1]) else: self.descripcion.set('') self.precio.set('') mb.showinfo("Información", "No existe un artículo con dicho código")
Creamos una tubla con un solo dato (es obligatoria la coma para que Python lo considere una tupla:
datos=(self.codigo.get(), )
Llamamos al método consulta de la clase 'Articulos' que se encuentra en el otro módulo. El método 'consulta' retorna una lista vacía si no existe el código de artículo ingresado o una lista con una tupla en su interior.
El método 'consulta' de la clase 'Articulos' llama al método 'fetchall' del cursor respectivo:
def consulta(self, datos): try: cone=self.abrir() cursor=cone.cursor() sql="select descripcion, precio from articulos where codigo=?" cursor.execute(sql, datos) return cursor.fetchall() finally: cone.close()
Para mostrar todas las filas de la tabla 'articulos' hemos dispuesto un objeto de la clase 'scrolledtext':
def listar(self): respuesta=self.articulo1.recuperar_todos() self.scrolledtext1.delete("1.0", tk.END) for fila in respuesta: self.scrolledtext1.insert(tk.END, "código:"+str(fila[0])+ "\ndescripción:"+fila[1]+ "\nprecio:"+str(fila[2])+"\n\n")
Llamamos al método 'recuperar_todos' de la clase 'Articulos' y obtenemos una lista con un conjunto de tuplas con cada fila de la tabla.
El algoritmo 'recuperar_todos' de la clase Articulos es:
def recuperar_todos(self): try: cone=self.abrir() cursor=cone.cursor() sql="select codigo, descripcion, precio from articulos" cursor.execute(sql) return cursor.fetchall() finally: cone.close()
Agregar dos pestañas al programa de administración de artículos que permitan borrar un artículo ingresando su código y otra opción que permita consultar y modificar la descripción y precio de un artículo.
La interfaces visuales a implementar son:
módulo: formularioarticulos.py import tkinter as tk from tkinter import ttk from tkinter import messagebox as mb from tkinter import scrolledtext as st import articulos class FormularioArticulos: def __init__(self): self.articulo1=articulos.Articulos() self.ventana1=tk.Tk() self.ventana1.title("Mantenimiento de artículos") self.cuaderno1 = ttk.Notebook(self.ventana1) self.carga_articulos() self.consulta_por_codigo() self.listado_completo() self.borrado() self.modificar() self.cuaderno1.grid(column=0, row=0, padx=10, pady=10) self.ventana1.mainloop() def carga_articulos(self): self.pagina1 = ttk.Frame(self.cuaderno1) self.cuaderno1.add(self.pagina1, text="Carga de artículos") self.labelframe1=ttk.LabelFrame(self.pagina1, text="Artículo") self.labelframe1.grid(column=0, row=0, padx=5, pady=10) self.label1=ttk.Label(self.labelframe1, text="Descripción:") self.label1.grid(column=0, row=0, padx=4, pady=4) self.descripcioncarga=tk.StringVar() self.entrydescripcion=ttk.Entry(self.labelframe1, textvariable=self.descripcioncarga) self.entrydescripcion.grid(column=1, row=0, padx=4, pady=4) self.label2=ttk.Label(self.labelframe1, text="Precio:") self.label2.grid(column=0, row=1, padx=4, pady=4) self.preciocarga=tk.StringVar() self.entryprecio=ttk.Entry(self.labelframe1, textvariable=self.preciocarga) self.entryprecio.grid(column=1, row=1, padx=4, pady=4) self.boton1=ttk.Button(self.labelframe1, text="Confirmar", command=self.agregar) self.boton1.grid(column=1, row=2, padx=4, pady=4) def agregar(self): datos=(self.descripcioncarga.get(), self.preciocarga.get()) self.articulo1.alta(datos) mb.showinfo("Información", "Los datos fueron cargados") self.descripcioncarga.set("") self.preciocarga.set("") def consulta_por_codigo(self): self.pagina2 = ttk.Frame(self.cuaderno1) self.cuaderno1.add(self.pagina2, text="Consulta por código") self.labelframe2=ttk.LabelFrame(self.pagina2, text="Artículo") self.labelframe2.grid(column=0, row=0, padx=5, pady=10) self.label1=ttk.Label(self.labelframe2, text="Código:") self.label1.grid(column=0, row=0, padx=4, pady=4) self.codigo=tk.StringVar() self.entrycodigo=ttk.Entry(self.labelframe2, textvariable=self.codigo) self.entrycodigo.grid(column=1, row=0, padx=4, pady=4) self.label2=ttk.Label(self.labelframe2, text="Descripción:") self.label2.grid(column=0, row=1, padx=4, pady=4) self.descripcion=tk.StringVar() self.entrydescripcion=ttk.Entry(self.labelframe2, textvariable=self.descripcion, state="readonly") self.entrydescripcion.grid(column=1, row=1, padx=4, pady=4) self.label3=ttk.Label(self.labelframe2, text="Precio:") self.label3.grid(column=0, row=2, padx=4, pady=4) self.precio=tk.StringVar() self.entryprecio=ttk.Entry(self.labelframe2, textvariable=self.precio, state="readonly") self.entryprecio.grid(column=1, row=2, padx=4, pady=4) self.boton1=ttk.Button(self.labelframe2, text="Consultar", command=self.consultar) self.boton1.grid(column=1, row=3, padx=4, pady=4) def consultar(self): datos=(self.codigo.get(), ) respuesta=self.articulo1.consulta(datos) if len(respuesta)>0: self.descripcion.set(respuesta[0][0]) self.precio.set(respuesta[0][1]) else: self.descripcion.set('') self.precio.set('') mb.showinfo("Información", "No existe un artículo con dicho código") def listado_completo(self): self.pagina3 = ttk.Frame(self.cuaderno1) self.cuaderno1.add(self.pagina3, text="Listado completo") self.labelframe3=ttk.LabelFrame(self.pagina3, text="Artículo") self.labelframe3.grid(column=0, row=0, padx=5, pady=10) self.boton1=ttk.Button(self.labelframe3, text="Listado completo", command=self.listar) self.boton1.grid(column=0, row=0, padx=4, pady=4) self.scrolledtext1=st.ScrolledText(self.labelframe3, width=30, height=10) self.scrolledtext1.grid(column=0,row=1, padx=10, pady=10) def listar(self): respuesta=self.articulo1.recuperar_todos() self.scrolledtext1.delete("1.0", tk.END) for fila in respuesta: self.scrolledtext1.insert(tk.END, "código:"+str(fila[0])+ "\ndescripción:"+fila[1]+ "\nprecio:"+str(fila[2])+"\n\n") def borrado(self): self.pagina4 = ttk.Frame(self.cuaderno1) self.cuaderno1.add(self.pagina4, text="Borrado de artículos") self.labelframe4=ttk.LabelFrame(self.pagina4, text="Artículo") self.labelframe4.grid(column=0, row=0, padx=5, pady=10) self.label1=ttk.Label(self.labelframe4, text="Código:") self.label1.grid(column=0, row=0, padx=4, pady=4) self.codigoborra=tk.StringVar() self.entryborra=ttk.Entry(self.labelframe4, textvariable=self.codigoborra) self.entryborra.grid(column=1, row=0, padx=4, pady=4) self.boton1=ttk.Button(self.labelframe4, text="Borrar", command=self.borrar) self.boton1.grid(column=1, row=1, padx=4, pady=4) def borrar(self): datos=(self.codigoborra.get(), ) cantidad=self.articulo1.baja(datos) if cantidad==1: mb.showinfo("Información", "Se borró el artículo con dicho código") else: mb.showinfo("Información", "No existe un artículo con dicho código") def modificar(self): self.pagina5 = ttk.Frame(self.cuaderno1) self.cuaderno1.add(self.pagina5, text="Modificar artículo") self.labelframe5=ttk.LabelFrame(self.pagina5, text="Artículo") self.labelframe5.grid(column=0, row=0, padx=5, pady=10) self.label1=ttk.Label(self.labelframe5, text="Código:") self.label1.grid(column=0, row=0, padx=4, pady=4) self.codigomod=tk.StringVar() self.entrycodigo=ttk.Entry(self.labelframe5, textvariable=self.codigomod) self.entrycodigo.grid(column=1, row=0, padx=4, pady=4) self.label2=ttk.Label(self.labelframe5, text="Descripción:") self.label2.grid(column=0, row=1, padx=4, pady=4) self.descripcionmod=tk.StringVar() self.entrydescripcion=ttk.Entry(self.labelframe5, textvariable=self.descripcionmod) self.entrydescripcion.grid(column=1, row=1, padx=4, pady=4) self.label3=ttk.Label(self.labelframe5, text="Precio:") self.label3.grid(column=0, row=2, padx=4, pady=4) self.preciomod=tk.StringVar() self.entryprecio=ttk.Entry(self.labelframe5, textvariable=self.preciomod) self.entryprecio.grid(column=1, row=2, padx=4, pady=4) self.boton1=ttk.Button(self.labelframe5, text="Consultar", command=self.consultar_mod) self.boton1.grid(column=1, row=3, padx=4, pady=4) self.boton1=ttk.Button(self.labelframe5, text="Modificar", command=self.modifica) self.boton1.grid(column=1, row=4, padx=4, pady=4) def modifica(self): datos=(self.descripcionmod.get(), self.preciomod.get(), self.codigomod.get()) cantidad=self.articulo1.modificacion(datos) if cantidad==1: mb.showinfo("Información", "Se modificó el artículo") else: mb.showinfo("Información", "No existe un artículo con dicho código") def consultar_mod(self): datos=(self.codigomod.get(), ) respuesta=self.articulo1.consulta(datos) if len(respuesta)>0: self.descripcionmod.set(respuesta[0][0]) self.preciomod.set(respuesta[0][1]) else: self.descripcionmod.set('') self.preciomod.set('') mb.showinfo("Información", "No existe un artículo con dicho código") aplicacion1=FormularioArticulos() módulo: articulos.py import sqlite3 class Articulos: def abrir(self): conexion=sqlite3.connect("c:/programaspython/bd1.db") return conexion def alta(self, datos): cone=self.abrir() cursor=cone.cursor() sql="insert into articulos(descripcion, precio) values (?,?)" cursor.execute(sql, datos) cone.commit() cone.close() def consulta(self, datos): try: cone=self.abrir() cursor=cone.cursor() sql="select descripcion, precio from articulos where codigo=?" cursor.execute(sql, datos) return cursor.fetchall() finally: cone.close() def recuperar_todos(self): try: cone=self.abrir() cursor=cone.cursor() sql="select codigo, descripcion, precio from articulos" cursor.execute(sql) return cursor.fetchall() finally: cone.close() def baja(self, datos): try: cone=self.abrir() cursor=cone.cursor() sql="delete from articulos where codigo=?" cursor.execute(sql, datos) cone.commit() return cursor.rowcount # retornamos la cantidad de filas borradas except: cone.close() def modificacion(self, datos): try: cone=self.abrir() cursor=cone.cursor() sql="update articulos set descripcion=?, precio=? where codigo=?" cursor.execute(sql, datos) cone.commit() return cursor.rowcount # retornamos la cantidad de filas modificadas except: cone.close()