Skip to content

Instantly share code, notes, and snippets.

Last active July 5, 2022 04:36
Show Gist options
  • Save Metaxal/5182719 to your computer and use it in GitHub Desktop.
Save Metaxal/5182719 to your computer and use it in GitHub Desktop.
Example of using `yield', and related usages of Racket's GUI
#lang racket/gui
(define xmax 200)
(define ymax 200)
(define seconds-in-loop 3)
(define fr (new frame% [label ""]))
(define cv (new canvas% [parent fr] [min-width xmax] [min-height ymax]))
(define dc (send cv get-dc))
(send fr show #t)
; Uncomment/comment some of the following:
;parameterize ([current-eventspace (make-eventspace)])
(let loop ([n (* 10 seconds-in-loop)]
[x1 0] [y1 0] [x2 0] [y2 0])
(queue-callback (λ()(send dc draw-line x1 y1 x2 y2)))
(sleep .1)
(when (> n 0)
(loop (sub1 n) x2 y2 (random xmax) (random ymax))))
(displayln "The end.")
Note: The main thread is the GUI thread.
Results, depending on what is uncommented:
void + no-thread:
waits for the loop to finish before displaying any line,
and then prints "The end.".
Events cannot be processed because the loop is blocking the main/GUI thread.
(But actually nothing may be drawn because the canvas may be cleared too quickly.)
void + thread:
Starts the thread that displays in the canvas,
and immediately prints "The end."
The program ends when the frame is closed.
The lines can be drawn (events can be processed) because the main/GUI thread
is not blocked.
sync + thread:
Behaves like void + no-thread.
Blocks the main/GUI thread until the second thread stops.
Then all events can be processed.
yield + thread:
Displays the lines 1 at a time but waits for the loop to finish before
printing "The end.".
Blocks on the thread, but let the main/GUI thread still handle GUI events.
make-eventspace + no-thread:
Same as yield+thread, even without queue-callback.
A new eventspace and its associated thread are created.
Not sure which events go where...
#lang racket/gui
;; Eli's examples for creating several plot windows, from:
;; Cheap hack that mimics a plot window.
(define (popup title)
(define f (new frame% [label title] [min-width 100] [min-height 100]))
(define c
(new (class canvas%
(define/override (on-char e)
(when (eq? 'escape (send e get-key-code)) (send f show #f)))
[parent f]
[paint-callback (λ(c dc) (send* dc (draw-line 0 0 100 100)
(draw-line 0 100 100 0)))]))
(send f show #t))
;; Shows the original problem: no gui is done until EOF.
[#f (for ([title (in-lines (current-input-port))])
(popup title))]
;; Laurent's solution: each popup has its own eventspace; kind of
;; works, but wasting resources (each eventspace = a new thread), and
;; EOF quits and kills all windows.
[#f (for ([title (in-lines (current-input-port))])
(parameterize ([current-eventspace (make-eventspace)])
(popup title)))]
;; Naive solution: put the reading in a side thread, but since no
;; windows are generated quickly enough, it just exists.
[#f (thread (λ() (for ([title (in-lines (current-input-port))])
(popup title))))]
;; Robby's solution: like the above, but have the first read happen on
;; the main thread.
[#f (popup (read-line))
(thread (λ() (for ([title (in-lines (current-input-port))])
(popup title))))]
;; IMO this is simpler: one new thread as before, but this is for the
;; gui now (and implied by the new eventspace), but it still just
;; kills all popups on EOF for the same reason as above.
[#f (parameterize ([current-eventspace (make-eventspace)])
(for ([title (in-lines (current-input-port))])
(popup title)))]
;; Simple solution: wait for the gui to be done after reading is done.
[#f (parameterize ([current-eventspace (make-eventspace)])
(for ([title (in-lines (current-input-port))])
(popup title))
(void (sync current-eventspace)))]
;; And finally, the *usual* case where there's no main-thread
;; blocking, which is why this subject came up, will now Just Work™.
[#t (popup "Foo")])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment