The interact loop

It is possible to replace the MainLoop. Take a look at the code in samples/mainloop/mainloop.py and also at the docs for wx.EventLoop. It can work for situations like this where you need to blend two event loops, but if you can do it with timers or an alternate thread that might be a better solution.

···

On 6/20/12 7:59 PM, Bruce Sherwood wrote:

I realize that wxPython's MainLoop() doesn't return but continually
dispatches events to callbacks, but is there a way that the rate
function shown above could drive the internals of MainLoop, and thereby
trigger event callbacks?

--
Robin Dunn
Software Craftsman

I tried samples/mainloop/mainloop.py on Windows and Mac Cocoa, putting a simple print(‘.’) in the inner loop. Works fine on Windows. On Mac Cocoa you get just one print output, and there are major problems quitting.

···

On Thursday, June 21, 2012 11:47:04 PM UTC-6, Robin Dunn wrote:

On 6/20/12 7:59 PM, Bruce Sherwood wrote:

It is possible to replace the MainLoop. Take a look at the code in
samples/mainloop/mainloop.py and also at the docs for wx.EventLoop. It
can work for situations like this where you need to blend two event
loops, but if you can do it with timers or an alternate thread that
might be a better solution.


Robin Dunn

Software Craftsman

http://wxPython.org

Success! The attached file user.py calls the attached module wxvisual.py, which creates a secondary thread in which to execute user.py, then creates MainLoop. The user program is very simple and has an infinite loop with a rate statement to control the animation speed of a simple object, a straight line that rotates:

from future import division, print_function

from wxvisual import *

rod = line()

while True:

rate(100)

rod.angle += .01

This runs on both Windows and Mac Cocoa, though the animation speed is quite slow on the Mac (probably a mistake in the rate routine or something like that), and the Mac File menu is missing the quit option (though there is one on the Python menu). I seem to have fixed the quit problems I saw earlier.

Many many thanks for all the extremely helpful advice from all of you!

wxvisual.py (2.78 KB)

user.py (132 Bytes)

Congrats!

···

On 7/1/12 9:42 PM, Bruce Sherwood wrote:

Success! The attached file user.py calls the attached module
wxvisual.py, which creates a secondary thread in which to execute
user.py, then creates MainLoop.

--
Robin Dunn
Software Craftsman

Hi Bruce,

The speed problems on Mac are caused by the use of wx.ClientDC. wx.ClientDC is for a few very specific drawing tasks that draw over, not into, your window. (Think a hovering selection rectangle.) Unfortunately for wx users, this isn’t documented, so you’re far from the first the first to run into this. :frowning:

Anyway, to fix this, move the render() code into your OnPaint event and then create a wx.AutoBufferedPaintDC instead of a wx.ClientDC, and then in your OnTimer event, just call self.Refresh() (and maybe an self.Update() for Windows). This should give you suitable performance.

Regards,

Kevin

···

On Jul 1, 2012, at 9:42 PM, Bruce Sherwood wrote:

Success! The attached file user.py calls the attached module wxvisual.py, which creates a secondary thread in which to execute user.py, then creates MainLoop. The user program is very simple and has an infinite loop with a rate statement to control the animation speed of a simple object, a straight line that rotates:

from future import division, print_function

from wxvisual import *

rod = line()

while True:

rate(100)

rod.angle += .01

This runs on both Windows and Mac Cocoa, though the animation speed is quite slow on the Mac (probably a mistake in the rate routine or something like that), and the Mac File menu is missing the quit option (though there is one on the Python menu). I seem to have fixed the quit problems I saw earlier.

Many many thanks for all the extremely helpful advice from all of you!

To unsubscribe, send email to wxPython-users+unsubscribe@googlegroups.com

or visit http://groups.google.com/group/wxPython-users?hl=en<wxvisual.py><user.py>

Thanks for the tips about rendering. The real problem however was that I was not aware that on the Mac (and other Unix systems) time.clock() does not advance during time.sleep(), unlike the situation on Windows. There’s even a very recent PEP addressing this platform dependence (
http://www.python.org/dev/peps/pep-0418/ ). I changed to the platform-independent wx.GetLocalTimeMillis() and wx.Usleep(), and now the program runs the same on Windows and Mac Cocoa. I’ve attached the new files (user.py imports wxvisual.py).

I’m not out of the woods, however, due probably to an insufficient understanding of how import works. The sequence is that I execute user.py, which contains the following:

from future import division, print_function

from wxvisual import rod, rate

rod = line()

while True:

rate(100)

rod.angle += .01

In wxvisual.py I set up the GUI environment but before executing app.MainLoop() I create a secondary thread whose run method reads the source in user.py (the file name is sys.argv[0]) and comments out import statements up to and including the import of wxvisual, then does an exec of the source. I find that if there are additional import statements (e.g. to import math functions), the program doesn’t run, but there are no error messages. I don’t even understand why I have to comment out the import statements before doing the exec, because importing wxvisual a second time shouldn’t do anything – shouldn’t execute the statements that established the GUI context, should it? I thought statements in an import file were executed just once. I tried putting print statements in various places but remain confused by what I observe.

It’s maybe asking a lot of this very knowledgeable group for advice on this, since I assume it’s really a Python question, not a wxPython question. Nevertheless, all advice is welcome!

user.py (139 Bytes)

wxvisual.py (2.84 KB)

···

On Monday, July 2, 2012 9:51:31 AM UTC-6, kevin...@theolliviers.com wrote:

Hi Bruce,

The speed problems on Mac are caused by the use of wx.ClientDC. wx.ClientDC is for a few very specific drawing tasks that draw over, not into, your window. (Think a hovering selection rectangle.) Unfortunately for wx users, this isn’t documented, so you’re far from the first the first to run into this. :frowning:

Anyway, to fix this, move the render() code into your OnPaint event and then create a wx.AutoBufferedPaintDC instead of a wx.ClientDC, and then in your OnTimer event, just call self.Refresh() (and maybe an self.Update() for Windows). This should give you suitable performance.

Regards,

Kevin

from __future__ import division, print_function
from wxvisual import rod, rate
rod = line()
while True:
    rate(100)
    rod.angle += .01

In wxvisual.py I set up the GUI environment but before executing
app.MainLoop() I create a secondary thread whose run method reads the source
in user.py (the file name is sys.argv[0]) and comments out import statements
up to and including the import of wxvisual, then does an exec of the source.
I find that if there are additional import statements (e.g. to import math
functions), the program doesn't run, but there are no error messages. I
don't even understand why I have to comment out the import statements before
doing the exec, because importing wxvisual a second time shouldn't do
anything -- shouldn't execute the statements that established the GUI
context, should it? I thought statements in an import file were executed
just once. I tried putting print statements in various places but remain
confused by what I observe.

I think the problem with the imports is that your are creating name
conflicts. So, you have this:

from wxvisual import rod, rate

Presumably "rod" is the name of a class in wxvisual. But then you have this:

rod = line()

now the name "rod" refers to a line object, instead of the rod class.
You are shadowing the class name with a newly assigned name, which is
a gotcha in my experience. What if you changed the line to this?

from wxvisual import rod, rate
my_rod = line()

And run it?

Che

time.clock() is attempting to report the amount of time the app is
using the CPU -- not wall time, time.time() may do the job fine, and
be what you want.

though you seem to have found another solution.

I wonder if either of the sleep calls is the right hting to do anyway,
though -- a wx.Timer may be a better way to go to get a somewhat
constant frame rate.

-CHB

···

On Mon, Jul 2, 2012 at 9:49 PM, Bruce Sherwood <bruce.sherwood@gmail.com> wrote:

Thanks for the tips about rendering. The real problem however was that I was
not aware that on the Mac (and other Unix systems) time.clock() does not
advance during time.sleep(),

--

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Thanks. Dumb typo. But alas fixing the typo doesn’t make any difference in my problems. In fact, I had been using from wxvisual import * during testing.

···

I think the problem with the imports is that your are creating name

conflicts. So, you have this:

from wxvisual import rod, rate

Presumably “rod” is the name of a class in wxvisual. But then you have this:

rod = line()

now the name “rod” refers to a line object, instead of the rod class.

You are shadowing the class name with a newly assigned name, which is

a gotcha in my experience. What if you changed the line to this?

from wxvisual import rod, rate

my_rod = line()

And run it?

Che

but that would make it even more likely to give you name conflicts...

-Chris

···

On Tue, Jul 3, 2012 at 10:26 AM, Bruce Sherwood <bruce.sherwood@gmail.com> wrote:

Thanks. Dumb typo. But alas fixing the typo doesn't make any difference in
my problems. In fact, I had been using from wxvisual import * during
testing.

--

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Thanks much; time.time() does indeed work fine on Windows and Mac. And thanks for the suggestiion to use a timer in this situation.

···

On Tuesday, July 3, 2012 11:16:31 AM UTC-6, Chris Barker wrote:

On Mon, Jul 2, 2012 at 9:49 PM, Bruce Sherwood wrote:

Thanks for the tips about rendering. The real problem however was that I was

not aware that on the Mac (and other Unix systems) time.clock() does not

advance during time.sleep(),

time.clock() is attempting to report the amount of time the app is

using the CPU – not wall time, time.time() may do the job fine, and

be what you want.

though you seem to have found another solution.

I wonder if either of the sleep calls is the right hting to do anyway,

though – a wx.Timer may be a better way to go to get a somewhat

constant frame rate.

-CHB

Christopher Barker, Ph.D.

Oceanographer

Emergency Response Division

NOAA/NOS/OR&R (206) 526-6959 voice

7600 Sand Point Way NE (206) 526-6329 fax

Seattle, WA 98115 (206) 526-6317 main reception

Indeed. In fact, the experimental code is much worse than you think, because my “exec(prog)” picks up all the globals from within the wxvisual module, no matter what is the form of the wxvisual import statement (which is why my typo of importing rod instead of line didn’t actually affect anything).

So the problem I outlined in my earlier note still stands: I don’t understand why I need to comment out import statements in the primary program. I suspect I may have to handle the imports myself, and construct an appropriate globals dictionary to pass to exec. Or maybe there’s some completely different scheme altogether. Robin had suggested something about “respawn” Python, but (a) I don’t see how to go about that and (b) I thought spawning was not something supported on Windows?

···

On Tuesday, July 3, 2012 11:32:35 AM UTC-6, Chris Barker wrote:

On Tue, Jul 3, 2012 at 10:26 AM, Bruce Sherwood wrote:

Thanks. Dumb typo. But alas fixing the typo doesn’t make any difference in

my problems. In fact, I had been using from wxvisual import * during

testing.

but that would make it even more likely to give you name conflicts…

-Chris

Christopher Barker, Ph.D.

Oceanographer

Emergency Response Division

NOAA/NOS/OR&R (206) 526-6959 voice

7600 Sand Point Way NE (206) 526-6329 fax

Seattle, WA 98115 (206) 526-6317 main reception

Indeed. In fact, the experimental code is much worse than you think, because
my "exec(prog)" picks up all the globals from within the wxvisual module, no
matter what is the form of the wxvisual import statement (which is why my
typo of importing rod instead of line didn't actually affect anything).

This does not seem like a good strategy; keeping namespaces clean is
important in Python. Generally globals are discouraged and so is from
module import *. (See tutorial at bottom)

So the problem I outlined in my earlier note still stands: I don't
understand why I need to comment out import statements in the primary
program.

Unless I'm missing something, it probably still is a namespace
conflict. Maybe something that is being imported in those lines (which
is *not* also being imported via your exec(prog)), but that same name
was being used in your code somewhere and now the import re-assigns
the name to the imported object.

How long is the code?

This should be helpful:
http://effbot.org/zone/import-confusion.htm

···

On Tue, Jul 3, 2012 at 1:44 PM, Bruce Sherwood <bruce.sherwood@gmail.com> wrote:

Indeed. In fact, the experimental code is much worse than you think, because
my "exec(prog)" picks up all the globals from within the wxvisual module, no
matter what is the form of the wxvisual import statement (which is why my
typo of importing rod instead of line didn't actually affect anything).

hmm -- I haven't used exec much -- but I agree that that seems odd.

. Robin had suggested something about
"respawn" Python, but (a) I don't see how to go about that and (b) I thought
spawning was not something supported on Windows?

the subprocess module may help here. I"d do a little googling for
python respawn, maybe daemon.

-CHB

···

On Tue, Jul 3, 2012 at 10:44 AM, Bruce Sherwood <bruce.sherwood@gmail.com> wrote:

--

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

I am indeed familiar with the warnings about “from xxx import *”, but I need to test all the import schemes. As I said, the situation with my experimental code is even worse, because exec(prog) is getting all the imports of the wxvisual module.

The code is very short, and I posted it in a previous note.

···

On Tuesday, July 3, 2012 1:54:11 PM UTC-6, Che M wrote:

On Tue, Jul 3, 2012 at 1:44 PM, Bruce Sherwood wrote:

Indeed. In fact, the experimental code is much worse than you think, because

my “exec(prog)” picks up all the globals from within the wxvisual module, no

matter what is the form of the wxvisual import statement (which is why my

typo of importing rod instead of line didn’t actually affect anything).

This does not seem like a good strategy; keeping namespaces clean is

important in Python. Generally globals are discouraged and so is from

module import *. (See tutorial at bottom)

So the problem I outlined in my earlier note still stands: I don’t

understand why I need to comment out import statements in the primary

program.

Unless I’m missing something, it probably still is a namespace

conflict. Maybe something that is being imported in those lines (which

is not also being imported via your exec(prog)), but that same name

was being used in your code somewhere and now the import re-assigns

the name to the imported object.

How long is the code?

This should be helpful:

http://effbot.org/zone/import-confusion.htm

that's because you passed globals() to the exec function -- that is
giving it the globals dict in the current namespace, i.e. the wxvisual
module.

I'd be tempted to put the wxvisual stuff that you want the user to
access in a different module than the one that has the startup code in
it.

It seems much of this complication is due to the fact that you want
people to run their scripts with the regular python interpreter, but
this would all be easier if you could have them run thier scripts with
a front-end script instead. But maybe it's critical that this plug in
the same way...

-Chris

···

On Tue, Jul 3, 2012 at 3:00 PM, Bruce Sherwood <bruce.sherwood@gmail.com> wrote:

I am indeed familiar with the warnings about "from xxx import *", but I need
to test all the import schemes. As I said, the situation with my
experimental code is even worse, because exec(prog) is getting all the
imports of the wxvisual module.

--

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Yes, that is indeed the complication, that for my purposes it is crucial to preserve the current behavior of the VPython/IDLE environment, and I’m close. It is possible that I myself need to build the globals dictionary used by exec, by interpreting the imports found in the user file. However, I’d like to understand why my program doesn’t run when I import wxvisual a second time; it seems like that should be harmless, since statements not buried in defs or classes presumably aren’t executed again. Perhaps Che’s comment about namespace conflicts is the answer. Maybe if I construct my own globals, which I need to do anyway, the problem will go away.

Thanks for the pointer to the subprocess module. My naive impression is that (re)spawning doesn’t represent an improvement over the exec procedure I’m doing now, but I’d be happy to shown the error of this impression.

···

On Tuesday, July 3, 2012 4:06:12 PM UTC-6, Chris Barker wrote:

I’d be tempted to put the wxvisual stuff that you want the user to

access in a different module than the one that has the startup code in

it.

It seems much of this complication is due to the fact that you want

people to run their scripts with the regular python interpreter, but

this would all be easier if you could have them run thier scripts with

a front-end script instead. But maybe it’s critical that this plug in

the same way…

-Chris

IIRC, the import mechanism makes the decision on whether to execute an imported module or reuse the existing one on whether there is a matching module name already in sys.modules. Most of the time it won't happen, but there are ways that a single module can end up being found by different paths, and then it will end up being imported more than once and will be in sys.modules more than once.

The one that most people may have run into but didn't realize it is when a module has the name "__main__" because it was named as the script to be run on the python command line. Now consider that there could be an import loop like A.py imports B.py and B.py imports C.py and it imports A.py. If you run "python A.py" then it will be imported twice and will be in sys.modules as both "__main__" and "A".

So I'm thinking that you may have run into a similar issue due to how you are rexecuting the main script before the first time has actually finished, and also before the first import of wxvisual has actually finished.

I think I would try to make wxvisual have as close as you can get to a net zero effect in one case or the other, and then when it is imported the next time it will either be an effective NOP, or will be the time that it actually does its expected work. For example, I'm thinking of something like this pseudo-code:

if isMainThread():
  create workerThread
  remove "wxvisual" module from sys.modules
  import visualWxHelpers
  visualWXHelpers.createAndStartApp(workerThread)
  # does not return
else:
  assert not isMainThread()
  import and/or create all the classes and etc. that should be
  importable from the wxvisual module
  
The visualWXHelpers.createAndStartApp() would look something like this pseudo-code:

  create wx.App object
  create main frame
  show main frame
  wx.CallAfter(workerThread.start)
  app.MainLoop()
  wx.Exit()

So like this when user.py is executed and it does the import of wxvisual it will be running in the main thread, and so the *only* thing that is done in wxvisual in this case is to set up the thread, setup the wx application, remove any traces of the wxvisual module from sys.modules, and then start the MainLoop which will be followed shortly thereafter by starting the worker thread to reload the user.py module. When it imports wxvisual again then it is not the main thread and so it can then import or create whatever user.py is expecting to be able to import from that module.

···

On 7/3/12 3:35 PM, Bruce Sherwood wrote:

Yes, that is indeed the complication, that for my purposes it is crucial
to preserve the current behavior of the VPython/IDLE environment, and
I'm close. It is possible that I myself need to build the globals
dictionary used by exec, by interpreting the imports found in the user
file. However, I'd like to understand why my program doesn't run when I
import wxvisual a second time; it seems like that should be harmless,
since statements not buried in defs or classes presumably aren't
executed again. Perhaps Che's comment about namespace conflicts is the
answer. Maybe if I construct my own globals, which I need to do anyway,
the problem will go away.

--
Robin Dunn
Software Craftsman

Many thanks for the detailed analysis and for the concrete proposal, Robin. I’ll work on it.

Attached: user.py, which imports wxvisual.py.There is some semi-outrageous but apparently totally legal black magic to make this work on all platforms, including in particular on Mac Cocoa.

I tried a number of architectures. A particularly clean one goes back to an early suggestion by Robin, which is to have the wxvisual module spawn a process “python wxhelper user.py”. The wxvisual was basically just a stub to set up the spawn. I got this to work, but with the unacceptable side effect that when run from IDLE the print output went to a terminal, not to the IDLE shell window. Maybe I could get around this somehow, but it wasn’t clear how.

The architecture represented by the attached files looks like this:

user.py imports wxvisual

wxvisual sets up the wxpython GUI environment but before calling MainLoop it also sets up and starts a secondary thread, which does an exec of modified source code from the user file.

The modifications to the user’s source, which are done before creating and starting the secondary thread, consist of finding all import statements and executing them, using the results of the imports, in globals(), to create a special globals environment for the exec. The code is a bit bizarre but necessary because I found experimentally that (1) it is impossible to execute an import statement in a secondary thread and (2) import statements executed in a module must be in the base level of the module, not inside a function (hence the code looks yucky).

Now that I have this architecture worked out for a simple test case, I will try to replace the GUI portions of VPython (vpython.org) with this wxpython framework, so that it is possible to run on Cocoa on the Mac (the attached files run fine on Windows, Mac Cocoa, and Ubuntu). The goal of VPython (and the web-based GlowScript, at glowscript.org) is to make it feasible for non-expert programmers to write simple programs that generate real-time navigable 3D animations. The contents of the attached user.py give a sense of what VPython and GlowScript aim to do in making graphics-oriented programming accessible to ordinary mortals. This generates a rotating line:

import wxvisual as w

rod = w.line()

while 1:

w.rate(100)

rod.angle += .01

This hides all of the GUI setup, which is beyond novice programmers.

Again, many thanks to all of you who helped me learn my way around wxPython (and around some aspects of Python, too). Special thanks to Robin for particularly informative suggestions, and for wxPython itself.

user.py (121 Bytes)

wxvisual.py (7.97 KB)