Bottom Page

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
 [Tkinter] Progress Bar While Sending an Email
Hello, I am trying to create a progress bar for my tkinter based gui that shows the user that an email is being sent. I've never successfully implemented threading or multi-processing before, simply because any application I thought I would need either of them, it was easier and faster without. My thought was that I would need to create a popup tkinter object with a progress bar that polls a separate function that sends the email. These two function would then be combined in a single caller function that uses concurrent.futures. I'm not really sure, however, how I can poll the send_mail function from the mail_waitbox function.

Here's the relevant code from my project:

class MainApplication(ttk.Frame):
    def __init__(self, parent, *args, **kwargs):
        ttk.Frame.__init__(self, parent, *args, **kwargs)

    #gui setup and variables.

        self.email_test_button = ttk.Button(self.email_tab, width=36, text="Test Email Settings", command=self.check_email)
        self.email_test_button.grid(row=11, column=1, padx=20, pady=5)

    def check_email_waitbox(self):
        popup = tk.Toplevel()
        popup_label = ttk.Label(popup, text="Message being sent").grid(row=0, column=0)
        progress = 0
        progress_var = tk.DoubleVar()
        progress_bar = ttk.Progressbar(popup, variable=progress_var, maximum=100)
        progress_bar.grid(row=1, column=0)

    def check_email_backend(self):
        sender_name = self.display_email_from.get()
        test = "testlog.txt"
        for x in self.display_email_recipient:
            recipients = [x]
        with open(test, "w+") as f:
            f.write(f"From: {sender_name}\n")
            f.write(f"To: {recipients}\n")
            f.write("Subject: Testing\n\n")
            f.write("This is a test mailer.")
            server = smtplib.SMTP_SSL(self.display_email_smtp.get(), self.display_email_port.get())
            server.login(self.display_email_login.get(), self.display_email_passwd.get())
            with open(test) as fp:
                msg = EmailMessage()
            msg["Subject"] = "Test"
            msg["From"] = self.display_email_from.get()
            msg["To"] = recipients
        except Exception as ex:
            if "getaddrinfo" in str(ex):
                messagebox.showerror("Something Went Wrong", "Check your settings or your internet connection before continuing.")
            if "A connection attempt failed" in str(ex):
                messagebox.showerror("something Went Wrong", f"Check your settings.\n\nError Returned: \n{ex}")
        print(t1 - t2)

    def check_email(self):
        # with concurrent.futures.ThreadPoolExecutor() as executor:
        #     g1 = executor.submit(self.check_email_waitbox)
        #     g2 = executor.submit(self.check_email_backend)

if __name__ == "__main__":
    root = tk.Tk()
Note: I know what I have won't work. I'm just not really sure how to proceed from here.
The first thing you have to do is get the percent done. I don't know of any way to do this, so the best that you may be able to do is a progress bar that moves back and forth until it is done.
(Oct-09-2019, 05:19 PM)woooee Wrote: The first thing you have to do is get the percent done. I don't know of any way to do this, so the best that you may be able to do is a progress bar that moves back and forth until it is done.

I was thinking that would likely be the route I take as well. Just something to prevent the user from clicking around or freaking out. The app tends to freeze up during the send_mail process, which is expected. But may make the end user uncomfortable. Again, though, not really sure how to accomplish this. And Multi-threading/processing. Does it look like I'm going down the right road?
You can use Tkinter's after module to do the back and forth progress bar. This is a canned program that I wrote a few years ago. Instead of the countdown timer, insert your sendmail code in the function(s).
import tkinter as tk

    class ProgressBar():
        def __init__(self, root):
            tk.Button(self.root, text="Quit", bg="red", command=self.root.destroy).pack()

        def start_progress_bar(self):
            """ create a simple progress bar widget on a canvas
  , takefocus=True)
  "Progress Bar")
            self.canvas = tk.Canvas(, width=261, height=60, background='lightgray')

            self.rc1 = self.canvas.create_rectangle(24, 20, 32, 50, outline='white', \
            self.one_25th = (self.end_x-self.start_x)/25.0
            rc2 = self.canvas.create_rectangle(self.start_x, 20, self.end_x, 50,
                                           outline='blue', fill='lightblue')
            self.rc1 = self.canvas.create_rectangle(self.start_x, 20, self.start_x+7, 50,
                                           outline='white', fill='blue')


        def start_countdown(self):
            """ a separate 'loop' in a separate GUI
            self.top2=tk.Toplevel(self.root, bg="lightyellow")
            self.label_ctr = tk.IntVar()
            tk.Label(self.top2, textvariable=self.label_ctr, width=10,
                          font=("Verdans", 15)).pack()

        def update(self):
            """ this function loops the counter
                if you only want to run sendmail once, then
                this function is not necessary
            if self.ctr > 0:
                self.ctr -= 1
                self.root.after(750, self.update)
                ## wait one half second to allow any remaining after() to execute
                ## can also use self.root.after_cancel(id)
                self.root.after(500, self.root.destroy)

        def update_scale(self):
            self.canvas.move(self.rc1, self.one_25th, 0)
            self.this_x += self.one_25th
            ## reverse direction at either end
            if (self.this_x >= self.end_x-12) or self.this_x <= self.start_x+7:
                self.one_25th *= -1

            ## only call after() while the countdown is running (self.ctr > 0)
            ## to avoid a dangling after() when the program terminates
            if self.ctr > 0:
                self.canvas.after(200, self.update_scale)

    root = tk.Tk()


Top Page

Possibly Related Threads...
Thread Author Replies Views Last Post
  How can I measure progress and display it in the progress bar in the interface? Matgaret 2 1,114 Dec-11-2019, 03:30 PM
Last Post: Denni
  Progress Bar While Sending an Email maxtimbo 0 345 Oct-08-2019, 02:13 PM
Last Post: maxtimbo
  GUI Progress Bar Anysja 6 2,340 Aug-29-2018, 02:34 PM
Last Post: swetanjali

Forum Jump:

Users browsing this thread: 1 Guest(s)