TheSwamp
Code Red => AutoLISP (Vanilla / Visual) => Topic started by: cadman6735 on March 28, 2011, 02:16:07 PM
-
I have 2 questions
1. What is the simplist way to store a variable for future use, so after AutoCAD has been shut down and re-started I can call this variable?
2. Why does USERS1 not store a variable in the drawing after saving and shutting down? What use is this system variable?
-
If you want something saved int the drawing use xdata.
If you want a global save use a text file or the registry.
-
Doesn't xdata just append to the entities dotted pair? (can't remember the other name for dotted pair)
so I would use a filter to look for the xdata now associated to this element so now my xdata works as the handle? (in my logic this would make the handle useless) I know I am missing a bigger picture
Was USERS1-5 not developed for such a task?
USERR1-5
USERI1-5 save to the drawing but USERS1-5 does not, what is USSERS1-5 good for?
-
xData can be attached to dictionaries. 8-)
-
I would echo what CAB suggests -
I mostly use text files for storing program settings that I wish to persist between drawing sessions (such as dialog settings), as these are safer and easier for the average user to remove than deleting registry keys.
As for storing data in drawings, if the data pertains to an entity in the drawing (for example, when using an Object Reactor perhaps) I would use the XData (eXtended data) for that entity, otherwise you can create your own dictionaries and add xRecords to those dictionaries (example here (http://www.theswamp.org/index.php?topic=29339.msg350137#msg350137)), and there is also LData.
Lee
-
Here is another to check out:
http://autolisp-exchange.com/Forums/Attachments/Arch_Eric/vlax-ldata.lsp
-
Xdata and Ldata there are not only per drawings ?
maybe (setenv "myenvironnement") ??
or another suggestion...
a config file ? like cfg or ini ?
-
Xdata and Ldata there are not only per drawings ?
I don't follow?
maybe (setenv "myenvironnement") ??
SetEnv is convenient, but one must still remember that the function is writing data to the registry, under the following location:
(strcat "HKEY_CURRENT_USER\\" (vlax-product-key) "\\FixedProfile\\General")
And hence I would be careful not to bloat the registry unnecessarily.
a config file ? like cfg or ini ?
I suppose these would fall under the umbrella 'text file' method...
-
If you want something saved int the drawing use xdata.
If you want a global save use a text file or the registry.
For what I do with lisp, the textfile method suits me best
My reasons:
1. they can be viewed from outside autocad by notepad (handy in case of trouble)
2. also this will give you the ability to check or modify settings
3. you can create any extension like MyTextFile.mtf just rename your *.txt file
My 2 cents.
I see what you mean, the USERI1-5 are stored in the drawing just as the USERR1-5 are.
The USERS1-5 however are NOT. (http://docs.autodesk.com/ACD/2011/ENU/filesACR/WS1a9193826455f5ffa23ce210c4a30acaf-4e0d.htm)
Mind this: I = integer / R = Real / S = string.
Anyway... I assume you need to save a string in a specific drawing so I'd use a textfile or investigate some Xdata options.
-
2. Why does USERS1 not store a variable in the drawing after saving and shutting down?
Just a thought ...
Imagine that someone inexperienced download a lisp file that contains something like:
(eval (read (getvar "USERS1")))
Imagine now what could be saved in a DWG in USERS1-5 and you have a "perfect bomb" ...
(setvar "USERS1" "(vl-registry-delete HKEY_CURRENT_USER\\... ... )")
... or other "goodies"
-
Hmmm... but that 'danger' would exist should a string be stored in the registry, text file, or even if the string was disguised and present in the LISP code itself...
On this topic, I posted some tips on how to spot malicious code in programs here (http://www.theswamp.org/index.php?topic=33654.msg413446#msg413446), might be useful to some.
-
I strongly suggest to not use the "USERS*" variables..
for the simple reason that to many program use it.
we had several problem with that on our offices..
and cause conflictual uncompatible problem between thirdparty software.
LDATA = saved by drawing
Xdata = saved by Entity
Vl-Registry-write = saved by PC
setenv = save data on registry without having administrative right.
(open "x:\\config\\yourfile.txt" "w") = allow you to store anything and share those info with your collegue over a network if you want. sure you can use any king of extension file and eaven...no extension.
so it is depend what the objective is.
Regards.
-
Personally I'd use XRecords ... the data sounds like it should stay with the document.
-
LDATA = saved by drawing
Xdata = saved by Entity
Vl-Registry-write = saved by PC
setenv = save data on registry without having administrative right.
(open "x:\\config\\yourfile.txt" "w") = allow you to store anything and share those info with your collegue over a network if you want. sure you can use any king of extension file and eaven...no extension.
This is a great breakdown for my feeble mind, thanks
I am reading AfraLisp Dictionary
What is an example I could use a dictionary for? (not code, just an idea)
From what I understand:
- Xdata appends to an existing entity assoiciation list
- Dictionary is a container of entities or objects
- Xrecord is the assoiciated list (dotted pair) of an entity or object inside of a dictionary
- ENTMAKEX is act of creating an object without an owner. Meaning it is a man made dictionary and a man made entity (inside this dictionary)... The meaning of "no owner" is that this container (dictionary) did not exist until I created it, so it is not associated to any existing dictionary?
-
Just another thought... XData can also be attached to Layers (and other Table objects). Attach your data to Layer "0" and it's pretty secure since you can't delete layer 0.
We started using this technique as soon as XData was introduced, before Dictionaries, LData, etc., and as long as you don't exceed the XData limit of 16K (I think), it works well. We store over 200 of our application specific "system" variables with this method.
Text files (.ini files) are used to store the initial settings, but if the variables need to change value from drawing to drawing, this system works.
We also use XData attached to entities and blocks. If XData had existed in the mid-80s, there would have no need for us to use attributes.
-
Why does the 'P' remaine for a return? If I put double space vs a single space the 'P' disapears
(defun c:exdata ( / )
(setq lastent (entget (car (entsel))))
(regapp "SHEETDATA")
(setq
exdata '((-3 ("SHEETDATA" (1000 . "SheetTitle"))))
)
(setq newent
(append lastent exdata)
)
(entmod newent)
(princ)
)
(defun c:test ( / )
(vl-load-com)
(setq
acadObject (vlax-get-acad-object)
acadActiveDocument (vla-get-ActiveDocument acadObject)
acadPaperSpace (vla-get-PaperSpace acadActiveDocument)
acadLayout (vla-get-Layout acadPaperSpace)
)
(setq ss (ssget "_x" '((0 . "MTEXT" ) (-3 ("SHEETDATA")))))
(setq
ssEnt (ssname ss 0)
sheetTitle (cdr (assoc 1 (entget ssEnt)))
)
(setq sheetTitle (vl-string-translate "\\P" " " sheetTitle)) <--- If I put a double space here the 'P' disapears
(setq sheetTitle (vl-string-translate "\\pxqc;" "" sheetTitle))
(print sheetTitle)
(princ)
)
-
Because vl-string-translate switches individual characters, look into the vl-string-subst function.
-
If it is individual then why does it work on
(setq sheetTitle (vl-string-translate "\\pxqc;" "" sheetTitle))
the entire "\\pxqc;" is removed
and why does it remove both \\ but leave the P?
Yes, I am starting to look in to the function (vl-string-subst) trying to learn how to loop thru a string right now.
-
If it is individual then why does it work on
(setq sheetTitle (vl-string-translate "\\pxqc;" "" sheetTitle))
the entire "\\pxqc;" is removed
I'm guessing because it doesn't determine which character to translate to which, since there is no destination character set, hence it just translates them all to empty strings.
EDIT: On testing, it doesn't translate any characters for me:
_$ (vl-string-translate "\\pxqc;" "" "CAD\\pxqc;Man")
"CAD\\pxqc;Man"
Which is more likely what I would've expected.
and why does it remove both \\ but leave the P?
"\\" is one character in LISP ( \ )
-
Yes, I am starting to look in to the function (vl-string-subst) trying to learn how to loop thru a string right now.
As a nudge in the right direction, this is what I use for string substitution in Visual LISP:
(defun LM:StringSubst ( new old string / l i ) (setq l (strlen new) i 0)
(while (setq i (vl-string-search old string i))
(setq string (vl-string-subst new old string i) i (+ i l))
)
string
)
For complex substitutions I use RegularExpressions (RegExp)
-
el-Crap-o
I have to first center the text for it to not remove the //pxqc; :ugly:
Ok, well at least I learned the // is one character, which makes no sense to me but I guess it is what it is, I'll accept it and move on.
Sorry for wasting your time.
-
(defun LM:StringSubst ( new old string / l i ) (setq l (strlen new) i 0)
(while (setq i (vl-string-search old string i))
(setq string (vl-string-subst new old string i) i (+ i l))
)
string
)
Thanks
-
Ok, well at least I learned the // is one character, which makes no sense to me but I guess it is what it is, I'll accept it and move on.
Note that it is the "\\" string which is interpreted as a single backslash. This is because the usual use of a backslash is within a escape character, such as "\n" for newline or "\t" for tab etc. Hence when we want to use a backslash on its own, we must double it up so that any following characters aren't interpreted as escape characters.
Sorry for wasting your time.
Not at all - I like questions I can answer :evil:
-
Lee
I am strugaling to understand, I think I am close but not sure. I am not sure if I am even passing the right info to the right variables...
(defun c:test ( / ent entString pattern string)
(setq ent (car (entsel)))
(setq entString (cdr (assoc 1 (entget ent))))
(setq pattern "\\P")
(setq string " ")
(setq l (strlen entString) i 0) [color=red]<----- I am not sure how this works[/color]
(while
(setq i (vl-string-search pattern string i))
(setq entString (vl-string-subst string pattern entString i) i (+ i l)) [color=red]<----- nor this[/color]
)
(print entString)
(princ)
)
-
Ah - you broke the sub! :-P
You could call it like this:
(defun c:test ( / ent str )
(if
(and
(setq ent (car (entsel)))
(setq str (cdr (assoc 1 (entget ent))))
)
(setq str (LM:StringSubst " " "\\P" str))
)
)
(defun LM:StringSubst ( new old string / l i ) (setq l (strlen new) i 0)
(while (setq i (vl-string-search old string i))
(setq string (vl-string-subst new old string i) i (+ i l))
)
string
)
To understand how the sub works, read all the documentation on the vl-string-search and vl-string-subst functions and all the arguments those functions can take.
-
Lee
I have stared at your code all weekend (well for a few hours anyway)
(setq str (LM:StringSubst " " "\\P" str)) [color=red]<---- What are you doing here?[/color]
-
Hi CADMan,
On the line to which you referred, I am passing the necessary arguments (or parameters) to the subfunction called 'LM:StringSubst'. My subfunction takes three arguments: 'new', 'old' and 'string' which represent respectively the new string to be substituted, the pattern string to be matched and replaced, and the string in which to do the replacing. When the subfunction is called, the symbols 'new', 'old' and 'string' will hold the argument data passed to the subfunction.
Think of the subfunction as just another LISP function. For example, I'm sure you are familiar with the LISP function '+'. This function takes one or more numerical arguments and proceeds to add them together and return the sum:
(+ arg1 arg2 ... argN)
Returns: arg1 + arg2 + ... + argN
But, say we wish to have a function that will return the supplied argument plus 2, we could define such a function in the following way:
(defun Add2 ( x ) (+ x 2))
Now, the above function takes a single numerical argument, denoted by the symbol 'x', and returns that supplied number incremented by 2. Just as we called the '+' function, we would call our subfunction in the following way:
(Add2 5)
Returns: 7
Hence in my example, I call the function 'LM:StringSubst' with the required arguments:
(LM:StringSubst " " "\\P" str)
Returns: the value of the variable 'str' with "\\P" substituted for " "
Lee
-
Hi Lee
I have the idea of what you are doing, the examples you give me make sense but they are basic examples using defun
and
your code uses setq
I ;|greyed out the subfunction|; ran it and it is looking for a function, this is interesting. I am not sure how this is happening and I don't know enough to ask a logical question to pinpoint the answer I am looking for. If I could see some more documentation or examples of this concept this would be great.
Is there a specific topic in the help menu, is there a good "key word" I can look for or any websites that discuss this concept.
Thanks
-
Hmmm... but that 'danger' would exist should a string be stored in the registry, text file, or even if the string was disguised and present in the LISP code itself...
On this topic, I posted some tips on how to spot malicious code in programs here (http://www.theswamp.org/index.php?topic=33654.msg413446#msg413446), might be useful to some.
Thanks for the info. Well written.
-
I have the idea of what you are doing, the examples you give me make sense but they are basic examples using defun and your code uses setq
My use of setq just sets the return of the subfunction to a symbol in the calling function, just as we might do this:
(setq x (+ 1 2 3))
Where 'x' now holds the return of the '+' function (i.e. 'x' has the value 6)
In a similar way I could do this:
(setq x (LM:StringSubst " " "\\P" str))
And 'x' would hold the string returned by my function.
I ;|greyed out the subfunction|; ran it and it is looking for a function, this is interesting. I am not sure how this is happening and I don't know enough to ask a logical question to pinpoint the answer I am looking for. If I could see some more documentation or examples of this concept this would be great.
Correct - the function needs to be defined before it can be called, hence we define the function using the defun expression.
Perhaps have a read of these topics:
Using Defun (http://docs.autodesk.com/ACD/2011/ENU/filesALG/WS73099cc142f4875516d84be10ebc87a53f-7c46.htm)
Functions with Arguments (http://docs.autodesk.com/ACD/2011/ENU/filesALG/WS73099cc142f4875516d84be10ebc87a53f-7c3d.htm#WS4b0506698c46277a100ab23ff1e0629b4-7fd4)
-
Hmmm... but that 'danger' would exist should a string be stored in the registry, text file, or even if the string was disguised and present in the LISP code itself...
On this topic, I posted some tips on how to spot malicious code in programs here (http://www.theswamp.org/index.php?topic=33654.msg413446#msg413446), might be useful to some.
Thanks for the info. Well written.
Thanks Gary :-)
-
I have not read the links you posted yet, so if my question is covered there I appologize
I guess my question is, what defines LM:StringSubst as a function? '+' is a built in functions used by LISP but 'LM:StringSubst' is defined by you. I think this is where I am getting lost. Is it in the structure of how your wrote the list itself?
Again if this is covered in the links just tell me to "read on" and tonight I will do my homework.
Thanks
Lee
-
Ok, I think I have it but still have some questions
I removed the (setq str) from
(setq str (LM:StringSubst " " "\\P" str))
and the function still works.
(LM:StringSubst " " "\\P" str)
So when it looks like this I understand it. the setq was throwing me.
from your explainationg of 'x' holding the value of 6, this got me to remove the the 'str' to make it a global variable so when I type at the command prompt !str I got the result of "line1 line2 line3" as you said, holds the value. I am with it so far.
The only reason I can think of for creating the 'str' variable (setq str) is for future use of the string, which we do not use or need here??? (is that a correct statment?) so it can be removed?
The tricky part for me now is lisp runs in a line by line format. so how does the (setq str) know what the string is when the sub-function runs after the (setq str) line? How does it go back and set itself to "line1 line2 line3".
-
The function LM:StringSubst returns a string value so the line
(setq str (LM:StringSubst " " "\\P" str))
stores that string value in the variable str.
So put the setq back as you found it. :-)
-
Very exciting discussion , I learn a lot .
Since this , I always save my Global Variables by the 'TEXT' way , save in the '.ini' file . :-)
-
Hi CAB
Yes, I have left the setq in place, I can only assume that you gurus know what you are doing, :-)
I am experimenting trying to understand how the code works. I don't see the str later in the program so I don't understand why it is there and the function seems to work fine without it. I am also trying not to be the annoying newb with too many questions, I am bent on learning this stuff. Anyways, I will understand it soon enough, I just need to find my lightswitch.
This has given me a better understanding how to use arguments, (that does not include my wife. She always wins, pees me off it does.)
-
I guess my question is, what defines LM:StringSubst as a function? '+' is a built in functions used by LISP but 'LM:StringSubst' is defined by you. I think this is where I am getting lost. Is it in the structure of how your wrote the list itself?
The 'defun' is what defines it as a function - those earlier links should help you understand this :-)
The only reason I can think of for creating the 'str' variable (setq str) is for future use of the string, which we do not use or need here??? (is that a correct statment?) so it can be removed?
OK, I can see how that might be misleading - in my example code, if run on its own, you wouldn't necessarily need this extra variable assigment, since the last expression evaluated is the 'LM:StringSubst' function and so the return of this function is printed to the command line. However, I assumed this would not be the final product of what you were trying to achieve, hence I assigned the return to the variable 'str' for future use in the code.
The tricky part for me now is lisp runs in a line by line format. so how does the (setq str) know what the string is when the sub-function runs after the (setq str) line? How does it go back and set itself to "line1 line2 line3".
LISP runs 'line by line' within the function definition, so if we call another function, the LISP now runs line by line within said function and then returns to the point at which the function was called. To illustrate how the code is evaluated, use the 'animate' option as part of the VLIDE debugging tools - tutorial here (http://lee-mac.com/debugvlide.html).
To understand how the 'setq' statement works, you need to understand that a LISP function will return the value of the last expression evaluated.
Looking back at my subfunction:
(defun LM:StringSubst ( new old string / l i ) (setq l (strlen new) i 0)
(while (setq i (vl-string-search old string i))
(setq string (vl-string-subst new old string i) i (+ i l))
)
string
)
Notice that I have the local variable 'string' on its own at the end of the code. This holds the value of the modified string and is the last expression in the function and hence the value of string is returned by the function, just as the sum of supplied numbers is returned by the '+' function.
The function LM:StringSubst returns a string value so the line
(setq str (LM:StringSubst " " "\\P" str))
stores that string value in the variable str.
So put the setq back as you found it. :-)
Thanks Alan for the support :-)
-
I am also trying not to be the annoying newb with too many questions, I am bent on learning this stuff. Anyways, I will understand it soon enough, I just need to find my lightswitch.
That doesn't even come into the equation, ask away - that is what this forum was created for :-)
-
Hi Lee
I assumed this would not be the final product of what you were trying to achieve, hence I assigned the return to the variable 'str' for future use in the code.
Yes, this is the answer I am looking for. Very good
The 'defun' is what defines it as a function - those earlier links should help you understand this
Yes, I understand this part, no worries. I did not ask my question right, let me try again.
When I run this by itself
(defun c:test ( / ent str )
(if
(and
(setq ent (car (entsel)))
(setq str (cdr (assoc 1 (entget ent))))
)
(setq str (LM:StringSubst " " "\\P" str))
)
)
I get this at the command line
Command: test
Select object: *Cancel*
no function definition: LM:STRINGSUBST
It is looking for LM:STRINGSUBST, how does it know to look for this? What makes it look for LM:STRINGSUBST? Is it in the way the list is structured? Because '+' is a AutoLisp functions that adds but LM:STRINGSUBST is nothing, I can change the spelling and it will look for different spelling too AC:NEWSPELLING
(defun c:test ( / ent str )
(if
(and
(setq ent (car (entsel)))
(setq str (cdr (assoc 1 (entget ent))))
)
(setq str (AC:NEWSPELLING " " "\\P" str))
)
)
Command: TEST
Select object: *Cancel*
no function definition: AC:NEWSPELLING
-
That doesn't even come into the equation, ask away - that is what this forum was created for
Sometimes I think it does, because it is hard to put into text what is in ones mind, specialy when one is limited in experiance and does not know how to express ones thought properly. You spent time and effort in explaining to me something that seems simple and logical to you when I am looking for a different answer but not sure how to express my thoughts without just rewording my question. I don't want to wear anyone out.
Thanks for the reassurance
-
cadman6735,
preform a search for my (Se7en) "refine your methods" document in the "teach" me forum. it should answer your last questions.
-
It is looking for LM:STRINGSUBST, how does it know to look for this? What makes it look for LM:STRINGSUBST? Is it in the way the list is structured?
It is the position of the symbol 'LM:STRINGSUBST' in the code that informs the interpreter (the facility that turns the code into 'machine language' so to speak) to look for a function defined as 'LM:STRINGSUBST'. When a symbol is placed immediately after an opening bracket, it is interpreted as a function to be evaluated.
Because '+' is a AutoLisp functions that adds but LM:STRINGSUBST is nothing, I can change the spelling and it will look for different spelling too AC:NEWSPELLING
In the same way that we give other symbols meaning when we assign variables:
(setq x 1)
The symbol 'x' is arbitrary, and could very well be renamed to 'y', however, this is irrelevant since it is merely a symbol to help us structure the code. From the above expression we know that 'x' is a variable holding a value of 1, hence we might use in the following way:
(1+ x)
However, consider that the symbol 'x' could very well be a defined as a function instead. We could have defined it as follows:
(defun x ( a ) (+ a 3))
In which case the symbol 'x' is now a function that adds three to the supplied argument. Now, of course, if we attempt to use the symbol 'x' in our previous expression we receive an error:
_$ (defun x ( a ) (+ a 3))
X
_$ (1+ X)
; error: bad argument type: numberp: #<USUBR @13df96f4 X>
Because the function '1+' was expecting a numerical argument, not a function. Used as a function, however:
_$ (x 5)
8
Note that this is because the symbol 'x' points to a user defined subroutine (USUBR), and hence this subroutine is evaluated when 'x' is used as a function.
If we tried to use another symbol in place of 'x', say 'y', we receive the following error:
_$ (y 5)
; error: no function definition: Y
Since the symbol 'y' has no value at this point.
OK, so I realise that this post isn't as structured as it could be, and hopefully you can glean more from Se7en's document to help with your understanding; but the point I am attempting to make is that the symbols we use for variables and functions are merely aids for the construction of a program, they are arbitrary - it is the value they point to which is relevant:
_$ (setq a 1 b "CAD" c 1+ d (eval (defun x ( a ) (+ a 3))) e 1.2 f '(1 2 3))
_$ (type a)
INT
_$ (type b)
STR
_$ (type c)
SUBR
_$ (type d)
USUBR
_$ (type e)
REAL
_$ (type f)
LIST
I hope this helps with your understanding, if you still have questions, please do ask.
Lee
-
When a symbol is placed immediately after an opening bracket, it is interpreted as a function to be evaluated.
I remember reading this early in my first weeks of starting my LISP journy, but I assosiated to actual built in Lisp functions such as '+', 'setq' etc ... This is the answer that I am looking for, this clears it for me. Now I just need to build upon my new found knowledge.
Thanks for being patient.
Se7en:
I found your post but the link is broke, I downloaded the PDF, is this the same thing?
-
When a symbol is placed immediately after an opening bracket, it is interpreted as a function to be evaluated.
I remember reading this early in my first weeks of starting my LISP journy, but I assosiated to actual built in Lisp functions such as '+', 'setq' etc ... This is the answer that I am looking for, this clears it for me. Now I just need to build upon my new found knowledge.
Thanks for being patient.
You're welcome CADMan, happy that I could clarify things for you with my incohesive waffling :lol:
I'm sure Se7en's document will be the icing on the cake :wink:
-
...
Se7en:
I found your post but the link is broke, I downloaded the PDF, is this the same thing?
The PDF is what you want. Read and study it; that document was written to be used by both beginers and "more advanced" users (I touch on a lot of subjects in that document). That document (because of how i wrote it) took me almost two years to write. Its almost sick that i put that much work into something most people just read and dismiss after the first read.
Good luck, and i hope you enjoy it.
-
Se7en
I have read only half of your document so far, I like the concept. This seems to be like building modules in Visual Basic. (I know nothing about VBA, this will be my next venture) but seems to follow the module concept as I would understand it.
I am going to have some questions when I am done reading it, would you be opposed if I stated a new thread to discuss this topic and get your document back to the top of the thread pile so others can join?
-
(I know nothing about VBA, this will be my next venture)
I'd steer clear, VBA is dead.
-
I'd steer clear, VBA is dead.
Really? Revit is programed using VBA or C# I am going in what ever direction Revit is programmed in, so I will look into your comment and adjust accordingly.
Thanks for the heads up.
-
Se7en
I have read only half of your document so far, I like the concept. This seems to be like building modules in Visual Basic. (I know nothing about VBA, this will be my next venture) but seems to follow the module concept as I would understand it.
I am going to have some questions when I am done reading it, would you be opposed if I stated a new thread to discuss this topic and get your document back to the top of the thread pile so others can join?
Hi cadman6735,
Yes sorta. All programing has the ability to build modules (as you put it) so the concepts i wrote about are concepts for all (worthwile) programing languages.
I dont do much lisp any more so i dont know how well i will be at answering spcific questions but i will definatly try to help as much as i can.
BTW, Revit is most likely written in C++ (but the language that Revit is written in has nothing to do with it). The API is what you are interisted in; you use C# to access the API for Revit. Search Wikipedia for "Application programming interface".