apatrick apatrick - 4 months ago 8
Python Question

TTK progress bar blocked when sending email

I am writing an app in python using tkinter. In this app I am trying to send out a batch of emails and I want to show a progress bar while they are being sent. I am able to create the progress bar and start it, but when the emails are being sent, the bar just stops moving (If it is started way before the emails are sent, I want to start the bar just before the emails are sent, but it just hangs and nothing moves on the bar when I do it like this.

startProgressBar()
sendEmails()
stopProgressBar()


I have tried putting the sending of emails into a separate thread, but I don't seem to be having any luck. I am using the high-level Threading module. Are there any suggestions on what to do? Perhaps I am not getting the threading part correct. I am using the smtplib to send emails.

Answer

This is an old question but the code recipe I'm referring to has helped me with a similar concept, so I thought it should be shared.

This type of problem needs to use threading so that we spilt up the job of updating the GUI and doing the actual task (such as sending emails). Have a look at this code recipe from Active State, I believe it's exactly what you're looking for as an example of threading and passing information between threads (via a queue).

I try to highlight the important parts from the code recipe. I don't include setting up the progress bar itself but rather the overall code structure and getting/setting a queue.

import Tkinter
import threading
import Queue

class GuiPart:
    def __init__(self, master, queue, endCommand):
        self.queue = queue
        # Do GUI set up here (i.e. draw progress bar)

        # This guy handles the queue contents
        def  processIncoming(self):
            while self.queue.qsize():
                try:
                    # Get a value (email progress) from the queue 
                    progress = self.queue.get(0)
                    # Update the progress bar here.

                except Queue.Empty:
                    pass

class ThreadedClient:
    # Launches the Gui and does the sending email task
    def __init__(self, master):
        self.master = master
        self.queue = Queue.Queue()

        # Set up the Gui, refer to code recipe
        self.gui = GuiPart(master, self.queue, ...)

        # Set up asynch thread (set flag to tell us we're running)
        self.running = 1        
        self.email_thread = threading.Thread(target = self.send_emails)
        self.email_thread.start()

        # Start checking the queue
        self.periodicCall()

     def periodicCall(self):
         # Checks contents of queue
         self.gui.processIncoming()
         # Wait X milliseconds, call this again... (see code recipe)

     def send_emails(self): # AKA "worker thread"
         while (self.running):
             # Send an email
             # Calculate the %age of email progress

             # Put this value in the queue!
             self.queue.put(value)

     # Eventually run out of emails to send.
     def endApplication(self):
         self.running = 0


root = Tkinter.Tk()
client = ThreadedClient(root)
root.mainloop()
Comments