[ltk-user] ensure-ltk ?

Peter Herth herth at peter-herth.de
Tue Jan 5 10:29:03 UTC 2010


Hi Daniel,

testing *wish* like that should work. However, why or for which
purpose do you want to use ensure-ltk?
I usually split my code in two parts: functions that create the UI
which assume that Wish is running, and
start functions like "main" which only start wish and then call the UI
creation functions. So if I need a
different entry point to my program I just write a different startup function.
Besides, you can have any number of wish processes running at the same
time. So e.g. for a debugging
tool, you can create a separate Wish with with-ltk.

On Sun, Jan 3, 2010 at 4:42 AM, Daniel Herring <dherring at tentpost.com> wrote:
> On Fri, 1 Jan 2010, Daniel Herring wrote:
>
>> What's the proper idiom when writing a function that might start wish or
>> might be called inside another with-ltk?
>>
>> For example,
>> (defun some-window ()
>>   (ensure-ltk ()
>>     ...))
>>
>> Where some-window could be called from a REPL or from a tk callback? Is
>> there already a way to do this in ltk, or should I define a macro
>> something like the following seemingly broken code?
>>
>> (defmacro ensure-ltk ((&rest options) &body body)
>>   (let ((fname (gensym)))
>>     `(labels ((,fname () , at body))
>>        (if (wish-stream *wish*)
>>            (,fname)
>>            (with-ltk ,options (,fname))))))
>
> Here's my current macro.  Could it be added to ltk.lisp?
>
> (defmacro ensure-ltk ((&rest options &key (debug 2)
>                              &allow-other-keys)
>                       &body body)
>   "Wrap BODY in with-ltk unless a connection already exists."
>   (declare (ignore debug))
>   (let ((fname (gensym)))
>     `(flet ((,fname () , at body))
>        (if (wish-stream *wish*)
>            (,fname)
>            (with-ltk ,options (,fname))))))
>
> It turns out that both ensure-ltk macros work.  However care must be
> exercised; my first use triggered Tk bug #219967.  My distilled Ltk sample
> appears below.
>
> ;;; CAUTION
> ;;
> http://sourceforge.net/tracker/index.php?func=detail&aid=219967&group_id=12997&atid=112997
> ;; This can freeze a graphical window manager.
> ;; To run on linux and recover:
> ;; - log into a text console (e.g. Ctrl-Alt-F2)
> ;; - switch graphical (Ctrl-F7) and run the bomb
> ;; - switch back to text and `killall wish`
> ;; - watch `top` until things calm down
> (defun bomb ()
>   (with-ltk ()
>     (pack (make-instance 'treeview))
>     ;; commenting out the sleep might save your window manager
>     (sleep 1) ; just to let the treeview appear
>     (grid (make-instance 'label :master nil) 0 0)))
>
>
> Even without this Tk bug, its probably a good idea to always pass a proper
> :master to other functions which create widgets...

Yes, you must not mix pack and grid. I think every Tk programmer does that
exactly one time :). In practice that has not been an issue with me. Whenever
the UI becomes so complex that I want to split it in several functions - that is
for any nontrivial LTk program - I actually split it in several "widgets".

So I do something like: (untested code, might not compile)

(defclass my-widget (frame)
  ...)

(defmethod initialize-instance :after ((w my-widget) &key)
 ;; .. now create the whole part of the UI that my-widget covers
 ;; all such created UI elements have :master w
  )

and then just use it by instantiating it. In the current LTk branch, there
is also the nice defwidget macro, which makes this even nicer...

So essentially, the layout of a container is only done within one function,
that reduces the chance of mixing up pack and grid. So a main function
could look like:
  (with-ltk ()
    (let ((w1 (make-instance 'my-widget))
          (w2 (make-instance 'another-widget))
     (pack w1)
     (pack w2)))


Peter




More information about the ltk-user mailing list