如何在Racket中停止这个睡眠程序

时间:2016-08-29 13:50:57

标签: user-interface graphics racket

我有以下倒数计时器的代码。它开始没事,但按下停止按钮时不会停止。

#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)

如果我将for循环限制为0,它会停止。但是我想让它继续显示停止按钮被按下的时间。

显然,运行循环不会读取停止按钮设置的停止标志。如何纠正?如何在此处使停止按钮正常工作?

1 个答案:

答案 0 :(得分:1)

让我们按照预期的控制流程进行操作。

首先我们点击开始按钮。系统生成一个事件。事件处理程序(调用回调)并处理单击。然后点击停止按钮。系统生成一个新事件,并调用停止按钮的事件处理程序。

程序中的问题是启动按钮的事件处理程序永远不会返回。系统一次只处理一个事件,因此如果您从未从事件处理程序返回,则不会处理任何后续事件。

解决问题的一种方法是启动一个新线程来完成实际工作:

(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) ) )))

此事件处理程序创建一个新线程(与事件循环并发运行),然后立即返回。事件循环能够处理新事件。同时新线程完成工作(这里打印数字)。