[Tkinter] Progress Bar While Sending an Email - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: GUI (https://python-forum.io/forum-10.html) +--- Thread: [Tkinter] Progress Bar While Sending an Email (/thread-21669.html) |
Progress Bar While Sending an Email - maxtimbo - Oct-09-2019 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): t1=time.perf_counter() print(t1) 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.") try: server = smtplib.SMTP_SSL(self.display_email_smtp.get(), self.display_email_port.get()) server.ehlo() server.login(self.display_email_login.get(), self.display_email_passwd.get()) with open(test) as fp: msg = EmailMessage() msg.set_content(fp.read()) msg["Subject"] = "Test" msg["From"] = self.display_email_from.get() msg["To"] = recipients server.send_message(msg) server.close() 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(ex) t2=time.perf_counter() print(t1 - t2) def check_email(self): self.check_email_backend() # 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() MainApplication(root) root.mainloop()Note: I know what I have won't work. I'm just not really sure how to proceed from here. RE: Progress Bar While Sending an Email - woooee - Oct-09-2019 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. RE: Progress Bar While Sending an Email - maxtimbo - Oct-09-2019 (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? RE: Progress Bar While Sending an Email - woooee - Oct-09-2019 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): self.root=root self.root.geometry("75x50+1200+100") tk.Button(self.root, text="Quit", bg="red", command=self.root.destroy).pack() self.ctr=25 self.start_progress_bar() self.start_countdown() def start_progress_bar(self): """ create a simple progress bar widget on a canvas """ self.top=tk.Toplevel(self.root, takefocus=True) self.top.title("Progress Bar") self.top.geometry("+1200+300") self.canvas = tk.Canvas(self.top, width=261, height=60, background='lightgray') self.canvas.pack() self.rc1 = self.canvas.create_rectangle(24, 20, 32, 50, outline='white', \ fill='blue') self.start_x=20 self.end_x=235 self.this_x=self.start_x 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') self.update_scale() def start_countdown(self): """ a separate 'loop' in a separate GUI """ self.top2=tk.Toplevel(self.root, bg="lightyellow") self.top2.geometry("100x25+1200+200") self.label_ctr = tk.IntVar() self.label_ctr.set(self.ctr) tk.Label(self.top2, textvariable=self.label_ctr, width=10, font=("Verdans", 15)).pack() self.update() 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.label_ctr.set(self.ctr) self.ctr -= 1 self.root.update_idletasks() self.root.after(750, self.update) else: ## 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.canvas.update() 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() PB=ProgressBar(root) root.mainloop() |