Reputation: 24623
I have following code of a countdown timer. It starts all right but it does not stop on pressing the stop button.
#lang racket/gui
(define myframe (new frame% [label "Timer"] [x 500] [y 200] ) )
(define tfsecs (new text-field% [parent myframe] [label "Secs:"]))
(define msgbmi (new message%
[parent myframe] [auto-resize #t] [label ""] ))
(define stopflag #f)
(define (OnButtonPressFn n)
(sleep 1)
(set! n (sub1 n))
(define m 0)
(send msgbmi set-label "")
(for((i (in-naturals)) #:break stopflag)
(set! m (- n i))
; (printf "running; i= ~a~n" m)
(send msgbmi set-label (number->string m))
(sleep 1) ) )
(define startbutton
(new button% [parent myframe] [label "Start"]
(callback (lambda (b e)
(define secs (string->number (send tfsecs get-value)))
(when secs (OnButtonPressFn secs))))))
(define stopbutton
(new button% [parent myframe] [label "Stop"]
(callback (lambda (b e)
(set! stopflag #t)))))
(send myframe show #t)
It stops if I limit the for loop to end it at 0. But I want it to keep showing how much time is over till the stop button is pressed.
Apparently, the stopflag being set by stop button is not being read by running loop. How can this be corrected? How can I make the stop button work properly here?
Upvotes: 2
Views: 90
Reputation: 31145
Let's follow the intended control flow.
First we click the start button. The system generates an event. The event handler (the call back is called) and the click is handled. Then later on the stop button is clicked. The system generates a new event and the event handler for the stop button is called.
The problem in your program is that the event handler for the start button never returns. The system only handles one event at a time, so if you never return from an event handler, any following events will not be handled.
One way to fix the problem is to start a new thread to do the actual work:
(define (OnButtonPressFn n)
(thread (λ ()
(sleep 1)
(set! n (sub1 n))
(define m 0)
(send msgbmi set-label "")
(for((i (in-naturals)) #:break stopflag)
(set! m (- n i))
; (printf "running; i= ~a~n" m)
(send msgbmi set-label (number->string m))
(sleep 1) ) )))
This event handler creates a new thread (that runs concurrently with the event loop) and then returns immediately. The event loop is there able to handle new events. In the mean time the new thread does the work (here printing numbers).
Upvotes: 1