[ltk-user] Another newbie question..

Peter Herth herth at peter-herth.de
Tue Mar 11 08:46:43 UTC 2008


Hi Neil,

On Tue, Mar 11, 2008 at 5:46 AM, Neil Baylis <neil.baylis at gmail.com> wrote:

>  Now I'm having trouble adding a button to my application. I'm not sure
>  whether this is a lisp question, or an ltk question, so please bear
>  with me. Here's a simplified version of the failing code:

It is a lisp question...

>
>
>  (in-package :ltk)
>  (defparameter *cvs* nil)

Here you created the dynamically scoped variable *cvs*, initializing
it with "nil" ...

>
>  (defun draw-circle (x y radius color)
>   (format t "B Canvas is ~a~%" *cvs*)
>   (itemconfigure
>    *cvs*

which is accessed here...

>    (create-oval *cvs*
>   (- x radius) (- y radius)
>   (+ x radius) (+ y radius))
>    :outline color))
>
>  (defun testit ()
>   (with-ltk ()
>     (let* ((*cvs* (make-instance 'canvas :width 200 :height 200))
Here you rebind the variable, setting it to the canvas

>    (b (make-instance 'button
>      :master nil
>      :text "Do it!"
>      :command (lambda ()
>   (draw-circle 100 100 20 :red)))))

This draw-circle is called when the button is pressed, that is *outside* the
dynamic contour of the let, and thats why you get the nil.

>
>       (format t "A Canvas is ~a~%" *cvs*)
>       (pack *cvs*)
>       (pack b)
>       (draw-circle 100 100 40 :blue))))
This draw-circle is in the dynamic contour of the let, so it succeeds..

>
>  When I load this, and call the function testit from the REPL, it
>  displays the blue circle correctly, but fails to display the red one.
>  Here is what I see in the REPL:
>
>
>  CL-USER> (load "testit.lisp")
>  #P"/Users/neil/devel/lispgui/testit.lisp"
>  CL-USER> (in-package :ltk)
>  #<Package "LTK">
>  LTK> (testit)
>  A Canvas is #<CANVAS #x873D586>
>  B Canvas is #<CANVAS #x873D586>
>  B Canvas is NIL
>
>  It prints the "B Canvas is NIL" when I click on the button, so this is
>  obviously why it fails to draw the red circle. But, I don't understand
>  why *cvs* is nil in this case. I'm sure this is painfully obvious, but
>  not to me. Can someone point me in the right direction please?

It is quite annoying, but the event handlers are run in a different dynamic
context as where they are bound. A help would be to pass the canvas as
a parameter to the event handler like:

 (defun draw-circle (canvas x y radius color) ...)

and then set up the button like:

    (b (make-instance 'button
       :master nil
      :text "Do it!"
      :command (lambda ()
                        (draw-circle *cvs* 100 100 20 :red)))))

As the lambda is a proper closure, it properly catches the value of *cvs* and
can pass it on to draw-circle.

HTH,
Peter



More information about the ltk-user mailing list