Tkinter managing my event loops alongside my mainloop

前端 未结 2 452
滥情空心
滥情空心 2020-12-03 16:17

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

相关标签:
2条回答
  • 2020-12-03 17:06

    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.

    0 讨论(0)
  • 2020-12-03 17:16

    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))
    
    0 讨论(0)
提交回复
热议问题