Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > after, after_cancel and Python 2.3

Reply
Thread Tools

after, after_cancel and Python 2.3

 
 
Edward K. Ream
Guest
Posts: n/a
 
      08-29-2003
Previous to Python 2.3 my app has destroyed the root Tk window using
root.destroy rather than the more usual root.quit. In Python 2.3 this does
not work so well. In some situations (i.e., for some data), Tk (not
Tkinter) complains that an "after" routine does not exist that has been
registered with the Tkinter after routine. Moreover, in Python 2.3, the
tkinter after_cancel routine does not appear to work at all, regardless of
whether root.destroy() or root.quit() follows after_cancel.

When using root.quit(), the after routine is called properly, so no real
harm is done. When using root.destroy(), it appears that Tkinter (or is it
Tk) attempts to call the "after" routine after the "after" routine has
already been destroyed.

For example, the following code appears in my app's shutdown code:

self.killed = true # Disable after events.

# New for Python 2.3: attempt to cancel the after handler.
if self.afterHandler != None:
print "finishQuit: canceling",self.afterHandler
self.root.after_cancel(self.afterHandler)
self.afterHandler = None

if 1: # Works in Python 2.1 and 2.2. Leaves Python window open.
self.root.destroy()

else: # Works in Python 2.3. Closes Python window.
self.root.quit()

In versions before Python 2.3 there was never any need for a call to
root.after_cancel and the call to root.destroy never complained. In Python
2.3, the call to root.destroy() sometimes gives an error message like this:

finishQuit: canceling after#18
>>> invalid command name "14189192callit"

while executing
"14189192callit"
("after" script)

If my code calls self.root.quit() what happens is that the "after" routine
is called, sees self.killed == true and returns. In other words, the
self.killed hack allows the after routine to work around the fact that
after_cancel has had no apparent effect.

Can anyone see any obvious problem in my code?

Here are the definitions of after and after_cancel in tkinter.py. This code
has not changed between Python 2.2 and Python 2.3.

def after(self, ms, func=None, *args):
"""Call function once after given time.

MS specifies the time in milliseconds. FUNC gives the
function which shall be called. Additional parameters
are given as parameters to the function call. Return
identifier to cancel scheduling with after_cancel."""
if not func:
# I'd rather use time.sleep(ms*0.001)
self.tk.call('after', ms)
else:
# XXX Disgusting hack to clean up after calling func
tmp = []
def callit(func=func, args=args, self=self, tmp=tmp):
try:
func(*args)
finally:
try:
self.deletecommand(tmp[0])
except TclError:
pass
name = self._register(callit)
tmp.append(name)
return self.tk.call('after', ms, name)

def after_cancel(self, id):
"""Cancel scheduling of function identified with ID.

Identifier returned by after or after_idle must be
given as first parameter."""
try:
data = self.tk.call('after', 'info', id)
# In Tk 8.3, splitlist returns: (script, type)
# In Tk 8.4, splitlist may return (script, type) or (script,)
script = self.tk.splitlist(data)[0]
self.deletecommand(script)
except TclError:
# print "ekr: tcl error"
pass
# print "ekr: calling cancel",id
self.tk.call('after', 'cancel', id)

The "disgusting" tmp hack does not appear in after_cancel. I'm not sure
what this does, especially since tmp.append(name) would appear to have no
effect because tmp is not a global. My guess is that if the hack is needed
in one place it might be needed in another.

Summary: For now I can just call root.quit and rely on the killed flag to
keep the after routine out of trouble, but it does appear that something
isn't quite right. Does anyone have a clue what is going on? Thanks.

Edward

P.S. My "after" routine is initially created with after_idle, and
thereafter reposts itself with after. Like this:

# An internal routine used to dispatch the "idle" hook.
def idleTimeHookHandler(*args):

a = app()
if a.killed: # New for Python 2.3...
print "idleTimeHookHandler: killed"
return

# ...Do stuff

# Requeue this routine after 100 msec.
if a.idleTimeHook:
a.afterHandler = a.root.after(a.idleTimeDelay,idleTimeHookHandler)
else:
a.afterHandler = None

EKR
--------------------------------------------------------------------
Edward K. Ream email:
Leo: Literate Editor with Outlines
Leo: http://webpages.charter.net/edreamleo/front.html
--------------------------------------------------------------------


 
Reply With Quote
 
 
 
 
Edward K. Ream
Guest
Posts: n/a
 
      08-29-2003
> Previous to Python 2.3 my app has destroyed the root Tk window using
> root.destroy rather than the more usual root.quit. In Python 2.3 this does
> not work so well...


Oops. I forgot to mention that I am using Python 2.3.0 on Windows XP. It
was installed using the standard Windows installer and so is running Tk
8.4.3.

Edward
--------------------------------------------------------------------
Edward K. Ream email:
Leo: Literate Editor with Outlines
Leo: http://webpages.charter.net/edreamleo/front.html
--------------------------------------------------------------------


 
Reply With Quote
 
 
 
 
Michael Peuser
Guest
Posts: n/a
 
      08-29-2003

"Edward K. Ream" <> schrieb im Newsbeitrag
news:...
> > Previous to Python 2.3 my app has destroyed the root Tk window using
> > root.destroy rather than the more usual root.quit. In Python 2.3 this

does
> > not work so well...


Your example is somewhat complicated to say the least. As I am not using
2.3/8.4 at the moment I have not encountered like problems. But note that
there are fundamental differences between Tk.quit and Tk.destroy. Tk.quit
just leaves the event loop and touches no widget. See this example:

from Tkinter import *
m=Tk()
Button(m,text="quit",command=m.quit).pack()
mainloop()
print "this is not The End"
mainloop()

Maybe destroy did not trigger Python garbage collection in 8.3 as expected,
so data could have been erronously accessable. The memory leak would not
have been to obvious because destroy is a rare instruction...

May also be there is some bug with destroy in Tk 8,4

Kindly
Michael P
>



 
Reply With Quote
 
Edward K. Ream
Guest
Posts: n/a
 
      08-29-2003
> Maybe destroy did not trigger Python garbage collection in 8.3 as
expected,
> so data could have been erroneously accessible.


Thanks for your comments. Note that my app does nothing with Tk data after
root.destroy(), so this shouldn't matter. The real mystery seems to be why
the "after" routine is being called even after after_cancel.

I suppose it is time to start digging into the Tk code...

Edward

P.S. My comments about the "disgusting" hack in the tkinter after method
were dim. The tmp list is the default value of the tmp argument of the
callit function, so whether tmp is global or not is irrelevant: changes to
tmp will affect later calls to callit.

EKR
--------------------------------------------------------------------
Edward K. Ream email:
Leo: Literate Editor with Outlines
Leo: http://webpages.charter.net/edreamleo/front.html
--------------------------------------------------------------------


 
Reply With Quote
 
Edward K. Ream
Guest
Posts: n/a
 
      08-29-2003
I just took a look at TK's generic event code in tkEvent.c. It's really
complex. For example:

/*
* There's a potential problem if a handler is deleted while it's
* current (i.e. its procedure is executing), since Tk_HandleEvent
* will need to read the handler's "nextPtr" field when the procedure
* returns. To handle this problem, structures of the type below
* indicate the next handler to be processed for any (recursively
* nested) dispatches in progress. The nextHandler fields get
* updated if the handlers pointed to are deleted. Tk_HandleEvent
* also needs to know if the entire window gets deleted; the winPtr
* field is set to zero if that particular window gets deleted.
*/


This is precisely the situation in which my code fails.



There may be a bug if the list is circular, in which case "updating" the
event handler might not work because p.nextPtr might be p itself. Also, the
winPtr hack might not work for time-specific events like "after" events.
These are just guesses, and my guess is that there are bugs here somewhere.
Lord! How does anyone ever get this kind of code to work?



Anyway, I have a workaround...


Edward
--------------------------------------------------------------------
Edward K. Ream email:
Leo: Literate Editor with Outlines
Leo: http://webpages.charter.net/edreamleo/front.html
--------------------------------------------------------------------


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
after_cancel? W. eWatson Python 2 04-18-2009 10:56 PM
Re: [Python-Dev] [Python-3000] RELEASED Python 2.6a1 and 3.0a3 Martin v. Löwis Python 0 03-01-2008 10:51 PM
Re: [Python-Dev] [Python-3000] RELEASED Python 2.6a1 and 3.0a3 Paul Moore Python 0 03-01-2008 10:39 PM
Searching comp.lang.python/python-list@python.org (was: UTF-8) skip@pobox.com Python 0 03-10-2007 02:50 PM
Memory leak with after_cancel() in Tkinter Ton K. Python 0 07-25-2003 03:19 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57