Reputation: 73
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()
Upvotes: 2
Views: 6600
Reputation: 17
I had an similar issue. Fixed that by removing self from my target function by changing it into a static method:
def start_processsing_thread(self):
process_thread = threading.Thread(target=self.dummy_processing)
process_thread.start()
@staticmethod
def dummy_processing():
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")
Upvotes: 1
Reputation: 386315
Tkinter objects cannot cross process boundaries, which is in effect what this error is telling you. Python uses pickle to transfer objects between processes and tkinter objects cannot be pickled.
You need to structure your code so that 100% of the GUI is in a single process.
Upvotes: 1