I\'ve been slowly learning Tkinter and object-oriented programming but I\'ve programmed myself into a corner with this one. please forgive my lack of critical thinking on th
You are looking for threading. Put the event you want to run in another thread. See this example:
import thread, time
def myfunc(a1,a2):
while True:
print a1,a2
time.sleep(1)
thread.start_new_thread(myfunc,("test","arg2")
tkroot.mainloop()
Now you have a function running along with the Tkinter window that prints the args every second.
EDIT: I don't know why so many down votes. Tkinter DOES work well with threads, I've already used this trick several times without problems. See this example:
Download a 10 MB file and log the progress to a Tkinter window.
Without threading:
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()
The window freezes till the download is done.
With thread:
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()
Doesn't freeze and the text is updated normally.
You already have an infinite loop running, so you shouldn't be trying to add another one. Instead, you can use the after
method to cause a function to be repeatedly called every so often. In your case, you can replace this:
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)
... with this:
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)
This will get a quote, add a label, then arrange for itself to be called again in 10 seconds (10,000 ms).
However, I doubt that you want to create a new label every 10 seconds, do you? Eventually the window will fill up with labels. Instead, you can create a label once, then update the label in each iteration. For example, create self.label
once in the init, then in the loop you can do:
self.labelName.configure(text=s.upper() + ": " + str(stockPrice))