Home > database >  Is there any way for PySimpleGUI or tkinter to run a thread inside a thread? I tried using both Thre
Is there any way for PySimpleGUI or tkinter to run a thread inside a thread? I tried using both Thre

Time:10-16

My problem is running a thread so that my GUI does not freeze which works fine with a window.perform_long_operation. But the problem arises when I want a popup to show up while running the thread and then wait for user input from the popup to continue the function. I am pasting a very small part of my full GUI code.

from threading import Thread
import time
import PySimpleGUI as sg
import queue


# A function to send text to an element one character at a time with a delay of 0.08s

def slow_type(el, text, delay=0.08):
    for character in text:
        el.send_keys(character)
        time.sleep(delay)


def cont_auto(que):
    # cont_auto_value = sg.PopupYesNo("Continue?")
    # que.put(item=cont_auto_value)
    # new_wo_window.write_event_value(key="-MASUM-", value=que.put(item=cont_auto_value))
    
**
TRIED USING THE POPUP AS A SEPARATE FUNCTION AND PUTTING THE VALUE IN A QUEUE. STILL 
DOES N0T WORK.**


def google_search(que):
    # sg.cprint_set_output_destination(window=main_window, multiline_key="-MAIN-ML-")

    from selenium.webdriver.common.by import By
    from selenium import webdriver
    from selenium.webdriver.common.action_chains import ActionChains
    from page_locators.PageLocators import dir_list

    opt = webdriver.ChromeOptions()

    opt.add_experimental_option("prefs", {
        'download.default_directory': dir_list.new_folder,
        'download.prompt_for_download': False,
        'download.directory_upgrade': True,
        'plugins.always_open_pdf_externally': True
    })

    opt.add_argument("--kiosk-printing")

    driver = webdriver.Chrome(r"C:\Users\me_a1\Desktop\chromedriver.exe", 
                              chrome_options=opt)

    action = ActionChains(driver)

    driver.get("https:www.google.com")

    driver.maximize_window()

    # Wait 2 seconds for elements to load

    time.sleep(2)

    item = que.get()

    # new_wo_window.write_event_value(key="-GOOGLE-", value=item)

    print(item)

    if item == "Yes":

        search_textbox = driver.find_element(By.XPATH, "//input[@class='gLFyf gsfi']")

        action.click(search_textbox).send_keys("real madrid").perform()

        time.sleep(2)

        driver.quit()

    else:

        search_textbox = driver.find_element(By.XPATH, "//input[@class='gLFyf gsfi']")

        action.click(search_textbox).send_keys("uefa").perform()

        time.sleep(2)

        driver.quit()

    que.task_done()


def new_wo():

    new_wo_layout = [[sg.Text("Search Term:".upper(), pad=((5, 0), (0, 0)))],
                     [sg.InputText("", key="search", pad=((5, 0), (0, 0)))]
                     ]

    layout_new_wo = [[sg.Column(new_wo_layout)],
                     [sg.Button("Create WO", pad=((10, 0), (15, 0))), sg.Button("Cancel", pad=((20, 0), (15, 0)))]]

    new_wo_window = sg.Window(title=window_title(), layout=layout_new_wo, finalize=True, modal=True)

    while not new_wo_window.is_closed():

        new_wo_event, new_wo_values = new_wo_window.read()

        search = [i.strip().upper()
                       for i in str(new_wo_values["search"]).split("_.)(") if i != ""]

        if new_wo_event in [sg.WIN_CLOSED, "Cancel"]:

            new_wo_window.close()

            break

        elif new_wo_event == "Create WO":

            if all(len(new_wo_info_list) != 0 for new_wo_info_list in [search]):

                create_wo = sg.PopupYesNo("Start search?".upper(),
                                          "(please check your data before clicking 
                                          yes)".upper(),
                                          f"Search terms: {''.join(search)}",
                                          modal=True, title="wo create data".upper())

                if create_wo == "Yes":

                    # que = queue.Queue()
                    
                    # function_thread = Thread(target=google_search, args=(que,), daemon=True)
                    # function_thread.start()

                    # continue_automation_popup_thread = Thread(target=cont_auto, args=(que,))
                    # continue_automation_popup_thread.start()

                    # continue_automation_popup_thread.join()

                    # wait for all work to be processed

                    # que.join()

                else:

                    sg.Popup("New WO creation cancelled!".upper(), modal=True, title="wo 
                              cancel".upper())

            else:

                sg.Popup("Please check that all data is provided".upper(), modal=True,
                         title="Missing Data".upper())


def window_title():
    title = []

    for key, value in main_values.items():

        if value:
            title.append(str(key).upper())

    return title[0]


# Define the layout of the program's first window

sg.theme_global("DarkBlack1")

text_list = ["New WO"]

layout = [[sg.Radio(str(text_list[i].upper()), enable_events=True, key=str(text_list[i]), group_id=0)]
          for i in range(0, len(text_list))]

layout  = [[sg.Multiline(size=(65, 23), key="-MAIN-ML-", pad=((5, 0), (0, 0)))],
           [sg.Button("Cancel", pad=((5, 0), (10, 6)))]]

layout  = [[sg.Menubar(menu_definition=[["SETTINGS", ("COLOR SCHEME",
                                                      ["Dark Mode", "Light Mode", "Rose Pink",
                                                       "Bright Red", "Midnight Blue"])]])]]

layout_frame = [[sg.Frame(title="Select Option".upper(), title_color="yellow", layout=layout)]]

main_window = sg.Window(title="Work Order Automation".upper(), layout=layout_frame, element_justification='c',
                        finalize=True)

while True:

    main_event, main_values = main_window.read()

    """End program if user closes window or
     presses the Cancel button"""

    if main_event == sg.WIN_CLOSED or main_event == "Cancel":
        break

    if main_event == "New WO":

        new_wo() ---> The Layout that should run the thread and show the POPUP

        continue

The exception trace is as follows:

Exception ignored in: <function Variable.__del__ at 0x000002A95AF86B90>
Traceback (most recent call last):
  File "C:\Users\me_a1\AppData\Local\Programs\Python\Python310\lib\tkinter\__init__.py", line 388, in __del__
    if self._tk.getboolean(self._tk.call("info", "exists", self._name)):
RuntimeError: main thread is not in main loop


Exception ignored in: <function Variable.__del__ at 0x000002A95AF86B90>
Traceback (most recent call last):
  File "C:\Users\me_a1\AppData\Local\Programs\Python\Python310\lib\tkinter\__init__.py", line 388, in __del__
    if self._tk.getboolean(self._tk.call("info", "exists", self._name)):
RuntimeError: main thread is not in main loop


Exception ignored in: <function Variable.__del__ at 0x000002A95AF86B90>
Traceback (most recent call last):
  File "C:\Users\me_a1\AppData\Local\Programs\Python\Python310\lib\tkinter\__init__.py", line 388, in __del__
    if self._tk.getboolean(self._tk.call("info", "exists", self._name)):
RuntimeError: main thread is not in main loop

Exception in thread Thread-2 (cont_auto):
Traceback (most recent call last):
  File "C:\Users\me_a1\AppData\Local\Programs\Python\Python310\lib\threading.py", line 1009, in _bootstrap_inner
    self.run()
  File "C:\Users\me_a1\AppData\Local\Programs\Python\Python310\lib\threading.py", line 946, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\me_a1\Desktop\WO_Automation\GUI\testing.py", line 18, in cont_auto
    cont_auto_value = sg.PopupYesNo("Continue?")
  File "C:\Users\me_a1\AppData\Local\JetBrains\PyCharmCE2022.1\demo\PyCharmLearningProject\venv\lib\site-packages\PySimpleGUI\PySimpleGUI.py", line 20106, in popup_yes_no
    return popup(*args, title=title, button_type=POPUP_BUTTONS_YES_NO, background_color=background_color,
  File "C:\Users\me_a1\AppData\Local\JetBrains\PyCharmCE2022.1\demo\PyCharmLearningProject\venv\lib\site-packages\PySimpleGUI\PySimpleGUI.py", line 19390, in popup
    button, values = window.read()
  File "C:\Users\me_a1\AppData\Local\JetBrains\PyCharmCE2022.1\demo\PyCharmLearningProject\venv\lib\site-packages\PySimpleGUI\PySimpleGUI.py", line 10072, in read
    results = self._read(timeout=timeout, timeout_key=timeout_key)
  File "C:\Users\me_a1\AppData\Local\JetBrains\PyCharmCE2022.1\demo\PyCharmLearningProject\venv\lib\site-packages\PySimpleGUI\PySimpleGUI.py", line 10143, in _read
    self._Show()
  File "C:\Users\me_a1\AppData\Local\JetBrains\PyCharmCE2022.1\demo\PyCharmLearningProject\venv\lib\site-packages\PySimpleGUI\PySimpleGUI.py", line 9883, in _Show
    StartupTK(self)
  File "C:\Users\me_a1\AppData\Local\JetBrains\PyCharmCE2022.1\demo\PyCharmLearningProject\venv\lib\site-packages\PySimpleGUI\PySimpleGUI.py", line 16817, in StartupTK
    root = tk.Toplevel(class_=window.Title)
  File "C:\Users\me_a1\AppData\Local\Programs\Python\Python310\lib\tkinter\__init__.py", line 2650, in __init__
    BaseWidget.__init__(self, master, 'toplevel', cnf, {}, extra)
  File "C:\Users\me_a1\AppData\Local\Programs\Python\Python310\lib\tkinter\__init__.py", line 2601, in __init__
    self.tk.call(
RuntimeError: main thread is not in main loop

The solution can be overcome if I run the function in the main "while" loop of the top-level GUI, but I want it to run in the sub-GUI that opens after clicking on the radio button.

For simplicity's sake, I have only provided the minimum GUI code if the problem arises in. There are more radio buttons that open sub-GUIs that perform other functions. It is only the "New WO" radio button whose function needs a popup right before the end of the script. I have replaced the long function with a google_search() function again to simplify everything. Hopefully, this will allow others to read and understand the code much more quicker.

CodePudding user response:

Maybe you can split the code in one thread to different threads.

Demo code

from time import sleep
import PySimpleGUI as sg

def func(win, index):
    for i in range(0, 51):
        sleep(0.1)
        win.write_event_value('Update', (f'P{index}', i))

sg.theme('DarkBlue3')
layout = [
    [sg.Text('', size=(50, 1), relief='sunken', font=('Courier', 11), text_color='yellow', background_color='black',key=f'P{i}')] for i in (1, 2)]   [
    [sg.Button('Start')],
]
window = sg.Window("Title", layout)
sg.theme('DarkBlue4')

while True:

    event, values = window.read()

    if event == sg.WIN_CLOSED:
        break
    elif event == 'Start':
        window['Start'].update(disabled=True)
        window['P1'].update('')
        window['P2'].update('')
        window.perform_long_operation(lambda win=window, index=1:func(win, index), "P1 Done")
    elif event == "P1 Done":
        if sg.popup_yes_no("Step 2 ?") == 'Yes':
            window.perform_long_operation(lambda win=window, index=2:func(win, index), "P2 Done")
        else:
            window['Start'].update(disabled=False)
    elif event == "P2 Done":
        window['Start'].update(disabled=False)
    elif event == 'Update':
        key, i = values[event]
        window[key].update('█'*i)

window.close()

enter image description here

  • Related