Stopping the main thread

My main thread sets up some GUI, and needs to cede the stage for the GUI to take over.

I wrote

#lang racket
(require racket/gui/base)
(require racket/class)
(define my-canvas%
  (class canvas% ; The base class is canvas%
    ; Call the superclass init, passing on all init args
    (super-new)))
(define frame (new frame% [label "Example"]))
(new my-canvas% [parent frame])
(define panel (new vertical-panel% [parent frame]))
(new button%
     [parent panel]
     [label "one"]
     [callback (lambda (button event)
                 (printf "act one~n")
                 )])
(new button%
     [parent panel]
     [label "two"]
     [callback (lambda (button event)
                 (printf "act two~n")
                 )])
(send frame show #t)
(yield)
; Don't execute the next bit unless the user has prssed a button
 ; ... rest of program ...
(printf "whoops!  can't stop this thread~n")

It creates a panel with two buttons.
Each button has an action that is esecuted when the button is pressed.
But I do not want execution to pass beyond the (yield)nuntil after
the button's action hs been executed.

How do I stop or delay executoin on the main thread?

Or is my whole approach to the problem the wrong one?

-- hendrik

Of course this is a much simplified version. The real application has a lot of possible button-induced actions, possibly even exiting this part of the program by tail-calling a saved-up continuation (in which case I don't want execution to proceed anywhere near here). And the real application keeps changing its mind as to which buttons to place on the canvas.

Consider using an async-channel.

#lang racket

(require racket/gui/base)
(require racket/class)
(require racket/async-channel)

(define my-canvas%
  (class canvas% ; The base class is canvas%
    ; Call the superclass init, passing on all init args
    (super-new)))

(define frame (new frame% [label "Example"]))

(new my-canvas% [parent frame])

(define panel (new vertical-panel% [parent frame]))

(define ch (make-async-channel))

(new button%
     [parent panel]
     [label "one"]
     [callback (lambda (button event)
                 (printf "act one~n")
                 (async-channel-put ch 'go)
                 )])

(new button%
     [parent panel]
     [label "two"]
     [callback (lambda (button event)
                 (printf "act two~n")
                 (async-channel-put ch 'go)
                 )])

(send frame show #t)
 
(yield ch)
; Don't execute the next bit unless the user has prssed a button
 ; ... rest of program ...
(printf "whoops!  can't stop this thread~n")

One solution is to define a continue semaphore with initial value 0 and have your button handlers post to the semaphore when execution of the main thread should continue. Then change (yield) to (yield continue).

I suppose I was hoping to find a UI element that would allow the user to decide between alternatives and then continue executing depending on the result of the user's decision.when the choice has been made.

Either of these two proposals could work.

But it looks as if the actual actions -- the (printf "act one~n")
and (printf "act two~n") will not be in tail-call position. Regrettable, but perhaps unavoidable.

And if the execution of the actions takes execution far away, perhaps by calling a saved-up continuation, will the async-channel or semaphores be garbage-collected away together with what I've called the main thread?

-- hendrik

Hmmm. I suppose I could use the channel to send a message from the choice to the main thread and let the main thread perform the appropriate action.
In fact, I suppose the message could even be the function the main thread should call.

It just seems more complicated than necessary.

-- hendrik

You might want to look at the functions in the Dialogs section, like message-box.