I got this nice little lisp from http://autocadtips.wordpress.com/2012/03/12/add-leader-to-text-make-multileader/
(defun c:mt2ml ( / oobj nobj nstrg) (vl-load-com) (setq oobj (vlax-ename->vla-object (car (nentsel "\nSelect source text: ")))) (if (= (vlax-get-property oobj 'ObjectName) "AcDbMText") (setq nstrg (vlax-get-property oobj 'TextString)) (exit) ) (command "_MLEADER") (while (= 1 (logand (getvar "CMDACTIVE") 1)) (command PAUSE)) (setq nobj (vlax-ename->vla-object (entlast))) (if (= (vlax-get-property nobj 'ObjectName) "AcDbMLeader") (vlax-put-property nobj 'TextString nstrg) (exit) ) (entdel (vlax-vla-object->ename oobj)) (princ) )
It converts MText to a Multileader.
For my purposes, it would be great to add a couple of features to this. Can anyone help me please? I understand Latin and Greek better than Lisp. Thanks.
My goals would be as follows:
Thanks for any pointers or help. Please let me know if some of my goals are unreasonable.
Mark Green
Working on Civil 3D in Canada
Solved! Go to Solution.
Solved by Lee_Mac. Go to Solution.
Looking around I found:
http://www.theswamp.org/index.php?topic=30934.msg364891#msg364891
That version seems to have a good start on your goals.
Mark Green
Working on Civil 3D in Canada
From what I can see, this is the latest version; but, you will need to see the page to read the changes and improvements over the version you posted.
;;; AUTHOR ;;; Copyright© 2009 Ron Perez (ronperez@gmail.com) ;;; Inserts selected text\mtext into an mleader. If an associated leader is found ;;; with the selected text it is used for the new mleader location. Else an option to select a ;;; leader is given. If still nothing, two points are required and an mleader is ;;; created. Enjoy :) (defun c:txt2ml (/ allleaders ent elst ldrpts ml objlst pt1 pt2 ss txt w x rjp-getbbwdth rjp-getpoints rjp-getassociatedleader ) (vl-load-com) (defun rjp-getbbwdth (obj / out ll ur) (vla-getboundingbox obj 'll 'ur) (setq out (mapcar 'vlax-safearray->list (list ll ur))) (distance (car out) (list (caadr out) (cadar out))) ) (defun rjp-getpoints (ent) (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= 10 (car x))) (entget ent))) ) (defun rjp-getassociatedleader (ent / pts) (if (and (setq ent (cdadr (member '(102 . "{ACAD_REACTORS") (entget ent)))) (setq pts (rjp-getpoints ent)) ) (list (car pts) (cadr pts) ent) ) ) (if (setq ss (ssget '((0 . "*TEXT")))) (progn (setq txt (apply 'strcat (mapcar 'cdr (vl-sort (mapcar '(lambda (x) (cons (vlax-get x 'insertionpoint) (strcat (vlax-get x 'textstring) " ") ) ) (setq objlst (mapcar 'vlax-ename->vla-object (setq elst (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss)) ) ) ) ) ) (function (lambda (y1 y2) (< (cadr (car y2)) (cadr (car y1))))) ) ) ) w (car (vl-sort (mapcar 'rjp-getbbwdth objlst) '>)) txt (substr txt 1 (1- (strlen txt))) ldrpts (car (setq allleaders (reverse (vl-remove 'nil (mapcar 'rjp-getassociatedleader elst) ) ) ) ) ) (mapcar 'vla-delete objlst) (cond ;;leader found for one of the selected text ((and ldrpts (setq pt1 (car ldrpts)) (setq pt2 (cadr ldrpts)) (setq ent (caddr ldrpts)) ) ) ;;Select a leader ((and (princ "\nSelect leader to replace [Enter to pick new points]: ") (setq ss (ssget '((0 . "leader")))) (setq ent (ssname ss 0)) (setq ldrpts (rjp-getpoints ent)) (setq pt1 (car ldrpts)) (setq pt2 (cadr ldrpts)) ) ) ;;Just add a new leader ((and (setq pt1 (getpoint "\nSpecify leader arrowhead location: ")) (setq pt2 (getpoint pt1 "\nSpecify landing location: ")) ) ) ) (if (and pt1 pt2) (progn (command "._MLEADER" pt1 pt2 "") (setq ml (vlax-ename->vla-object (entlast))) (vla-put-textstring ml txt) (vla-put-textwidth ml w) (if ent (progn (if (setq txt (cdr (assoc 340 (entget ent)))) (entdel txt) ) (entdel ent) ) ) (mapcar 'entdel (mapcar 'caddr (cdr allleaders))) ) ) ) (princ) ) )
Mark Green
Working on Civil 3D in Canada
Did you watch the prompts? After you select the text or Mtext, you are prompted to continue selecting objects. So, select your text or Mtext and press enter twice. You will be prompted with "Select leader to replace [Enter to pick new points]:", if you press enter you will be expected to click two points to place the Mleader with your text or Mtext. You could then select the Mleader object, right click, & select remove leader.
Also, I see some simple way to make a few changes for your needs. I'll try to peel away some time to make those changes unless someone else beats me to it.
Well, to be honest, last time I was looking over my colleague's shoulder while I got him to test it out. So I went and tried it for myself.
I watched the prompts. You are asked to select a leader. I tried selecting one, or I tried skipping past it and clicking points for a leader. Either way, it glitches. It asks me to enter text. Whether I try to type something or not, it won't proceed correctly. It does not automatically enter the text I selected; rather it asks me to enter the text, but at the same time does not actually allow me to do so. I hit enter a few times to get it to proceed, and it will create a multileader object, with just a leader and no text. I think if you hit enter and don't pick a leader or pick points for a leader, you don't even get the multileader object at all, which is what was happening to my coworker.
Win 7 Pro, 32 bit; Intel Core i5 @ 2.80GHz; 4GB RAM
Civil 3D 2013, SP2, 32 bit
Mark Green
Working on Civil 3D in Canada
I don't know your AutoCAD version, but anyway, here is my attempt to help with your struggle:
;;; AUTHOR ;;; Copyright© 2009 Ron Perez (ronperez@gmail.com) ;;; Inserts selected text\mtext into an mleader. If an associated leader is found ;;; with the selected text it is used for the new mleader location. Else an option to select a ;;; leader is given. If still nothing, two points are required and an mleader is ;;; created. Enjoy :) ;;; Modified 2013 - By Chuck Middaugh (mid-awe at http://www.mid-awe.com) ;;; Modifications made to allow for: ;;; - The multileader should end up on the same layer as the Mtext. ;;; - The multileader should hold the same rotation as the Mtext. ;;; - The multileader should be created in the same location as the Mtext, without the user having to click. ;;; - The multileader should be created without a leader. (The leader "Dog Leg" is still created even without clicks - Someone smarter than me can figure this out. Sorry.) (DEFUN c:txt2ml (/ allleaders ang clay ent elst lay ldrpts ml objlst pt1 pt2 ss txt w x rjp-getbbwdth rjp-getpoints rjp-getassociatedleader) (VL-LOAD-COM) (SETQ clay (GETVAR "clayer")) (DEFUN acos (x) (COND ((EQUAL x -1 1e-6) PI) ((EQUAL x 1 1e-6) 0.0) ((< -1 x 1) (ATAN (SQRT (- 1 (* x x))) x)) ) ) (DEFUN rjp-getbbwdth (obj / out ll ur) (VLA-GETBOUNDINGBOX obj 'll 'ur) (SETQ out (MAPCAR 'VLAX-SAFEARRAY->LIST (LIST ll ur))) (DISTANCE (CAR out) (LIST (CAADR out) (CADAR out))) ) (DEFUN rjp-getpoints (ent) (MAPCAR 'CDR (VL-REMOVE-IF-NOT '(LAMBDA (x) (= 10 (CAR x))) (ENTGET ent))) ) (DEFUN rjp-getassociatedleader (ent / pts) (IF (AND (SETQ ent (CDADR (MEMBER '(102 . "{ACAD_REACTORS") (ENTGET ent)))) (SETQ pts (rjp-getpoints ent)) ) (LIST (CAR pts) (CADR pts) ent) ) ) (IF (SETQ ss (SSGET ":S" '((0 . "*TEXT")))) (PROGN (SETQ lay (CDR (ASSOC 8 (ENTGET (SSNAME ss 0)))) pt2 (CDR (ASSOC 10 (ENTGET (SSNAME ss 0)))) ang (VLA-GET-ROTATION (VLAX-ENAME->VLA-OBJECT (SSNAME ss 0))) ) (PRIN1 ang) (SETVAR "clayer" lay) (SETQ txt (APPLY 'STRCAT (MAPCAR 'CDR (VL-SORT (MAPCAR '(LAMBDA (x) (CONS (VLAX-GET x 'insertionpoint) (STRCAT (VLAX-GET x 'textstring) " "))) (SETQ objlst (MAPCAR 'VLAX-ENAME->VLA-OBJECT (SETQ elst (VL-REMOVE-IF 'LISTP (MAPCAR 'CADR (SSNAMEX ss)))))) ) (FUNCTION (LAMBDA (y1 y2) (< (CADR (CAR y2)) (CADR (CAR y1))))) ) ) ) w (CAR (VL-SORT (MAPCAR 'rjp-getbbwdth objlst) '>)) txt (SUBSTR txt 1 (1- (STRLEN txt))) ldrpts (CAR (SETQ allleaders (REVERSE (VL-REMOVE 'nil (MAPCAR 'rjp-getassociatedleader elst))))) ) (MAPCAR 'VLA-DELETE objlst) (COND ;;leader found for one of the selected text ((AND ldrpts (SETQ pt1 (CAR ldrpts)) (SETQ pt2 (CADR ldrpts)) (SETQ ent (CADDR ldrpts)) ) ) ;;Select a leader ((AND (PRINC "\nSelect leader to replace [Enter to pick new points]: ") (SETQ ss (SSGET '((0 . "leader")))) (SETQ ent (SSNAME ss 0)) (SETQ ldrpts (rjp-getpoints ent)) (SETQ pt1 (CAR ldrpts)) (SETQ pt2 (CADR ldrpts)) ) ) ;;Just add a new leader ((IF (SETQ pt1 (GETPOINT "\nSpecify leader arrowhead location [Enter to skip]: ")) (SETQ pt2 (GETPOINT pt1 "\nSpecify landing location: ")) ) ) ) (IF (AND pt1 pt2) (PROGN (COMMAND "._MLEADER" pt1 pt2 "") (SETQ ml (VLAX-ENAME->VLA-OBJECT (ENTLAST))) (VLA-PUT-TEXTSTRING ml txt) (VLA-PUT-TEXTWIDTH ml w) (VLA-PUT-TEXTROTATION ml ang) (IF ent (PROGN (IF (SETQ txt (CDR (ASSOC 340 (ENTGET ent)))) (ENTDEL txt) ) (ENTDEL ent) ) ) (MAPCAR 'ENTDEL (MAPCAR 'CADDR (CDR allleaders))) ) (IF pt2 (PROGN (COMMAND "._MLEADER" pt2 pt2 "") (SETQ ml (VLAX-ENAME->VLA-OBJECT (ENTLAST))) (VLA-PUT-TEXTSTRING ml txt) (VLA-PUT-TEXTWIDTH ml w) (VLA-PUT-TEXTROTATION ml ang) (IF ent (PROGN (IF (SETQ txt (CDR (ASSOC 340 (ENTGET ent)))) (ENTDEL txt) ) (ENTDEL ent) ) ) (MAPCAR 'ENTDEL (MAPCAR 'CADDR (CDR allleaders))) ) ) ) ) (PRINC) ) (SETVAR "clayer" lay) (PRINC) )
I've tested this a few times on my setup and it is working. Hopefully this with help you.
Thanks for your efforts, seriously. I did put my spec's at the end of my last message, but here it is again: Civil 3D 2013, SP2, 32 bit.
Unfortunately, it is still not working. It did work one time (and changed the text height, which was weird) but now it won't anymore, even on the same piece of text that it did work on. It is hitting the same error:
Enter text: ; error: Automation Error. Description was not provided.
It is creating a multileader, with no text (just the dog-leg as you call it). The edits you've made are working: it is on the right layer and orientation. But there is no text.
Here is a look at when the error happens. It is asking me to enter text, but it doesn't permit me to do so. So I have to Cancel out and leave the command.
Incidentally, it leaves the current layer on the layer of the object I was editing. Perhaps this is due to canceling the command without completing it.
Mark Green
Working on Civil 3D in Canada
Here is a very rough draft of a program to generate MLeaders from a selection of MText.
The program will use the current MLeader Style at the time of running the program, and will adhere to the MText height, rotation & all other standard properties (such as Layer / Color / Linetype / Lineweight etc.), and should also be compatible with MText with any justification.
;; MText to MLeader (with no Leader) ;; Lee Mac - 2013-11-19 (defun c:mt2ml ( / e h i j m r s w ) (if (setq s (ssget "_:L" '((0 . "MTEXT")))) (repeat (setq i (sslength s)) (setq e (entget (ssname s (setq i (1- i)))) r (cdr (assoc 50 e)) w (cdr (assoc 42 e)) h (cdr (assoc 43 e)) j (cdr (assoc 71 e)) ) (if (and (setq m (entmakex (append '( (000 . "MULTILEADER") (100 . "AcDbEntity") (100 . "AcDbMLeader") ) (mapcar '(lambda ( x ) (cond ((assoc (car x) e)) ( x ))) '( (008 . "0") (006 . "BYLAYER") (039 . 0.0) (062 . 256) (048 . 1.0) (370 . -1) ) ) (list '(300 . "CONTEXT_DATA{") (cons 041 (cdr (assoc 40 e))) (assoc 010 e) '(290 . 1) (cons 304 (cdr (assoc 1 e))) (list 111 (cos r) (sin r) 0.0) (list 112 (cos (+ r (/ pi 2.0))) (sin (+ r (/ pi 2.0))) 0.0 ) '(301 . "}") (cons 090 (+ 1024 65536 262144)) ) ) ) ) (setq m (entget m)) (entmod (subst (cons 12 (mapcar '+ (mxv (list (list (cos r) (sin (- r)) 0.0) (list (sin r) (cos r) 0.0) '(0.0 0.0 1.0) ) (list (cond ((member j '(2 5 8)) (/ w -2.0)) ((member j '(3 6 9)) (- w)) (0.0) ) (cond ((member j '(4 5 6)) (/ h 2.0)) ((member j '(7 8 9)) h) (0.0) ) 0.0 ) ) (cdr (assoc 10 e)) ) ) (assoc 12 m) m ) ) ) (entdel (cdr (assoc -1 e))) ) ) ) (princ) ) ;; Matrix x Vector - Vladimir Nesterovsky ;; Args: m - nxn matrix, v - vector in R^n (defun mxv ( m v ) (mapcar '(lambda ( r ) (apply '+ (mapcar '* r v))) m) ) (princ)
Note that the above is only compatible with MText residing in the WCS plane.
I hope this helps.
Lee
Mark Green
Working on Civil 3D in Canada
Mark Green
Working on Civil 3D in Canada
@troma wrote:
Lee, that's great!
Your comment crossed mine, so I didn't even notice it until I came back here an hour later. Very smooth.
Excellent, thank you troma - it was an interesting one to write
You could probably pull the mtext string and append the selected mleader. Maybe this will get you started (don't know how to get/append the multileader string)? You could add on a section to create the mleader if none are existing. This one should repeat.
(defun C:MLSWAP (/ YN) (while (or (initget "Yes No") (not (setq YN (getkword "\nUse existing Multileader? [Yes/No] <Yes>: "))) (= YN "Yes") );or (SWAPTEXT) );while (princ "\nDone") ) (defun SWAPTEXT ( / e1 e2 ) (while (and (setq e1 (SEL-TEXT "\nSelect text object <exit>: ")) (setq e2 (SEL-TEXT "\nSelect Multileader <exit>: "))) (setq e1 (entget e1) e2 (entget e2)) (mapcar '(lambda ( a b ) (entmod (subst (assoc 1 a) (assoc 1 b) b))) (list e1 e2) (list e2 e1) );and );while (princ) )
(defun SEL-TEXT ( msg / ent )
(while (progn (setvar 'errno 0)
(setq ent (car (nentsel msg)))
(cond ((= 7 (getvar 'errno))
(princ "\nNo text object selected:"))
((null ent) nil)
( (not (wcmatch (cdr (assoc 0 (entget ent))) "ATTRIB,*TEXT"))
(princ "\nSelect a Text, MText or Attribute object:")
) ) ) )
ent
)
(princ)
I would love to see a finished product. Perhaps even let it reverse swap to put multileader string into text?
@Lee_Mac wrote:
Here is a very rough draft of a program to generate MLeaders from a selection of MText.
The program will use the current MLeader Style at the time of running the program, and will adhere to the MText height, rotation & all other standard properties (such as Layer / Color / Linetype / Lineweight etc.), and should also be compatible with MText with any justification.
...
Lee
Hi @Lee_Mac I just wanted to share a funny story.
After running this lisp pretty often for the last year or so, today is the first time I hit a snag. The converted object disappeared! I couldn't find it with zoom extents, and the previous selection set yielded no objects. Where could it be going? Frozen layer, UCS...?
Nope. Just me. I had forgotten to check my current MLeader Style. The current one was using a block as the leader object, not mtext. So I guess the mtext converted to a mleader with no leader and no mtext. That's the weirdest way of erasing something I can think of!
Mark Green
Working on Civil 3D in Canada
Hello,
I tried your LSP today, and it worked great!
I don't really use, and therefore don't like annotative labelling, so I went in, and made a layer style, applied it, and went along my merry way.
Five minutes later, and txt2ml yeilds the same error as the other user was complaining about
Specify next point: Specify leader landing location: Enter text: ; error: Automation Error. Description was not provided.
I couldn't understand it! It was working fine! ...before I applied my style.
I went in and poured over the style looking for a weird point that may be throwing the LSP for a loop.
Somehow, the "Maximum Leader Points" under "leader structure" had been turned up from 2 to 3.
By setting this back down to 2, everything worked great.
I should note, that this was for a STRAIGHT leader style, not SPLINE.
This doesn't affect me, because I don't NEED 3 point leaders, I just thought you might like to know.
Thanks for authoring this!
~C