Foros Python

Versión completa: BASE DATOS PYTHON TKINTER
Actualmente estas viendo una versión simplificada de nuestro contenido. Ver la versión completa con el formato correcto.
Saludos. Alguien puede corregir el código de tal manera que luego de eliminar un registro éste cambio sea permanente??? Luego de eliminar y abrir la BD el registro reaparece.
No hay manera de lograrlo. ME dicen que por el ID pero modifico para que lo muestre y me desajusta lo demás como guardar un nuevo registro.

import tkinter as tk
from tkinter import ttk, messagebox
import sqlite3
import re
from datetime import datetime, timedelta
def conectar_db():
    conexion = sqlite3.connect('mi_base_de_datos3.db')
    cursor = conexion.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS registros (
            ID INTEGER PRIMARY KEY AUTOINCREMENT,
            REG INT,
            GBT INT,
            CMF INT,
            HOGAR TEXT,
            CPAT_DPAT TEXT,
            CI TEXT,
            NOMBRE TEXT,
            APELLIDO1 TEXT,
            APELLIDO2 TEXT,
            EDAD INTEGER,
            CALLE TEXT,
            No_KM TEXT,
            BARRIO_O_REPARTO TEXT,
            USD TEXT,
            FUM_FUSD DATE,
            FPP DATE,
            SEM INTEGER,
            DIAS INTEGER,
            EG TEXT,
            Trim TEXT,
            CLASIF TEXT,
            FACTORES_DE_RIESGO TEXT,
            OBSERVACIONES TEXT
        )
    ''')
    conexion.commit()
    conexion.close()
def calcular_edad(fecha_nacimiento):
    fecha_actual = datetime.now()
    edad = fecha_actual.year - fecha_nacimiento.year - ((fecha_actual.month, fecha_actual.day) < (fecha_nacimiento.month, fecha_nacimiento.day))
    return edad
class Aplicacion:
    def __init__(self, root):
        self.root = root
        self.root.title("Gestión de Base de Datos")
        self.root.state('zoomed')
        conectar_db()
       
        # Configuración de estilos para el Treeview
        style = ttk.Style(root)
        style.configure("RET.Treeview", background="light blue", foreground="black")
        style.configure("PRO.Treeview", background="light blue", foreground="black")
        style.configure("HOS.Treeview", background="yellow", foreground="black")
        style.configure("NEC.Treeview", background="light green", foreground="black")
        style.configure("PRE.Treeview", background="light grey", foreground="black")
       
        self.campos = ['REG', 'GBT', 'CMF', 'HOGAR', 'CPAT_DPAT', 'CI', 'NOMBRE', 'APELLIDO1', 'APELLIDO2', 'EDAD', 'CALLE', 'No_KM', 'BARRIO_O_REPARTO', 'USD', 'FUM_FUSD', 'FPP', 'SEM', 'DIAS', 'EG', 'Trim', 'CLASIF', 'FACTORES_DE_RIESGO', 'OBSERVACIONES']
        self.entries = {}
        frame_inputs = tk.Frame(root)
        frame_inputs.grid(row=0, column=0, sticky="nsew")
       
        for i, campo in enumerate(self.campos):
            label = tk.Label(frame_inputs, text=campo)
            entry = tk.Entry(frame_inputs, width=17, justify=tk.CENTER)  # Ajusta el ancho según sea necesario
            if i < len(self.campos) / 2:  # Posicionar en la primera fila
                label.grid(column=i, row=0, padx=5, pady=5)
                entry.grid(column=i, row=1, padx=5, pady=5)
            else:  # Posicionar en la segunda fila
                label.grid(column=i - len(self.campos) // 2, row=2, padx=5, pady=5)
                entry.grid(column=i - len(self.campos) // 2, row=3, padx=5, pady=5)
            self.entries[campo] = entry
       
       # Crear el Label para el Entry de filtrado por CMF
        label_filtrar_cmf = tk.Label(frame_inputs, text="Filtrar CMF", font=('Arial', 8, 'bold'))
        label_filtrar_cmf.grid(row=3, column=0, padx=5, pady=5, sticky='w')
       
        # Crear el Entry para filtrar por CMF
        self.entry_filtrar_cmf = tk.Entry(frame_inputs, width=5)
        self.entry_filtrar_cmf.grid(row=4, column=0, padx=5, pady=5)
        self.entry_filtrar_cmf.bind("<KeyRelease>", self.filtrar_por_cmf)
         
        self.boton_ordenar_hogar = tk.Button(frame_inputs, text="Ordenar por HOGAR", command=self.ordenar_por_hogar)
        self.boton_ordenar_hogar.grid(row=len(self.campos) + 1, column=2, pady=10)
       
        self.boton_ordenar_gbt = tk.Button(frame_inputs, text="Ordenar por GBT", command=self.ordenar_por_gbt)
        self.boton_ordenar_gbt.grid(row=len(self.campos) + 1, column=3, pady=10)
       
        self.boton_ordenar_cmf = tk.Button(frame_inputs, text="Ordenar por CMF", command=self.ordenar_por_cmf)
        self.boton_ordenar_cmf.grid(row=len(self.campos) + 1, column=4, pady=10)
       
        self.boton_agregar = tk.Button(frame_inputs, text="Agregar Registro", command=self.agregar_registro)
        self.boton_visualizar = tk.Button(frame_inputs, text="Visualizar Registros", command=self.visualizar_registros)
        self.boton_eliminar = tk.Button(frame_inputs, text="Eliminar Registro", command=self.eliminar_registro)
       
        self.boton_agregar.grid(row=len(self.campos)+1, column=0, pady=10)
        self.boton_visualizar.grid(row=len(self.campos)+1, column=11, pady=10)
        self.boton_eliminar.grid(row=len(self.campos)+1, column=1, pady=10)
    #....................................................................................    
       
        frame_table = tk.Frame(root)
        frame_table.grid(row=1, column=0, sticky="nsew")
        self.tabla = ttk.Treeview(frame_table, columns=self.campos, show="headings")
        for campo in self.campos:
            self.tabla.heading(campo, text=campo)
            self.tabla.column(campo, width=60, minwidth=50)
        self.tabla.grid(row=0, column=0, sticky="nsew")
        frame_table.grid_rowconfigure(0, weight=1)
        frame_table.grid_columnconfigure(0, weight=1)
        root.rowconfigure(1, weight=1)
    #....................................................................................  
       
        self.entries['CI'].bind("<KeyRelease>", self.calcular_edad_evento)
        self.entries['CI'].bind("<FocusOut>", self.validar_ci_evento)
        self.entries['FUM_FUSD'].bind("<FocusOut>", self.validar_fecha_fum_fusd)
        self.entries['FUM_FUSD'].bind("<KeyRelease>", self.actualizar_datos_gestacionales)
        self.entries['DIAS'].bind("<KeyRelease>", self.validar_dias)
        self.entries['SEM'].bind("<KeyRelease>", self.actualizar_datos_gestacionales)
       
        self.visualizar_registros()
        self.ordenar_por_cmf()
       
    #....................................................................................
    def agregar_registro(self):
        if not self.validar_campos() or not self.validar_factores_de_riesgo():
            return
        self.actualizar_datos_gestacionales()  # Asegurarse de que FPP, EG y Trim estén actualizados
        valores = {campo: self.entries[campo].get() for campo in self.entries}
        with sqlite3.connect('mi_base_de_datos3.db') as conexion:
            cursor = conexion.cursor()
            campos = ', '.join(valores.keys())
            placeholders = ', '.join(['?']*len(valores))
            sql = f'INSERT INTO registros ({campos}) VALUES ({placeholders})'
            cursor.execute(sql, list(valores.values()))
            conexion.commit()
        self.visualizar_registros()
    #....................................................................................
   
    def eliminar_registro(self):
        selected_item = self.tabla.selection()
        if not selected_item:
            messagebox.showinfo("Error", "No hay ningún registro seleccionado")
            return
        id = self.tabla.item(selected_item[0])['values'][0]
        try:
            with sqlite3.connect('mi_base_de_datos3.db') as conexion:
                cursor = conexion.cursor()
                cursor.execute('DELETE FROM registros WHERE ID=?', (id,))
                conexion.commit()
            self.tabla.delete(selected_item[0])
        except Exception as e:
            messagebox.showerror("Error", f"No se pudo eliminar el registro: {str(e)}")
   
    #....................................................................................
   
    def filtrar_por_cmf(self, event=None):
        valor_filtro = self.entry_filtrar_cmf.get().strip()
        if valor_filtro:
            try:
                valor_filtro = int(valor_filtro)
                # Obtener los registros filtrados por CMF
                with sqlite3.connect('mi_base_de_datos3.db') as conexion:
                    cursor = conexion.cursor()
                    cursor.execute("SELECT * FROM registros WHERE CMF=?", (valor_filtro,))
                    rows = cursor.fetchall()
                    self.actualizar_vista_treeview(rows)
            except ValueError:
                messagebox.showerror("Error", "El valor para filtrar por CMF debe ser un número entero")
        else:
            # Si el Entry está vacío, mostrar todos los registros
            self.visualizar_registros()
   
    #....................................................................................
   
    def ordenar_por_hogar(self):
        # Obtener los datos y ordenarlos
        with sqlite3.connect('mi_base_de_datos3.db') as conexion:
            cursor = conexion.cursor()
            cursor.execute("SELECT * FROM registros ORDER BY HOGAR")
            rows = cursor.fetchall()
            self.actualizar_vista_treeview(rows)
    #....................................................................................
    def ordenar_por_gbt(self):
        # Obtener los datos y ordenarlos
        with sqlite3.connect('mi_base_de_datos3.db') as conexion:
            cursor = conexion.cursor()
            cursor.execute("SELECT * FROM registros ORDER BY GBT")
            rows = cursor.fetchall()
            self.actualizar_vista_treeview(rows)
    #....................................................................................
    def ordenar_por_cmf(self):
        # Obtener los datos y ordenarlos
        with sqlite3.connect('mi_base_de_datos3.db') as conexion:
            cursor = conexion.cursor()
            cursor.execute("SELECT * FROM registros ORDER BY CMF")
            rows = cursor.fetchall()
            self.actualizar_vista_treeview(rows)
   
    #....................................................................................
   
    def actualizar_vista_treeview(self, rows):
        # Limpiar el Treeview antes de rellenarlo
        for i in self.tabla.get_children():
            self.tabla.delete(i)
        # Insertar los nuevos datos ordenados
        for row in rows:
            estilo = ''
            if row[4] == 'RET':  # Suponiendo que 'HOGAR' es el quinto elemento en la fila
                estilo = 'RET.Treeview'
            elif row[4] == 'PRO':
                estilo = 'PRO.Treeview'
            elif row[4] == 'HOS':
                estilo = 'HOS.Treeview'
            elif row[4] == 'NEC':
                estilo = 'NEC.Treeview'
            elif row[4] == 'PRE':
                estilo = 'PRE.Treeview'
            self.tabla.insert('', 'end', values=row[1:], tags=estilo)
            if estilo:
                self.tabla.tag_configure(estilo, background=('light blue' if row[4] == 'RET' else
                                              'yellow' if row[4] == 'HOS' else
                                              'light green' if row[4] == 'NEC' else
                                              'light grey' if row[4] == 'PRE' else
                                              'light blue' if row[4] == 'PRO' else
                                              'black'))
   
    #....................................................................................
   
    def actualizar_datos_gestacionales(self, event=None):
        if self.validar_fecha(self.entries['FUM_FUSD'].get().strip()):
            self.actualizar_fpp()
            self.actualizar_eg()
            # New: Calculate and update trimester based on EG
            eg = float(self.entries['EG'].get().strip() or 0)
            self.calcular_trim(eg)
    #....................................................................................
    def actualizar_fpp(self):
        fpp = self.calcular_fpp()
        if fpp:
            self.entries['FPP'].delete(0, tk.END)
            self.entries['FPP'].insert(0, fpp)
    #....................................................................................
    def actualizar_eg(self):
        fecha_fum_fusd = self.entries['FUM_FUSD'].get().strip()
        dias_adicionales = int(self.entries['DIAS'].get().strip() or 0)
        semanas_adicionales = int(self.entries['SEM'].get().strip() or 0) * 7
        if self.validar_fecha(fecha_fum_fusd):
            fecha_fum_fusd = datetime.strptime(fecha_fum_fusd, "%Y-%m-%d")
            total_dias = (datetime.now() - fecha_fum_fusd).days + dias_adicionales + semanas_adicionales
            semanas = total_dias // 7
            dias = total_dias % 7
            self.entries['EG'].delete(0, tk.END)
            self.entries['EG'].insert(0, f"{semanas}.{dias}")
            self.calcular_trim(semanas + dias / 7)  # Update Trim based on the new EG        
    #....................................................................................
   
    def calcular_edad_evento(self, event):
        ci = self.entries['CI'].get().strip()
        if len(ci) >= 6:
            try:
                fecha_nacimiento = datetime.strptime(ci[:6], "%y%m%d")
                edad = calcular_edad(fecha_nacimiento)
                self.entries['EDAD'].delete(0, tk.END)
                self.entries['EDAD'].insert(0, str(edad))
            except ValueError:
                pass
   
    #....................................................................................
    def calcular_fpp(self):
        try:
            fecha_fum_fusd = datetime.strptime(self.entries['FUM_FUSD'].get(), "%Y-%m-%d")
            dias = int(self.entries['DIAS'].get() or 0)
            sem = int(self.entries['SEM'].get() or 0)
            fpp = fecha_fum_fusd + timedelta(days=280 - (dias + sem*7))
            return fpp.strftime("%Y-%m-%d")
        except ValueError:
            return ""
    #....................................................................................
    def calcular_trim(self, eg):
        """Calculates and updates the trimester based on gestational age."""
        if eg <= 12.6:
            trim = 'I'
        elif 13 <= eg <= 22.6:
            trim = 'II'
        elif eg >= 23:
            trim = 'III'
        else:
            trim = ''  # In case of an out-of-range or invalid EG value
        self.entries['Trim'].delete(0, tk.END)
        self.entries['Trim'].insert(0, trim)
    #....................................................................................
   
    def visualizar_registros(self):
        for i in self.tabla.get_children():
            self.tabla.delete(i)
        with sqlite3.connect('mi_base_de_datos3.db') as conexion:
            cursor = conexion.cursor()
            cursor.execute("SELECT * FROM registros")
            rows = cursor.fetchall()
            for row in rows:
                estilo = ''
                if row[4] == 'RET':  # Suponiendo que 'HOGAR' es el quinto elemento en la fila
                    estilo = 'RET.Treeview'
                elif row[4] == 'PRO':
                    estilo = 'PRO.Treeview'
                elif row[4] == 'HOS':
                    estilo = 'HOS.Treeview'
                elif row[4] == 'NEC':
                    estilo = 'NEC.Treeview'
                elif row[4] == 'PRE':
                    estilo = 'PRE.Treeview'
                self.tabla.insert('', 'end', values=row[1:], tags=estilo)
                if estilo:
                    self.tabla.tag_configure(estilo, background=('light blue' if row[4] == 'RET' else
                                              'yellow' if row[4] == 'HOS' else
                                              'light green' if row[4] == 'NEC' else
                                              'light grey' if row[4] == 'PRE' else
                                              'light blue' if row[4] == 'PRO' else
                                              'black'))
                   
    #....................................................................................
    def validar_ci_evento(self, event):
        ci = self.entries['CI'].get().strip()
        if not self.validar_ci(ci):
            messagebox.showerror("Error de Validación", "El campo CI debe tener exactamente 11 dígitos y el penúltimo debe ser impar")
            self.entries['CI'].delete(0, tk.END)
            self.entries['CI'].focus_set()
    #....................................................................................
    def validar_ci(self, ci):
        if len(ci) == 11 and ci.isdigit():
            penultimo_digito = int(ci[-2])
            return penultimo_digito % 2 != 0
        return False
   
    #....................................................................................
    def validar_dias(self, event=None):
        dias = self.entries['DIAS'].get().strip()
        if dias:  # Si hay algún valor, verifica que sea adecuado
            if not dias.isdigit() or not (1 <= int(dias) <= 6):
                messagebox.showerror("Error de Validación", "El campo DIAS debe estar en blanco o ser un número entre 1 y 6")
                self.entries['DIAS'].delete(0, tk.END)  # Borra el contenido incorrecto
                self.entries['DIAS'].focus_set()  # Devuelve el enfoque al campo DIAS
            else:
                # Si el valor es válido, actualizamos datos gestacionales.
                self.actualizar_datos_gestacionales()
        else:
            # También actualizar si el campo se deja en blanco, lo que puede cambiar los cálculos.
            self.actualizar_datos_gestacionales()
   
    #....................................................................................
    def validar_factores_de_riesgo(self):
        clasif = self.entries['CLASIF'].get().strip()
        factores_de_riesgo = self.entries['FACTORES_DE_RIESGO'].get().strip()
        if clasif == 'Normal' and factores_de_riesgo:
            messagebox.showerror("Error de Validación", "Si CLASIF es 'Normal', el campo FACTORES_DE_RIESGO debe estar vacío.")
            self.entries['FACTORES_DE_RIESGO'].focus_set()
            return False
        elif clasif in ['ARO', 'BRO', 'CAV'] and not factores_de_riesgo:
            messagebox.showerror("Error de Validación", "Si CLASIF es 'ARO', 'BRO' o 'CAV', el campo FACTORES_DE_RIESGO no puede estar vacío.")
            self.entries['FACTORES_DE_RIESGO'].focus_set()
            return False
        return True
   
    #....................................................................................
    def validar_fecha_fum_fusd(self, event):
        fecha = self.entries['FUM_FUSD'].get().strip()
        if fecha:
            if not self.validar_fecha(fecha):
                messagebox.showerror("Error", "El campo FUM_FUSD debe tener el formato YYYY-MM-DD")
                self.entries['FUM_FUSD'].delete(0, tk.END)
                self.entries['FUM_FUSD'].focus_set()
    #....................................................................................
    def validar_fecha(self, fecha):
        patron = r'\d{4}-\d{2}-\d{2}'
        return re.match(patron, fecha)
    #....................................................................................
    def validar_clasif(self, clasif):
        permitidos = ['Normal', 'ARO', 'BRO', 'CAV']
        return clasif in permitidos
    #....................................................................................
    def validar_campos(self):
        campos_obligatorios = ['GBT', 'CMF', 'CI', 'NOMBRE', 'APELLIDO1', 'APELLIDO2', 'CALLE', 'No_KM', 'BARRIO_O_REPARTO', 'FUM_FUSD', 'CLASIF']
        for campo in campos_obligatorios:
            if not self.entries[campo].get().strip():
                messagebox.showerror("Error", f"El campo {campo} no puede estar vacío")
                self.entries[campo].focus_set()
                return False
            if campo == 'CLASIF' and not self.validar_clasif(self.entries['CLASIF'].get().strip()):
                messagebox.showerror("Error de Validación", "El campo CLASIF debe ser uno de los siguientes: Normal, ARO, BRO, CAV")
                self.entries['CLASIF'].delete(0, tk.END)
                self.entries['CLASIF'].focus_set()
                return False
        return True                
           
root = tk.Tk()
app = Aplicacion(root)
root.mainloop()
Buenas ocniel. Hagamos una prueba:

Luego de esta línea "id = self.tabla.item(selected_item[0])['values'][0]", ponele a continuación un "print(id)".

Y en este módulo:

with sqlite3.connect('mi_base_de_datos3.db') as conexion:
cursor = conexion.cursor()
cursor.execute('DELETE FROM registros WHERE ID=?', (id,))
conexion.commit()
agregale un mensaje de showinfo "messagebox.showinfo("xx", "xx")"

A ver qué ocurre. Si es que está tomando valor de id (en la primer prueba), y si está ejecutando (debería hacerlo) el delete en el segundo caso. Con respecto a esto último, probá modificando la sentencia a 'DELETE * FROM registros WHERE ID=?', (id,).
Luego me cuentas.
Hola Diego, gracias. Era el ID oculto, ya lo solucioné.

Gracias por todo.



(02-05-2024, 10:42 AM)Diego escribió: [ -> ]Buenas ocniel. Hagamos una prueba:

Luego de esta línea "id = self.tabla.item(selected_item[0])['values'][0]", ponele a continuación un "print(id)".

Y en este módulo:

            with sqlite3.connect('mi_base_de_datos3.db') as conexion:
                cursor = conexion.cursor()
                cursor.execute('DELETE FROM registros WHERE ID=?', (id,))
                conexion.commit()
agregale un mensaje de showinfo "messagebox.showinfo("xx", "xx")"

A ver qué ocurre. Si es que está tomando valor de id (en la primer prueba), y si está ejecutando (debería hacerlo) el delete en el segundo caso. Con respecto a esto último, probá modificando la sentencia a 'DELETE * FROM registros WHERE ID=?', (id,).
Luego me cuentas.