I am making a small program that gets the latest revenue from a webshop, if its more than the previous amount it makes a sound, I am using Pyglet but I get errors because its not being called from the main thread. I would like to know how to call a method on the main thread. see error below:
'thread that imports pyglet.app' RuntimeError: EventLoop.run() must be called from the same thread that imports pyglet.app
def work ():
threading.Timer(5, work).start()
file_Name = "save.txt"
lastRevenue = 0
data = json.load(urllib2.urlopen(''))
newRevenue = data["revenue"]
if (os.path.getsize(file_Name) <= 0):
with open(file_Name, "wb") as f:
f.write('%d' % newRevenue)
f.flush()
with open(file_Name, "rb") as f:
lastRevenue = float(f.readline().strip())
print lastRevenue
print newRevenue
f.close()
if newRevenue > lastRevenue:
with open(file_Name, "wb") as f:
f.write('%f' % newRevenue)
f.flush()
playsound()
def playsound():
music = pyglet.resource.media('cash.wav')
music.play()
pyglet.app.run()
work()
It's not particularly strange. work
is being executed as a separate thread from where pyglet
was imported.
pyglet.app
when imported sets up a lot of context variables and what not. I say what not because I actually haven't bothered checking deeper into what it actually sets up.
And OpenGL can't execute things out of it's own context (the main thread where it resides). There for you're not allowed to poke around on OpenGL from a neighboring thread. If that makes sense.
However, if you create your own .run()
function and use a class based method of activating Pyglet you can start the GUI from the thread.
This is a working example of how you could set it up:
import pyglet
from pyglet.gl import *
from threading import *
# REQUIRES: AVBin
pyglet.options['audio'] = ('alsa', 'openal', 'silent')
class main(pyglet.window.Window):
def __init__ (self):
super(main, self).__init__(300, 300, fullscreen = False)
self.x, self.y = 0, 0
self.bg = pyglet.sprite.Sprite(pyglet.image.load('background.jpg'))
self.music = pyglet.resource.media('cash.wav')
self.music.play()
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def render(self):
self.clear()
self.bg.draw()
self.flip()
def run(self):
while self.alive == 1:
self.render()
if not self.music.playing:
self.alive = 0
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
class ThreadExample(Thread):
def __init__(self):
Thread.__init__(self)
self.start()
def run(self):
x = main()
x.run()
Test_One = ThreadExample()
Note that you still have to start the actual GUI code from within the thread.
Seeing as mixing threads and GUI calls is a slippery slope, I would suggest you go with a more cautious path.
from threading import *
from time import sleep
def is_main_alive():
for t in enumerate():
if t.name == 'MainThread':
return t.isAlive()
class worker(Thread):
def __init__(self, shared_dictionary):
Thread.__init__(self)
self.shared_dictionary
self.start()
def run(self):
while is_main_alive():
file_Name = "save.txt"
lastRevenue = 0
data = json.load(urllib2.urlopen(''))
newRevenue = data["revenue"]
if (os.path.getsize(file_Name) <= 0):
with open(file_Name, "wb") as f:
f.write('%d' % newRevenue)
f.flush()
with open(file_Name, "rb") as f:
lastRevenue = float(f.readline().strip())
print lastRevenue
print newRevenue
f.close()
if newRevenue > lastRevenue:
with open(file_Name, "wb") as f:
f.write('%f' % newRevenue)
f.flush()
#playsound()
# Instead of calling playsound() here,
# set a flag in the shared dictionary.
self.shared_dictionary['Play_Sound'] = True
sleep(5)
def playsound():
music = pyglet.resource.media('cash.wav')
music.play()
pyglet.app.run()
shared_dictionary = {'Play_Sound' : False}
work_handle = worker(shared_dictionary)
while 1:
if shared_dictionary['Play_Sound']:
playsound()
shared_dictionary['Play_Sound'] = False
sleep(0.025)
It's a rough draft of what you're looking for.
Basically some sort of event/flag driven backend that the Thread and the GUI can use to communicate with each other.
Essentially you have a worker thread (just as you did before), it checks whatever file you want every 5 seconds and if it detects newRevenue > lastRevenue
it will set a specific flag to True
. Your main loop will detect this change, play a sound and revert the flag back to False.
I've by no means included any error handling here on purpose, we're here to help and not create entire solutions. I hope this helps you in the right direction.