[Apologies to the postmaster if this was originally sent via rburnside@fumico.net, please delete that thread. This account is the registered one]
Hello All,
I'm currently caught in a catch22. I wish to make a CAD like program for diagram drawing. This means I need to draw lines and boxes and such - stuff Ltk's canvas is quite capable of.
Here is the kicker, I want to let the user define their own complex drawing functions which would get sucked up into my Ltk program when it starts.
The problem is, the minute a user's function gets executed, Ltk would stop updating. So, if they had a dynamic user defined function that needed points (as clicked by the canvas) Ltk would freeze and their user defined function could not get the points from the interface.
Suppose a user loaded a command like so: (defun draw-foo (&key (pt1 nil) (pt2 nil)) (unless (or pt1 pt2) (setf pt1 (prompt-canvas "Define point 1 of FOO.")) (setf pt2 (prompt-canvas "Define point 2 of FOO."))) (user-defined-drawing-nonsense pt1 pt2))
We can't hit PROMPT-CANVAS because at that point the program flow is entered the DRAW-FOO body. Ltk is great for forms but keeping both Lisp waiting for a value and Ltk catching events seems very tricky.
Sincerely, Ryan Burnside (Pixel_Outlaw)
On Mon, Dec 07, 2020 at 11:27:19AM -0700, Ryan Burnside wrote:
Hello All,
Hi!
Suppose a user loaded a command like so: (defun draw-foo (&key (pt1 nil) (pt2 nil)) (unless (or pt1 pt2) (setf pt1 (prompt-canvas "Define point 1 of FOO.")) (setf pt2 (prompt-canvas "Define point 2 of FOO."))) (user-defined-drawing-nonsense pt1 pt2))
I think i can see the point (no pun intended), but i wonder if events are the proper way to get user input, then draw with the provided data/functions/shapes.
Bye! C.
On Tue, Dec 08, 2020 at 08:38:36PM +0100, cage wrote:
Hi!
By the way does this code works as you wanted in you message?
I am using nodgui because includes a modal inputbox but could be easily translated to LTK.
---8<-------8<-------8<---------
(in-package :nodgui)
(defun ask-point (title message) (parse-integer (nodgui.mw:text-input-dialog *tk* title message)))
(defun draw-line (canvas) (let ((x1 (ask-point "point?" "x1?")) (x2 (ask-point "point?" "x2?")) (y (/ (canvas-h canvas) 2))) (make-line canvas (list x1 y x2 y))))
(with-nodgui () (let ((canvas (make-canvas nil :width 300 :height 300))) (configure canvas :background "#FFFFFF") (pack canvas) (draw-line canvas)))
---8<-------8<-------8<---------
The code above is licensed FWIW with MIT license https://opensource.org/licenses/MIT
bye! C.
Hello cage, Thanks for the reply.
Your code did demonstrate the general idea if I was collecting from a field. But as this is a CAD program (think drafting) it's important that the user be able to pick points of interest from a main canvas. As important that the canvas cursor can "snap" to points of interest such as endpoints intersections etc. (I'll handle that myself).
Here is a diagram if it helps... https://i.imgur.com/5uxY7Tq.png
Hopefully this is possible! The with-ltk macro and single thread seem to fight against updating ltk's widgets explicitly while collecting input.
Sincerely, Ryan Burnside
On 12/9/2020 5:52 AM, cage wrote:
On Tue, Dec 08, 2020 at 08:38:36PM +0100, cage wrote:
Hi!
By the way does this code works as you wanted in you message?
I am using nodgui because includes a modal inputbox but could be easily translated to LTK.
---8<-------8<-------8<---------
(in-package :nodgui)
(defun ask-point (title message) (parse-integer (nodgui.mw:text-input-dialog *tk* title message)))
(defun draw-line (canvas) (let ((x1 (ask-point "point?" "x1?")) (x2 (ask-point "point?" "x2?")) (y (/ (canvas-h canvas) 2))) (make-line canvas (list x1 y x2 y))))
(with-nodgui () (let ((canvas (make-canvas nil :width 300 :height 300))) (configure canvas :background "#FFFFFF") (pack canvas) (draw-line canvas)))
---8<-------8<-------8<---------
The code above is licensed FWIW with MIT license https://opensource.org/licenses/MIT
bye! C.
On Wed, Dec 09, 2020 at 03:38:03PM -0700, Ryan Burnside wrote:
Hello cage,
Hi!
Thanks for the reply.
You're welcome! I am always happy to discuss programming with other lispers! :)
Your code did demonstrate the general idea if I was collecting from a field. But as this is a CAD program (think drafting) it's important that the user be able to pick points of interest from a main canvas. As important that the canvas cursor can "snap" to points of interest such as endpoints intersections etc. (I'll handle that myself).
I wrote a (unfortunately never released) software -many years ago- that vaguely remember me what you want to accomplish. I had a canvas and a set of buttons next to the former. Each button allowed the user to draw a shape (nothing too complicated: box, circle a bezier arrow and a few more). If an user would wants to draw a bezier they just have to press the corresponding button (to enter in "bezier mode" so to say) and press four times the mouse button on the canvas; the program collected the points calculated the bezier and drawn a set of segments and an arrow head on the last ends.
I am not sure that this workflow fits into your program but all i did was forget about a main loop and just let the user events drive the process. I waited for <1> (i mean, mouse click) event (event bound to the canvas) checked the drawing mode (box, bezier etc.) and acts following the mode (collect two point [one more events] for a box -the diagonal- or for a segment and so on). Once got all the data needed to draw the program updated the canvas with actual drawing.
I even draw a little placeholder for each collected point in the canvas for each of this events.
There was also interactions that allowed to move control points of a shape (in this case i waited for <Button1-Motion> event).
You can even tag canvas items (the shapes) and bind different custom events to each one, see:
https://www.autistici.org/interzona/lisp.html#orge51c1f0
If this is -with good approximation- what you want to do i can guarantee is doable with ltk. :)
Here is a diagram if it helps... https://i.imgur.com/5uxY7Tq.png
Looking to the diagram i started to think that if you call the procedure 'draw-foo' inside the function bound to a canvas event you get more or less what i described above, but i could easily be wrong, as usual! :)
Inside the event function you can even run another toplevel (modal if needed and hijack completely the input system) as i did in the code i wrote before.
Hopefully this is possible!
I also hope it was! :)
Bye!!! C.
Thanks for your assistance cage - this should give me something to tinker with!
On 12/10/2020 4:08 AM, cage wrote:
On Wed, Dec 09, 2020 at 03:38:03PM -0700, Ryan Burnside wrote:
Hello cage,
Hi!
Thanks for the reply.
You're welcome! I am always happy to discuss programming with other lispers! :)
Your code did demonstrate the general idea if I was collecting from a field. But as this is a CAD program (think drafting) it's important that the user be able to pick points of interest from a main canvas. As important that the canvas cursor can "snap" to points of interest such as endpoints intersections etc. (I'll handle that myself).
I wrote a (unfortunately never released) software -many years ago- that vaguely remember me what you want to accomplish. I had a canvas and a set of buttons next to the former. Each button allowed the user to draw a shape (nothing too complicated: box, circle a bezier arrow and a few more). If an user would wants to draw a bezier they just have to press the corresponding button (to enter in "bezier mode" so to say) and press four times the mouse button on the canvas; the program collected the points calculated the bezier and drawn a set of segments and an arrow head on the last ends.
I am not sure that this workflow fits into your program but all i did was forget about a main loop and just let the user events drive the process. I waited for <1> (i mean, mouse click) event (event bound to the canvas) checked the drawing mode (box, bezier etc.) and acts following the mode (collect two point [one more events] for a box -the diagonal- or for a segment and so on). Once got all the data needed to draw the program updated the canvas with actual drawing.
I even draw a little placeholder for each collected point in the canvas for each of this events.
There was also interactions that allowed to move control points of a shape (in this case i waited for <Button1-Motion> event).
You can even tag canvas items (the shapes) and bind different custom events to each one, see:
https://www.autistici.org/interzona/lisp.html#orge51c1f0
If this is -with good approximation- what you want to do i can guarantee is doable with ltk. :)
Here is a diagram if it helps... https://i.imgur.com/5uxY7Tq.png
Looking to the diagram i started to think that if you call the procedure 'draw-foo' inside the function bound to a canvas event you get more or less what i described above, but i could easily be wrong, as usual! :)
Inside the event function you can even run another toplevel (modal if needed and hijack completely the input system) as i did in the code i wrote before.
Hopefully this is possible!
I also hope it was! :)
Bye!!! C.