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()