pyglet.event.EventDispatcher

156 views
Skip to first unread message

Philippe

unread,
Feb 9, 2012, 4:22:46 AM2/9/12
to cocos2d discuss
Hello again ..

I have an issue with the update of my sprites.
I pasted a small sample that reproduce my problem here:
http://pastebin.com/KrajS6RX

I set 3 ways of changing the mask of my_sprite.

1- user can click on the sripte, it reduce the mask width => it works
well

2- user can press a key on the Layer, the layer use a
pyglet.event.EventDispatcher to push an event, my_sprite receive it =>
it works well too !

3- I go a thread, in my pyglet.event.EventDispatcher that push an
event every 5s. my_sprite receive it, but does not update itself in
the window.
I tried to add the self.draw() instruction, but got an error:
GLException: invalid operation

Any idea ?
If I move the mouse over the window, I got clean refresh, even for
-3-. I would like to have the same, but without moving the mouse
around!

thanks

Philippe

unread,
Feb 9, 2012, 9:10:20 AM2/9/12
to cocos2d discuss
similar problem with Label, but different exception raise:
http://pastebin.com/7Pjr2KAm

I got :
raise GLException(msg)
GLException: invalid operation

In this small sample, a Label in a Layer.
I can update the label.element.text on a key_press.
But if I do it on a Pyglet.Event, I get an exception "GLException:
invalid operation"

I do not see what I am doing wrong :/

claudio canepa

unread,
Feb 9, 2012, 3:33:04 PM2/9/12
to cocos-...@googlegroups.com
I don't really know about threads, but

1. I remember some comments in the line 'calls to openGL must be done from the same thread that acquired the openGL context'. So I don't expect that  ultimately calling to .draw from other thread can work

2. Maybe is an artifact from your sample, but you are modifying self.gap from two different threads. That will explode at some time, when one thread is amidst of changing gap, the other thread kicks in and also tries to change gap

3. Usually code that can be called from multiple threads is explicitly announced as multithread safe ; I don't remember any such comments in the pyglet event docs.

4. Perhaps some of the posts here:

may help you

5. Out of curiosity, what is your real use case for threading with pyglet ? Are you sure you are not killing flies with missiles ? 
  
--
claudio

Philippe

unread,
Feb 9, 2012, 4:20:40 PM2/9/12
to cocos2d discuss
Hello,

In my threading.Thread(target=self.timed_event)...
I simplified timed_event to get a small sample that reproduce my
problem.

But actually, inside timed_event, I have a UDP communication with some
other software.
That's why I need that thread. It's a simple communication that
include a python Queue().
In that thread, when there is something in the Queue, it will be
pushed as a pyglet event for the Layers.

That's the idea ! It works fine in other python software I made.
In cocos, I do not know how to remove that thread. Is there some sort
of "pyglet thread" available for that purpose ?

My real timed_event function is more like:
while self.running:
msg = self.my_queue.get()
self.dispatch_event('on_data_update_from_thread', msg)

On Feb 9, 9:33 pm, claudio canepa <ccanep...@gmail.com> wrote:
> http://search.gmane.org/search.php?group=gmane.comp.python.pyglet.use...

Philippe

unread,
Feb 9, 2012, 4:24:35 PM2/9/12
to cocos2d discuss
you are right for -2-
Actually, in my real code, gap is modified by on_mouse_press or
on_key_press (only by the other thread).
That was juste for the sample.
I should have put a lock() to be cleaner.


On Feb 9, 9:33 pm, claudio canepa <ccanep...@gmail.com> wrote:
> http://search.gmane.org/search.php?group=gmane.comp.python.pyglet.use...

Philippe

unread,
Feb 9, 2012, 4:25:54 PM2/9/12
to cocos2d discuss
forgot a word...
was: "gap is NOT modified"

claudio canepa

unread,
Feb 9, 2012, 5:22:43 PM2/9/12
to cocos-...@googlegroups.com
On Thu, Feb 9, 2012 at 6:20 PM, Philippe <philipp...@gmail.com> wrote:
Hello,

In my threading.Thread(target=self.timed_event)...
I simplified timed_event to get a small sample that reproduce my
problem.

But actually, inside timed_event, I have a UDP communication with some
other software.
That's why I need that thread. It's a simple communication that
include a python Queue().
In that thread, when there is something in the Queue, it will be
pushed as a pyglet event for the Layers.

That's the idea ! It works fine in other python software I made.
In cocos, I do not know how to remove that thread. Is there some sort
of "pyglet thread" available for that purpose ?

My real timed_event function is more like:
while self.running:
   msg = self.my_queue.get()
   self.dispatch_event('on_data_update_from_thread', msg)


If your 
    self.dispatch_event('...')
is in the main thread that would look a lot better than in the sample.
And, queues from Queue seem to be thread-safe [1]

But some comments in the threads [2][3] suggest that maybe the pyglet mainloop code should be changed 

Probably the better would be to rewrite the sample code using a queue, with the call to dispatch_event in the main thread, and maybe post at the pyglet mail list



--
claudio

Philippe

unread,
Feb 10, 2012, 2:27:39 AM2/10/12
to cocos2d discuss
I could probably pass datas from my thread to my Sprite using a Queue.
But actually, that thread owns the datas for all my layers and
sprites.
And the datas are updated by a distant application (UDP)..
I do not think that the queue could be shared between all my
cocosnodes.

I saw the pyglet user-group. it deals a lot on how to embed pyglet in
a thread.
Actually, maybe what I need is to embed my thread into pyglet.

If I can share a queue between my thread and pyglet main thread, it
will work.
I know how to deal with queue, but I do not know where to do that ?
In the Director ?
I should subclass it ?

thanks for you help !

On Feb 9, 11:22 pm, claudio canepa <ccanep...@gmail.com> wrote:

Philippe

unread,
Feb 10, 2012, 5:13:06 AM2/10/12
to cocos2d discuss
a quick and dirty workaround ...

http://pastebin.com/8ZpyEZ6U

use a Lock() to share a list of data from director.
use schedule_interval to retrieve datas.

it's really sad to update the datas through a schedule_interval
but works..
I hope I can find a way to use events later !

claudio canepa

unread,
Feb 10, 2012, 9:22:32 AM2/10/12
to cocos-...@googlegroups.com
On Fri, Feb 10, 2012 at 4:27 AM, Philippe <philipp...@gmail.com> wrote:
I could probably pass datas from my thread to my Sprite using a Queue. 
But actually, that thread owns the datas for all my layers and
sprites.
And the datas are updated by a distant application (UDP)..
I do not think that the queue could be shared between all my
cocosnodes.


I was thinking the UDP thread updating data to a model in the main thread, model an EventDispatcher that further updates all the parts in the view.

To that end I mixed a bit of [1] and [2] to get a script with a fake app not using pyglet-cocos: a simple main loop which also manages to get data from other thread. See pastebin

Then I wanted to replace the fake app with a cocos app; essentially adapting the pump function

Whitout using MVC the thing would be shorter, but seems that you have a model in your app. So, using the Tetrico [0] style of MVC and bits of your first code I wrote a second script where the UDP thread sends data to a queue, a Controller layer in the main thread moves that data to the model , from were it founds its way to the view.

All very rought, and I'm sure it can be vastly improved
See at your own risk at

 

--
claudio

Philippe

unread,
Feb 11, 2012, 10:12:01 AM2/11/12
to cocos2d discuss
nice help ! thank you.

about the queue, personally, I use the thread safe Queue.Queue()
http://docs.python.org/library/queue.html#module-Queue

then, something like that
while running:
msg = my_queue.get(True, 0.1)

no need of threading.Lock() like that.

I checked the schedule function you used in the GameCtrl (line 65).
from the doc "Schedule a function to be called every frame"
What does it mean "every frame" ?
Is there a fix framerate in cocos2D ?

I will try to do like above (in a thread):
while running:
msg = my_queue.get(True, 0.1)
meaning that it waits all the time, and sometime get something from
the queue.
the 0.1 means that it will not wait for ever anyway. then, if
runnning==false, the thread will exit (simple way to close the
application).

I think that with you 2nd sample, I can do what I need.
thanks a lot !

On Feb 10, 3:22 pm, claudio canepa <ccanep...@gmail.com> wrote:
> On Fri, Feb 10, 2012 at 4:27 AM, Philippe <philippe.cr...@gmail.com> wrote:
> > I could probably pass datas from my thread to my Sprite using a Queue.
>
> But actually, that thread owns the datas for all my layers and> sprites.
> > And the datas are updated by a distant application (UDP)..
> > I do not think that the queue could be shared between all my
> > cocosnodes.
>
> I was thinking the UDP thread updating data to a model in the main thread,
> model an EventDispatcher that further updates all the parts in the view.
>
> To that end I mixed a bit of [1] and [2] to get a script with a fake app
> not using pyglet-cocos: a simple main loop which also manages to get data
> from other thread. See pastebinhttp://pastebin.com/ucMsUCEN
>
> Then I wanted to replace the fake app with a cocos app; essentially
> adapting the pump function
>
> Whitout using MVC the thing would be shorter, but seems that you have a
> model in your app. So, using the Tetrico [0] style of MVC and bits of your
> first code I wrote a second script where the UDP thread sends data to a
> queue, a Controller layer in the main thread moves that data to the model ,
> from were it founds its way to the view.
>
> All very rought, and I'm sure it can be vastly improved
> See at your own risk athttp://pastebin.com/N45vUa8w
>
> [0]http://code.google.com/p/los-cocos/source/browse/#svn%2Ftrunk%2Fsampl...

claudio canepa

unread,
Feb 11, 2012, 2:31:21 PM2/11/12
to cocos-...@googlegroups.com
On Sat, Feb 11, 2012 at 12:12 PM, Philippe <philipp...@gmail.com> wrote:
nice help ! thank you.

about the queue, personally, I use the thread safe Queue.Queue()
http://docs.python.org/library/queue.html#module-Queue

then, something like that
while running:
   msg = my_queue.get(True, 0.1)

no need of threading.Lock() like that.

I checked the schedule function you used in the GameCtrl (line 65).
from the doc "Schedule a function to be called every frame"
What does it mean "every frame" ?
Is there a fix framerate in cocos2D ?


While starting the window  with director.init(...) you can pass a kw-param vsync = True / False
Default is True, which means pyglet will try to synchronize with the vertical retrace.
But, if your code don't have any schedule() call, it will try to spare battery and cpu / gpu , thus calling about 10 times per second.

You can see the actual fps with
director.show_FPS = True   

 
I will try to do like above (in a thread): 
 while running: 
   msg = my_queue.get(True, 0.1)
meaning that it waits all the time, and sometime get something from
the queue.
the 0.1 means that it will not wait for ever anyway. then, if
runnning==false, the thread will exit (simple way to close the
application).

Why not get with
    try:
        msg = my_queue.get(False)
    except Queue.Empty:
        return
    # process message

This way you don't spend time waiting in an empty queue


I think that with you 2nd sample, I can do what I need.
thanks a lot !

Glad

 No doubt you will find some interesting things with threading + pyglet, consider to take notes and blog / post somewhere your findings

--
claudio

Philippe

unread,
Feb 12, 2012, 6:13:23 AM2/12/12
to cocos2d discuss
> try:
> msg = my_queue.get(False)
> except Queue.Empty:
> return
> # process message
I think that this spends a lot of CPU checking the queue (for nothing
most of the time)

something like this, will wait, using no CPU, most of the time
and sometimes, will get something out of the queue and process it.

> t = threading.Thread(target=check)
> def check():
> running = True
> while running:
> msg = my_queue.get()
> if msg == 'stop':
> runnning = False
> else:
> process_msg(msg)

but, it's in a thread :/

I am not a specialist, so maybe, this is not correct !
but it's what I think...

Thank you for your explanation of pyglet FPS. It's clear to me now.
Still, I do not find how to emit a event that will redraw my window,
or part of it.
Your code with use of schedule() is the best solution so far.

I feel sad to not be able to simply refresh the objects that need to,
based on an event (instead of checking periodically).
I will search little but more !


On 11 fév, 20:31, claudio canepa <ccanep...@gmail.com> wrote:

Philippe

unread,
Feb 12, 2012, 8:50:18 AM2/12/12
to cocos2d discuss
come back on that
> try:
> msg = my_queue.get(False)
> except Queue.Empty:
> return
> # process message
I was wrong.
As you put it in the schedule function, it will not take that much
CPU, of course.

Maybe, instead of using a queue, in your sample, I will use a simple
dict with a lock.
I have many Consumers (view) for the same variable.
If one view get the message out of the queue, the other one will not
get it.

claudio canepa

unread,
Feb 12, 2012, 3:51:34 PM2/12/12
to cocos-...@googlegroups.com


On Sun, Feb 12, 2012 at 8:13 AM, Philippe <philipp...@gmail.com> wrote:
snip 
something like this, will wait, using no CPU, most of the time
and sometimes, will get something out of the queue and process it.

>  t = threading.Thread(target=check)
>  def check():
>      running = True
>      while running:
>          msg = my_queue.get()
>          if msg == 'stop':
>              runnning = False
>          else:
>              process_msg(msg)

but, it's in a thread :/

I am not a specialist, so maybe, this is not correct !
but it's what I think...


The choice depends on your application style:

Style 1: highly static, meaning no animations, particles. Basically the view only changes in response to UDP messages.
There your use of a *blocking* get may work, because no message implies no changes  in the view.
But blocking the main thread can have side effects with the pyglet scheduling, and add lag to the response of local controllers (keys, mouse)

Style 2: you want smooth animations, maybe particles.
Non blocking would be my choice here: you give the main thread the most of CPU available, a more smooth time flow, and not mess pyglet timings.

 
Thank you for your explanation of pyglet FPS. It's clear to me now. 
Still, I do not find how to emit a event that will redraw my window,
or part of it.
Your code with use of schedule() is the best solution so far.

I feel sad to not be able to simply refresh the objects that need to,
based on an event (instead of checking periodically).
I will search little but more !



Roughly, the standard for Open GL drawing is to redraw all the screen at each frame,
Partial refreshes are hacks or stunts.

--
claudio

Philippe

unread,
Feb 13, 2012, 2:54:00 AM2/13/12
to cocos2d discuss
Thanks again for you explaination.
I understand better your sample.

One layer using schedule() to check messages works !

In my application, I do not have particles. But I have some actions
(FadeIn, MoveBy, ...)

About your queue, then, it depends on how fast it gets filled by the
UDP thread.
Seems a better idea to empty it (or try to) at each call of the
scheduled function.
But should hope for a fast schedule, and slower UDP anyway :)


On Feb 12, 9:51 pm, claudio canepa <ccanep...@gmail.com> wrote:

Philippe

unread,
Feb 15, 2012, 4:45:34 AM2/15/12
to cocos2d discuss
finally, instead of
self.schedule(self.update_data)

I will use
self.schedule_interval(self.update_data, 0.5)

To save some CPU.
Reply all
Reply to author
Forward
0 new messages