1

Este es el problema...

Estoy realizando un GUI en tkinter donde tengo una clase en la que se reciben datos generales de un usuario a través de un Entry con un par de funciones:

  1. Comprobar que todas las letras sean mayúsculas.
  2. Sacar la edad a través de la fecha de nacimiento y comprobar que solo se introduzcan caracteres numéricos.
  3. Validar que la información introducida sea correcta (en caso afirmativo pasa a la siguiente clase)

Finalmente paso la información como argumentos a otra función para usarla mas adelante.

class PaginaDatos(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        
        #Funcion para corroborar que se ingresen caracteres alfabéticos en el nombre y que sean en mayúsculas. 
        def validar_mayuscula(nomApAm_mayuscula):
           pass

        #Funcion para corrobra el ingreso de la fecha de nacimiento con datos numéricos y con 8 dígitos a lo máximo.
        def edad_por_nacimiento(f_nacimiento):
           pass

        #Funcion para corroborar que los datos introducidos sean correctos. 
        def corroborar_datos(self):
            nomEnt= nombreEntry.get()
            apPatEnt= apellPaterno.get()
            apMatEnt= self.apellMaterno.get()

            #Aqui va codigo para corroborar datos...
            #Si todo esta correcto entonces:
            self.obtener_informacion(opcionNombre, opcionApPat, opcionApMat)


        opcionNombre= tk.StringVar()
        tk.Label(self, text= "Nombre").grid(row=1, column=0, sticky="e")
        nombreEntry= tk.Entry(self,
                              textvariable=opcionNombre, 
                              validate="key",
                              validatecommand=(self.register(validar_mayuscula), "%P"))
        nombreEntry.grid(row=1, column=2)
        
        self.opcionApPat= tk.StringVar()
        tk.Label(self, text= "Apellido Paterno").grid(row=2, column=0, sticky="e")
        apellPaterno= tk.Entry(self,
                               textvariable=opcionApPat,
                               validate="key",
                               validatecommand=(self.register(validar_mayuscula), "%P"))
        apellPaterno.grid(row=2, column=2)

        opcionApMat= tk.StringVar()
        tk.Label(self, text= "Apellido Materno").grid(row=3, column=0, sticky="e")
        self.apellMaterno= tk.Entry(self,
                               textvariable=opcionApMat, 
                               validate="key",
                               validatecommand=(self.register(validar_mayuscula), "%P"))
        self.apellMaterno.grid(row=3, column=2)
        
        botonGuardarDatos= tk.Button(self, 
                                     text="Guardar",
                                    command=lambda: corroborar_datos(self))
        botonGuardarDatos.grid(row=7, column=0, columnspan=3)
        

    def obtener_informacion(self, nombre, aP, aM):
        self.nombre= nombre
        self.apPa= aP
        self.apMa= aM

Posteriormente...

  1. Creo una instancia de la clase anterior
  2. Accedo a los atributos de la función de los datos introducidos.
  3. Creo unos Labels donde deben aparecer los datos introducidos anteriormente pero lo que me pone es PY_VAR y un numero.
class PaginaConciencia(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        tk.Frame.configure(self)

        datos= PaginaDatos(master)
        
        nuevoApPat= datos.opcionApPat.get()
        nuevoApMat= datos.apellMaterno.get()

        titulo= tk.Label(self, 
                         text= "Pagina Ejemplo")
        titulo.grid(row=0, column=0, columnspan=3)
        
        #tk.Label(self, text= datos.opcionNombre).grid(row=1, column=0) De esta manera es como aparecían los PY_VAR
        tk.Label(self, textvariable=(f"Nombre: {datos.nombre}")).grid(row=1, column=0)
        tk.Label(self, textvariable=(f"Apellido Paterno: {nuevoApPat}")).grid(row=1, column=1)
        tk.Label(self, textvariable=(f"Apellido Materno: {nuevoApMat}")).grid(row=1, column=3)

Actualización No.1

Agregue los tres métodos que me tratado de implementar hasta ahora...

  1. Crear una función en PaginaDatos para obtener la información como argumentos y después llamar a los atributos de este (vemos este método al intentar obtener la información del nombre)
  2. Añadir self a la variable asociada al Entry, o sea el StringVar en este caso, crear otra variable en PaginaConciencia con la un get de dicha variable (vemos este método al intentar obtener la información del apellido paterno)
  3. Añadir self a la variable que guarda el Entry, crear otra variable en PaginaConciencia con un get de dicha variable (vemos este método al intentar obtener la información de apellido materno).

Actualizacion No.2

class PaginaDatos(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        tituloDatos= tk.Label(self, text= "Datos")
        tituloDatos.grid(row=0, column=0, columnspan=3)

        self.opcionNombre= tk.StringVar()
        tk.Label(self, text= "Nombre").grid(row=1, column=0, sticky="e")
        self.nombreEntry= tk.Entry(self,
                              textvariable=self.opcionNombre, 
                              validate="key",
                              validatecommand=(self.register(self.validar_mayuscula), "%P"))
        self.nombreEntry.grid(row=1, column=2)

        self.opcionApPat= tk.StringVar()
        tk.Label(self, text= "Apellido Paterno").grid(row=2, column=0, sticky="e")
        self.apellPaterno= tk.Entry(self,
                               textvariable=self.opcionApPat,
                               validate="key",
                               validatecommand=(self.register(self.validar_mayuscula), "%P"))
        self.apellPaterno.grid(row=2, column=2)

        self.opcionApMat= tk.StringVar()
        tk.Label(self, text= "Apellido Materno").grid(row=3, column=0, sticky="e")
        self.apellMaterno= tk.Entry(self,
                               textvariable=self.opcionApMat, 
                               validate="key",
                               validatecommand=(self.register(self.validar_mayuscula), "%P"))
        self.apellMaterno.grid(row=3, column=2)
        
        self.opcionNacimiento= tk.StringVar()
        tk.Label(self, text= "Fecha de Nacimiento").grid(row=4, column=0, sticky="e")
        self.fechaNacimiento= tk.Entry(self,
                                  textvariable=self.opcionNacimiento,
                                  validate="key",
                                  validatecommand=(self.register(self.edad_por_nacimiento), "%P"))
        self.fechaNacimiento.grid(row=4, column=2)

        self.botonGuardarDatos= tk.Button(self, 
                                     text="Guardar",
                                    command=lambda: [self.corroborar_datos()]) 
        self.botonGuardarDatos.grid(row=7, column=0, columnspan=3)
        
        self.modificarDatos= lambda: (master.switch_frame(PaginaDatos))
        self.cambiarFrame= lambda: (master.switch_frame(PaginaConciencia))
                                    
    #Funcion para corroborar que se ingresen caracteres alfabéticos en el nombre y que sean en mayusculas. 
    def validar_mayuscula(nomApAm_mayuscula):            
        pass

    #Funcion para corrobar el ingreso de la fecha de nacimiento con datos numericos y con 8 digitos a lo maximo (2 dia/2 mes/4 año) y 2 caracteres barra diagonal para separarlos.
    def edad_por_nacimiento(f_nacimiento):
        pass
   
    def corroborar_datos(self):
        epn= self.edad_por_nacimiento
        
        nomEnt= self.nombreEntry.get()
        apPatEnt= self.apellPaterno.get()
        apMatEnt= self.apellMaterno.get()
#        fechNacEnt= self.edadPx.get()
        
        toplevel = tk.Toplevel(self)
        datosPersona= tk.Label(toplevel, text=(f"¿Datos ingresados correctos?\n\n Nombre: {nomEnt}\n\n Apellido Paterno: {apPatEnt}\n\n Apellido Materno: {apMatEnt}\n\n")) # Edad: {fechNacEnt}"))
        datosPersona.grid()
        guardarDatos= tk.Button(toplevel, text="Guardar y continuar", command= self.cambiarFrame)
        guardarDatos.grid()
        modificarDatos= tk.Button(toplevel, text="Modificar datos", command= self.modificarDatos)
        modificarDatos.grid()
        
        nuevaclass = PaginaConciencia(self)
        nuevaclass.obtener(nomEnt,apPatEnt,apMatEnt)

Los widgets deberían aparecer en el Frame pero este ni siquiera aparece. Trate de heredad del inicializador de la clase pero no sucedió nada.

Intente de estas formas:

  1. super()
  2. super().__init__(): aquí me pedía que pasara un constructor
  3. super.__init__: aquí no pasaba nada.
#Este es el primer frame con el primer parametro a evaluar.
class PaginaConciencia(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        tk.Frame.configure(self)

        self.cambiarFrame= lambda: (master.switch_frame(PaginaColorPiel))

    def obtener(self,nom,pa,ma):       
        super().__init__
        tituloGeneralConciencia= tk.Label(self, 
                                          text= "Ejemplo")
        tituloGeneralConciencia.grid(row=0, column=0, columnspan=3)
        
        persona = tk.Label(self, text=(f"Paciente: {nom} {pa} {ma}"))
        persona.grid(row=2, column=0, columnspan=2)

        
        boton= tk.Button(self, text="Soy un boton")

Adjunto codigo con el que se cambian de frames. Esta en un modulo aparte.

class SampleApp(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self._frame = None
        self.switch_frame(PaginaDatos)

    def switch_frame(self, frame_class):
        new_frame = frame_class(self)
        if self._frame is not None:
            self._frame.destroy()
        self._frame = new_frame
        self._frame.pack()

if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()

Actualización No.3

  1. Decidí crear una clase con su método para recibir los parámetros.
class Datos():
    def obtener(self,nom,pa,ma):        
        # ESTO ES PARA QUE APAREZCA EL NOMBRE DE LA PERSONA EN EL FRAME.
        self.persona= f"Persona: {nom} {pa} {ma}"
  1. Despues creo una instancia de esa clase.
        d= Datos()
  1. Finalmente llamo al atributo persona en la siguiente clase, pero me dice que la clase Datosno tiene ese atributo.

        tk.Label(self, text=d.persona).grid(row=2, column=0, columnspan=2)

Actualizacion No.4

Visto lo complicado que se me ha vuelto lograr este cometido incide en el de otra forma.

def ValidarMayuscula(*letraMayuscula):
    validacion= []
    if len(letraMayuscula) == None:
        return False
    
    for char in letraMayuscula:
        if char.isupper():
            validacion.append(char)
        else:
            validacion= str(letraMayuscula).upper()
            return validacion
    validacion= f"Persona{validacion[0]}"

    return all(validacion)

Lo que hace esta función es validar que todas las letras estén escritas en mayúsculas y regresa una lista con la validacion.

        vNombre= FuncionesExtra.ValidarMayuscula

        tk.Label(self, text= vNombre).grid()

¿Cómo podría utilizar ese valor en el Label? De la forma descrita no funciona.

p.d. estoy utilizando lo visto aquí, aquí, este también, y este y sobre todo este...

¡Muchas gracias por su orientación!

Logica Miau
  • 122
  • 16

2 Answers2

1

Cuando ingresas tu variable en, por ejemplo text=datos.nombre, lo que en realidad hacer es mandarle el objeto Stringvar datos.nombre, pero no su valor.

Para solucionar tu problema, tienes que obtener el valor del StringVar y luego pasarlo al text asi: text=datos.nombre.get().

También puedes hacer que el contenido del Label cambie en tiempo real con respecto al del entry asociando el Stringvar de tu Entry a dicho label asi: textvariable=datos.nombre


Mas info sobre las variables de control como Stringvar aqui: https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/control-variables.html


Actualización

No entendi muy bien tu comentario, quiza por cansancio, pero que tal si guardas los valores obtenidos dentro de PaginaDatos y luego obtienes los valores en PaginaConciencia para luego aplicar mi segunda recomendacion? Los valores tienen que ser los stringvars. Sin usar .get.

Perdona que no lo explique con código, pero estoy en celular. Puede que dentro de unas horas vea todo esto mas a fondo.

Ya estoy en una PC! Me refería a algo como esto. En PaginaDatos hacer por ejemplo:

self.opcionNombre= tk.StringVar()

Y en PaginaConciencia hacer:

class PaginaConciencia(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        tk.Frame.configure(self)

        datos= PaginaDatos(self)
        
        titulo= tk.Label(self, 
                         text= "Pagina Ejemplo")
        titulo.grid(row=0, column=0, columnspan=3)
        
        tk.Label(self, textvariable=datos.opcionNombre).grid(row=1, column=0)
        (...)
Dante S.
  • 4,912
  • 2
  • 6
  • 31
  • Gracias por contestar, no me ha funcionado ninguna de las recomendaciones:(. Ahora no aparece nada. En la función `corroborar_datos` creo variables donde obtengo el valor de los `Entry` y funciona perfectamente pero no se como llevarlas a la otra clase sin convertirlas en `globales`... intente pasarlo como argumentos ahí pero tampoco funciono. ¡Gracias por la info! la estaré leyendo. – Logica Miau Feb 17 '21 at 01:53
  • 1
    De nada! Ojala sirva! He actualizado mi respuesta con una idea. – Dante S. Feb 17 '21 at 02:10
  • Me refería a que ojala sirva la información extra c: – Dante S. Feb 17 '21 at 12:54
  • ¡Gracias por la actualización! Esa fue una de las cosas que intente, en el caso que me refieres sale este error: `NameError: name 'opcionNombre' is not defined` – Logica Miau Feb 17 '21 at 20:20
  • Edito la respuesta porque hacia referencia a un error dado por una circunstancia no relacionada XD. Aplicando la solución no aparece nada:( se supone que debería aparecer pero no. – Logica Miau Feb 17 '21 at 20:26
  • Ubicas a PaginaConciencia en alguna parte de tu programa principal? Es decir, en alguna parte del código que no pudiste pasar haces `PaginaConciencia.pack` o `PaginaConciencia.place`? – Dante S. Feb 17 '21 at 21:22
  • No, esta tal cual en el ejemplo que puse. Lo curioso es que si me imprime el objeto, no entiendo por que no imprime el valor:( – Logica Miau Feb 17 '21 at 21:25
  • 1
    La verdad no se me ocurre que puede estarte pasando, aun así, te aviso, recuerda que estás creando widgets dentro del frames. Si al frame no lo ubicas en la ventana, entonces no se va a ver. Te lo digo por las dudas, sin embargo, creo que ya haces de antes lo que dije. – Dante S. Feb 17 '21 at 21:30
  • La cosa es que el `widget` si aparece, pero no con el valor del `entry`. Cuando aplica las recomendaciones que me diste, no lanza error pero no aparece nada. ¿Sera algo del `font` por defecto de los `label`? Seguiré experimentando a ver que encuentro. ¡Gracias! – Logica Miau Feb 17 '21 at 21:32
  • 1
    Entiendo, suerte y ojala alguien más logre ayudarte o veas lo que pasa! De nada! – Dante S. Feb 17 '21 at 21:38
1

Te sale PY_VAR porque estas obteniendo el valor del StringVar y no del Entry como tal

nuevoApPat= datos.opcionApPat.get()

Segun tu codigo, opcionApPat es un tk.StringVar(), y lo que tu necesitas es el valor del entry, que se llama apellPaterno, pero vas a tener que agregarle un self para poderlo llamar desde fuera de la función.

Tampoco te recomiendo que incluyas todo tu codigo y funciones dentro del mismo def __ init __(self), recuerda que este deberia ser solo el constructor.

He modificado un poco y rapidamente tu codigo, de pronto te sirva:

import tkinter as tk

class PaginaDatos(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        
        #Funcion para corroborar que se ingresen caracteres alfabéticos en el nombre y que sean en mayúsculas. 
    
        opcionNombre= tk.StringVar()
        tk.Label(self, text= "Nombre").grid(row=1, column=0, sticky="e")
        self.nombreEntry= tk.Entry(self,textvariable=opcionNombre, validate="key",
                              validatecommand=(self.register(self.validar_mayuscula), "%P"))
        self.nombreEntry.grid(row=1, column=2)
        
        self.opcionApPat= tk.StringVar()
        tk.Label(self, text= "Apellido Paterno").grid(row=2, column=0, sticky="e")
        self.apellPaterno= tk.Entry(self,textvariable=self.opcionApPat,validate="key",
                               validatecommand=(self.register(self.validar_mayuscula), "%P"))
        self.apellPaterno.grid(row=2, column=2)

        opcionApMat= tk.StringVar()
        tk.Label(self, text= "Apellido Materno").grid(row=3, column=0, sticky="e")
        self.apellMaterno= tk.Entry(self,textvariable=opcionApMat,validate="key",
                               validatecommand=(self.register(self.validar_mayuscula), "%P"))
        self.apellMaterno.grid(row=3, column=2)
        
        botonGuardarDatos= tk.Button(self, 
                                     text="Guardar",command=self.corroborar_datos)
        botonGuardarDatos.grid(row=7, column=0, columnspan=3)
        
        self.grid(row=0,column=0)
        
    
    def validar_mayuscula(nomApAm,mayuscula):
        pass

        #Funcion para corrobra el ingreso de la fecha de nacimiento con datos numéricos y con 8 dígitos a lo máximo.
    def edad_por_nacimiento(f,nacimiento):
        pass

        #Funcion para corroborar que los datos introducidos sean correctos. 

Me pareció más practico que esta funcion corroborar_datos hiciera el trabajo de la funcion que tenias obtener_información, ya que cumple la misma función e incluso puede enviar como parametros los valores que necesitas ejecutar en tu otra clase.

    def corroborar_datos(self):
        nomEnt= self.nombreEntry.get()
        apPatEnt= self.apellPaterno.get()
        apMatEnt= self.apellMaterno.get()
        
        nuevaclass = PaginaConciencia(ventana)
        nuevaclass.obtener(nomEnt,apPatEnt,apMatEnt)  

class PaginaConciencia(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        tk.Frame.configure(self)

    
    def obtener(self,nom,pa,ma):
        
        toplevel = tk.Toplevel(ventana)

        titulo= tk.Label(toplevel, 
                         text= "Pagina Ejemplo")
        titulo.grid(row=0, column=0, columnspan=3)
        
        #tk.Label(self, text= datos.opcionNombre).grid(row=1, column=0) De esta manera es como aparecían los PY_VAR
        label1 = tk.Label(toplevel, text=(f"Nombre: {nom}")).grid(row=1, column=0)
        label2 =tk.Label(toplevel, text=(f"Apellido Paterno: {pa}")).grid(row=1, column=1)
        label3 =tk.Label(toplevel, text=(f"Apellido Materno: {ma}")).grid(row=1, column=3)
        toplevel.update()
        
        self.grid(row=0,column=0)


ventana = tk.Tk()

pagina = PaginaDatos(ventana)

ventana.mainloop()
  • ¡Muchas gracias! Trato de analizar como adaptar tus recomendaciones al código. Hasta ahora no lo he logrado pero creo que aquí esta la solución para lograrlo. – Logica Miau Feb 20 '21 at 03:21
  • puedes copiar mi codigo y probarlo en tu pc, a mi me ejecuto correctamente, solo debes hacer unos pequeños cambios para que se ajuste a lo que tienes – Juan Carlos Rios Feb 20 '21 at 03:23
  • Si tu código funciona perfectamente, el problema viene con las funciones para validar mayúsculas y la fecha de nacimiento jaja. Lo otro es que no usaba `Toplevel` aun no los he estudiado suficientemente y me genera otro problema con el método para cambiar entre frames. – Logica Miau Feb 20 '21 at 03:24
  • @ Juan Carlos Rios logre aplicar un poco de tu idea. La perspectiva era algo errónea. El `Messagebox` (ahora `Toplevel`) para corroborar la información tenia que aparecer antes de cambiar de `Frame` cosa que no ocurre en el ejemplo, cosa que ya logre solucionar. Ahora el problema es que no aparece el `Frame` jajaja XD Creo que tengo que heredar o hacer algo con la función `super` en el método `obtener` pero no lo he logrado descifrar. Si me pudieras orientar… gracias:) O sea tiene que aparecer dos veces, uno para corroborar y otro nomas para visualizar ¿no se si me explico? – Logica Miau Feb 20 '21 at 05:45
  • Comparteme el error que te aparece y la parte del codigo de ambas clases como las tienes ahora para yo revisar como te puedo ayudar. Yo por lo general cuando trabajo con tkinter procuro utilizar un solo frame para todo el proyecto, salvo casos ya puntuales. – Juan Carlos Rios Feb 20 '21 at 06:21
  • No aparecen ningún error. Solo no aparece el `frame` y ninguno de los widgets:( déjame actualizo la pregunta para que lo veas:) – Logica Miau Feb 20 '21 at 06:28