Python Forum
Using Tkinter With Concurrent.Futures / ThreadPoolExecutor Class
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Using Tkinter With Concurrent.Futures / ThreadPoolExecutor Class
#1
Lightbulb 
I've been trying to combine the two, but the ThreadPool will not connect to the GUI. Is it even possible to connect the two together?

I am just using this as an example to learn how to use ThreadPooling. I'd like each name to be printed on it's own thread. Eventually I'll be using a Treeview to collect data from their own separate threads, but not sure if it's possible to use with the ThreadPoolExecutor.

Here's what I've tried to no avail:

from tkinter import Tk, Button, Listbox
from concurrent.futures import ThreadPoolExecutor

class MainWindow(Tk):

    def __init__(self):
        super().__init__()

        self.lb1 = Listbox(self, width=26, cursor='hand2')
        self.lb1.pack(side='left', fill='y', padx=20, pady=20)

        self.b1 = Button(self, text='START', bg='green', fg='white', cursor='hand2', command=self.start)
        self.b1.pack(side='left')

        self.lb1.insert('end', 'Aaron')
        self.lb1.insert('end', 'Billy')
        self.lb1.insert('end', 'Chris')
        self.lb1.insert('end', 'David')
        self.lb1.insert('end', 'Edward')
        self.lb1.insert('end', 'Frank')
        self.lb1.insert('end', 'George')
        self.lb1.insert('end', 'Howard')
        self.lb1.insert('end', 'Ian')
        self.lb1.insert('end', 'Johnny')



    def worker1(self):
        for i in range(self.lb1.size()):
            print(self.lb1.get(i))


    def start(self):
        with ThreadPoolExecutor(max_workers=2) as executor:
            executor.submit(self.worker1)



if __name__ == '__main__':
    app = MainWindow()
    app.title('Main Window')
    app.configure(bg='#333333')

    #center the Main Window:
    w = 500  # Width
    h = 420  # Height

    screen_width = app.winfo_screenwidth()  # Width of the screen
    screen_height = app.winfo_screenheight()  # Height of the screen

    # Calculate Starting X and Y coordinates for Window
    x = (screen_width / 2) - (w / 2)
    y = (screen_height / 2) - (h / 2)

    app.geometry('%dx%d+%d+%d' % (w, h, x, y))
    #app.maxsize(1400, 620)
    app.mainloop()
Reply
#2
You must be running on Windows. Windows and Linux do threading differently. You can read about the differences here:

https://rhodesmill.org/brandon/2010/pyth...x-windows/

So python programs running on windows cannot use shared variables to communicate between the parent process and the child threads as can be done when running the EXACT SAME PROGRAM on linux. But what about passing arguments? In your example, you pass "self" as an argument to the thread. Why doesn't that work?

For the same reason you cannot share variables between the parent process and threads on windows, you cannot share objects either, even if the object is passed as an argument. In your program "app" is an object in the parent process. It resides in memory that is only accessible by the parent process. Passing self (app) to the thread does not make that object available to the thread. The object is essentially a memory pointer, and the parent process and threads do not share memory.

To allow passing objects to the thread, windows Python pickles the object, passes the pickled object as an argument, and un-pickles the object in the thread. Now the thread has a copy of the object. Unfortunately, not all objects can be pickled, and as this program shows, tkinter app cannot be pickled.
from tkinter import Tk
import pickle

class MainWindow(Tk):
    def __init__(self):
        super().__init__()

if __name__ == '__main__':
    app = MainWindow()
    print(pickle.dumps(app))
    app.mainloop()
Error:
Traceback (most recent call last): File "test.py", line 19, in <module> print(pickle.dumps(app)) TypeError: cannot pickle '_tkinter.tkapp' object
For your particular example, you could create a list of the Listbox items and pass that as an argument. Lists of strings are picklable. The worker function would be a standalone function with no knowledge of the tkinter app. Your button click method would be:
    def start(self):
        labels = [self.lbl1.get(i) for i in range(self.lbl1.size()]
        with ThreadPoolExecutor(max_workers=2) as executor:
            executor.submit(worker1, labels)
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
Lightbulb [Tkinter] Tkinter Class Import Module Issue AaronCatolico1 6 3,148 Sep-06-2022, 03:37 PM
Last Post: AaronCatolico1
  [Tkinter] Redirecting all print statements from all functions inside a class to Tkinter Anan 1 2,661 Apr-24-2021, 08:57 AM
Last Post: ndc85430
  tkinter moving an class object with keybinds gr3yali3n 5 3,299 Feb-10-2021, 09:13 PM
Last Post: deanhystad
  [Tkinter] Troubles with accessing attr from other class zarize 3 2,656 Aug-20-2020, 06:05 PM
Last Post: deanhystad
  [Tkinter] Use function from other class (Tkinter) zarize 8 4,869 Aug-17-2020, 09:47 AM
Last Post: zarize
  Unable fetch fucntion data in class in tkinter jenkins43 2 3,901 Nov-30-2019, 09:47 PM
Last Post: jenkins43
  Tkinter Class pythonenthusiast2 1 2,646 Nov-24-2019, 03:51 AM
Last Post: Larz60+
  Tkinter Gui ScrolledText Insert From Different Class whisperquiet 1 4,344 Jan-08-2019, 09:25 PM
Last Post: Larz60+
  Using a class to create instances of Tkinter Toplevel() windows nortski 2 11,012 Mar-27-2018, 11:44 AM
Last Post: nortski

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020