Módulo de nómina

En el módulo de nómina se van a registrar com entradas todos los pagos recibidos por los clientes y las salidos todo lo relacionado con los pagos de nómina. El código queda de la siguiente manera.
 
  
    
import os
import sys
import sqlite3
import tkinter as tk
from tkinter import messagebox, Entry
from tkinter import ttk
from tkcalendar import DateEntry



def get_db_path():
    # Detect if the application is running as a bundled PyInstaller executable
    if getattr(sys, 'frozen', False):
        # Path where the executable file lives
        return os.path.join(os.path.dirname(sys.executable), "miconoprac.db")
    else:
        # Path during standard Python development execution
        return os.path.join(os.path.dirname(os.path.abspath(__file__)), "miconoprac.db")






def bloquear_cierre():
    pass

def verificar_credenciales():
    db_path = get_db_path()
    email = entry_usuario.get()
    contra = entry_contrasena.get()
    conexion = sqlite3.connect(db_path)
    cursor = conexion.cursor()
    cursor.execute('SELECT * FROM usuario WHERE email = ? AND password = ?', (email,contra))

    usuario_valido = cursor.fetchone()

    conexion.close()



    if  usuario_valido:
        if email == usuario_valido[3] and contra == usuario_valido[4]:
            messagebox.showinfo("Éxito", "¡Inicio de sesión exitoso!")
            ventana.withdraw()
            abrir_ventana_principal()

    else:
        messagebox.showerror("Error", "Usuario o contraseña incorrectos")


def abrir_ventana_principal():
    # Creamos la nueva ventana
    ventana_principal = tk.Toplevel()
    ventana_principal.title("Mi Control de nómina práctico")
    ventana_principal.geometry("400x300")
    barra_menus = tk.Menu()
    menu_usuario = tk.Menu(barra_menus, tearoff=False)
    menu_nomina = tk.Menu(barra_menus, tearoff=False)
    menu_salir = tk.Menu(barra_menus, tearoff=False)
    barra_menus.add_cascade(menu=menu_usuario, label="Usuarios")
    barra_menus.add_cascade(menu=menu_nomina, label="Nómina")
    barra_menus.add_cascade(menu=menu_salir, label="Salir")
    menu_usuario.add_command(label="Registrar Usuarios", command=ventana_usuarios)
    menu_nomina.add_command(label="Registrar Nómina", command=ventana_nomina)
    menu_salir.add_command(label="Salir", command=ventana.destroy)
    # Elementos de la nueva ventana
    tk.Label(ventana_principal, text="MICONOPRAC", font=("Arial", 18)).pack(pady=50)
    ventana_principal.protocol("WM_DELETE_WINDOW", bloquear_cierre)
    ventana_principal.config(menu=barra_menus)

def ventana_usuarios():
    abrir_ventana_usuarios()

def ventana_nomina():
    abrir_ventana_nomina()

def abrir_ventana_usuarios():
    ventana_usuario = tk.Toplevel()
    ventana_usuario.title("Panel Usuarios")
    ventana_usuario.geometry("800x670")
    tk.Label(ventana_usuario, text="Registro de usuarios", fg="black",font=("Comic Sans", 17, "bold"), pady=10).pack()
    marco = tk.LabelFrame(ventana_usuario, text="Información de Usuarios", font=("Comic Sans", 10, "bold"), pady=5)
    marco.config(bd=2)
    marco.pack()
    tk.Label(marco, text="Nombre: ", font=("Comic Sans", 10, "bold")).grid(row=0, column=0,pady=8)
    nombre=tk.Entry(marco,width=25)
    nombre.focus()
    nombre.grid(row=0, column=1, padx=5, pady=8)
    tk.Label(marco, text="Apellido Paterno: ", font=("Comic Sans", 10, "bold")).grid(row=1, column=0, pady=8)
    apellido_paterno = tk.Entry(marco, width=25)
    apellido_paterno.grid(row=1, column=1, padx=5, pady=8)
    tk.Label(marco, text="Apellido Materno: ", font=("Comic Sans", 10, "bold")).grid(row=2, column=0, pady=8)
    apellido_materno = tk.Entry(marco, width=25)
    apellido_materno.grid(row=2, column=1, padx=5, pady=8)
    tk.Label(marco, text="Email: ", font=("Comic Sans", 10, "bold")).grid(row=0, column=2, pady=8)
    email = tk.Entry(marco, width=40)
    email.grid(row=0, column=3, padx=5, pady=8)
    tk.Label(marco, text="Password: ", font=("Comic Sans", 10, "bold")).grid(row=1, column=2, pady=8)
    contra = tk.Entry(marco, width=25)
    contra.grid(row=1, column=3, padx=5, pady=8)
    frame_botones = tk.Frame(ventana_usuario)
    frame_botones.pack()
    tk.Button(frame_botones,command=lambda:[agregar_usuario(nombre.get() ,apellido_paterno.get() ,apellido_materno.get(),email.get(),contra.get(),tree),Limpiar_formulario(nombre,apellido_paterno,apellido_materno,email,contra)], text="REGISTRAR", height=2, width=10, bg="green", fg="white",font=("Comic Sans", 10, "bold")).grid(row=0, column=1, padx=10, pady=15)
    tk.Button(frame_botones,command=lambda:editarUsuario(tree),text="EDITAR", height=2, width=10, bg="gray", fg="white",font=("Comic Sans", 10, "bold")).grid(row=0, column=2, padx=10, pady=15)
    tk.Button(frame_botones,command=lambda:Eliminar_usuario(tree), text="ELIMINAR", height=2, width=10, bg="red", fg="white",font=("Comic Sans", 10, "bold")).grid(row=0, column=3, padx=10, pady=15)

    tree = ttk.Treeview(ventana_usuario, columns=("columna1", "columna2", "columna3", "columna4", "columna5"),show="headings")
    tree.heading("columna1", text='Nombre', anchor="center")
    tree.column("columna1", width=150)
    tree.heading("columna2", text='Apellido Paterno', anchor="center")
    tree.column("columna2", width=100)
    tree.heading("columna3", text='Apellido Materno', anchor="center")
    tree.column("columna3", width=100)
    tree.heading("columna4", text='Email', anchor="center")
    tree.column("columna4", width=150)
    tree.heading("columna5", text='Password', anchor="center")
    tree.column("columna5", width=100)

    tree.pack()
    obtener_usuarios(tree)

def abrir_ventana_nomina():
    ventana_nomina = tk.Toplevel()
    ventana_nomina.title("Panel Nómina")
    ventana_nomina.geometry("800x670")
    tk.Label(ventana_nomina, text="Registro de Nómina", fg="black",font=("Comic Sans", 17, "bold"), pady=10).pack()

    frame_botones = tk.Frame(ventana_nomina)
    frame_botones.pack()
    tk.Button(frame_botones, command=lambda:registrar_pago(tree),text="REGISTRAR PAGO", height=2, width=20, bg="green", fg="white",font=("Comic Sans", 10, "bold")).grid(row=0, column=1, padx=10, pady=15)
    tk.Button(frame_botones,command=lambda:registrar_nomina(tree),text="REGISTRAR NÓMINA", height=2, width=20, bg="black", fg="white",font=("Comic Sans", 10, "bold")).grid(row=0, column=2, padx=10, pady=15)
    tk.Button(frame_botones,command=lambda :Eliminar_registro_balance(tree),text="ELIMINAR REGISTRO", height=2, width=20, bg="red", fg="white",font=("Comic Sans", 10, "bold")).grid(row=0, column=3, padx=10, pady=15)
    scrollbar = ttk.Scrollbar(ventana_nomina, orient=tk.VERTICAL)


    tree = ttk.Treeview(ventana_nomina, columns=( "columna2", "columna3", "columna4", "columna5"),show="tree headings",yscrollcommand=scrollbar.set)
    scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
    tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)


    tree.heading("#0", text='Id Registro', anchor="center")
    tree.column("#0", width=18)
    tree.heading("columna2", text='Concepto', anchor="center")
    tree.column("columna2", width=200)
    tree.heading("columna3", text='Fecha', anchor="center")
    tree.column("columna3", width=20)
    tree.heading("columna4", text='Salida', anchor="center")
    tree.column("columna4", width=40)
    tree.heading("columna5", text='Entrada', anchor="center")
    tree.column("columna5", width=40)

    tree.pack()
    obtener_nomina(tree)








def obtener_usuarios(tree):
    db_path = get_db_path()
    records = tree.get_children()
    for element in records:
        tree.delete(element)
    conexion = sqlite3.connect(db_path)
    cursor = conexion.cursor()
    cursor.execute('SELECT * FROM usuario ')
    usuarios = cursor.fetchall()
    for row in usuarios:
        tree.insert("","end", text="", values=(row[0], row[1], row[2], row[3], row[4]))
    conexion.close()

def Ejecutar_consulta(query, parameters=()):
        db_path = get_db_path()
        with sqlite3.connect(db_path) as conexion:
            cursor=conexion.cursor()
            result=cursor.execute(query,parameters)
            conexion.commit()
        return result


def Ejecutar_consulta_id(query, parameters=()):
    db_path = get_db_path()
    with sqlite3.connect( db_path) as conexion:
        cursor = conexion.cursor()
        cursor.execute(query, parameters)
        id_generado = cursor.lastrowid
        conexion.commit()
    return id_generado

def agregar_usuario(nombre,apellido_paterno,apellido_materno,email,contra,tree):
    if Validar_formulario_completo(nombre,apellido_paterno, email, contra):
        query = 'INSERT INTO usuario VALUES( ?, ?, ?, ?, ?)'
        parameters = (nombre,apellido_paterno,apellido_materno,email,contra)
        Ejecutar_consulta(query, parameters)
        messagebox.showinfo("REGISTRO EXITOSO", f'Usuario registrado: {email}')
        obtener_usuarios(tree)

def Validar_formulario_completo(nombre,apellido_paterno,email,contra):
    if len(nombre) != 0 and len(apellido_paterno) != 0 and len(email) != 0 and len(contra) != 0:
        return True
    else:
        messagebox.showerror("ERROR", "Complete todos los campos del formulario")


def Validar_formulario_pago(concepto,fecha,monto):
    if len(concepto) != 0 and len(fecha) != 0 and len(monto) != 0:
        return True
    else:
        messagebox.showerror("ERROR", "Complete todos los campos del formulario")



def Limpiar_formulario(nombre,apellido_paterno,apellido_materno,email,contra):
    nombre.delete(0,'end')
    apellido_paterno.delete(0,'end')
    apellido_materno.delete(0,'end')
    email.delete(0,'end')
    contra.delete(0,'end')

def Eliminar_usuario(tree):

    seleccion=tree.selection()



    if seleccion:
        item_id = seleccion[0]
        info = tree.item(item_id, "values")
        #print("Valores seleccionados:", info)
        nombre = info[0]
        email = info[3]
        respuesta = messagebox.askquestion("ADVERTENCIA", f"¿Seguro que desea eliminar el usuario: {nombre}?")
        if respuesta == 'yes':
            query = "DELETE FROM usuario WHERE email = ?"
            parameters=(email,)
            Ejecutar_consulta(query,parameters)
            obtener_usuarios(tree)
            messagebox.showinfo('EXITO', f'Usario eliminado: {nombre}')
        else:
            messagebox.showerror('ERROR', f'Error al eliminar el usario: {nombre}')
    else:
        messagebox.showerror("ERROR", "Porfavor selecciona un elemento")


def Eliminar_registro_balance(tree):
    seleccion = tree.selection()
    if seleccion:
        item_id = seleccion[0]
        info = tree.item(item_id, "text")
        print("Valores seleccionados:", info)
        id_registro = info

        respuesta = messagebox.askquestion("ADVERTENCIA", f"¿Seguro que desea eliminar el registro?")
        if respuesta == 'yes'  and id_registro != "":
            query = "DELETE FROM sueldo WHERE id_registro = ?"
            parameters=(id_registro,)
            Ejecutar_consulta(query,parameters)
            query = "DELETE FROM balance WHERE id_registro = ?"
            parameters = (id_registro,)
            Ejecutar_consulta(query, parameters)
            obtener_nomina(tree)
            messagebox.showinfo('EXITO', 'Registro eliminado')
        else:
            messagebox.showerror('ERROR', 'Error al eliminar el registro')
    else:
        messagebox.showerror("ERROR", "Porfavor selecciona un elemento")

def editarUsuario(tree):
    seleccion = tree.selection()
    if seleccion:
        item_id = seleccion[0]
        info = tree.item(item_id, "values")
        nombre = info[0]
        apellido_paterno=info[1]
        apellido_materno=info[2]
        email=info[3]
        contra=info[4]
        modal = tk.Toplevel()
        modal.title("Editar Usuario")
        modal.geometry("700x500")

        # Hacer que la ventana sea modal
        modal.grab_set()
        tk.Label(modal, text="Registro de usuarios", fg="black", font=("Comic Sans", 17, "bold"),
                 pady=10).pack()
        marco = tk.LabelFrame(modal, text="Información de Usuarios", font=("Comic Sans", 10, "bold"), pady=5)
        marco.config(bd=2)
        marco.pack()
        tk.Label(marco, text="Nombre: ", font=("Comic Sans", 10, "bold")).grid(row=0, column=0, pady=8)
        nuevoNombre = tk.Entry(marco,textvariable=tk.StringVar(marco,value=nombre), width=25)
        nuevoNombre.focus()
        nuevoNombre.grid(row=0, column=1, padx=5, pady=8)
        tk.Label(marco, text="Apellido Paterno: ", font=("Comic Sans", 10, "bold")).grid(row=1, column=0, pady=8)
        nuevoApellido_paterno = tk.Entry(marco,textvariable=tk.StringVar(marco,value=apellido_paterno), width=25)
        nuevoApellido_paterno.grid(row=1, column=1, padx=5, pady=8)
        tk.Label(marco, text="Apellido Materno: ", font=("Comic Sans", 10, "bold")).grid(row=2, column=0, pady=8)
        nuevoApellido_materno = tk.Entry(marco,textvariable=tk.StringVar(marco,value=apellido_materno), width=25)
        nuevoApellido_materno.grid(row=2, column=1, padx=5, pady=8)
        tk.Label(marco, text="Email: ", font=("Comic Sans", 10, "bold")).grid(row=0, column=2, pady=8)
        nuevoEmail = tk.Entry(marco,textvariable=tk.StringVar(marco,value=email), width=40,state="readonly")
        nuevoEmail.grid(row=0, column=3, padx=5, pady=8)
        tk.Label(marco, text="Password: ", font=("Comic Sans", 10, "bold")).grid(row=1, column=2, pady=8)
        nuevoContra = tk.Entry(marco,textvariable=tk.StringVar(marco,value=contra), width=25)
        nuevoContra.grid(row=1, column=3, padx=5, pady=8)

        frame_botones = tk.Frame(modal)
        frame_botones.pack()
        tk.Button(frame_botones, command=modal.destroy,text="Cancelar", height=2, width=10, bg="green", fg="white", font=("Comic Sans", 10, "bold")).grid(row=0, column=1, padx=10, pady=15)
        tk.Button(frame_botones,command=lambda:Actualizar_usuario(nuevoNombre.get(),nuevoApellido_paterno.get(),nuevoApellido_materno.get(),nuevoEmail.get(),nuevoContra.get(),tree,modal), text="ACTUALIZAR USUARIO", height=2, width=30, bg="gray", fg="white",
                  font=("Comic Sans", 10, "bold")).grid(row=0, column=2, padx=10, pady=15)









    else:
        messagebox.showerror("ERROR", "Porfavor selecciona un elemento")



def Actualizar_usuario(nuevoNombre,nuevoApellido_paterno,nuevoApellido_materno,nuevoEmail,nuevoContra,tree,modal):
    if Validar_formulario_completo(nuevoNombre,nuevoApellido_paterno,nuevoEmail,nuevoContra):
        query='Update usuario SET nombre=?,apellido_paterno=?,apellido_materno=?,password=? WHERE email=?'
        parameters=(nuevoNombre,nuevoApellido_paterno,nuevoApellido_materno,nuevoContra,nuevoEmail)
        Ejecutar_consulta(query,parameters)
        messagebox.showinfo('EXITO', f'Usuario actualizado:{nuevoNombre}')
        modal.destroy()
        obtener_usuarios(tree)



def registrar_pago(tree):
    modal = tk.Toplevel()
    modal.title("Registrar Pago")
    modal.geometry("700x500")
    modal.grab_set()
    validatecommand = modal.register(is_valid_char)
    tk.Label(modal, text="Registro de Pago", fg="black", font=("Comic Sans", 17, "bold"),
             pady=10).pack()
    marco = tk.LabelFrame(modal, text="Información de Pago", font=("Comic Sans", 10, "bold"), pady=5)
    marco.config(bd=2)
    marco.pack()
    tk.Label(marco, text="Concepto: ", font=("Comic Sans", 10, "bold")).grid(row=0, column=0, pady=8)
    concepto = tk.Entry(marco, width=50)
    concepto.focus()
    concepto.grid(row=0, column=1, padx=5, pady=8)
    tk.Label(marco, text="Fecha: ", font=("Comic Sans", 10, "bold")).grid(row=1, column=0, pady=8)
    fecha = DateEntry(marco, width=12, background='darkblue',foreground='white', borderwidth=2, date_pattern='dd/mm/yyyy')
    fecha.grid(row=1, column=1, padx=5, pady=8)
    tk.Label(marco, text="Monto: ", font=("Comic Sans", 10, "bold")).grid(row=2, column=0, pady=8)
    monto = tk.Entry(marco, width=25,validate="key", validatecommand=(validatecommand, "%S"))
    monto.grid(row=2, column=1, padx=5, pady=8)
    frame_botones = tk.Frame(modal)
    frame_botones.pack()
    tk.Button(frame_botones, command=modal.destroy, text="Cancelar", height=2, width=10, bg="green", fg="white",
              font=("Comic Sans", 10, "bold")).grid(row=0, column=1, padx=10, pady=15)
    tk.Button(frame_botones, command=lambda: agregar_pago(concepto.get(),fecha.get(),monto.get(), tree, modal),
              text="Ingresar Pago", height=2, width=30, bg="gray", fg="white",
              font=("Comic Sans", 10, "bold")).grid(row=0, column=2, padx=10, pady=15)


def registrar_nomina(tree):
    modal = tk.Toplevel()
    modal.title("Registrar Nómina")
    modal.geometry("800x500")
    validatecommand = modal.register(is_valid_char)
    modal.grab_set()
    tk.Label(modal, text="Registro de Nómina", fg="black", font=("Comic Sans", 17, "bold"),
             pady=10).pack()
    marco = tk.LabelFrame(modal, text="Información de Nómina", font=("Comic Sans", 10, "bold"), pady=5)
    marco.config(bd=2)
    marco.pack()
    tk.Label(marco, text="Fecha: ", font=("Comic Sans", 10, "bold")).grid(row=0, column=0, pady=8)
    fecha = DateEntry(marco, width=12, background='darkblue', foreground='white', borderwidth=2,date_pattern='dd/mm/yyyy')
    fecha.grid(row=0, column=1, padx=5, pady=8)
    tk.Label(marco, text="Agregar Empleados: ", font=("Comic Sans", 10, "bold")).grid(row=1, column=0, pady=8)
    tk.Label(marco, text="Nombre Empleado: ", font=("Comic Sans", 10, "bold")).grid(row=2, column=0, pady=8)
    tk.Label(marco, text="Monto: ", font=("Comic Sans", 10, "bold")).grid(row=2, column=1, pady=8)
    concepto = tk.Entry(marco, width=50)
    concepto.grid(row=3, column=0, padx=5, pady=8)
    monto = tk.Entry(marco, width=25,validate="key", validatecommand=(validatecommand, "%S"))
    monto.grid(row=3, column=1, padx=5, pady=8)
    tk.Button(marco,command=lambda:agregar_empleado(concepto.get(),monto.get(),tree2,concepto,monto),text="Agregar Empleado", height=2, width=20, bg="green", fg="white",font=("Comic Sans", 10, "bold")).grid(row=3, column=2, padx=10, pady=15)
    tree2 = ttk.Treeview(modal, columns=("Columna1","Columna2"), show="headings")
    tree2.heading("Columna1", text="Nombre Empleado:")
    tree2.column("Columna1", width=250, anchor="w")
    tree2.heading("Columna2", text="Monto:")
    tree2.column("Columna2", width=250, anchor="w")



    # 4. Crear Scrollbar vertical
    scrollbar = ttk.Scrollbar(modal, orient="vertical", command=tree2.yview)

    # Vincular Treeview y Scrollbar
    tree2.configure(yscrollcommand=scrollbar.set)

    # Empaquetar elementos dentro del frame
    tree2.pack(side="left", expand=True, fill="both")
    scrollbar.pack(side="right", fill="y")
    tk.Button(modal,command=lambda :agregar_nomina(fecha.get(),tree,tree2,modal),text="Agregar Nómina", height=2, width=20, bg="red", fg="white",font=("Comic Sans", 10, "bold")).pack()

def agregar_empleado(empleado,monto,tree2,entrada,entrada1):
    if len(empleado) != 0  and len(monto) != 0:
        tree2.insert("", "end", values=(empleado, monto))
        entrada.delete(0, tk.END)
        entrada1.delete(0, tk.END)

    else:
        messagebox.showerror("ERROR", "Complete todos los campos del formulario  de empleado")



def is_valid_char(char):
    return char in "0123456789."






def agregar_pago(concepto,fecha,monto,tree,modal):
    if Validar_formulario_pago(concepto,fecha,monto):
        monto_real=float(monto)
        query = 'INSERT INTO balance (concepto,fecha,monto,tipo_movimiento) VALUES( ?, ?, ?, ?)'
        parameters = (concepto,fecha,monto_real,'pago')
        Ejecutar_consulta(query, parameters)
        messagebox.showinfo("REGISTRO EXITOSO")
        modal.destroy()
        obtener_nomina(tree)

def agregar_nomina(fecha,tree,tree2,modal):
    monto=0;
    if len(tree2.get_children()) > 0:
        for hijo in tree2.get_children():
            fila_valores = tree2.item(hijo, 'values')
            monto=monto+float(fila_valores[1])

        query = 'INSERT INTO balance (concepto,fecha,monto,tipo_movimiento) VALUES( ?, ?, ?, ?)'
        parameters = ("Pago de nómina", fecha, monto, 'nomina')
        id_balance=Ejecutar_consulta_id(query, parameters)
        for hijo in tree2.get_children():
            fila_valores = tree2.item(hijo, 'values')
            query = 'INSERT INTO sueldo (id_registro,nombre_empleado,sueldo) VALUES( ?, ?, ?)'
            parameters = (id_balance,fila_valores[0],float(fila_valores[1]))
            Ejecutar_consulta(query, parameters)

        messagebox.showinfo("REGISTRO EXITOSO")
        modal.destroy()
        obtener_nomina(tree)
    else:
        messagebox.showerror("ERROR", "Debes agregar empleados")


def obtener_nomina(tree):
    records = tree.get_children()
    total_nomina=0
    total_pagos=0
    for element in records:
        tree.delete(element)
    conexion = sqlite3.connect('miconoprac.db')
    cursor = conexion.cursor()
    cursor.execute('SELECT * FROM balance ')
    balance = cursor.fetchall()
    for row in balance:
        if(row[4] == 'pago'):
            precio_entrada = f"${row[3]:,.2f}"
            tree.insert("","end", text=row[0], values=(row[1], row[2],"",precio_entrada))
            total_pagos=total_pagos+row[3]
        else:
            precio_salida=f"${row[3]:,.2f}"
            subtabla=tree.insert(parent="",index="end", text=row[0], values=( row[1], row[2],precio_salida,""))
            total_nomina=total_nomina+row[3]
            cursor.execute('SELECT * FROM sueldo WHERE id_registro = ? ',(row[0],))
            sueldo = cursor.fetchall()
            for empleado in sueldo:
                sueldo_empleado=f"${empleado[3]:,.2f}"
                tree.insert(parent=subtabla, index="end", text="",values=(empleado[2],row[2],sueldo_empleado,""))
    total_balance=total_pagos-total_nomina
    tree.insert("",index="end", text="",values=(f"Total pagos-nómina = ${total_balance:,.2f}","",f"${total_nomina:,.2f}",f"${total_pagos:,.2f}"), tags=("footer",))
    tree.tag_configure("footer", background="#e0e0e0")

    cursor.close()
    conexion.close()








        # Configuración de la ventana principal
ventana = tk.Tk()
ventana.title("Inicio de sesión")
ventana.geometry("400x500")
ventana.eval('tk::PlaceWindow . center') # Centra la ventana en la pantalla

# Etiquetas y campos de entrada
tk.Label(ventana, text="Email:").pack(pady=10)
entry_usuario = tk.Entry(ventana,width=30)
entry_usuario.pack()

tk.Label(ventana, text="Contraseña:").pack(pady=10)
entry_contrasena = tk.Entry(ventana, show="*",width=30) # El 'show' oculta los caracteres
entry_contrasena.pack()

# Botón de Login
tk.Button(ventana, text="Iniciar Sesión", command=verificar_credenciales).pack(pady=20)

# Bucle principal para mantener la ventana abierta
ventana.mainloop()
	  
	  
	  
    
   

Probando Aplicación

Corremos el código quedando de la siguiente manera:

Crear Ejecutable

Abrimos el cmd de windows y ejecutamos el siguiente comando:
 
  
    
    
    pip install pyinstaller
    
      
   

phyton project
Nos vamos a pycharm y abrimos una terminal
phyton project
Ejecutamos el siguiente comando:
 
  
    
	  pyinstaller --onefile --windowed --hidden-import=babel.numbers  --add-data "miconoprac.db;." miconoprac.py
	  
  
   

phyton project
En la carpeta dist se genera el ejecutable phyton project
Pegamos nuestra base de datos creada en el directorio donde se genero el ejecutable
Creamos un acceso directo de nuestro ejecutable yo lo voy a pasar a mi escritorio
phyton project
phyton project
Probamos