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.
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()
import turtle
def replaygame():
replay_label = turtle.Turtle()
replay_label.speed(0)
replay_label.color("White")
replay_label.penup()
replay_label.setposition(-290,280)
againornot = replay_label.textinput("Do you want to play again Y/N ?",False, align = "right", font = ("Arial" , 20, "normal"))
if againornot == Y:
True
else:
False
replaygame()
I'm not sure what the problem is. I imported turtle and I went through it twice. Here's the error I'm getting:
Traceback (most recent call last):
File "/Users/nn/Documents/sfgh.py", line 189, in <module>
replaygame()
File "/Users/nn/Documents/sfgh.py", line 158, in replaygame
againornot = replay_label.textinput("Do you want to play again Y/N ?",False, align = "right", font = ("Arial" , 20, "normal"))
AttributeError: 'Turtle' object has no attribute 'textinput'
When you have a minute, please post your error. In the meantime, I presume the error stems from here:
Try editing this line to put quotes around the Y. againornot == āYā
(Apologies for poor the formatting, Iām using my phone.)
Try it like this :
import turtle
def replaygame():
replay_label = turtle
replay_label.speed(0)
replay_label.color("White")
replay_label.penup()
replay_label.setposition(-290,280)
againornot = replay_label.textinput('Play Again', "Do you want to play again Y/N ?")
if againornot == Y:
True
else:
False
replaygame()
I have my code for a game here. I have commented out the displayScore(score) call in the main function to allow the program to run. When that call is uncommented the program window closes immediately after opening.
The objective of the function displayScore is to display the game score in the top left corner. Which also needs to be displayed in the right corner for the opposing player's score.
Here is the code for the game with displayScore commented out in the main function so you can run the game and everything will work. Uncomment it to see where the problem is:
ball = ballmovement(ball, ballDirX, ballDirY)
ballDirX, ballDirY = collisionwithedges(ball, ballDirX, ballDirY)
score = checkscore(paddle1, ball, score, ballDirX)
ballDirX = ballDirX * collisionwithpaddles(ball, paddle1, paddle2, ballDirX)
pygame.display.update() #updates the display to clear surface per the frame rate
FRAMECLOCK.tick(FRAMERATE) #Sets the Frames of program to defined rate
if __name__=='__main__':
main()
Just replace the line
displayScore(score)
By:
displayScore(str(score))
You are trying to use a number instead of a string to the argument of render ;) Score is an int and BASICFONT.render((score), True, WHITE)
asks for score to be a string or an array of bytes :)
I found the solution only by reading the console output which was a good indication ^^
Traceback (most recent call last):
File "test.py", line 130, in <module>
main()
File "test.py", line 118, in main
displayScore(score)
File "test.py", line 71, in displayScore
resultSurf = BASICFONT.render((score), True, WHITE)
TypeError: text must be a unicode or bytes
I have a problem with pyspotify. I'm trying to search for an artist name, get a list of their top tracks and then play one of them. This is my code segment:
search_string = "artist:"+artist_to_find
result = session.search(search_string)
result.load()
print result.track_total
browser = result.artists[0].browse()
browser.load()
print browser.tophit_tracks
for track in browser.tophit_tracks:
print track.name, track
toptracks = browser.tophit_tracks
print "error check"
if toptracks!=-1:
print "------------------"
tracktoplay=choice(toptracks)
rand = randint(0,10)
print "random track number = %s" %rand
tracktoplay = browser.tophit_tracks[rand]
print tracktoplay.link
print tracktoplay.name
print "------------------"
session.player.load(tracktoplay)
session.player.play()
I frequently call this code without the previous track finishing. Perodically (every few tracks) I get the following error:
random track number = 7
spotify:track:6vQN2a9QSgWcm74KEZYfDL
Take A Chance On Me
------------------
Traceback (most recent call last):
File "../jj.py", line 146, in <module>
app.run()
File "../jj.py", line 116, in run
conversation.handleForever()
File "/home/mh/Projects/jjo/client/conversation.py", line 44, in handleForever
listen(self)
File "/home/mh/Projects/jjo/client/new_play.py", line 110, in listen
play(self,response)
File "/home/mh/Projects/jjo/client/new_play.py", line 194, in play
session.player.load(tracktoplay)
File "/usr/local/lib/python2.7/dist-packages/spotify/player.py", line 45, in load
self._session._sp_session, track._sp_track))
File "/usr/local/lib/python2.7/dist-packages/spotify/error.py", line 30, in maybe_raise
raise LibError(error_type)
spotify.error.LibError: The track cannot be played
Can anyone advise on what I'm doing wrong?
Thanks
I don't have experiece on pyspotify, but I was looking at it out of curiosity.
If I was you I would check the PlayerState is LOADED before I call play()
Hope it helps.
I have updated this code with modifications suggested by Reticality. I would like to access the value of hands_count which resides within the array of packets[] and or the array type[] below.
def history2packets(self, history, game_id, cache):
game_index = 0
player_list_index = 7
packets = []
for event in history:
self.event = event
type = event[0]
if type == "game":
(type, level, hand_serial, hands_count, time, variant, betting_structure, player_list, dealer, serial2chips) = event
if len(serial2chips) > 1:
nochips = 0
for (serial, chips) in serial2chips.iteritems():
if serial == 'values':
continue
packets.append(PacketPokerPlayerChips(game_id = game_id,
serial = serial,
bet = nochips,
money = chips))
packets.append(PacketPokerInGame(game_id = game_id,
players = player_list))
if self.previous_dealer == dealer:
previous_dealer = -1
else:
previous_dealer = self.previous_dealer
packets.append(PacketPokerDealer(game_id = game_id,
dealer = dealer,
previous_dealer = previous_dealer))
self.previous_dealer = dealer
packets.append(PacketPokerStart(game_id = game_id,
hand_serial = hand_serial,
hands_count = hands_count,
time = time,
level = level))
and use that value in another function like so:
def autoDeal(self):
self.cancelDealTimeout()
if not self.allReadyToPlay():
for player in self.game.playersAll():
if player.getUserData()['ready'] == False:
for avatar in self.avatar_collection.get(player.serial):
if self.factory.verbose > 1:
self.message("Player %d marked as having a bugous PokerProcessingHand protocol" % player.serial)
avatar.bugous_processing_hand = True
if self.event[2] != 0:
self.beginTurn()
self.update()
else:
reactor.callLater(10, self.beginTurn)
self.update()
I am still getting an error but a different one.
2015-02-19 22:10:11-0500 [-] Unhandled Error
Traceback (most recent call last):
File "/usr/lib/python2.6/dist-packages/twisted/application/app.py", line 445, in startReactor
self.config, oldstdout, oldstderr, self.profiler, reactor)
File "/usr/lib/python2.6/dist-packages/twisted/application/app.py", line 348, in runReactorWithLogging
reactor.run()
File "/usr/lib/python2.6/dist-packages/twisted/internet/base.py", line 1170, in run
self.mainLoop()
File "/usr/lib/python2.6/dist-packages/twisted/internet/base.py", line 1179, in mainLoop
self.runUntilCurrent()
--- <exception caught here> ---
File "/usr/lib/python2.6/dist-packages/twisted/internet/base.py", line 778, in runUntilCurrent
call.func(*call.args, **call.kw)
File "/usr/lib/python2.6/dist-packages/pokernetwork/pokertable.py", line 738, in autoDeal
if self.event[2] != 0:
exceptions.AttributeError: PokerTable instance has no attribute 'event'
Both function are within the same module and class. What else could be wrong?
The variable event is defined within the local scope of the method history2packets. If you refer to it within another scope (e.g. a function or method) it will raise a NameError, as it was not defined in that scope.
A quick way to fix this is putting a line saying global event at the top of both methods, but this is usually frowned upon. Another way is returning event from history2packets and make the parameters of autoDeal autoDeal(self, event). This means you could call:
e = history2packets(...)
# 5 lines later
autodeal(e)
Another way to do this assumes history2packets and autoDeal are 2 methods of the same class. Modify dome code here in history2packets:
packets = []
for event in history:
self.event = event
type = event[0]
And then, make autoDeal
def autoDeal(self):
self.cancelDealTimeout()
if not self.allReadyToPlay():
# Some lines later
if self.event[2] != 0: # This is the changed line
# Same as before
The variable event is defined in history2packets, but it is not global. So when you refer to a variable with this name in a different function, that raises a NameError.