G'day Jean-Michel
I read your messages about running the python interpreter in a separate
thread. I found your remarks very interesting, because I have the feeling,
they have to do with the application I am developping.
I'm glad my (unexpectedly long!) comments were useful. After the heartache I put into that threaded interpreter I'm glad it can help someone
else!
psi is working. But quite early in the development, I noticed a
"problem" between the gui and the interpreter. Roughly speaking,
when the interpreter is working, the GUI is "locked"
and can not be used. Of course, this is not always what we expect.
Let's take an exemple, in psi:
from time import sleep
for i in range(3):
print i
sleep(10)
What does happen? The interpreter is working, and only when it has finished
its job (after 30 seconds), 0, 1, 2 will be printed. The correct way should
be: 0 -ten seconds-
1 -ten seconds-, ...
Yep - this is exactly the multithreading thing. If you multithread your app you will have your GUI interactive at all times, and your print
messages will appear immediately (or close to it).
(There are probably some clever alternatives... but I reckon the investment in multithreading is the best)
Its a bit hard to explain in email (easier on paper or in person) but I'll try...
In a single threaded program there is only one thing being executed at a time - seems simple so far.
So if you do this:
i = 3
print i
What you want to happen is eventually this:
myTextCtrl.Append( "3\n" )
On its own, the above will work.
But if you do this:
for i in range(1000):
print i
Its a different story - the one and only thread in your application can't service the GUI because its so busy running the loop.
Another exemple, that is not working in psi:
while 1:
pass
I am unable to break this infinite loop, since I can not catch any event
coming from the gui.
Yep, you got it.
In the threaded version I can do something like:
while 1:
if shell.interrupted( ): break
shell.sleep( 10 )
or, for a simple animation:
rotation = 0
while 1:
if shell.interrupted( ): break
studio.setRotation( rotation )
rotation += 1
studio.updateDisplay( )
shell.sleep( 10 )
The two key things here the shell and studio objects. I'd have something like:
class ShellFacade:
def interrupted( self ):
return self.isInterrupted # NOTE - should have critical sections here to be safe
def requestInterrupt( self ):
self.isInterrupted = 1 # NOTE - should have critical sections here to be safe
def sleep( self, milli ):
os.sleep( milli/1000 ) # NOTE - can't remember the proper thing here
and
class StudioFacade:
def setRotation( self, value ):
postCommand( SetRotationCommand( value ))
def updateDisplay( self ):
postCommand( UpdateDisplayCommand( ))
The StudioFacade, in particular, is my style implementation - you don't have to use command objects but it works for me.
But they key thing points:
- the facade objects (accessible in your shell) communicate back to the main gui thread by posting events. See wxPostEvent.
- the sleep function sleeps in the NON-gui thread (so the GUI is always interactive)
- the interrupted() function allows the loop to be broken.
How to trigger an interrupt?
Something like:
class MyShellCtrl( wxTextCtrl ):
...
def OnChar( ... )
if key is control+c or ESCAPE:
self.interpreter.requestInterrupt( )
I do not know how to solve this type of problem. I tried different thing
like using wxYield() to give the hand to the gui, but this does not help to
much. May be I'm using a wrong strategy.
Yeah - I tried but couldn't figure that out... whilst using threads is a pain to get going it is conceptually much easier to understand (believe it
or not! ...)
Hope this is of some use
Regards
Ellers