Is it possible to create a lisp that remembers your previous string selection?

Is it possible to create a lisp that remembers your previous string selection?

Anonymous
Not applicable
4,999 Views
37 Replies
Message 1 of 38

Is it possible to create a lisp that remembers your previous string selection?

Anonymous
Not applicable

I am relatively new to AutoLISP so I don't much about the things that can be done with this language. I have experience with other languages like C, C++, Python, etc. I was wondering if its possible to create a lisp that remembers your previously selected strings and will prompt you and ask whether or not lets say for example, you want to use the previous 3 strings? Don't worry I am not asking anyone to do this for me. Just want to know is it possible to do it using this language? If yes, then can you guide me as to which command/function to look at in order to achieve this.
Any help will be appreciated 🙂

0 Likes
Accepted solutions (1)
5,000 Views
37 Replies
Replies (37)
Message 2 of 38

cadffm
Consultant
Consultant
Explain what you mean with "previously selected strings".
Object selection?
All three selected in one (last) selection or whithin multiple selection, previous the past selectionset?
Or totally other things?
I am not sure what the procedure.

And what you want to do with the result.

Sebastian

0 Likes
Message 3 of 38

marko_ribar
Advisor
Advisor

String is common type of data in ALISP that can be assigned to any variable you want... For assigning string, you simply use (setq varname "blablabla"). This way when you call variable from LISP or command prompt with !varname, output will be its value "blablabla"... You can use in lisp all kinds of manipulations with variables - creating lists (also common type of data) which is the most common thing you do in ALISP - (Auto List Processing API); doing some math operations with numbers as values; concatenating strings with (strcat) function; getting substring from main string using function (substr); and with lists you can do all kind of things (appending - (append), constructing - (cons, list), reversing (reverse), getting nth item-element (nth pos lst) where pos is integer starting from 0 to any positive number less than length of list -1 and lst is reference list, getting position of element - opposite than nth (vl-position) function, getting sublist by using (member item lst) where item is some element in list and lst reference list - output is sublist that starts from item element up to the end of list; here you can use some kind of combinations to get sublist from middle of main list (reverse (member lastitm (reverse (member startitm lst)))) - startitm and lastitm are corresponding items from middle of reference list and lst is reference list...

And so on...

Now to your question :

You need to store more strings and keep it in memory :

(while (/= "" (setq str (getstring t)))

  (setq lst (cons str lst))

)

(setq lst (reverse lst))

With this combinations of ALISP functions you are entering any number of strings and constructing variable lst that contains your strings... If you want to keep that list in memory it is preferable not to (setq lst nil) which will drop its content and remove lst variable from memory - also lst variable shouldn't be localized inside main (defun) function and it should be treated as global variable... For globals, there is one common scripting practice - add * before and after variable name, so that everyone that reads lisp can recognize that it is global... So lst, should actually be written as *lst*... Now that's all fine all until you are in active ACAD session, but when you quit CAD and restart it again *lst* will be lost from memory, so if you want to keep its value on safe place, you should either use (setenv) and (getenv) functions to access CADs environment memory storage, or save variable value and its name in some external *.txt file that can be loaded after you start new session...

Now if you stored your *lst* and you want to use first 3 strings, then simply :

- 1st string = (car *lst*) or (nth 0 *lst*)

- 2nd string = (cadr *lst*) or (nth 1 *lst*)

- 3rd string = (caddr *lst*) or (nth 2 *lst*)

So to get and print 3rd string, you assign it to variable with (setq 3rdstr (nth 2 *lst*))

To print it to command line use, either (princ) function which will return that variable value as T or (prompt) function which will just print it on command line and return after nil :

(prompt (strcat "\n" 3rdstr))

(princ)

"\n" symbol is new line character and (princ) at the end is to suppress nil return at the end of routine... (princ) is for that reason very common way to finish routine - quiet exit and ending expression...

 

So I hope I shed some light to what your request is, now experiment and practice to overcome all upcoming challenges... If you're stuck with some problem don't be afraid to ask, someone will always be willing to answer...

Regards, M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
Message 4 of 38

roland.r71
Collaborator
Collaborator

Like @cadffm i'm not sure what exactly you are looking for, but as you mention strings, i'm guessing you wonder about variables and their values:

 

By default any variable with AutoLISP is global. Meaning ACAD will keep (/ remember) their values. So, if your lisp routine sets such a variable, it will be retrievable the next time.

 

I frequently use this method to keep usersettings active.

 

Example:

   (setq *ln_addLine (cond (*ln_addLine)(T))                                    ; Previous or default for drawing line
         *ln_addPoly (cond (*ln_addPoly)(nil))                                  ; Previous or default for drawing polyline
         *ln_clrSel  (cond (*ln_clrSel) ("Blue"))                               ; Previous or default color
         *ln_ltSel   (cond (*ln_ltSel)  ("Normal"))                             ; Previous or default linetype (scale)
         *ln_transp  (cond (*ln_transp) (50))                                   ; Previous or default transparancy
         *ln_custCol (cond (*ln_custCol)(7))                                    ; Previous or default custom color
         *ln_mode    (cond (*ln_mode)   (nil))
         ltname  (getvar "CELTYPE")
         ltscale (getvar "CELTSCALE")
   )

All the variables starting with a * are global. (the * is not mandatory, but it helps (me) to reckognize them)

 

The first time the routine runs they are all empty (nil)

With the above code they are set with a default value, unless they already have a value. (set on a previous run)

 

If you where to set the color to green, it will be green the next time too, instead of the default blue.

 

Note: this only works within 1 and the same session. Closing the drawing (or acad when in SDI) will clear the memory used by it as well. The keep/remember anything beyond that, you will need to store the values inside a .ini file or something similar.

 

   (defun setColor ( / )
      (initget "Red Green Blue Custom")
      (setq *ln_clrSel
         (cond
            ((getkword (strcat "\nSelect color [Red/Green/Blue/Custom] <" *ln_clrSel ">: ")))
            (*ln_clrSel)
         )
      )
      (if (= *ln_clrSel "Custom")
         (progn
            (princ "\nSelect color")
            (setq *ln_custCol (acad_colordlg *ln_custCol))
            (princ (strcat "\nColor set to: " (itoa *ln_custCol)))
         )
      )
   )

 

With the above code, the user can select a color, with a default or any previous selected color set. So the user only has to press enter, to keep the default or previous choice.

 

So, you run the routine the first time and the color will be set to the default value (Blue, in this case as set with the first code snippet.) If you set the color to Red, the next time you run the routine it will be set to Red. (i use this for red/blue markings of drawings (remove/add) (& green is for "comments"))

Message 5 of 38

Moshe-A
Mentor
Mentor

Hi,

 

as with C, C++ you can write keys and values (only strings) to registry

with (vl-registry-write) function and retrieve it with (vl-registry-read)

 

moshe

 

Message 6 of 38

roland.r71
Collaborator
Collaborator

@Anonymous wrote:

I am relatively new to AutoLISP so I don't much about the things that can be done with this language. I have experience with other languages like C, C++, Python, etc. I was wondering if its possible to create a lisp that remembers your previously selected strings and will prompt you and ask whether or not lets say for example, you want to use the previous 3 strings? Don't worry I am not asking anyone to do this for me. Just want to know is it possible to do it using this language? If yes, then can you guide me as to which command/function to look at in order to achieve this.
Any help will be appreciated 🙂


I've read and reread this post, and despite giving an example of how to make a lisp remember & use previous choices/selections, like most here i realy can't make heads or tail of it.

 

Are we talking strings, as in "variable value (text)", or a user choice? a commandline entry? any kind of text typed? or... What exactly do you mean by: "Previously selected strings" ?

...and how does/should it "ask whether or not lets say for example, you want to use the previous 3 strings" ?

 

Typically a string is not "selected".

I'm a bit at a los to what exactly it is you are looking for. Which makes it hard to give a correct answer.

 

Depending on what exactly you mean, the answer could be: No, you can't. -or- read/write to registry -or- use globals -or- anything inbetween or a combination.

Message 7 of 38

Anonymous
Not applicable
Thank you all for your help and explanations. I really appreciate it. I realize I didn’t give a more detailed explanation of my problem. I am sorry for that. Basically, I have LISP that promts the user to select multiple texts at a time by left clicking on each text, once the user is done selecting and wishes to display the select texts as a concatenated string, the user right clicks and can place that text string in the desire location. So for example I have multiple separate letters on my drawing lets say ‘A’ ‘B’ ‘C’ ‘D’ ‘E’ ‘F’ ‘G’ ‘H’and when the user runs the lisp lets say the user clicks on A first then C then E and then B. When the user right clicks and places the string it shows up as ‘A-C-E-B’. The problem is that the first three letters A C and E are repeated multiple times in what I want to do, only the 4th letter or any other letters after that vary. So I want to know if its possible that once the user selects the first group of string, then the first three letters get stored somewhere on the next selections the user just has to select the 4th letter etc. and once the user rights clicks and places the first three are there already added with the new 4th letter.
P.S. I don’t how to select and reply to all of you so I am sorry for that 🙂 but this reply if for everyone trying to help
0 Likes
Message 8 of 38

ronjonp
Mentor
Mentor

So you just need to define a common prefix then pick text to append to it?

A sample drawing might help.

Message 9 of 38

Moshe-A
Mentor
Mentor

to solve this you have to post the lisp or you prefer to get a new lisp?

0 Likes
Message 10 of 38

roland.r71
Collaborator
Collaborator

I knew i should have added: -or- something completely different.

Smiley Very Happy

Message 11 of 38

Moshe-A
Mentor
Mentor
Accepted solution

here is my version to for your lisp.

i did not understand though the right click thing, why it is need? isn't it enough to let user to press enter to signal done selecting texts

 

look at the default prefix text, it is not saved between drawing sessions but if it needed then

write the default prefix text to registry (vl-registry-write) (vl-registry-read)

 

enjoy

Moshe

 

 

 

(defun c:concat (/ pickEntity de-highlight GetPrefixText ; local functions
		   default i ss0 tconcat pick ename0 elist0 i value hgt rot p0) 

 (defun pickEntity (def / ent)
  (initget "Prefix")

  (if (/= def "")
   (setq ent (entsel (strcat "\n<Pick text>/Prefix <" def ">: ")))
   (setq ent (entsel "\n<Pick text>/Prefix: "))
  )
 ); pickEntity
  
 (defun de-highlight (ss / j)
  (setq j -1)
  (repeat (sslength ss)
   (setq j (1+ j))
   (redraw (ssname ss j) 4) 
  ); repeat
 ); de-highlight
  
 (defun GetPrefixText (/ j ss1 ss2 ptext ename2 elist2 val)
  (setq j 0 ss1 (ssadd) ptext "") 
  (while (setq ss2 (ssget ":S" '((0 . "text"))))
   (setq j (1+ j) ename2 (ssname ss2 0))
   (ssadd ename2 ss1)
   (redraw ename2 3)
   (setq elist2 (entget ename2))
   (setq val (cdr (assoc '1 elist2)))
 
   (if (> j 1) 
    (setq ptext (strcat ptext "-" val))
    (setq ptext (strcat ptext val))
   )
  ); while

  (de-highlight ss1) 
   
  ptext
 ); GetPrefixText
  
 (setvar "cmdecho" 0)
 (command "._undo" "_begin")
 (setq default (getvar "users1")))
  
 (setq i 0 ss0 (ssadd) tconcat "")
 (while (setq pick (pickEntity default))
  (cond
   ((eq pick "Prefix")
    (setvar "users1" (setq default (GetPrefixText)))
   ); case
   ( t
    (setq ename0 (car pick))
    (setq elist0 (entget ename0))
    (if (eq (strcase (cdr (assoc '0 elist0))) "TEXT")
     (progn
      (setq i (1+ i))
      (ssadd ename0 ss0)
      (redraw ename0 3)
      (setq value (cdr (assoc '1 elist0)))
 
      (if (> i 1) 
       (setq tconcat (strcat tconcat "-" value))
       (setq tconcat (strcat tconcat value))
      )
     ); progn
    ); if
   ); case
  ); cond
 ); while
  
 (de-highlight ss0)
  
 (setq hgt (cdr (assoc '40 elist0)))
 (setq rot (cdr (assoc '50 elist0)))

 (if (setq p0 (getpoint "\nPlace concatinate text: "))
  (if (/= default "")
   (command "._text" p0 hgt (angtos rot 2 4) (strcat default "-" tconcat))
   (command "._text" p0 hgt (angtos rot 2 4) tconcat)
  )
 )
  
 (command "._undo" "_end")
 (setvar "cmdecho" 1)
  
 (princ)
); c:concat

0 Likes
Message 12 of 38

scot-65
Advisor
Advisor
Yes, topic was a little vague.

Do you wish to have this string "remember from last time"?
For example, remember the last entered destination layer name
for a custom CHPROP command (/ ENTMOD).

There are several methods for storage and retrieval of declared variables.

a) The Gremlin. Let's call it variable 'd' as in (setq d "AHA!")
Do not declare the variable in the opening line: (defun c:LC ( / a b c)...
where 'a' 'b' 'c' are cleared when program ends. I have a habit of using
a prefix to the command name: (setq USER_LC "AHA!"). This variable is
session only, running loose, and will go away when the editor is closed.
More than likely this variable is structured as a LIST. Don't worry, lists are
easy to manipulate as compared to other languages (ARRAY).

b) The drawing file's dictionary, specifically VLAX-LDATA-GET and VLAX-LDATA-PUT.
This is used for drawing-specific data (an example would be DWGPROPS
information). Again, a LIST is possible.

c) The Registry. Generally used for user-specific settings. VL-REGISTRY-READ
and VL-REGISTRY-WRITE. An example is to restore OSMODE to a value that
you desire due to a poorly/incomplete written program.

d) Some sort of external file such as INI-structured. One will have to create
a custom parser as I do not believe there is one built in that will read this.

e) Obsolete: acad.CFG configuration (INI) file. This is a pre-registry method
I believe still works, but is no longer preferred.

However, it looks like you are having issues with STRCAT.
If that's the case, never mind...
In it's simplest form:
(setq a "")
(while (setq b (cdr (assoc 1 (entget (car (entsel "Select a text object: "))))))
(setq a (strcat a (if (> (strlen a) 0) "-" "") b))
);while
(alert a)

???

Scot-65
A gift of extraordinary Common Sense does not require an Acronym Suffix to be added to my given name.

Message 13 of 38

john.uhden
Mentor
Mentor

You have had a number of very smart and helpful talent here give you a lot of valuable information about strings.  All I really know is that you want to save the collection (of strings, be they one character or an entire paragraph) somewhere for later use, so that the user doesn't have to select or type them all over again.

 

YES, you can.  The question is where to save them...

a)  Just in temporary memory within the current drawing only?

b)  In memory for the current AutoCAD session?

c)  In the current drawing only but for use in a current and later session?

d)  Outside the drawing and accessible during any future editing session?

 

We can show you how to do any and all of that.  Plus, you can save multiple strings as well.  In fact an entire Webster's Dictionary, but I really think that would slow you down a tad, not to mention sending your computer into a "terminal" seizure.  <Get it?>

 

BTW, any reply you make is posted for everyone to see and read and respond.  Sort of like going to AA but without as much stigma.  And you don't have to be a drunkard, even if sometimes it helps or is more fun.

John F. Uhden

Message 14 of 38

Anonymous
Not applicable

In the code I have you can either enter or right click to signal that the user is done selecting the strings. So I get your point. 🙂 
Thank you for this code I tried to run it, but it says "no function definition: PICKENTITY". I tried to look at the code and search for any missing parentheses or other syntax errors because when I look at it, I can see that its defined. I am trying to troubleshoot right now but if you know why its giving the error let me know. Thanks a lot. 

0 Likes
Message 15 of 38

Anonymous
Not applicable

Thank you for the information, its helpful. 🙂 No I am okay with STRCAT already have lisp which does the selection of strings and then prints it on my drawing all together just have a problem with making it remember the first 3 selected texts for the future selections.

0 Likes
Message 16 of 38

Anonymous
Not applicable

Thank you and I agree they are very smart and talented 🙂
Its enough for me if it stays in memory for the current drawing only. 
Haha ok got it thanks. 🙂

0 Likes
Message 17 of 38

Moshe-A
Mentor
Mentor

Yes you're right (sorry) find this line and remove the last closing parenthesis

 

(setq default (getvar "users1")))

0 Likes
Message 18 of 38

Anonymous
Not applicable

I just tested it. This code does exactly what my code does right now. I was wondering if you guide on how to store the first 3 selected strings as prefix so in the future whenever I click another text, it just add the new clicked text after the 3 texts in the memory. So the first time I have to select the entire string and then it stores the first 3 characters and the next time I select another string it just add it after them. If the first time I select "A-B-C-D-E" and place it then it remembers "A-B-C" and then when I click "F" now it should print "A-B-C-F". Thats why I want to know how to store in memory and use back. Thank you for your help 🙂

0 Likes
Message 19 of 38

cadffm
Consultant
Consultant

Only temporarily or across different sessions?


For temporarily you have to do nothing special, do not declare the prefix var local.

(defun c:XYZCOMMAND (/ newstring_local)
...
(while (progn
(princ (if *XYZCOMMAND_PREFIXGLOBAL "Suffic texts:" "Start selection:"))
(setq newstring_local ...(userinputfunc))
)
(if (null *XYZCOMMAND_PREFIXGLOBAL)
(setq *XYZCOMMAND_PREFIXGLOBAL (substr newstring_local 1 3)
newstring_local (substr newstring_local 4)
)
)
(dosomethingwithwhileString (strcat *XYZCOMMAND_PREFIXGLOBAL newstring_local))
)
...
)


If you want to store the Prefix over some sessions per file, you have save this information
in one place inside the drawing, but i don't want to write more if you don't need it.

Sebastian

Message 20 of 38

Moshe-A
Mentor
Mentor

have you used the 'prefix' option? this does exactly what you want

have looked in autolisp docs for (vl-registry-write) & (vl-registry-read)?

there are another (old) functions that do much the same (setenv) and (getenv) see on docs

 

 

0 Likes