[resolved] signal-slot concept of Qt => Cells

Discussion of Common Lisp
hewih
Posts: 30
Joined: Tue Jan 19, 2010 9:36 am

[resolved] signal-slot concept of Qt => Cells

Post by hewih » Tue Jan 19, 2010 2:26 pm

Hello fellow Lispers!

i'm a newbie to Common Lisp and developing a small game engine where i want to implement the signal-slot concept of Qt. basically it means that you can specify "signals" and "slots" (not Lisp slots) for classes and connect them for some instances. everytime the signal is called the connected slots are also called. please could you give me some hints on how to do this. i would really appreciate it!

example:

Code: Select all

(defclass A ()
   ((foo :accessor foo))
   (:signals (setf foo)))

(defclass B ()
   ((bar :accessor bar))
   (:slots (bar)))

(connect a1 (setf foo) b1 (setf bar))
(connect a1 (setf foo) b2 (setf bar))

(setf (foo a1) 537) ; => b1.bar and b2.bar = 537
Last edited by hewih on Sun Jan 31, 2010 11:14 am, edited 3 times in total.

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: signal-slot concept of Qt for Lisp classes

Post by ramarren » Tue Jan 19, 2010 3:15 pm

Unless I misunderstand the example (I don't know how Qt signals/slots work exactly), Cells (there is more documentation in cells-doc) gives you something similar, except perhaps better in some ways, since dependencies do not have to be established explicitly, just by accessing the values of other cells they are created automatically.

drewc
Posts: 1
Joined: Sat Jun 28, 2008 1:16 pm

Re: signal-slot concept of Qt for Lisp classes

Post by drewc » Tue Jan 19, 2010 4:00 pm

Untested:

Code: Select all


(defclass a ()
  ((foo :accessor foo)))

(defclass b ()
  ((bar :accessor bar)))

(let ((a1 (make-instance 'a))
      (b1 (make-instance 'b))
      (b2 (make-instance 'b)))
 (defmethod (setf foo) :after (value (object (eql a1))) 
   (setf (bar b1) (setf (bar b2) value))))
You don't need a special mechanism for this in CL, a superset of the functionality is built in. However, doing it manually is error prone. Something like CELLS, FORMULATE, or COMPUTED-CLASS is probably a better bet depending on your actual needs.

Also note that, to do this properly, you have to come up with a way to make it work with garbage collection.... almost any of the simple ways to do this are going to hold references to your objects. Qt probably gets around this by not collecting garbage automatically.

Cheers,

drewc

nuntius
Posts: 538
Joined: Sat Aug 09, 2008 10:44 am
Location: Newton, MA

Re: signal-slot concept of Qt for Lisp classes

Post by nuntius » Tue Jan 19, 2010 6:53 pm

Qt has a gc-like behavior; it essentially uses weak pointers to store the mappings, and deregisters a connection when either end is destroyed.

hewih
Posts: 30
Joined: Tue Jan 19, 2010 9:36 am

Re: signal-slot concept of Qt for Lisp classes

Post by hewih » Wed Jan 20, 2010 4:29 am

you guys are great, thank you! ^^

cells is exactly what i need and it looks pretty mature. they seem to call it "instance-specific formulas".

Code: Select all

(defmethod (setf foo) :after (value (object (eql a1)))
haven't thought of that, thanks. the values in my game engine may change 100 times per second, maybe i will fall back to this approach if cells turns out to be to slow.

dmitry_vk
Posts: 96
Joined: Sat Jun 28, 2008 8:01 am
Location: Russia, Kazan
Contact:

Re: signal-slot concept of Qt for Lisp classes

Post by dmitry_vk » Wed Jan 20, 2010 4:38 am

The signal/slot concept in Qt appeared because of C++ limitations (lack of GC and lack of first-class functions).
In Common Lisp, it is more idiomatic to implement events (analogous to events in .Net and signals in Gtk+). Basically an event is a list (or array) of functions (or weak pointers to functions depending on your needs). To raise an event you call all functions in an event and you can add and remove event handlers to the event.
Here's an example:

Code: Select all

(defclass event ()
  ((handlers :initform (make-array 0 :adjustable t :fill-pointer t)
             :accessor event-handlers)))

(defun event-invoke (event &rest args)
  (map nil
       (lambda (handler) (apply handler args))
       (event-handlers event)))

(defun event-add-handler (event handler)
  (vector-push-extend handler (event-handlers event)))

(defun event-remove-handler (event handler)
  (setf (event-handlers event)
        (delete handler (event-handlers event))))

(defun subscribe (object event handler)
  (event-add-handler (slot-value object event) handler))

(defun unsubscribe (object event handler)
  (event-remove-handler (slot-value object event) handler))

(defun raise (object event &rest args)
  (apply #'event-invoke (slot-value object event) args))

;; Example

(defclass widget ()
  ((click-event :initform (make-instance 'event))))

(defun test ()
  (let ((w (make-instance 'widget)))
    (subscribe w 'click-event (lambda (x y) (format t "The widget was clicked at ~A,~A~%" x y)))
    (raise w 'click-event 10 15)))

(test)
==>The widget was clicked at 10,15

dmitry_vk
Posts: 96
Joined: Sat Jun 28, 2008 8:01 am
Location: Russia, Kazan
Contact:

Re: signal-slot concept of Qt for Lisp classes

Post by dmitry_vk » Wed Jan 20, 2010 4:45 am

hewih wrote:you guys are great, thank you! ^^

cells is exactly what i need and it looks pretty mature. they seem to call it "instance-specific formulas".

Code: Select all

(defmethod (setf foo) :after (value (object (eql a1)))
haven't thought of that, thanks. the values in my game engine may change 100 times per second, maybe i will fall back to this approach if cells turns out to be to slow.
Adding methods to generic function is definitely not the way to do this. First is that it prevents your object from being garbage-collected. Second is that you can only define one method for one qualifier. Third is that defining a method is quite expensive operation.

hewih
Posts: 30
Joined: Tue Jan 19, 2010 9:36 am

Re: signal-slot concept of Qt for Lisp classes

Post by hewih » Wed Jan 20, 2010 6:25 am

events are not quite the thing i need. thanks for the example though.

i have a spacecraft sprite which depends on a position and is followed by 2 side-kicks (also sprites) which depend on an offset position relative to the spacecraft. the spacecraft position is changed by a mystical force, but every time it is changed, the new position should be propagated to the offsets, which in turn calculate the new absolute positions.

Code: Select all

GameObject Spacecraft
   Position
      Sprite0 -> draw Spacecraft image at Position.position
   Offset1 -> add predefined-offset (:x 10 :y 0) to Position.parent-position
      Sprite1 -> draw Sidekick image at Offset1.actual-position
   Offset2 -> add (:x -10 :y 0) to Position.parent-position
      Sprite2 -> draw Sidekick image at Offset2.actual-position
thus, every time (setf (position Position)) is called, the connected slots (Offset1,2.parent-position) should be updated, which update their actual-position to parent-position + predefined-offset, but only if they are changed.

i think this results in quite a nice architecture if it works how i imagined it. currently it should only support slot propagation, maybe i will need it for function-call propagation too.

dmitry_vk
Posts: 96
Joined: Sat Jun 28, 2008 8:01 am
Location: Russia, Kazan
Contact:

Re: signal-slot concept of Qt for Lisp classes

Post by dmitry_vk » Wed Jan 20, 2010 6:56 am

Signal/slot is actually a limited implementation of events (signal handlers can only be specific methods of some objects).
In many frameworks, you just define events like 'position-changed' or 'foo-changed', 'bar-changed' that are raised when the value of 'position', 'foo' and 'bar' slot (the CLOS slot, not slot of signal/slot) is changed (those are 'signals' in signal/slot terminology). And you have handlers (the 'slots' in signal/slot terminology) that handle updating dependent objects.
Of course, this is much more error-prone and hard than using something like Cells. But much better that Qt's signal/slot.

hewih
Posts: 30
Joined: Tue Jan 19, 2010 9:36 am

Re: signal-slot concept of Qt for Lisp classes

Post by hewih » Wed Jan 20, 2010 9:27 am

does anybody know how to apply rules in Cells on class instances? i can only find examples for class definitions.

Post Reply