1

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?

2
  • No idea, but would it be better with nodgui that advertises to work well with multithreaded code (since its latest release)?
    – Ehvince
    Commented May 22, 2023 at 9:22
  • For the moment, LTK worked; I'd want to try nodgui but I also want to wait for it to become stable. Thanks for the suggestion!
    – digikar
    Commented May 22, 2023 at 15:50

1 Answer 1

2

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))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.