Python Forum

Full Version: Python Tkinter Simple Multithreading Question
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I have a simple Listbox with a list of names that I would like to print concurrently, but also in the order of which they were added to the Listbox (alphabetically in this case). I'm just using this example to practice creating multiple threads to handle each name in the listbox.

So, just to clarify, I'd like to have 1 thread for each name in the list to print each name. I prefer to do this in the order they are in queue from whatever the listbox has in it.

Here's what I've tried thus far:

from tkinter import Tk, Button, Listbox
import threading

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_thread)
    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 start_thread(self):
    for i in range(self.lb1.size()):
        t1 = threading.Thread(target = self.work, args=(i,))
        t1.start()



def work(self, i):
    print(self.lb1.get(i))



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

    app.mainloop()
The problem that I'm having is that the threads do not fire off properly. They print the names out in random order and also on the same exact line as if they are just one name at times. How do I get better control of each thread that will print each name concurrently?

Below is an example of what is being printed out:


ChrisEdward
DavidBilly

George

Aaron
Frank
Ian
JohnnyHoward
If you want to control when they run, don't put each in their own thread? Unless the code something to synchronize execution of the threads, they just finish in whatever order they want. Normally you don't care.

As for them all printing on the same line, I am not seeing that, but it is probably just because stdout is not being flushed after each write
There are multiple threads each trying to control the same resource, terminal output, so the results are unpredictable. I use multiprocessing and a Manager (common) list in the cases when I want to do something like this. But while memory is faster than printing, the order can not be guaranteed and so you would sort the list before printing.

from tkinter import Tk, Button, Listbox
from functools import partial
import multiprocessing as mp
 
class MainWindow():
  def __init__(self, mp_list):
    self.root=Tk()
    self.root.title('Main Window')
    self.root.configure(bg='#333333')
 
    self.lb1 = Listbox(self.root, width=26, cursor='hand2')
    self.lb1.pack(side='left', fill='y', padx=20, pady=20)
 
    self.b1 = Button(self.root, text='START', bg='green', 
              fg='white', cursor='hand2',
               command=partial(self.start_thread, mp_list))
    self.b1.pack(side='left')
 
    Button(self.root, text='QUIT', bg='orange', 
               command=self.root.quit).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')
 
    self.root.mainloop()

  def start_thread(self, mp_list):
        self.b1.config(state="disabled")
        self.process_list=[]
        for i in range(self.lb1.size()):
            t1 = mp.Process(target = self.work, args=(i, mp_list,))
            t1.start()
            self.process_list.append(t1)

  def work(self, i, mp_list):
        mp_list.append(self.lb1.get(i))
 
 
 
if __name__ == '__main__':
    manager=mp.Manager()
    mp_list=manager.list()
    app = MainWindow(mp_list)
 
    print(mp_list) 
C:\Users\Aaron\AppData\Local\Programs\Python\Python310\python.exe "C:/Users/Aaron/Documents/Python/EBSEO PRO/2.py"
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\Aaron\AppData\Local\Programs\Python\Python310\lib\tkinter\__init__.py", line 1921, in __call__
    return self.func(*args)
  File "C:\Users\Aaron\Documents\Python\EBSEO PRO\2.py", line 41, in start_thread
    t1.start()
  File "C:\Users\Aaron\AppData\Local\Programs\Python\Python310\lib\multiprocessing\process.py", line 121, in start
    self._popen = self._Popen(self)
  File "C:\Users\Aaron\AppData\Local\Programs\Python\Python310\lib\multiprocessing\context.py", line 224, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "C:\Users\Aaron\AppData\Local\Programs\Python\Python310\lib\multiprocessing\context.py", line 336, in _Popen
    return Popen(process_obj)
  File "C:\Users\Aaron\AppData\Local\Programs\Python\Python310\lib\multiprocessing\popen_spawn_win32.py", line 93, in __init__
    reduction.dump(process_obj, to_child)
  File "C:\Users\Aaron\AppData\Local\Programs\Python\Python310\lib\multiprocessing\reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
TypeError: cannot pickle '_tkinter.tkapp' object
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\Aaron\AppData\Local\Programs\Python\Python310\lib\multiprocessing\spawn.py", line 116, in spawn_main
    exitcode = _main(fd, parent_sentinel)
  File "C:\Users\Aaron\AppData\Local\Programs\Python\Python310\lib\multiprocessing\spawn.py", line 126, in _main
    self = reduction.pickle.load(from_parent)
EOFError: Ran out of input
(Dec-14-2022, 08:03 PM)woooee Wrote: [ -> ]There are multiple threads each trying to control the same resource, terminal output, so the results are unpredictable. I use multiprocessing and a Manager (common) list in the cases when I want to do something like this. But while memory is faster than printing, the order can not be guaranteed and so you would sort the list before printing.

from tkinter import Tk, Button, Listbox
from functools import partial
import multiprocessing as mp
 
class MainWindow():
  def __init__(self, mp_list):
    self.root=Tk()
    self.root.title('Main Window')
    self.root.configure(bg='#333333')
 
    self.lb1 = Listbox(self.root, width=26, cursor='hand2')
    self.lb1.pack(side='left', fill='y', padx=20, pady=20)
 
    self.b1 = Button(self.root, text='START', bg='green', 
              fg='white', cursor='hand2',
               command=partial(self.start_thread, mp_list))
    self.b1.pack(side='left')
 
    Button(self.root, text='QUIT', bg='orange', 
               command=self.root.quit).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')
 
    self.root.mainloop()

  def start_thread(self, mp_list):
        self.b1.config(state="disabled")
        self.process_list=[]
        for i in range(self.lb1.size()):
            t1 = mp.Process(target = self.work, args=(i, mp_list,))
            t1.start()
            self.process_list.append(t1)

  def work(self, i, mp_list):
        mp_list.append(self.lb1.get(i))
 
 
 
if __name__ == '__main__':
    manager=mp.Manager()
    mp_list=manager.list()
    app = MainWindow(mp_list)
 
    print(mp_list) 
Ran fine on my Linux box. Don't have any idea what this means

EOFError: Ran out of input
It is a pickling error. You don't get it running on linux because linux python doesn't pickle arguments passed to processes.