Randolph Bentson wrote:
> 
> I'm having problems with button.connect() which puzzle me.  I've attached
> a simple example program which was extracted from a much larger program.
> 
> Clicking "start TASK" starts the TASK function, which opens a status
> window and updates the label ten times a second.  Every time I click
> "start TASK" before the current TASK finishes, the current TASK is
> interrupted and a new TASK is started.  When the newer TASK finishes,
> the interrupted TASK proceeds to completion.  (Absent threads, it is
> quite reasonable that one TASK would displace another in this nested
> manner.  I know how to prevent this nesting if that's desired.)
> 
> If TASK is not running, clicking the "QUIT" button invokes quit_pgm,
> but if I click "QUIT" while the first TASK is running, I see a new
> TASK started!
> 
> If I click "QUIT" before the second TASK finishes, a third instance
> starts, but surprisingly if I click "QUIT" after the second or third
> loop terminates, quit_pgm is called and the main window goes away.

This is a good one.  The major problem is your use of   
  'button.connect("button_release_event", ...)'
instead of 
  'button.connect("clicked", ...)'.
The sequence of events is this:
  1. You press the mouse button on 'start' button
  2. Button's "button_press_event" handler grabs the mouse, so any
     further mouse events go to the button.
  3. You release the button
  4. Your handler for "button_release_event" starts running
  5. Since your handler has not returned, the default "release" handler
     cannot run, and thereby release the "grab" of the mouse.

The solution is to use the 'clicked' signal.  It's generally better, in
GTK, to use the higher level signals the the xxx_event types.  


> 
> I haven't got much further in the analysis, but it looks like the
> button connections follow these rules (where each entry to state
> 1 starts TASK):
>                                  click
>       event | click "QUIT" | "start TASK" | TASK exit
>       ------+--------------+--------------+----------
> state 0     |    exit      |      1       |   N/A
>       1     |      1       |      1       |    0
> 
> In confirmation of this observation, I've found that while TASK
> is running, the "QUIT" button no longer shows a PRELIGHT color,
> and the "start TASK" button shows the ACTIVE color.  This holds
> until one instance of TASK has finished.
> 
The "stacking" behavior is due to the structure of your code. The C
equivalent of this would be to do much work in a signal handler.
Graphically:

click 'start'
  task() runs
    click 'start'
      mainiteration()
        task() runs

the solution, is to add a source of 'timer' events to the main event
loop for each task. You do this with 'timeout_add(interval, callback,
args...)'
Your callback should look like:
def callback(args):
  print 'callback called'
  if <I am done with this timer>: return FALSE # I won't get called
again
  else: return TRUE # I will be called after another <interval>
milliseconds.

See the attached modification of small_fail.py (although it should no
longer fail :).

> --
> Randolph Bentson
> [EMAIL PROTECTED]
> 
>   ------------------------------------------------------------------------

-- 
Casey Carter
[EMAIL PROTECTED]
#!/usr/bin/python -v
from gtk import *   # from pygtk
# Don't need this anymore: import time

def quit_pgm(button, window):
    print "Quit Pgm"
    window.destroy()

def callback(window, label):
    val = int(label.get()) + 1
    label.set_text(str(val))
    if val >= 50:
        window.destroy()
        return FALSE
    else:
        return TRUE

def TASK(button):
    newin = GtkWindow()
    lbl = GtkLabel('__')
    newin.add(lbl)
    lbl.show()
    newin.show()

    lbl.set_text('0')
    timeout_add(100, callback, newin, lbl)

rc_parse("gtkrc")

win = GtkWindow()
win.connect('destroy', mainquit)
vbox0 = GtkVBox()
win.add(vbox0)

btn1 = GtkButton(' start TASK ')
btn1.connect("clicked", TASK)
vbox0.pack_start(btn1,TRUE, TRUE, 0)

btn2 = GtkButton(' QUIT ')
btn2.connect("clicked", quit_pgm, win)
vbox0.pack_start(btn2,TRUE, TRUE, 0)

vbox0.show()
btn1.show()
btn2.show()
win.show()

mainloop()

Reply via email to