球拍立即刷新、线程和产量

发布于 2024-11-17 04:18:57 字数 2642 浏览 2 评论 0原文

我一直在编写一些简单的 racket GUI 程序,为我秋季教授的课程做准备。我在动画方面遇到一些问题。我使用基本画布,并使用动画模型,其中通过调用绘制过程,每帧刷新整个画布。下面是一个示例程序。

我的问题是,我必须将动画作为单独的线程运行,或者在每个refresh-now实例之后调用yield。这是为什么呢?我希望 refresh-now 能够立即刷新图像,而无需我进行额外的工作。

我已经阅读了racket页面上的动画示例,发现它们通常直接绘制到画布上。我知道,由于画布是双缓冲的,所以效果很好......但对于我的应用程序来说,让绘画程序承担负载会更容易,因为无论如何我都需要一个工作绘画程序,以防最小化等。(当然,产量并不是一个巨大的负担,但如果不需要的话,教学会更容易。)

谢谢,
约翰

#lang racket

; Demonstrate simple animation in Racket

(require racket/gui)

(define min-x 0)
(define min-y 0)
(define max-x 200)
(define max-y 200)

; Three vertexes of the triangle, expressed relative to a starting x and y location.
(define triangle-vertexes [list 
(list 10 0) 
(list 0 20) 
(list 20 20)])

(define triangle-x 20)
(define triangle-y 20)

; Move a triangle by a (delta-x, delta-y) pair
(define (move-triangle adjust)
(set! triangle-x (+ triangle-x (first adjust)))
(set! triangle-y (+ triangle-y (second adjust))))

; Adjust the location of a vertex by adding an (x,y) adjustment to it.
; Could also be defined using map.
(define (triangle-adjust adjust vertex)
(list (+ (first adjust) (first vertex))
(+ (second adjust) (second vertex))))

; Create the paint-callback function.
; It should:
; - draw a triangle at the current location
(define (draw-triangle dc)
(let ((vertex1 (triangle-adjust (list triangle-x triangle-y) (first  triangle-vertexes)))
(vertex2 (triangle-adjust (list triangle-x triangle-y) (second triangle-vertexes)))
(vertex3 (triangle-adjust (list triangle-x triangle-y) (third  triangle-vertexes))))
(send dc draw-line (first vertex1) (second vertex1) (first vertex2) (second vertex2))
(send dc draw-line (first vertex2) (second vertex2) (first vertex3) (second vertex3))
(send dc draw-line (first vertex3) (second vertex3) (first vertex1) (second vertex1))))


(define frame (new frame%
[label "Animation Example"]
[width 800]
[height 800]))

(define triangle-canvas (new canvas% [parent frame]
[paint-callback
(lambda (canvas dc)
(display "callback called")
(draw-triangle dc))]))

(send frame show #t)

; run a thunk (a procedure of zero arguments) n times
; only useful if thunk has side-effects
(define (loop n thunk)
(cond 
((> n 0) (thunk)
(loop (- n 1) thunk))
(else false)))

; Animate the triangle.  We have to either run this in a different thread from
; the event loop or yield each time we want something to be drawn.
(define (animate-triangle)
(loop 30 
(lambda ()
(move-triangle (list 10 10))
(send triangle-canvas refresh-now)
;      (send triangle-canvas flush)
(yield)
;      (sleep 0.1)
)))

I've been writing some simple racket GUI programs to prepare for a class I'm teaching in the fall. I'm having some problems with animation. I'm using a basic canvas, and using the model for animation in which the entire canvas is refreshed each frame, by calling the paint procedure. An example program is below.

My problem is that I have to either run the animation as a separate thread, or call yield after each instance of refresh-now. Why is this? I expected the refresh-now to cause the image to refresh right away, without additional work on my part.

I have read the animation examples on the racket pages, and see that they usually draw directly to the canvas. I understand that since the canvas is double-buffered this works fine ... but for my application it's easier to just have the paint procedure carry the load, since I need a working paint procedure anyway in case of minimizing, etc. (Of course, the yield is not a huge burden, but it would be easier to teach if it were not needed.)

Thanks,
John

#lang racket

; Demonstrate simple animation in Racket

(require racket/gui)

(define min-x 0)
(define min-y 0)
(define max-x 200)
(define max-y 200)

; Three vertexes of the triangle, expressed relative to a starting x and y location.
(define triangle-vertexes [list 
(list 10 0) 
(list 0 20) 
(list 20 20)])

(define triangle-x 20)
(define triangle-y 20)

; Move a triangle by a (delta-x, delta-y) pair
(define (move-triangle adjust)
(set! triangle-x (+ triangle-x (first adjust)))
(set! triangle-y (+ triangle-y (second adjust))))

; Adjust the location of a vertex by adding an (x,y) adjustment to it.
; Could also be defined using map.
(define (triangle-adjust adjust vertex)
(list (+ (first adjust) (first vertex))
(+ (second adjust) (second vertex))))

; Create the paint-callback function.
; It should:
; - draw a triangle at the current location
(define (draw-triangle dc)
(let ((vertex1 (triangle-adjust (list triangle-x triangle-y) (first  triangle-vertexes)))
(vertex2 (triangle-adjust (list triangle-x triangle-y) (second triangle-vertexes)))
(vertex3 (triangle-adjust (list triangle-x triangle-y) (third  triangle-vertexes))))
(send dc draw-line (first vertex1) (second vertex1) (first vertex2) (second vertex2))
(send dc draw-line (first vertex2) (second vertex2) (first vertex3) (second vertex3))
(send dc draw-line (first vertex3) (second vertex3) (first vertex1) (second vertex1))))


(define frame (new frame%
[label "Animation Example"]
[width 800]
[height 800]))

(define triangle-canvas (new canvas% [parent frame]
[paint-callback
(lambda (canvas dc)
(display "callback called")
(draw-triangle dc))]))

(send frame show #t)

; run a thunk (a procedure of zero arguments) n times
; only useful if thunk has side-effects
(define (loop n thunk)
(cond 
((> n 0) (thunk)
(loop (- n 1) thunk))
(else false)))

; Animate the triangle.  We have to either run this in a different thread from
; the event loop or yield each time we want something to be drawn.
(define (animate-triangle)
(loop 30 
(lambda ()
(move-triangle (list 10 10))
(send triangle-canvas refresh-now)
;      (send triangle-canvas flush)
(yield)
;      (sleep 0.1)
)))

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

琴流音 2024-11-24 04:18:57

这不是您有关 refresh-now 问题的答案,但显式线程和循环的更好替代方案是 timer% 类:

;; This goes after (send frame show #t), replacing loop and animate-triangle
(define timer-counter 0)
(define timer
  (new timer%
       (interval 100)  ;; update every 100 ms
       (notify-callback
        (lambda ()
          (cond [(< timer-counter 30)
                 (set! timer-counter (add1 timer-counter))
                 (move-triangle (list 10 10))
                 (send triangle-canvas refresh)]
                [else
                 (send timer stop)])))))

如果您重新定义停止条件根据三角形的状态,你可以去掉辅助的定时器计数器;我将其放入以模仿原始代码的行为。

计时器是在与帧相同的事件空间中创建的,并且事件空间有一个事件处理线程,这就是为什么您不必显式创建自己的线程。

如何设计程序,第二版有另一种动画方法,其中画布并自动管理更新。您只需调用 big-bang 函数来(功能性地)更新“世界状态”并将“世界状态”渲染为图像。具体取决于您所教的内容,它可能对您有用,也可能没有用。

This isn't an answer to your question about refresh-now, but a better alternative to an explicit thread and loop is the timer% class:

;; This goes after (send frame show #t), replacing loop and animate-triangle
(define timer-counter 0)
(define timer
  (new timer%
       (interval 100)  ;; update every 100 ms
       (notify-callback
        (lambda ()
          (cond [(< timer-counter 30)
                 (set! timer-counter (add1 timer-counter))
                 (move-triangle (list 10 10))
                 (send triangle-canvas refresh)]
                [else
                 (send timer stop)])))))

If you redefine your stopping condition based on the triangle's state, you can get rid of the auxiliary timer-counter; I put it in to mimic the behavior of your original code.

The timer is created in the same eventspace as the frame, and the eventspace has an event-handling thread, which is why you don't have to create your own thread explicitly.

How to Design Programs, 2nd ed has another approach to animation, where the canvas and updates are managed automatically. You just call big-bang with functions to (functionally) update the "state of the world" and render the "state of the world" as an image. Depending on exactly what you're teaching, it may or may not be useful to you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文