| 
		
	
	
	
		
	Mensajes: 109 
	Temas: 28 
	Registro en: Feb 2019
	
 Reputación: 
0 
	
	
		Buenas aquí sigo con mi app. Esto ya esta muy muy avanzado y claro van surgiendo otras necesidades y otros problemas. 
Estoy "empezado" con las clases y tengo este código(Es una prueba, no hagáis caso de los import ya que son para la app):
 Código: import tkinter as tkfrom tkinter import ttk
 from datetime import *
 from tkinter import messagebox
 import time
 import calendar
 import psycopg2
 import threading
 from threading import Timer
 import os
 import sys
 import tkinter.font as tkfont
 from functools import partial
 import subprocess
 import smtplib
 from email.mime.multipart import MIMEMultipart
 from email.mime.text import MIMEText
 from email.mime.base import MIMEBase
 from email import encoders
 import Pmw
 
 
 
 class Phone:
 
 def __init__(self):
 
 ######## MAIN WINDOW #########
 
 self.win01 = tk.Tk()
 self.win01.title('Prueba Botón Llamada')
 self.win01.geometry('600x400+0+0')
 
 ########## GRAL VARIABLES ###########
 
 self.fila = 0
 self.columna = 0
 
 
 ######## GRAL FUNTIONS ##########
 
 def addbot():
 
 self.fila += 1
 
 self.botgr = ttk.Button(self.win01, text='925' + str(self.fila))
 self.botgr.grid(row=self.fila, column = 0)
 
 
 # Entry para imprimir números
 
 self.ent01 = ttk.Entry(self.win01, justify= tk.CENTER, font=("DejaVu Sans Condensed",20,'bold'))
 self.ent01.grid(row = 0, column=1)
 
 
 # Botón para añadir botones.
 
 self.bot01 = ttk.Button(self. win01, command=addbot, text='ADD')
 self.bot01.grid(row=0, column = 0, padx=5, pady=5)
 
 self.win01.mainloop()
 
 
 
 def main():
 
 myapp = Phone()
 
 if __name__ == '__main__':
 main()
Bien, todo funciona perfectamente. Con el botón ADD vas añadiendo botones los cuales muetran un número que se va incrementando. Ahora quiero que cada botón una vez pulsado me imprima en el entry el número reflejado en dicho botón.(He pensado que lo mejor y más "pythoniano" para no repetirse es añadir una clase con un método que me permita esto heredando dicho método al pulsar cada  botón(No se si me explico bien)
 
1º Duda: No tengo muy claro dónde tengo que poner la nueva clase (class). Fuera de la principal o dentro.(Lo he probado y no me ha funcionado ninguna. Algo estoy haciendo mal).
	 
	
	
	
		
	Mensajes: 1.308 
	Temas: 3 
	Registro en: Feb 2016
	
 Reputación: 
71 
	
	
		Hola. Podrías usar algo así: Código: from tkinter import messagebox, ttkimport tkinter as tk
 
 
 class MyButton(ttk.Button):
 
 def __init__(self, *args, **kwargs):
 if "command" not in kwargs:
 kwargs["command"] = self.default_command
 super().__init__(*args, **kwargs)
 
 def default_command(self):
 messagebox.showinfo(title="Número", message=self["text"])
 
 
 root = tk.Tk()
 root.config(width=400, height=300)
 
 button1 = MyButton(text="1234")
 button1.place(x=30, y=70)
 
 root.mainloop()
En tu caso la definición de la clase iría fuera de la clase principal.
 
Saludos!
	 
	
	
	
		
	Mensajes: 109 
	Temas: 28 
	Registro en: Feb 2019
	
 Reputación: 
0 
	
	
		Muchísimas Gracias Francisco. 
El problema era que al añadir la clase lo hacía con el __init__ y no sé por qué no funcionaba. Me salía un error de que determinada clase no tenía el atributo(método).
 
Si creo la clase dónde solo meto el método(un método simple como print algo) si no añado el __init__ sí que funciona y no sé por qué. Tengo un poco atragantado esto de las clases.
 
Vamos a ir poco a poco (También necesitaré un poco de explicación de los argumentos *args y **kwargs, que los veo en montones de sitios y he leído algunas explicaciones pero no me queda claro su uso.
 
Os pongo el ejemplo sencillo que funciona.
 Código: import tkinter as tk
 from tkinter import ttk
 
 from datetime import *
 
 from tkinter import messagebox
 
 import time
 
 import calendar
 
 import psycopg2
 
 import threading
 
 from threading import Timer
 
 import os
 
 import sys
 
 import tkinter.font as tkfont
 
 from functools import partial
 
 import subprocess
 
 import smtplib
 
 from email.mime.multipart import MIMEMultipart
 
 from email.mime.text import MIMEText
 
 from email.mime.base import MIMEBase
 
 from email import encoders
 
 import Pmw
 
 
 
 class Button():
 
 
 
 def hola():
 
 
 
 print("HOLA MUNDO")
 self.ent01.insert(0, "HOLA MUNDO")
 
 
 
 
 
 
 class Phone:
 
 
 
 def __init__(self):
 
 
 
 ######## MAIN WINDOW #########
 
 
 
 self.win01 = tk.Tk()
 
 self.win01.title('Prueba Botón Llamada')
 
 self.win01.geometry('600x400+0+0')
 
 
 
 ########## GRAL VARIABLES ###########
 
 
 
 self.fila = 0
 
 self.columna = 0
 
 
 
 
 
 ######## GRAL FUNTIONS ##########
 
 
 
 def addbot():
 
 
 
 self.fila += 1
 
 
 
 self.botgr = ttk.Button(self.win01, text='925' + str(self.fila), command=Button.hola)
 
 self.botgr.grid(row=self.fila, column = 0)
 
 
 
 
 
 # Entry para imprimir números
 
 
 
 self.ent01 = ttk.Entry(self.win01, justify= tk.CENTER, font=("DejaVu Sans Condensed",20,'bold'))
 
 self.ent01.grid(row = 0, column=1)
 
 
 
 
 
 # Botón para añadir botones.
 
 
 
 self.bot01 = ttk.Button(self. win01, command=addbot, text='ADD')
 
 self.bot01.grid(row=0, column = 0, padx=5, pady=5)
 
 
 
 self.win01.mainloop()
 
 
 
 
 
 
 
 def main():
 
 
 
 myapp = Phone()
 
 
 
 if __name__ == '__main__':
 
 main()
Bien, aquí funciona excepto al intentar insertar algo en el Entry que me sale el siguiente error:
 
Exception in Tkinter callback 
Traceback (most recent call last): 
  File "/usr/lib/python3.7/tkinter/__init__.py", line 1705, in __call__ 
    return self.func(*args) 
  File "pythonbuttonclass.py", line 27, in hola 
    self.ent01.insert(0, "HOLA MUNDO") 
NameError: name 'self' is not defined
 
Creo que tiene que ver con la herencia de una clase a otra.
	 
	
	
	
		
	Mensajes: 1.308 
	Temas: 3 
	Registro en: Feb 2016
	
 Reputación: 
71 
	
	
		Hola. Tal vez te faltaría un poco más de información sobre la orientación a objetos. Te paso este artículo que escribí hace unos años (del cual no me siento muy orgulloso, así que sentite libre de buscar algún otro también): https://recursospython.com/guias-y-manua...a-objetos/ .
 
Yendo a tu ejemplo:
 Código: class Button():
 def hola():
 print("HOLA MUNDO")
 self.ent01.insert(0, "HOLA MUNDO")
Acá no hay ninguna diferencia con definir hola() fuera de la clase. Solo que en este caso accedes a la función vía Button.hola(). Es natural que al llamar a la función Python arroje un error diciendo que "self" no existe, ya que ese objeto está dentro de la función Phone.__init__(). Las funciones tienen ámbitos privados para sus variables; una variable definida dentro de una función no puede ser accedida fuera de ella. Si querés que hola() tenga acceso al self de la otra función, tenés que pasárselo como argumento, aquí probablemente con un partial(). Pero en ese caso no veo la necesidad de usar clases, con una función ya basta.
 
Sobre *args y **kwargs te dejo este artículo: https://recursospython.com/guias-y-manua...gs-kwargs/ .
 
Saludos!
	 
	
	
	
		
	Mensajes: 109 
	Temas: 28 
	Registro en: Feb 2019
	
 Reputación: 
0 
	
	
		Muchas Gracias Francisco!!!!!. Me había leído el artículo je je je.(Me he leído casi todos aunque el de *args y **kwargs no lo había visto ¡Está genial!). 
Bueno en cuanto a mi problema en realidad no es tan grave puesto que aunque los puristas pythonianos inciden mucho en aquello de Don't you repeat, simple better than complex etc etc de momento me conformo con que la aplicación funciona y poco a poco lo estoy consiguiendo.
 
El tema de liarme ahora con las clases es para avanzar un poco y controlar todos los aspectos de python pero bueno no llevo prisa.
 
Creo que incluso lo que quiero hacer se podría hacer simplemente con una función.
 
Te pongo un ejemplo muy sencillito y te digo que es lo que pretendo:
 Código: import tkinter as tk
 from tkinter import ttk
 
 
 
 class App:
 
 
 
 def __init__(self):
 
 
 
 
 
 self.root = tk.Tk()
 
 self.root.title("PRUEBAS")
 
 self.root.geometry('300x200')
 
 
 
 def mostrar():
 
 
 
 self.ent.delete(0, tk.END)
 
 self.ent.insert(0, self.boton.cget('text'))
 
 
 
 def mostrar1():
 
 
 
 self.ent.delete(0, tk.END)
 
 self.ent.insert(0, self.boton1.cget('text'))
 
 
 
 
 
 self.boton = ttk.Button(self.root, text='925', command=mostrar)
 
 self.boton.grid(row=0, column=0, pady=5)
 
 
 
 self.boton1 = ttk.Button(self.root, text='926', command=mostrar1)
 
 self.boton1.grid(row=1, column=0, pady=5)
 
 
 
 self.boton2 = ttk.Button(self.root, text='927')
 
 self.boton2.grid(row=2, column=0, pady=5)
 
 
 
 self.ent = ttk.Entry(self.root, width=5)
 
 self.ent.grid(row=0, column=1, padx=10)
 
 
 
 self.root.mainloop()
 
 
 
 def main():
 
 my_app = App()
 
 
 
 if __name__ == '__main__':
 
 main()
Bien en el ejemplo tenemos un app con tres botones. La idea es que cada botón al ser clickeado inserte en el entry el texto que tiene. Como pongo en el ejemplo se puede conseguir de manera fácil creando una función para cada botón. La cuestión es que me gustaría saber como crear una única función para los tres botones y no tener que crear tres que son exactamente iguales y no repetirme.(Solo cambia la variable que define el botón en cada caso). Penśe que tendría que ser una clase pero pienso que a lo mejor con una simple función se puede hacer y es mucho más sencillo.
 
P.D. Vuelvo a decir que poniéndo las tres funciones(Una para cada botón) funciona todo perfectamente y es muy sencillo. Pero me estoy obsesinando con el no te repitas y a lo mejor es un error).
 
Gracias de nuevo por todo.
	 
	
	
	
		
	Mensajes: 1.308 
	Temas: 3 
	Registro en: Feb 2016
	
 Reputación: 
71 
	
		
		
		27-03-2021, 02:33 PM 
(Última modificación: 27-03-2021, 02:35 PM por Francisco.)
		
	 
		¡De nada! Está perfecto que busques optimizar tu código evitando la repetición de código, y efectivamente las clases cumplen en gran medida ese propósito. 
Con respecto a este último ejemplo, se podría solucionar creando una función genérica y usando functools.partial() :
 Código: import tkinter as tkfrom tkinter import ttk
 from functools import partial
 
 
 class App:
 
 def __init__(self):
 self.root = tk.Tk()
 self.root.title("PRUEBAS")
 self.root.geometry('300x200')
 
 def mostrar_generico(boton):
 self.ent.delete(0, tk.END)
 self.ent.insert(0, boton.cget('text'))
 
 self.boton = ttk.Button(self.root, text='925')
 self.boton["command"] = partial(mostrar_generico, self.boton)
 self.boton.grid(row=0, column=0, pady=5)
 
 self.boton1 = ttk.Button(self.root, text='926')
 self.boton1["command"] = partial(mostrar_generico, self.boton1)
 self.boton1.grid(row=1, column=0, pady=5)
 
 self.boton2 = ttk.Button(self.root, text='927')
 self.boton2.grid(row=2, column=0, pady=5)
 
 self.ent = ttk.Entry(self.root, width=5)
 self.ent.grid(row=0, column=1, padx=10)
 
 self.root.mainloop()
 
 
 def main():
 my_app = App()
 
 if __name__ == '__main__':
 main()
(Necesariamente el command lo tengo que indicar una línea despúes de crear el botón, para poder tener la referencia).
 
Pero también se puede solucionar creando una nueva clase para esos botones, que herede toda la funcionalidad de ttk.Button, como el código que te pasé antes:
 Código: import tkinter as tkfrom tkinter import ttk
 
 
 class MyButton(ttk.Button):
 
 def __init__(self, *args, entry=None, **kwargs):
 if "command" not in kwargs:
 kwargs["command"] = self.default_command
 self.entry = entry
 super().__init__(*args, **kwargs)
 
 def default_command(self):
 self.entry.delete(0, tk.END)
 self.entry.insert(0, self.cget('text'))
 
 
 class App:
 
 def __init__(self):
 self.root = tk.Tk()
 self.root.title("PRUEBAS")
 self.root.geometry('300x200')
 
 self.ent = ttk.Entry(self.root, width=5)
 self.ent.grid(row=0, column=1, padx=10)
 
 self.boton = MyButton(self.root, text='925', entry=self.ent)
 self.boton.grid(row=0, column=0, pady=5)
 
 self.boton1 = MyButton(self.root, text='926', entry=self.ent)
 self.boton1.grid(row=1, column=0, pady=5)
 
 self.boton2 = ttk.Button(self.root, text='927')
 self.boton2.grid(row=2, column=0, pady=5)
 
 self.root.mainloop()
 
 
 def main():
 my_app = App()
 
 if __name__ == '__main__':
 main()
La clave acá está en que creo una nueva clase MyButton que hereda de ttk.Button, de modo que al crear un MyButton, puedo pasarle los mismos argumentos que a un ttk.Button (de ahí el uso de *args y **kwargs), con el agregado de "entry", que es un argumento propio de MyButton e indica la caja de texto en la cual el botón mostrará el texto al ser presionado.
 
Saludos
	 
	
	
	
		
	Mensajes: 109 
	Temas: 28 
	Registro en: Feb 2019
	
 Reputación: 
0 
	
	
		¡Muchas Gracias!!. Esto tengo que estudiarlo que veo que me va a hacer falta.
 GRACIAS DE NUEVO!!!!
 |