digikar
digikar

Reputation: 588

Recommended way to use LTK asynchronously

I want a function to create a LTK frame/canvas that lets it persist in the background. The function then updates the LTK frame/canvas through other functions as it does some other work.

What I have so far is the following two functions and one global variable:

(defvar *my-canvas*)

(defun initialize-window (...)
  (with-ltk (:serve-event t )
    (setq *my-canvas* (make-instance 'canvas))
    (let ((c *my-canvas*))
      ...draw some objects on the canvas...
      (pack c))

(defun update-window (...)
  (with-ltk ()
    (let ((c *my-canvas*))
      ...do something with the canvas...)))

I call these functions from a separate function:

(defun visualize (...)
   ... do something ...
   (initialize-window ...)
   ... do something more ...
   (update-window ...))

initialize-window works without issues. But whenever I call the update-window function, I run into a Tcl/Tk error: invalid command name ".wc" error from (ltk::read-data) called by any functions in the "do something with the canvas" part.

What is the recommended way to get update-window to work without issues?

Upvotes: 1

Views: 99

Answers (1)

digikar
digikar

Reputation: 588

Here's one way I was able to get it working.

It seems that the error Tcl/Tk error: invalid command name ".wc" arises because the wish inside the with-ltk in update-window is different from that in the initialize-window.

This can be seen in the call-with-ltk code that the expansion of with-ltk calls:

(defun call-with-ltk (thunk &rest keys &key (debug 2) stream serve-event remotep
                      &allow-other-keys)
  "Functional interface to with-ltk, provided to allow the user the build similar macros."
  (declare (ignore stream))
  (flet ((start-wish ()
           (apply #'start-wish
                  :remotep remotep
                  (append (filter-keys '(:stream :debugger-class :debug-tcl)
                                       keys)
                          (list :debugger-class (debug-setting-condition-handler debug)))))
         (mainloop () (apply #'mainloop (filter-keys '(:serve-event) keys))))
    (let ((*wish* (make-ltk-connection :remotep remotep)))
      (catch *wish*
        (unwind-protect
             (progn
               (start-wish)
               (multiple-value-prog1
                   (with-ltk-handlers ()
                     (with-atomic (funcall thunk)))
                 (mainloop)))
          (unless serve-event
            (exit-wish)))))))

A work-around is to do this handling yourself. We base our code upon the above code:

(defun initialize-window (...)
  (unless (wish-stream *wish*) (start-wish))
    (catch *wish*
      ... do something with the canvas or GUI ...
      ... but do not call (start-wish) or (exit-wish) ...)))
(defun update-window (...)
  (catch *wish*
    ... do something with the canvas or GUI ...
    ... but do not call (start-wish) or (exit-wish) ...))

However, if the plan is to call visualize repeatedly, then we also need a finalize-window function that exits the wish:

(defun visualize (...)
   (unwind-protect
        (progn
          ... do something ...
          (initialize-window ...)
          ... do something more ...
          (update-window ...))
     (finalize-window)))

with its definition as:

(defun finalize-mot-window ()
  (catch *wish*
    (exit-wish))))

Upvotes: 2

Related Questions