Common lisp 在Windows上的Allegro Common Lisp表单应用程序中是否存在全局表单和按钮变量?

Common lisp 在Windows上的Allegro Common Lisp表单应用程序中是否存在全局表单和按钮变量?,common-lisp,allegro-cl,Common Lisp,Allegro Cl,Allegro Common Lisp表单与Delphi表单非常相似。但delphi表单至少允许您访问全局变量,如Form1、Button1、Button2等 在Allegro common lisp中,我唯一能弄清楚如何访问按钮属性和表单属性的方法是使用find sibling并使用LET设置局部变量,或者设置我自己的全局变量。是否已经有一个全局变量来访问common lisp中的button1、form1等小部件,您可以方便地访问这些小部件 例如,如果我想通过单击另一个按钮2访问Allegr

Allegro Common Lisp表单与Delphi表单非常相似。但delphi表单至少允许您访问全局变量,如Form1、Button1、Button2等

在Allegro common lisp中,我唯一能弄清楚如何访问按钮属性和表单属性的方法是使用find sibling并使用LET设置局部变量,或者设置我自己的全局变量。是否已经有一个全局变量来访问common lisp中的button1、form1等小部件,您可以方便地访问这些小部件

例如,如果我想通过单击另一个按钮2访问Allegro CL中form1上的按钮1,我会:

(let ((but1 (find-sibling :button1 widget)))  
  (setf (title but1) "hello world" )) 
使用find sibling很乏味,与delphi中只有全局变量可访问相比,似乎是浪费时间:

form1.color := clBlue;
button1.caption := 'Hello world';
button2.caption := 'I am button2';
如何在allegro common lisp中设置按钮1标题(与delphi中的标题相同),而不使用“查找同级…”。。。。如果我在与函数中的对象对话,我可以使用widget函数参数(如delphisender as TButton),但必须为其他组件使用find sibling。看起来allegro common lisp迫使人们编写查找同级代码,而不仅仅是给你一个全局变量,比如button1、button2和form1

编辑:在delphi中,form1是全局的,但是button1和button2只是全局表单类的一部分,它们本身不是全局的,但是它们的行为就像全局的,因为您可以这样做

form1.button1
form1.button2
来自其他单位

(或当前单元1中的self.button1,但delphi并不要求您一直说self,以方便键盘输入)


编辑:不,产品“allegro common lisp”根本不能处理基本的编程任务,因为它是lisp,不是一种实用的语言

这是浪费时间,但在大多数情况下并不多。我将首先回答,然后提供一些事后思考的经验

对于大多数情况,这可能就足够了:

(defmacro with-components ((&rest names) dialog &body body)
  (assert (every #'symbolp names))
  (let ((dialog-sym (gensym (symbol-name '#:dialog))))
    `(let ((,dialog-sym ,dialog))
       (let (,@(mapcar #'(lambda (name)
                           `(,name
                             (find-component
                              ,(intern (symbol-name name) :keyword)
                              ,dialog-sym)))
                       names))
         ,@body))))

(defun do-something (my-dialog)
  (with-components (my-progress-bar) my-dialog
    ;; ...
    ))
另一种方法是为窗口定义插槽,从而定义一个特定的类

这与Delphi或VB非常接近,因为它们使用对象字段,而不是全局变量作为控件。这只是语法和范围的问题:在某些语言中,您可以在方法中引用实例字段,而在公共Lisp中,您可以使用accessor函数/或/

这可以通过一个宏来进一步简化,在宏中定义对话框项及其initarg的名称,它应该为每个对话框项和
初始化实例生成一个带有插槽的类,这取决于IDE生成的生成器函数

(defmacro defdialog (name (&rest supers) (&rest slots) &rest options)
  (let ((static-dialog-item-descs (find :static-dialog-items options
                                        :key #'first))
        (dialog-sym (gensym (symbol-name '#:dialog)))
        (initargs-sym (gensym (symbol-name '#:initargs)))
        (owner-sym (gensym (symbol-name '#:owner))))
    `(progn

       (defclass ,name (,@supers dialog)
         (,@slots
          ;; TODO: intern reader accessors
          ,@(mapcar #'(lambda (static-dialog-item-desc)
                        `(,(first static-dialog-item-desc)
                          :reader ,(intern (format nil "~a-~a"
                                                   name
                                                   (first static-dialog-item-desc)))))
                    (rest static-dialog-item-descs)))
         ,@(remove static-dialog-item-descs options))

       (defmethod initialize-instance :after ((,dialog-sym ,name) &rest ,initargs-sym)
         (declare (ignore ,initargs-sym))
         (with-slots (,@(mapcar #'first (rest static-dialog-item-descs))) ,dialog-sym
           ,@(mapcar #'(lambda (static-dialog-item-desc)
                         `(setf ,(first static-dialog-item-desc)
                                (find-component
                                 ,(intern (symbol-name (first static-dialog-item-desc))
                                          :keyword)
                                 ,dialog-sym)))
                     (rest static-dialog-item-descs))))

       ;; Optional
       (defun ,name ()
         (find-or-make-application-window ,(intern (symbol-name name) :keyword)
                                          'make-my-dialog))

       (defun ,(intern (format nil "~a-~a" '#:make name))
           (&key ((:owner ,owner-sym)) #| ... |#)
         (make-window ,(intern (symbol-name name) :keyword)
           :owner (or ,owner-sym (screen *system*))
           :class ',name
           :dialog-items (,(intern (format nil "~a-~a-~a" '#:make name '#:widgets)))
           ;; ...
           ))

       (defun ,(intern (format nil "~a-~a-~a" '#:make name '#:widgets)) ()
         (list
          ,@(mapcar #'(lambda (static-dialog-item-desc)
                        `(make-instance ,(second static-dialog-item-desc)
                           :name ,(intern (symbol-name (first static-dialog-item-desc))
                                          :keyword)
                           ,@(rest (rest static-dialog-item-desc))))
                    (rest static-dialog-item-descs)))))))

(defdialog my-dialog ()
  ()
  (:static-dialog-items
   (my-progress-bar #| Optional |# 'progress-indicator
     :range '(0 100)
     :value 0               
     ;; ...
     )))
这里有很多选择

例如,您可能不希望自动定义
:after
方法,因为您可能希望自己使用业务初始化逻辑定义一个方法,因此可以在dialog maker函数中初始化插槽。但是接下来,您将与IDE生成的代码进行斗争(您可以始终使用它进行原型设计,然后调整代码),这就是我将某些代码表示为可选代码的原因

或者您可以扩展宏以将初始化代码作为参数(包括在生成的
初始化实例中),或者在
初始化实例:之后使用单独的宏,或者在
初始化实例中使用单独的宏,或者两者都使用,前者将使用后者


我可以告诉你,当有很多用户界面更新时,这个次要的,但重复浪费时间的问题就变得重要了。我指的是在几十秒或几十分钟内,每秒至少有几十个电话。大多数对话框窗口不应该这样做,因为它们只是从用户处查询数据,或者像带有操作按钮的工具窗口一样

但是,假设您遇到了这种情况,例如进度对话框

使用accessor或slot而不是
find
将大大提高性能,正如您在使用Allegro的profiler时所看到的,但这只是最热门的问题

在这种情况下,可能需要知道您是否真的需要UI更新,因此保留一些轻量级簿记,以了解您是否真的需要触摸对话框或其项。这实际上非常简单,而且这样做可能比优化对话框项访问节省更多。好的候选数据类型是计数器和时间戳

另一种技术是按确定的时间间隔延迟更新,可能是使用一个定时器来更新UI,该定时器会批量处理以前的更新请求(例如,对更新进行排队,如果尚未启动,则启动定时器,使定时器为一次性,以便在不需要时不会运行,使定时器功能在实际更新之前减少排队更新)。如果您希望每个时间单位有许多更新,这可能是最大的优化。然而,它也是最具体、最费力的一个,如果事情不简单,它很容易出错

好处是,如果实现该队列,您可能获得线程间通信,例如,在业务模型属性更改/状态更改/进度事件上注册UI更新,这可能发生在非UI后台工作线程中

PS:有了这个,我并不是说你应该只实施这些方法中的一种,我是在解释如果你不能花太多时间来解决这个问题,你会得到什么样的改进


PS:Allegro已经支持跨线程UI操作排队,包括使用
:delete types
参数的累积操作和使用
:除非types
参数的幂等操作


问题在于,此队列仅在通常用作顶级事件循环的队列中进行处理(相对于模式或菜单事件循环,或)。在非
事件循环
消息处理中,操作不会退出队列,也不会被处理。

我感谢您提供的代码。我认为allegro common lisp应该提供内置功能的产品,而不是我们必须添加自己的代码来完成某些任务
(defmacro defdialog (name (&rest supers) (&rest slots) &rest options)
  (let ((static-dialog-item-descs (find :static-dialog-items options
                                        :key #'first))
        (dialog-sym (gensym (symbol-name '#:dialog)))
        (initargs-sym (gensym (symbol-name '#:initargs)))
        (owner-sym (gensym (symbol-name '#:owner))))
    `(progn

       (defclass ,name (,@supers dialog)
         (,@slots
          ;; TODO: intern reader accessors
          ,@(mapcar #'(lambda (static-dialog-item-desc)
                        `(,(first static-dialog-item-desc)
                          :reader ,(intern (format nil "~a-~a"
                                                   name
                                                   (first static-dialog-item-desc)))))
                    (rest static-dialog-item-descs)))
         ,@(remove static-dialog-item-descs options))

       (defmethod initialize-instance :after ((,dialog-sym ,name) &rest ,initargs-sym)
         (declare (ignore ,initargs-sym))
         (with-slots (,@(mapcar #'first (rest static-dialog-item-descs))) ,dialog-sym
           ,@(mapcar #'(lambda (static-dialog-item-desc)
                         `(setf ,(first static-dialog-item-desc)
                                (find-component
                                 ,(intern (symbol-name (first static-dialog-item-desc))
                                          :keyword)
                                 ,dialog-sym)))
                     (rest static-dialog-item-descs))))

       ;; Optional
       (defun ,name ()
         (find-or-make-application-window ,(intern (symbol-name name) :keyword)
                                          'make-my-dialog))

       (defun ,(intern (format nil "~a-~a" '#:make name))
           (&key ((:owner ,owner-sym)) #| ... |#)
         (make-window ,(intern (symbol-name name) :keyword)
           :owner (or ,owner-sym (screen *system*))
           :class ',name
           :dialog-items (,(intern (format nil "~a-~a-~a" '#:make name '#:widgets)))
           ;; ...
           ))

       (defun ,(intern (format nil "~a-~a-~a" '#:make name '#:widgets)) ()
         (list
          ,@(mapcar #'(lambda (static-dialog-item-desc)
                        `(make-instance ,(second static-dialog-item-desc)
                           :name ,(intern (symbol-name (first static-dialog-item-desc))
                                          :keyword)
                           ,@(rest (rest static-dialog-item-desc))))
                    (rest static-dialog-item-descs)))))))

(defdialog my-dialog ()
  ()
  (:static-dialog-items
   (my-progress-bar #| Optional |# 'progress-indicator
     :range '(0 100)
     :value 0               
     ;; ...
     )))