Tkinter gestionando mis bucles de eventos junto con mi mainloop

He estado aprendiendo lentamente Tkinter y la progtwigción orientada a objetos, pero me he progtwigdo en una esquina con este. perdonen mi falta de pensamiento crítico en este caso, pero les he preguntado a todos los que conozco que conocen Python mejor que yo y no podemos encontrar una solución funcional aquí

Tengo una aplicación de interfaz gráfica de usuario en la que estoy trabajando para permitir que el usuario ingrese símbolos comunes, cree nuevas tags para cada símbolo y luego actualice cada etiqueta periódicamente. (Algo así como una aplicación de etrade realmente básica o algo así). Descubrí que es muy fácil hacer esto sin una interfaz gráfica de usuario porque solo puedo decir:

while True: sPrice = get_stock_price(s) print sPrice 

pero he enlazado mi función get_stock_price (s) a un botón, que genera un sub-marco y una etiqueta contenida dentro de él. El problema que he enfrentado es que la etiqueta no se actualizará. Un amigo recomendó agregar otro método únicamente para actualizar la etiqueta, sin embargo, la única manera que conozco de actualizarla continuamente es haciendo una

 while True: # get new price # update the label # time.sleep(~1 minute?) 

Esto hace que la ventana de mi GUI se congele y gire para siempre. He estado leyendo sobre todos los otros hilos relacionados con esta situación particular, y he visto muchos consejos diferentes; no llames a dormir en tu hilo principal, no utilices root.update, utilices eventos, llames root.something.after (500, function) y he intentado implementar. Lo que me queda es un código de código que aún recuperará los valores de mis acciones, pero no los actualizaré, y algunos métodos que no sé cómo actualizar o dónde llamar en mi código.

Lo que espero es una explicación (potencialmente larga, lo sé. ¡Lo siento!) De lo que estoy haciendo mal, y sugerencias sobre cómo solucionarlo. Realmente estoy tratando de entender y solucionar el problema yo mismo, pero las soluciones de código serían increíbles siempre y cuando se expliquen.

¡¡¡Muchas gracias de antemano!!!

PS: Aquí está mi código hasta ahora:

 from Tkinter import * import urllib import re import time class MyApp(object): def __init__(self, parent): self.myParent = parent self.myContainer1 = Frame(parent) self.myContainer1.pack() self.createWidgets() button1 = Button(self.myContainer1, command = self.addStockToTrack) self.myContainer1.bind("", self.addStockToTrack) button1.configure(text = "Add Symbol") button1.pack() def createWidgets(self): # title name root.title("Stock App") # creates a frame inside myContainer1 self.widgetFrame = Frame(self.myContainer1) self.widgetFrame.pack() # User enters stock symbol here: self.symbol = Entry(self.widgetFrame) self.symbol.pack() self.symbol.focus_set() def addStockToTrack(self): s = self.symbol.get() labelName = str(s) + "Label" self.symbol.delete(0, END) stockPrice = get_quote(s) self.labelName = Label(self.myContainer1, text = s.upper() + ": " + str(stockPrice)) self.labelName.pack() self.myContainer1.after(500, self.get_quote) def updateStock(self): while True: labelName = str(s) + "Label" stockPrice = get_quote(s) self.labelName = Label(self.myContainer1, text = s.upper() + ": " + str(stockPrice)) self.labelName.pack() time.sleep(10) def get_quote(symbol): base_url = 'http://finance.google.com/finance?q=' content = urllib.urlopen(base_url + symbol).read() m = re.search('id="ref_\d*_l".*?>(.*?)<', content) if m: quote = m.group(1) else: quote = 'Not found: ' + symbol return quote root = Tk() myapp = MyApp(root) root.mainloop() 

Ya tiene un bucle infinito en ejecución, por lo que no debe intentar agregar otro. En su lugar, puede usar el método after para hacer que una función se llame repetidamente cada cierto tiempo. En su caso, puede reemplazar esto:

 def updateStock(self): while True: labelName = str(s) + "Label" stockPrice = get_quote(s) self.labelName = Label(self.myContainer1, text = s.upper() + ": " + str(stockPrice)) self.labelName.pack() time.sleep(10) 

… con este:

 def updateStock(self): labelName = str(s) + "Label" stockPrice = get_quote() self.labelName = Label(self.myContainer1, text = s.upper() + ": " + str(stockPrice)) self.labelName.pack() self.after(10000, self.updateStock) 

Esto obtendrá una cotización, agregará una etiqueta y luego se ordenará que se vuelva a llamar en 10 segundos (10,000 ms).

Sin embargo, dudo que quieras crear una nueva etiqueta cada 10 segundos, ¿verdad? Eventualmente la ventana se llenará de tags. En su lugar, puede crear una etiqueta una vez y luego actualizarla en cada iteración. Por ejemplo, cree self.label una vez en el init, luego en el bucle puede hacer:

 self.labelName.configure(text=s.upper() + ": " + str(stockPrice)) 

Usted está buscando para enhebrar. Pon el evento que quieres ejecutar en otro hilo. Vea este ejemplo:

 import thread, time def myfunc(a1,a2): while True: print a1,a2 time.sleep(1) thread.start_new_thread(myfunc,("test","arg2") tkroot.mainloop() 

Ahora tiene una función que se ejecuta junto con la ventana Tkinter que imprime los argumentos cada segundo.

EDIT: No sé por qué tantos votos a la baja. Tkinter funciona bien con hilos, ya he usado este truco varias veces sin problemas. Vea este ejemplo:

Descargue un archivo de 10 MB y registre el progreso en una ventana de Tkinter.

Sin roscar:

 import urllib2,thread import Tkinter as tk class Example(tk.Frame): def __init__(self, parent): tk.Frame.__init__(self, parent) self.parent = parent self.initUI() def initUI(self): self.pack(fill=tk.BOTH, expand=1) canvas = tk.Canvas(self) self.text = canvas.create_text(18,18,anchor=tk.W,font="Purisa",text="Status: Press start to download...") but=tk.Button(text="Start",command=self.start) canvas.create_window((270,18),window=but) canvas.pack(fill=tk.BOTH, expand=1) self.canvas=canvas def start(self): #thread.start_new_thread( self.download("http://ipv4.download.thinkbroadband.com/10MB.zip","10mb.zip") #) def onEnd(self): self.canvas.itemconfig(self.text, text="Status: done!") def download(self,url,file_name): u = urllib2.urlopen(url) f = open(file_name, 'wb') meta = u.info() file_size = int(meta.getheaders("Content-Length")[0]) print "Downloading: %s Bytes: %s" % (file_name, file_size) file_size_dl = 0 block_sz = 1024*50 #50 kb while True: buffer = u.read(block_sz) if not buffer: break file_size_dl += len(buffer) f.write(buffer) status = r"[%3.2f%%]" % (file_size_dl * 100. / file_size) self.canvas.itemconfig(self.text,text="Status: downloading..."+status) f.close() self.onEnd() def main(): root = tk.Tk() root.resizable(0,0) ex = Example(root) root.geometry("300x70") root.mainloop() main() 

La ventana se congela hasta que se realiza la descarga.

Con hilo:

 import urllib2,thread import Tkinter as tk class Example(tk.Frame): def __init__(self, parent): tk.Frame.__init__(self, parent) self.parent = parent self.initUI() def initUI(self): self.pack(fill=tk.BOTH, expand=1) canvas = tk.Canvas(self) self.text = canvas.create_text(18,18,anchor=tk.W,font="Purisa",text="Status: Press start to download...") but=tk.Button(text="Start",command=self.start) canvas.create_window((270,18),window=but) canvas.pack(fill=tk.BOTH, expand=1) self.canvas=canvas def start(self): thread.start_new_thread( self.download("http://ipv4.download.thinkbroadband.com/10MB.zip","10mb.zip") ) def onEnd(self): self.canvas.itemconfig(self.text, text="Status: done!") def download(self,url,file_name): u = urllib2.urlopen(url) f = open(file_name, 'wb') meta = u.info() file_size = int(meta.getheaders("Content-Length")[0]) print "Downloading: %s Bytes: %s" % (file_name, file_size) file_size_dl = 0 block_sz = 1024*50 #50 kb while True: buffer = u.read(block_sz) if not buffer: break file_size_dl += len(buffer) f.write(buffer) status = r"[%3.2f%%]" % (file_size_dl * 100. / file_size) self.canvas.itemconfig(self.text,text="Status: downloading..."+status) f.close() self.onEnd() def main(): root = tk.Tk() root.resizable(0,0) ex = Example(root) root.geometry("300x70") root.mainloop() main() 

No se congela y el texto se actualiza normalmente.