Tkinter protocol("WM_DELETE_WINDOW,...)

Dave Hansen iddw at hotmail.com
Thu May 8 11:43:57 EDT 2003


I don't write a lot of Python, but it's my choice when a scripting
application comes along.

This is a long tale of a problem I encountered, and I think I fixed,
but I'm soliciting the opinions of those more experienced in such
matters.  I don't want this biting me again...

Platform: Windows 98 (ugh), Python 2.1.3

Recently I converted a text-based question-and-answer script to
Tkinter.  Basically the script builds a tree of nodes.  Each node asks
the user a question, and depending on the response, returns another
node.  IYC, the purpose is to find the part number of a product that
matches the user's intended application.

The Tkinter script keeps the same basic structure.  There are many
kinds of nodes, but they basically all look something like this:

   class Node(Frame):
      def __init__(self, <question, answers, results>):
         <Build the node.  No Tkinter calls>

      def ask(self):
         Frame.__init__(self)
         <Build and pack a frame for the user>
         self.result = Terminate        # Special flag
         self.wait_window(self)
         return self.result

      def process(self, <user input>):  # user input callback
         self.result = <user input result>
         self.destroy()

After the tree of nodes is built, the questions are asked using
something like

   def traverse(tree):
      while tree != Terminate:
         tree = tree.ask()

Note that setting the node result to Terminate in the ask method means
that if the window is destroyed before the user supplies input, the
Terminate flag is returned to the loop and the script exits.

Also note that no call to mainloop is made.  This is due to the way
the script eveolved (text mode => Tkinter).

This worked well.  Until I had a node with a Scale widget.  Even then,
if you close the Toplevel window (with the "X" button in the title
bar), it worked fine.  Unless you slide the scale first.  Then closing
the window hangs the DOS window (and it's very painful to get rid of
it).

Note that the process method of the scale node is bound to the return
key and an "OK" button.  Moving the scale does not affect the node
itself, just the displayed frame.

My guess was that I was seeing a race condition of some sort.  Somehow
the Toplevel was being destroyed before the node frame, so the ask
method never returned because wait_window never returned.  My first
attempt at a fix was to add

   self.master.protocol("WM_DELETE_WINDOW", self.cleanup)

to the ask method, and add a cleanup method to the nodes:

   def cleanup(self):
      self.result = Terminate
      self.destroy()

This broke everything.  Closing the Toplevel would now unconditionally
hang the interpreter.  But I took that as further evidence I was
seeing a race of some sort, and the addition of the protocol handler
just guaranteed I was going to lose the race.

I fixed it by changing the cleanup method as follows:

   def cleanup(self):
      self.result = Terminate
      self.destroy()
      self.update()

The theory being that the update will ensure the current frame is
destroyed before the protocol handler returns.  This seems to correct
all the problems.

Questions: Is this the right approach?  Are there problems with this
approach?  Are there better ways to handle this condition?  Or have I
been completely clueless?

Thanks in advance for any response,

                               -=Dave
-- 
Change is inevitable, progress is not.




More information about the Python-list mailing list