0

I'm looking for "best" way to populate treeview using threads. I have multiple mail account which I'm checking for new emails.

My plan is to use Queue to store accounts which will be checked using check_mail method. This method will return a list of new mails.

Can I use another Queue which I will populate with new mails and somehow loop while threads are alive?

Is there any thread-safe, good pattern to solve this?

sstevan
  • 477
  • 2
  • 9
  • 25

2 Answers2

0

Your question is very broad, so this answer will also be.

Generally speaking, tkinter doesn't play well with multi-threading. You can do it, but must make sure only the main thread interacts with the GUI. A common way to do this is to use the universal widget method after() to schedule handling of data going out to or being retrieved from background threads, typically via Queues, at regular intervals.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • Is there any good examples? I've found this [one](http://effbot.org/zone/tkinter-threads.htm) but it doesn't reflect what I need. There's exactly the same example [here](http://stackoverflow.com/questions/16860961/python-tkinter-multithreading-functions) but I don't find it useful. – sstevan Aug 08 '16 at 20:07
  • Something vaguely along this lines is illustrated in the `errorwindow.py` in [this answer](http://stackoverflow.com/a/18091356/355230) of mine to another question. It's a little tricky because the module is designed to be both directly imported and indirectly spawned as a separate process by the main application. It also only has one Queue, so there isn't a bidirectional data flow. The main takeaway would be to note its use of `after()` to handle the `Queue`. – martineau Aug 08 '16 at 20:45
  • Thanks for the help! I'll mark this answer as correct and post (maybe not so great) solution. – sstevan Aug 10 '16 at 08:14
0

I'm not sure if this is the best idea but it works

class Main(tk.Frame):

    def __init__(self, master):       
        tk.Frame.__init__(self, master)
        self.master = master
        self.some_service = SomeService()
        self.some_service.run()
        self.init_gui()
        self.after(60000, self.check_for_updates)  # Use it to run service
        self.after(2000, self.update_gui)  # Update GUI every 2 seconds.

    def check_for_updates(self):
        data = ['a', 'b', 'c', 'd']
        self.some_service.populate_job_queue(data)
        self.after(60000, self.check_for_updates)

    def update_gui(self):
        if not self.some_service.update_queue.empty():
            update = self.some_service.update_queue.get()
            ## Do something with update ##
            self.text.insert(tk.END, update)
        self.after(2000, self.update_gui)


class SomeService(object):

    def __init__(self):
        self.update_queue = Queue()
        self.job_queue = Queue()

    def populate_job_queue(self, jobs):
        for job in jobs:
            self.job_queue.put(job)

    def run(self):
        for x in range(8):
            worker = Thread(target=self.do_something, daemon=True)
            worker.start()

    def do_something(self):
        ## Do something with data
        while True:
            if not self.job_queue.empty():
                job = self.job_queue.get()
                # Do something
                self.update_queue.put(some_data)
sstevan
  • 477
  • 2
  • 9
  • 25
  • A problem with this may be the fact that there's a burst of multi-threading occurring only once every 60 secs during which time the GUI effectively hangs until they're all idle again (because the `self.pool.join()` at the end of `run()` blocks). I think what you want to do is have the threads potentially putting stuff in a queue all the time and have the main GUI periodically (via `after()`) retrieve things from it and update any affected widgets based on them as necessary. – martineau Aug 10 '16 at 11:29
  • @martineau Thanks! I've fixed it to match my needs. Haven't tested the example above but I hope it works. – sstevan Aug 10 '16 at 12:58
  • Looks like the update addressed the things I mentioned. The indentation of the `update_gui()` method is a little off, though. `;-)` – martineau Aug 10 '16 at 13:31