Racket GUI:如何引用运行时动态创建的小部件?

Racket GUI:如何引用运行时动态创建的小部件?,racket,Racket,这是我第一次使用球拍图形用户界面,我必须说,我感到非常困惑 我有以下要求: 由几个小部件组成的窗口 小部件状态必须随用户操作而改变 可以同时拥有这些窗口的多个实例 由于(3),我创建了一个函数createwindow,它使用新的小部件创建了一个新窗口。 但正因为如此,我没有全局变量来引用我的小部件 如何引用运行时动态创建的小部件 如何以惯用的Racket GUI风格组织GUI代码?这取决于需要引用的位置。谁需要和谁说话 例如,如果一个小部件在同一帧中有一个对另一个小部件的回调,并且如果您同时创建

这是我第一次使用球拍图形用户界面,我必须说,我感到非常困惑

我有以下要求:

  • 由几个小部件组成的窗口
  • 小部件状态必须随用户操作而改变
  • 可以同时拥有这些窗口的多个实例
  • 由于(3),我创建了一个函数
    createwindow
    ,它使用新的小部件创建了一个新窗口。 但正因为如此,我没有全局变量来引用我的小部件

    如何引用运行时动态创建的小部件


    如何以惯用的Racket GUI风格组织GUI代码?

    这取决于需要引用的位置。谁需要和谁说话

    例如,如果一个小部件在同一帧中有一个对另一个小部件的回调,并且如果您同时创建两个小部件,那么您可以命名它们,并让回调闭包引用正确的名称(即变量)。下面是一个小示例程序:

    #lang racket/gui
    
    ;; create-hello-window : -> Void
    (define (create-hello-window)
      (define f (new frame% (label "Example") (width 600) (height 400)))
      (define t (new text%))
      (define ec (new editor-canvas% (parent f) (editor t)))
      (define b (new button% (parent f) (label "Say hello")
                     (callback (lambda (b ce) (say-hello t)))))
      (send f show #t)
      (void))
    
    ;; say-hello : Editor -> Void
    (define (say-hello t)
      (send t set-position (send t last-position))
      (send t insert "Hello world!\n"))
    
    ;; Create two independent hello windows
    (for ([i 2]) (create-hello-window))
    
    请注意,按钮回调引用局部变量
    t
    (编辑器)

    对于更复杂的通信,需要将引用保存到要引用的对象的某个位置。这是前一个程序的另一个版本,其中窗口被组织成组,每个窗口都有一个按钮,用于向组中的其他窗口写入问候语。该组管理窗口及其编辑器的列表

    #lang racket/gui
    
    ;; A HelloWin is (hellowin Editor Frame)
    (struct hellowin (editor frame))
    
    ;; A HelloGroup is (hellogroup String (Listof HelloWin))
    (struct hellogroup (name [wins #:mutable]))
    
    ;; create-hello-window : HelloGroup -> Void
    (define (create-hello-window group)
      (define f
        (new frame% (label (hellogroup-name group)) (width 600) (height 400)))
      (define t (new text%))
      (define ec (new editor-canvas% (parent f) (editor t)))
      (define hi-b
        (new button% (parent f) (label "Say hello")
             (callback (lambda (b ce) (add-to-end t "Hello world!\n")))))
      (define greet-b
        (new button% (parent f) (label "Greet others in group")
             (callback (lambda (b ce) (greet-everyone-else t group)))))
      (send f show #t)
      (set-hellogroup-wins! group (cons (hellowin t f) (hellogroup-wins group))))
    
    ;; add-to-end : Editor String -> Void
    (define (add-to-end t str)
      (send t set-position (send t last-position))
      (send t insert str))
    
    ;; greet-everyone-else : Editor HelloGroup -> Void
    (define (greet-everyone-else my-t group)
      (for ([h (in-list (hellogroup-wins group))])
        (define t (hellowin-editor h))
        (unless (equal? t my-t)
          (add-to-end t (format "~a, hello from another window!\n"
                                (hellogroup-name group))))))
    
    ;; Create two groups, and create windows for each group.
    
    (define group1 (hellogroup "Group 1" null))
    (for ([i 3]) (create-hello-window group1))
    
    (define group2 (hellogroup "Group 2" null))
    (for ([i 2]) (create-hello-window group2))
    
    我的代码不使用hellowin frame,但您可以使用它编写一个过程,例如关闭组中的所有窗口


    也许您不希望组直接访问框架及其小部件。然后,您可以更改协议,使组包含某种问候回调,或者包含实现您定义的
    greetable
    接口的对象(而不是小部件本身),等等。然后回调(或
    greetable
    对象)将具有对小部件的引用(或字段),但不会将它们直接公开给组码。此外,可能组应该是具有注册方法的对象,而不是具有可变字段的结构。

    您的示例非常有用!非常感谢。