1

Summary: I am experiencing the issue TypeError: cannot pickle '_tkinter.tkapp' object I believe this is fixable by

ensuring the GUI is running in an entirely separate process

How do I do that with my code - new to multiprocessing.

This is a sample of the Code I am working with:

import customtkinter
import tkinter
import multiprocessing
import time


def SampleProcess(self,x):
    MY_STATUS = None
    slaved = False
    
    # Iterate through frames
    for row in range(4):
        for col in range(3):
            if not(slaved):
                name = f"frame{row}{col}"

                # Find frame that hasn't been initialized
                if getattr(self,'status_for_' + name).get() == f"Waiting for Input for {name}":
                    # claim frame
                    MY_STATUS = getattr(self,'status_for_' + name)
                    slaved = True
        
    if slaved:
        # rename text of frame
        MY_STATUS.set("I have a Window!")
        self.update_idletasks()
        self.update()

class App(customtkinter.CTk):

    '''GUI APP'''

    # NAME DEFAULT WIDTH AND HEIGHT
    APP_NAME = "Simulation Manager"
    WIDTH = 1000
    HEIGHT = 500
    
        

    
    def __init__(self, *args, **kwargs):

        customtkinter.set_appearance_mode("dark")
        customtkinter.set_default_color_theme("dark-blue")
    
        super().__init__(*args, **kwargs)
        
        
        # Set Size info
        self.title(App.APP_NAME)
        self.geometry(f"{App.WIDTH}x{App.HEIGHT}")
        self.minsize(App.WIDTH, App.HEIGHT)
        


        # ============ create CTkFrames ============

        # 4 x 3 grid
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=1)
        self.grid_columnconfigure(2,weight=1)
        self.grid_rowconfigure(0, weight=1)
        self.grid_rowconfigure(1, weight=1)
        self.grid_rowconfigure(2, weight=1)
        self.grid_rowconfigure(3, weight=1)
        self.grid_rowconfigure(4, weight=0)



        # make all frames
        for row in range(4):
            for col in range(3):

                name = f"frame{row}{col}"
                
                setattr(self, name, customtkinter.CTkFrame(master=self, corner_radius=10))
                getattr(self,name).grid(row=row, column=col, padx=5, pady=5, sticky="nsew")

                setattr(self,'status_for_' + name,tkinter.StringVar(value=f"Waiting for Input for {name}"))
                setattr(self,'label_for_' + name, customtkinter.CTkLabel(master=getattr(self,name),
                                                    textvariable=getattr(self, 'status_for_' + name),
                                                    
                                                    text_font=("Roboto Medium", -12)))
                getattr(self,'label_for_' + name).grid(row=10, column=0, pady=(0,0), padx=0)

        # Add Button
        self.bottom_frame = customtkinter.CTkFrame(master=self, corner_radius=10)
        self.bottom_frame.grid(row=4, column=1, padx=5, pady=5, sticky="nsew")
        self.button = customtkinter.CTkButton(master=self.bottom_frame,
                                                    text="Begin Running",
                                                    command=self.start_sims,
                                                    width=120, height=30,
                                                    border_width=0,
                                                    corner_radius=8)
        self.button.grid(pady=10, padx=20)
        

    # closing func
    def on_closing(self, event=0):
        self.destroy()

    def start(self):
        self.mainloop()

    # Button press Function
    def start_sims(self):

        pool = multiprocessing.Pool()
        processes = [pool.apply_async(SampleProcess, (self,x,)) for x in range(10)]
        result = [p.get() for p in processes]


# Run Client
if __name__ == "__main__":
    app = App()
    app.start()

What the code is intended to do, is to set up 12 "Frames" for each process running. Then launch each of the processes. Within each process it will assign itself to a specific frame, then "SampleProcess" should update the String Var of that frame to thus change the GUI display, and give new information on the status of the subprocess.

The sample code given works, and outputs the intended error.

How should I restructure the code to account for the pickling issue, by running the GUI separately. Again, the only interaction needed between TKinter and the multiprocesses is to send strings over to update the frames to give the programs current status.

Davidb314
  • 11
  • 2

0 Answers0