Hi,
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 () ,@body)) (if (wish-stream *wish*) (,fname) (with-ltk ,options (,fname))))))
Thanks, Daniel
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 () ,@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 () ,@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&grou... ;; 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...
Later, Daniel
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@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 () ,@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 () ,@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&grou... ;; 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