发送邮件时TTK进度条被阻塞

2 投票
3 回答
1751 浏览
提问于 2025-04-17 08:53

我正在用Python和tkinter写一个应用程序。在这个应用里,我想批量发送邮件,同时显示一个进度条来让用户知道进度。我可以创建进度条并启动它,但在发送邮件的时候,进度条就不动了(如果我在发送邮件之前就启动了进度条,我想在发送邮件前启动它,但这样做时进度条就停在那里,什么都不动)。

startProgressBar()
sendEmails()
stopProgressBar()

我尝试把发送邮件的部分放到一个单独的线程里,但似乎没有成功。我使用的是高级的线程模块。有没有什么建议?也许我对线程的理解有问题。我用smtplib来发送邮件。

3 个回答

0

我最近在更新我的应用程序时又回到了这个问题上。我把它转换成了一个使用Swing作为界面的jython项目。

不过,我发现使用观察者模式是解决我问题的最简单方法。我的项目并不需要同时运行多个线程,我只是想大致显示一下进度。观察者模式完全符合我的需求,而Java中的观察者模式实现特别有帮助。

0

试试这样做:

progress = ttk.Progressbar(bottommenuframe, orient=HORIZONTAL, length=100, maximum=(NUMBEROFEMAILS), mode='determinate')
progress.pack(side=RIGHT)

def emailing():
    progress.start()   
    ##send 1 email
    progress.step(1)

    if all emails sent:
        progress.stop()

    root.after(0, emailing)

这样应该可以解决你的问题。希望对你有帮助 :)

2

这是一个老问题,但我提到的代码示例对我理解类似的概念很有帮助,所以我觉得应该分享一下。

这种类型的问题需要用到线程,这样我们才能把更新界面和实际执行任务(比如发送邮件)的工作分开。你可以看看这个来自Active State的代码示例,我相信它正好是你需要的关于线程和线程之间传递信息(通过队列)的例子。

我会强调代码示例中的重要部分。我不包括进度条的设置,而是关注整体的代码结构以及如何获取和设置队列。

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()

撰写回答