intractable(?) Tkinter scrollbar problem

Dieter Maurer dieter at handshake.de
Sun Jun 6 06:45:21 EDT 1999


Hello Greg

Greg McFarlane <gregm at iname.com> writes on Mon, 31 May 1999 14:10:54 GMT:
> I include a Tkinter script below, modified from one by Mark C Favas,
> using, I believe, Fredrik Lundh's AutoScrollbar class.  When this
> script is run, the two scrollbars continuously get mapped and unmapped
> and the window continuously changes size.

I think, I now understand the instability and I should have
a solution.

What happens:

  When the grid is changed in "AutoScrollbar.set",
  Tk initiates two waves that propagate this information.

  The waves are initiated and propagated through
  successive "Tcl_DoWhenIdle(ArrangeGrid,...)"

  One wave goes upward towards the top level. Once
  the top level is reached, the window manager is told
  to adjust the toplevel size. Tk waits locally
  for a ConfigureNotify event, telling that the
  update is completed.
  Then, "Tcl_DoWhenIdle(UpdateGeometryInfo,...)" initiates
  a new wave down towards the "scrollFrame" informing
  about the new window geometry. This wave, too,
  is propagated through successive "Tcl_DoWhenIdle(ArrangeGrid,...)
  In some sense, the wave from the grid change is reflected
  at the top level and now goes back towards the scrollFrame.

  The second wave goes downward towards the grid slaves.
  When it reaches the canvas, "CanvasUpdateScrollbars"
  is called in "DisplayCanvas" which in turn calls
  "AutoScrollbar.set".

  At this time, the toplevel has already been adjusted
  and the wave informing about the change is going downward,
  but it is behind the first downward wave.
  Based on the outdated information, a new grid change
  is performed, resulting again in two waves upward
  and downward.

  Shortly later, the geometry propagation wave from
  the toplevel reaches the canvas. In "CanvasUpdateScrollbars",
  the grid structure is changed a third time,
  two new waves are initiated ....


The solution
  We must prevent the first downward wave to do bad
  things before the upward (and eventually reflected)
  wave has reached the canvas.

  The following code should do this:

class AutoScrollbar(Tkinter.Scrollbar): 
  _active= 0
  def set(self, lo, hi):
    self.lo= lo; self.hi= hi
    #if self._active: print 'active'; return
    self._active= 1; self.update_idletasks(); self._active= 0
    self._set()
  #
  def _set(self):
    lo= self.lo; hi= self.hi
    #print self.id, lo, hi
    if float(lo) <= 0.0 and float(hi) >= 1.0:
      self.tk.call("grid", "remove", self)
    else:
      self.grid()
    Tkinter.Scrollbar.set(self, lo, hi)


  The first wave downward reaches the canvas and there
  calls "AutoScrollbar.set".
  It sets "_active=1" and then waits in "update_idletasks"
  for the arrival of the reflected wave.

  When the reflected wave reaches the canvas, it provides
  new updated values for "lo" and "hi".
  It then sees the "_active == 1" and returns.

  The first wave takes over and updates the grid
  with up to date "lo" and "hi" values.


Potential risks:
  "update_idletasks" is a powerful mechanism of which the use
  may have nasty side effects.
  For example, it may result in flickering.

  However, this is probably better than the instability,
  it avoids.


- Dieter





More information about the Python-list mailing list