I am trying to do multiprocessing from a tkinter UI when a button is pressed, but am getting an error: TypeError: cannot pickle '_tkinter.tkapp' object.
The below code works when I change the tkinter object to local (By removing the "self." part). Why is it so?
I have found similar posts (links below), and as I understand the problem is that tkinter objects cannot be pickled to share information between processes. I don't understand why does the code below create a problem since I am not trying to update the UI from the process.
Can what I am trying to do be achieved and what would be the best practices for that?
Python multiprocessing - TypeError: cannot pickle '_tkinter.tkapp' object
Cannot 'pickle' a Tkinter object in a multiprocessing environment in Windows
def main():
main = MainClass()
class MainClass:
def __init__(self):
self.create_gui()
def create_gui(self):
self.ui_root = Tk()
btn = Button(self.ui_root, text="Button", command=self.start_processsing_thread)
btn.pack()
self.ui_root.mainloop()
def start_processsing_thread(self):
process_thread = threading.Thread(target=self.dummy_processing)
process_thread.start()
def dummy_processing(self):
with concurrent.futures.ProcessPoolExecutor() as executor:
list = [1, 2, 3]
results = executor.map(self.process, list)
for number in results:
print(number)
end = time.perf_counter()
print("Finished all")
def process(self, secs):
time.sleep(secs)
return secs
if __name__ == '__main__':
main()
EDIT:
After doing some searching on how to make the GUI run on a single process (Thanks to answers by @Bryan Oakley and @Reblochon Masque) I came up with the below code. I am a beginner with Python and would appreciate if someone could comment if this is a good way of doing this.
In my actual app after the user presses the button some image processing is done (text created). After the images are processed I need some objects to be returned to the UI process. I found that the way to do that is using Queue. But if using Queue I cannot use the concurrent.futures.ProcessPoolExecutor() as I was doing before. As I understand this is somewhat unwanted since ProcessPoolExecutor knows how to allocate the Processes to the cores of the Processor, but in the way I am doing it now, everything is done on a single core. Is what I wrote correct and is there a way to use the ProcessPoolExecutor?
def main():
main = UIClass()
class UIClass:
def __init__(self):
self.queue = Queue()
self.create_gui()
def create_gui(self):
self.ui_root = Tk()
btn = Button(self.ui_root, text="Button", command=self.button_pressed)
btn.pack()
self.ui_root.mainloop()
def button_pressed(self):
print("Button pressed")
self.ui_root.after(1000, self.check_results)
self.dummy_processing()
def dummy_processing(self):
nr_list = [1, 2, 3, 4, 5]
for secs in nr_list:
process = TestProcess(self.queue, secs)
process.start()
def check_results(self):
print("Checking results")
try:
result = self.queue.get(timeout=0.1)
print("Result is " + str(result))
except Exception as e:
print("No results yet, waiting again")
self.ui_root.after(1000, self.check_results)
class TestProcess(Process):
def __init__(self, queue, secs):
Process.__init__(self)
self.queue = queue
self.secs = secs
def run(self):
print("Run in process")
result = self.process(self.secs)
self.queue.put(result)
def process(self, secs):
time.sleep(secs)
print("Done sleeping")
return secs
if __name__ == '__main__':
main()