modal frame from wx python?
pkienzle
pkienzle at gmail.com
Wed Feb 6 08:40:26 PST 2008
Hi! I'm trying to create a wx.Frame which has a method allowing the
user to click at several points on the canvas and returning the points
clicked. This is like a modal dialog, except that only one window is
modal, and it is only modal part of the time.
Does anyone have code for something like this?
I'm including the solutions I tried below.
Thanks in advance,
- Paul
----
Attempted solutions for modal wx event processing:
1. Run a private event loop with a timer every 0.1s to see if the user
has done selecting. This works, but it doesn't like it if the window
is closed before completion. I didn't test for it, but super-fast
clicking should let the user sometimes select an extra point, so not
a great solution.
def event_loop(self, done=lambda:False, timeout=0):
'run the eventloop until done or timeout'
print "Running private event loop"
loop = wx.EventLoop()
# stop after timeout period
def donewaiting(*args,**kw):
print "Done waiting"
loop.Exit()
if timeout > 0:
print "Starting timer"
outtimer = wx.Timer(self, id=MODAL_TIMEOUT_TIMER)
outtimer.Start(timeout*1000, oneShot=True)
self.Bind(wx.EVT_TIMER, donewaiting, outtimer)
# check if done every 0.1 s
def checkdone(*args,**kw):
if done(): loop.Exit()
steptimer = wx.Timer(self, id=MODAL_STEP_TIMER)
steptimer.Start(100, oneShot=False)
self.Bind(wx.EVT_TIMER, checkdone, steptimer)
loop.Run()
steptimer.Stop()
return
2. What seems like it should work is to use an event handler which
checks after processing each event whether or not this completes
the condition and so we can exit the loop. However, ProcessEvent
never seems to be called. Reading elsewhere I see that ProcessEvent
is not a virtual function in wx, so presumably we can't override
it in a subclass of wx.EvtHandler.
def event_loop(self, done=lambda:False, timeout=0):
'run the eventloop until done or timeout'
print "Running private event loop"
loop = wx.EventLoop()
# stop after timeout period
def donewaiting(*args,**kw):
print "Done waiting"
loop.Exit()
if timeout > 0:
print "Starting timer"
outtimer = wx.Timer(self, id=MODAL_TIMEOUT_TIMER)
outtimer.Start(timeout*1000, oneShot=True)
self.Bind(wx.EVT_TIMER, donewaiting, outtimer)
# Redirect input events to new handler
class ModalHandler(wx.EvtHandler):
def ProcessEvent(self, evt):
print "Processing event"
if done(): loop.Exit()
return False # True if processed
print "Pushing handler"
handler = ModalHandler()
self.PushEventHandler(handler)
print "enabled",handler.GetEvtHandlerEnabled()
print "handler->next",handler.GetNextHandler()
print "self->previous",self.GetPreviousHandler()
loop.Run()
self.PopEventHandler(False)
print "Done!"
3. Revising attempt #2 somewhat, I send all events for the canvas through
my own event handler. To make sure I see each one I bind each window
event to my own dispatcher as well as to the underlying window. I
then use this handler rather than self in the canvas __init__.
This fails because either the window handler is not called or it
is called twice, depending on whether PushEventHandler and ProcessEvent
are called. It also fails if the window is closed unexpectedly.
class ModalHandler(wx.EvtHandler):
def __init__(self, window):
print "Creating handler"
wx.EvtHandler.__init__(self)
self.done = lambda:False
window.PushEventHandler(self)
self.window = window
self.timer = wx.Timer()
self.Bind(wx.EVT_TIMER, self.OnTimeout, self.timer)
self.loop = wx.EventLoop()
def _dispatch(self, evt):
"""Ick! ProcessEvent is not virtual, so we can't override
directly!"""
#print "Processing event"
self.window.ProcessEvent(evt)
if self.done(): self.loop.Exit()
print "Returning event"
return True
def EndModal(self):
"""Force the loop to exit"""
self.done = lambda:True
def Bind(self,evt,action,*args,**kw):
# Force all events through ProcessEvent. This is the first binding.
# ProcessEvent will dispatch the event to the window itself, so be
# sure to tell the window what to do with the event. This is the
# second binding.
if wx.VERSION_STRING >= '2.5': # Event handlers 2.5
print "Binding 2.5"
wx.EvtHandler.Bind(self.window,evt,action,*args,**kw)
wx.EvtHandler.Bind(self,evt,self._dispatch)
else:
print "Binding 2.4"
evt(self,self.ProcessEvent)
evt(self.window,*args,**kw)
def OnTimeout(self):
print "timeout"
self.loop.Exit()
def RunLoop(self, done=lambda:False, timeout=0):
print "Running loop"
self.done = done
if timeout > 0: self.timer.Start(timeout, oneShot=True)
try:
self.loop.Run()
finally:
self.timer.Stop()
self.done = lambda:False
--
View this message in context: http://www.nabble.com/modal-frame-from-wx-python--tp15306964p15306964.html
Sent from the wxWidgets - Users mailing list archive at Nabble.com.
More information about the wx-users
mailing list