need lisp routine that replaces existing text with new value generated by lisp

Discussion in 'AutoCAD' started by Michael Pape & Associates, Jul 9, 2004.

  1. I have created a lisp routine that automates plant quantity retrieval using
    a boundary. You type in the spacing of the plant, click the boundary, and
    the lisp returns the quantity to the command line. You are then prompted to
    select a text to edit, and then you have to manually enter the quantity it
    returned. What I need is the steps that will take that value and paste it
    over the generic text in the call out I select without my having to do it
    manually.

    I'm not able to devote the time to writing lisps from scratch, but I'm real
    good at taking pieces out of existing lisps and bending them to my will. My
    problem is that all the lisp routines I've tried place a new text object
    instead of offering to edit an existing one.

    Does anyone out there have or know of a lisp I may be able to pirate what I
    need out of? Btw, I'm using 2002.

    Thanks a bunch - Allison
     
    Michael Pape & Associates, Jul 9, 2004
    #1
  2. Michael Pape & Associates

    dstein Guest

    Instead of returning the string to the command prompt, take the value and pass it into the TextString property of a selected TEXT entity. You can also pass into the DXF 1 code via LISP and do a entmod if you don't want to use ActiveX/VLISP.
     
    dstein, Jul 9, 2004
    #2
  3. Michael Pape & Associates

    andywatson Guest

    Allison,
    The following code replaces the text value of TEXT or MTEXT entities with the text value stored in variable strText:

    Code:
    (defun c:NewText ( / strText e edata etype newdata)
    ;; set text value to insert
    (setq strText "Hola Allison")
    ;; user selects entity
    (setq e (car (entsel "\nText to edit: ")))
    ;; get entity data, type of entity
    (setq edata (entget e)
    etype (cdr (assoc 0 edata)))
    ;; if entity is text or mtext
    (if (or (= etype "TEXT") (= etype "MTEXT"))
    (progn
    ;; create new entity data list with substituted text
    (setq newdata (subst (cons 1 strText) (assoc 1 edata) edata))
    ;; apply new entity data
    (entmod newdata)
    )
    )
    ;; exit quietly
    (princ)
    )
    
    In your case, you would substitute the variable strText with the variable containing the quantity.

    (setq newdata (subst (cons 1 YOURVARIABLEHERE) (assoc 1 edata) edata))

    You will want to make sure that the quantity variable you use is holding a string (alphanumeric) value rather than a number. If it is a number, you will get an error trying to apply a numeric value to the text value of a TEXT or MTEXT entity. If in case your quantity variable holds a number value, do the following first...

    (setq yourVariableName (atoi yourVariableName))

    ... then you can feed it to the text replacement routine
     
    andywatson, Jul 9, 2004
    #3
  4. Thanks so much!! I'll get going pasting this together, and let you know if
    I can get it to work!! Allison

    the text value stored in variable strText:
    a string (alphanumeric) value rather than a number. If it is a number, you
    will get an error trying to apply a numeric value to the text value of a
    TEXT or MTEXT entity. If in case your quantity variable holds a number
    value, do the following first...
     
    Michael Pape & Associates, Jul 12, 2004
    #4
  5. I keep getting a "bad argument type: stringp" when I include the (setq
    yourVariableName (atoi yourVariableName))
    bit. Also, how do I control how many decimal places it returns to the
    string?


     
    Michael Pape & Associates, Jul 20, 2004
    #5
  6. Michael Pape & Associates

    ECCAD Guest

    What is the 'name' of the variable that contains the Total, in your (existing) routine ?

    Bob
     
    ECCAD, Jul 20, 2004
    #6
  7. Michael Pape & Associates

    T.Willey Guest

    What is the new value you are trying to substitute into the existing text string? How are you setting the variable "yourVariableName"?

    Tim
    ps. It might help the trouble shot if you post the finished code you did.
     
    T.Willey, Jul 20, 2004
    #7
  8. I will post the code as soon as I get a moment. For several reasons, it's
    not working, but I hack along taking one problem at a time.... Allison


    string? How are you setting the variable "yourVariableName"?
     
    Michael Pape & Associates, Jul 20, 2004
    #8
  9. Ok, to reiterate, the point of this is at the command prompt, you type in
    our name for a plant, click on a boundary where the plants are, then click a
    preplaced leader text to insert the value and the name of the plant. So,
    using this one for and example, you would type leg, click the boundary,
    click the text, and the text would change to 142 LEG (obviously the number
    is the variable in question). There are only 5 or 6 spacings for plants,
    but hundreds of varieties, so with this method I would have to create a
    different routine for each plant, but I have to go about this the long way
    since I know very little about lisp. I was thinking of using an if
    statement, but I wanted to figure out how to get it working for one first.
    Please don't waste too much time on this, I can muddle through eventually.
    I'm really not trying to get someone to do a ton of work for me. I really
    appreciate your taking the time to look this over.

    ;For leg
    (defun c:leg ( / a e edata etype newdata)
    (setq OS (getvar "osmode"))
    (setvar "osmode" 512)
    (setq ENT (entsel "\nSelect Closed Polyline "))
    (terpri)
    (setvar "cmdecho" 0)
    (setvar "osmode" OS)
    (command ".area" "e" ENT)
    (setq Area (getvar "area"))
    (setq plant 0.29)
    (setq a (* area plant))
    (setq a (atoi a))
    (setq e (a "LEG")
    (setvar "cmdecho" 1)
    (princ (rtos a 2 0))
    (setq e (car (entsel "\nText to edit: ")))
    (setq edata (entget e)
    etype (cdr (assoc 0 edata)))
    (if (or (= etype "TEXT") (= etype "MTEXT"))
    (progn
    (setq newdata (subst (cons 1 e) (assoc 1 edata) edata))
    (entmod newdata)
    )
    )
    (princ)
    (terpri)
    )
     
    Michael Pape & Associates, Jul 20, 2004
    #9
  10. Michael Pape & Associates

    andywatson Guest

    Allison,

    My bad. I originally gave you the wrong funciton!! Oops.
    It should have been "itoa" (integer to alpha), not "atoi" (alpha to integer).
    The reason you were getting that error was because it was expecting to convert a string value to a numeric value, but you were supplying it with a numeric value already.

    Also, I assumed since you were counting trees that you would never have 0.25 (decimal) trees.
    If you will be counting decimal trees, forget the "itoa" function altogether.

    You will be using "rtos" (real number to string) function. It work like this...
    (rtos 3.141516 2 2)
    where the first number is the number you want rounded, the second number is the format you want to use (1=scientific, 2=decimal, etc. etc), and the last number specifies the number of decimal to round.
    The above would give you 3.14.
    (rtos 3.141516 2 4)
    would give you 3.1415.

    SO, replace...
    (setq yourVariableName (atoi yourVariableName))
    with....
    (setq yourVariableName (rtos yourVariableName 2 2))
    ...and you will get a decimal number rounded to the hundredths.

    Sorry for steering you down the wrong path!
    Andrew
     
    andywatson, Jul 20, 2004
    #10
  11. Michael Pape & Associates

    andywatson Guest

    Allison,
    Here you go....didn't spend much time on it as you requested (on my break)
    This will avoid you having to create a lisp for each type of plant.
    In the section titled "Compile list of plants and areas", just add each type of plant you have with their respective sizes.
    When you run the routine, you will see the prompt...
    Select closed polyline for plant-type LEG or [Change plant] :
    Here you can either select a polyline to insert the text, or you can type "C" to change plants.
    If you type "C" you will see the following:
    1-LEG
    2-ARM
    3-HAND
    Plant number:
    At this point, type the number of the plant you want, and then you will be returned to the regular prompt.
    Hope this works for you.

    Code:
    (defun GetPlantData ( / returnData plantName)
    (textscr)
    (setq index 0)
    (foreach plant PlantList
    (setq plantName (car (nth index PlantList))
    index (1+ index))
    (princ (strcat "\n" (itoa index) "-" plantName))
    )
    (setq selection (getint "\nPlant number: "))
    (setq returnPlantData (nth (- selection 1) PlantList))
    (textscr)
    returnPlantData
    )
    
    
    
    (defun c:PlantCount ( / a e edata etype newdata)
    ;; compile list of plants and their areas
    (setq PlantList
    (list
    (cons "LEG" 0.29)
    (cons "ARM" 0.50)
    (cons "HAND" 0.75)
    )
    )
    
    ;; store and set system variables
    (setq osmode (getvar "osmode")
    cmdecho (getvar "cmdecho")
    )
    (setvar "osmode" 512)
    (setvar "cmdecho" 0)
    
    ;; set first plant as default if it is first time program is run
    (if (not PlantName)
    (setq PlantName (car (nth 0 PlantList)))
    )
    
    ;; initialize user input to look for keyword "Change"
    (initget "Change")
    ;; loop as long as user selects something or types "C"
    (while (setq ENT (entsel (strcat "\nSelect closed polyline for plant-type " PlantName " or [Change plant] : ")))
    (terpri)
    (cond
    ;; user wants to change plants
    ((= ENT "Change")
    (setq PlantData (GetPlantData)
    PlantName (car PlantData)
    PlantSize (cdr PlantData)
    )
    (initget "Change")
    )
    ;; user picked an entity
    (T
    (setvar "cmdecho" 0)
    (setvar "osmode" osmode)
    (command ".area" "e" ENT)
    (setq area (getvar "area"))
    (setq a (* area PlantSize))
    ;; round number to nearest hundredths
    (setq a (rtos a 2 2))
    ;; concatenate strings to get one string
    (setq NewText (strcat a " " PlantName))
    (setvar "cmdecho" 1)
    (princ NewText)
    (setq e (car (entsel "\nText to edit: ")))
    (setq edata (entget e)
    etype (cdr (assoc 0 edata)))
    (if (or (= etype "TEXT") (= etype "MTEXT"))
    (progn
    (setq newdata (subst (cons 1 NewText) (assoc 1 edata) edata))
    (entmod newdata)
    )
    )
    (initget "Change")
    )
    ); cond
    ); while
    (setvar "osmode" osmode)
    (setvar "cmdecho" cmdecho)
    (princ)
    ); defun
    
     
    andywatson, Jul 20, 2004
    #11
  12. aha! I've used rtos before to round numbers off, and did try it in place of
    atoi, but I had the syntax wrong so it didn't work. Nice to know I was on
    the right track. I'll have to try the other routine you wrote, I'm not 100%
    sure I get what it's doing by reading it, it's difficult to think of doing
    this a different way after so long. I can't believe you wrote that on your
    break. I don't know if I could accomplish that on a sabbatical! Thanks
    again!! Allison

    convert a string value to a numeric value, but you were supplying it with a
    numeric value already.
    is the format you want to use (1=scientific, 2=decimal, etc. etc), and the
    last number specifies the number of decimal to round.
     
    Michael Pape & Associates, Jul 21, 2004
    #12
  13. Michael Pape & Associates

    andywatson Guest

    Yes, I did notice you using rtos! Nice work!
    I realize the code is cryptic, especially since I didn't include many comments. I know what you mean about having to change the way you do something. Furthermore, I don't think the method I provided you will suit you any better. My main point is, there is absolutely no reason to have 100's of lisp routines whose only differences are two variables--the plant name and the size. I would also argue that it would make more sense to only have one command to do ALL types of plants.
    Anyhow, I've rewritten it so that you can have a command for every plant, but they will still use the same "heart" of the program to do the calculations.

    You'll notice two sections. The first section is the "heart" of the program, the PlantCount function--asks user to pick polyline, does calculations, modifies text value. The second section is a list of commands--one per plant--that sends the plant name and the plant size to the first section, the PlantCount function, so that it can apply those two variables.

    I've tried to comment on most everything, but I'm sure you'll ask if you have questions! Isn't this just so much fun?
    Code:
    ;; PlantCount function
    ;; This function will be used by ALL plant commands
    ;; (see toward bottom of routine)
    ;; This function receives the plant name and the plant size
    ;; It will then ask user for polyline selection, do the calculations,
    ;; and finally ask for a text entity to modify.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (defun PlantCount (PlantName PlantSize / a e edata etype newdata)
    ;; store and set system variables
    (setq osmode (getvar "osmode")
    cmdecho (getvar "cmdecho")
    ); setq
    (setvar "osmode" 0)
    (setvar "cmdecho" 0)
    
    ;; ****if you want the program to "loop"
    ;; and keep asking you for a polyline,
    ;; UNcomment the next line AND the line further down****
    ;;(while
    
    ;; get polyline from user selection
    (setq pline (car (entsel "\nSelect polyline: ")))
    ;; get the area, do the math
    (command ".area" "e" pline)
    (setq area (getvar "area"))
    (setq a (* area PlantSize))
    ;; round number to nearest hundredths
    (setq a (rtos a 2 2))
    ;; concatenate strings to get one string
    (setq NewText (strcat a " " PlantName))
    (setvar "cmdecho" 1)
    (princ NewText)
    (setvar "cmdecho" 0)
    ;; get user selected text entity
    (setq e (car (entsel "\nText to edit: ")))
    ;; get entity data, entity type
    (setq edata (entget e)
    etype (cdr (assoc 0 edata))
    ); setq
    ;; if entity type is Text or Mtext
    (if (or (= etype "TEXT") (= etype "MTEXT"))
    (progn
    ;; substitute new text for old text
    (setq newdata (subst (cons 1 NewText) (assoc 1 edata) edata))
    ;; apply the text change
    (entmod newdata)
    ); progn
    ); if
    
    ;; ****UNcomment the following line (and the one above) for a loop****
    ;;)
    
    ;; reset system variables
    (setvar "osmode" osmode)
    (setvar "cmdecho" cmdecho)
    (princ)
    ); defun
    
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; The following commands will send the two necessary arguments
    ;; to the PlantCount function--the plant name and the plant size.
    ;; Create a command for each plant, following the format shown below
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (defun c:LEG ()
    (PlantCount "LEG" 0.29)
    )
    
    (defun c:ARM ()
    (PlantCount "ARM" 0.50)
    )
    
    ;; etc. etc.
    
     
    andywatson, Jul 21, 2004
    #13
  14. It is fun actually. I sometimes think I should have been a programmer. Oh
    well, maybe in my next life. This one looks interesting. I'm going to go
    try it right now. It's so nice and short, I may be able to figure it out!
    Thanks for all your help! - Allison

    comments. I know what you mean about having to change the way you do
    something. Furthermore, I don't think the method I provided you will suit
    you any better. My main point is, there is absolutely no reason to have
    100's of lisp routines whose only differences are two variables--the plant
    name and the size. I would also argue that it would make more sense to only
    have one command to do ALL types of plants.
    but they will still use the same "heart" of the program to do the
    calculations.
    program, the PlantCount function--asks user to pick polyline, does
    calculations, modifies text value. The second section is a list of
    commands--one per plant--that sends the plant name and the plant size to the
    first section, the PlantCount function, so that it can apply those two
    variables.
    have questions! Isn't this just so much fun?
     
    Michael Pape & Associates, Jul 22, 2004
    #14
  15. Oh my gawd that's so cool. I love it! It'll be so easy to add all my
    plants the way it works. All I have to do to it is change the number of
    decimal places it uses. You were correct in an earlier message when you
    said there would not be a part of a plant. This is so great, I've been
    wanting to work out a better way to do this for 3 years. Yay!! You made my
    day :)
     
    Michael Pape & Associates, Jul 22, 2004
    #15
  16. Michael Pape & Associates

    andywatson Guest

    Great!
    Glad it worked out for you. You can mark that off your 3 yr. old to-do list. Some day you might want to look into a little error-handling--did the user pick a lightweight polyline, did the user hit cancel, etc. As the routine currently stands, in either of those situations, the osmode and cmdecho variables would not be set back to their original values. At least it won't crash AutoCAD or reformat your drive :)
    Andrew
     
    andywatson, Jul 22, 2004
    #16
Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.