pythonmultithreadinguser-interfacetkinterpysimplegui

Main thread is not in main loop tkinter, multi-thread with multiple pysimplegui or tkinter objects


As far as I can see, this is a constant problem when using tkinter since it is not multi-threaded. I have read a lot about this issue but I don't know how I can solve it in my case.

My application consists of a plotly dash running in a local server with a button that calls a function that does a series of processes with the excel. At first I got main thread is not in main loop when pressing the button. Nevertheless, setting a daemon I managed to avoid it. However, if I press the button again the program stops working and the error mentioned above appears.

This is a summarized script of my plotly dash:

def interact_callbacks(self):
        @callback(
            Output('add-automatically-button', 'children'),
            Input('add-automatically-button', 'n_clicks')
        )
        def update_output(n_clicks):
            if n_clicks is not None:
                t = threading.Thread(target=startInteraction)
                t.setDaemon(True)
                t.start()
                return 'Clicked!'
            else:
                return 'Add automatically'

This is the function startInteraction() that is in a different .py

def startInteraction():
    gui = GUI()
    gui.get_excel_path()
    path=gui.path

    confirmation = gui.confirmationPopUp(path)
    sys.stderr.write("***OK: selected**\n")
    
    if(confirmation):
        sys.stderr.write("***OK***\n")
        defectLoader(path)
        
    else:
        sys.stderr.write("***CANCEL***\n")
        gui.errImport()
        exit()

This is my gui class (I have more pop-ups in this class)

class GUI():
    def __init__(self):
        self.path = None    

    def get_excel_path(self):
        size = {
            "size": (40, 1)
        }

        layout = [
            [sg.Text('Select Database (Excel):')],
            [sg.Input(**size), sg.FileBrowse(file_types=(("Excel File", "*.xlsx"),))],
            [sg.OK(), sg.Cancel()]
        ]

        window = sg.Window('File Browser', layout)
        event, values = window.read()
        window.close()

        try:
            file_path = values[0]
            if event == 'Cancel' or not file_path:
                sys.stderr.write("***CANCEL***\n")
                raise Exception('No file selected')

            self.path= file_path
        except Exception as e:
            sg.popup(f'Error: {e}')
            exit()

How can I write a thread queue in this code? Is there other option?


Solution

  • This is a typical problem when trying to combine the plotly dash library with tkinter or pysimplegui. I had a similar problem a few years ago and solved it by doing the following:

    p = Process(target=yourFunctionName)
    p.start()
    

    Do not forget to do the following import:

    from multiprocessing import Process
    

    In your case:

    def interact_callbacks(self):
        @callback(
            Output('add-automatically-button', 'children'),
            Input('add-automatically-button', 'n_clicks')
        )
        def update_output(n_clicks):
            if n_clicks is not None:
                p = Process(target=startInteraction)
                p.start()
                return 'Clicked!'
            else:
                return 'Add automatically'
    

    I hope this solves your problems and you can finish your app.