[Stackless] RE: [wxPython] Replacing Mainloop for wxPython Apps (re-send)

Mike Fletcher mfletch at tpresence.com
Fri Nov 10 20:30:22 CET 2000


[minor edits, apparently stackless list was down for a while, was wondering
why no responses to previous post]

Here's one thing I discovered, wxDialog.ShowModal() will prevent the
substituted mainloop from running (or seems to), so if you're running under
uthreads you need to replace wxDialog with wxStacklessDialog or something
similar (wrong name for the class).  This is not a good solution, some way
to override that "recursive mainloop" would be better, (would allow for
actually freezing the parent), but I don't see any hooks for that.  Note:
For the current implementation, should probably explicitly keep track of
"returned" status instead of just using whether the thread is blocked.

Also discovered, you _really_ don't want to run actual code in the wxPython
event callbacks, spawn a new micro-thread to do anything that could take any
amount of time/might block.  What with the ability of the system to suspend
a thread at any time you can wind up with the mainloop going wacky as a
callback is suspended (I'm just going on supposition here, you get errors,
anyway).

Hanging is still going on, I'm guessing it's a callback that's getting
suspended, but I haven't been able to confirm that.

Enjoy all,
Mike

from wxPython.wx import *
import uthread9

class wxStacklessDialog( wxDialog):
	__caller = None
	def ShowModal( self ):
		self.__caller = uthread9.getCurrentThread()
		self.Show( 1 )
		self.__caller.block()
		return self.__result
	def EndModal( self, result ):
		uthread9.atomic( self.__endModal, result )
	def __endModal( self, result ):
		self.__result = result
		if self.__caller and self.__caller.isBlocked():
			self.__caller.start()
		self.Close()


-----Original Message-----
From: Mike Fletcher [mailto:mfletch at tpresence.com]
Sent: Thursday, November 02, 2000 1:49 AM
To: wxPython Users (E-mail)
Subject: [wxPython] Replacing Mainloop for wxPython Apps


I have been using a main loop solution for wxPython which tries to make
wxPython's  main loop run in a micro-thread.  For the most part, this works
fine, but if I leave the application running and work on other applications
for a while (~half an hour or so), I return to the application to find that
the GUI is unresponsive (as if the main loop has hung).  From reading the
documentation for wxApp, it seems as though it might be the ProcessMessage
or OnIdle methods which are causing the problem (as I don't currently
override them, I figure they may be running with the assumption that the
wxWindows event loop is available).

The documentation isn't particularly clear on what to do with these methods,
for instance, should I call ProcessMessage from the mainloop? If so, what do
I used as the msg parameter (I'm working on the assumption that if Windows
sends a Windows message the default ProcessMessage may be interfering with
the main loop)?  Should OnIdle be used when there is a ProcessIdle call in
the MainLoop (ProcessIdle doesn't seem to be mentioned anywhere in the
documentation)?

The other possible explanation would be that something in Dispatch or
ProcessIdle is blocking in C code, this doesn't seem likely (the entire
application is fairly rigorously asynchronous (since micro-threading by its
nature requires asynchronous operation)), unless, for instance, ProcessIdle
could block waiting for idle events.

Here is the main loop code...

Any suggestions from others who have created their own main loops?
Enjoy yourselves,
Mike


import uthread9, traceback
from wxPython.wx import *
from portals.ui.mainframe import MainFrame
from portals.portalserror import *

class GUIApplication( wxPySimpleApp ):
	applicationClass = None # need to override in sub-classes
	mainFrameClass = MainFrame
	TITLE = "GUI Application"
	mainloopThread = None
	MUTech = None
	def __init__( self, dataFile=None, *arguments, **namedarguments ):
		self.databaseLocation = dataFile
		apply( wxPySimpleApp.__init__, (self,)+arguments,
namedarguments )
		EVT_END_SESSION( self, self.OnEndSession )
	def OnEndSession( self, event ):
		'''Override to provide handling of end-of-session events'''
		if self.MUTech:
			try:
				self.MUTech.close()
			except:
				pass
		if self.mainFrame:
			try:
				self.mainFrame.Close()
			except:
				pass
	def OnInit(self):
		log( 'Loading Image Handlers', INITIALISATIONDEBUG )
		wxImage_AddHandler(wxJPEGHandler())
		wxImage_AddHandler(wxPNGHandler())
		wxImage_AddHandler(wxGIFHandler())
		log( 'Creating Application Window', INITIALISATIONDEBUG )
		self.mainFrame = self.mainFrameClass(
			NULL,
			title = self.TITLE,
		)
		self.mainFrame.Show(true)
		self.SetTopWindow(self.mainFrame)
		log( 'GUI Initialisation Complete', INITIALISATIONDEBUG )
		return true
	def StartMUTech( self ):
		'''
		Override this in subclasses to use your MUTech sub-class
		(using the default application class is not possible, as
it's
		an abstract class that doesn't provide the needed functions)
		'''
		try:
			log( 'MUTech Startup', INITIALISATIONDEBUG )
			self.MUTech = self.applicationClass( guiApplication
= self, dataFile= self.databaseLocation )
			self.MUTech.start()
			log( 'MUTech Startup Complete', INITIALISATIONDEBUG
)
		except:
			log( 'MUTech Startup Failed with an Exception!',
INITIALISATIONDEBUG )
			traceback.print_exc()
			self.OnExit( None )
	def OnExit( self, event ):
		if self.MUTech:
			try:
				self.MUTech.close()
			except:
				pass
		try:
			event.Skip()
		except: # catches cases where a non-event is used to signal
this shutdown
			pass
		if self.mainloopThread:
			try:
				self.mainloopThread.exit()
				print 'exited mainloop thread'
			except:
				pass
		sys.exit( 0)
	def MainLoop(self):
		self.mainloopThread = uthread9.newResistent( self.__mainLoop
)
		uthread9.new( self.StartMUTech )
		if not uthread9.microThreadsRunning():
			uthread9.run()
	def __mainLoop( self ):
		try:
			while 1:
				# This inner loop will process any GUI
events until there
				# are no more waiting.
				while self.Pending():
					try:
						uthread9.atomic(
self.Dispatch )
					except:
						traceback.print_exc()
				# Send idle events to idle handlers.  You
may want to throtle
				# this back a bit so there is not too much
CPU time spent in
				# the idle handlers.  For this example, I'll
just snooze a
				# little...
				uthread9.switchContext()
				try:
					uthread9.atomic( self.ProcessIdle )
				except:
					traceback.print_exc()
				uthread9.wait( 0.01 ) # hack!
		except:
			traceback.print_exc()
	def ProcessMessage (self, message):
		print "ProcessMessage received message", message
		return None

	def getBaseWindow ( self ):
		'''Abstract interface for getting the top window pointer
from the
		GUIApplication (as opposed to a wxPython interface)'''
		return self.mainFrame

__________________________________
 Mike C. Fletcher
 Designer, VR Plumber
 http://members.home.com/mcfletch
_______________________________________________
wxPython-users mailing list
wxPython-users at lists.sourceforge.net
http://lists.sourceforge.net/mailman/listinfo/wxpython-users
_______________________________________________
Stackless mailing list
Stackless at starship.python.net
http://starship.python.net/mailman/listinfo/stackless



More information about the Stackless mailing list