I'm working on a routine to copy objects from blocks and x-refs into the current drawing using refedit. I've got 2 big problems:
1. I need to save the CP selection window so it can be applied to each selected x-ref / block.
2. When running the refedit routine, I need to change the settings for "Create unique layer, style, and block names" and "Lock objects not in working set" but can't seem to find where these variables are.
Solved! Go to Solution.
Solved by dbroad. Go to Solution.
If you obtain the selection with ssget, then the method of selection and the points that are used in the CP method are accessible for each object in the selection set by (ssnamex ss #)
I don't know much about (SSNAMEX, so maybe I can bounce some stuff off of you.
Say I use (command "._Select" "CP"), how would I save the pointlist to be used later?
Here is what I have so far. I'm not done with this yet mind you, so any help would be greatly appreciated.
(defun c:XCOPY (/ SS POINTLIST XREF ZBASE CMD XRLIST BNDY XROBJECT)
(setq ZBASE "0,0,0")
(setq CMD (getvar "cmdecho"))
(setvar "cmdecho" 0)
(STARTUNDO (ACADDOC))
(princ "\nCommand: Select copy area:")
(setq BNDY (C:GETWINDOW))
(princ "\nCommand: Pick X-Refs or Blocks to copy objects from:")
(setq XRLIST (ssget '((-4 . "<or") (0 . "BLOCK") (0 . "INSERT") (-4 . "or>"))))
(if (= null XRLIST)
(while (= null XRLIST)
(princ "\nCommand: No XREF/BLOCK selected:")
(ssget (setq XRLIST (ssget '((-4 . "<or") (0 . "BLOCK") (0 . "INSERT") (-4 . "or>"))))))
(foreach XREF XRLIST
(PROGN (setq inc (sslength XRLIST))
(setq XREF (cons (ssname XRLIST (setq inc (1- inc))) 1))
(command "-refedit" XREF "O" "N" "WP" BNDY "N"
"_copy" "ALL" "" ZBASE ZBASE
"_refset" "R" "ALL" ""
"_refclose" "D" "Y"))))
(ENDUNDO (ACADDOC))
(princ "\nCommand: Done")
(setvar "cmdecho" CMD)
(princ)
)
(defun C:GETWINDOW (/ )
(command "._Select" "CP")
(setq POINTLIST ())
(princ)
(POINTLIST)
)
Without actually diagnosing your code (which does need a bit of review) here is a method of aquiring a reusable set of points, call with: (setq VarNam (Selpoly)) where VarNam is the name of your desired pointlist variable:
;| define reusable point list with a user defined 3D polyline This is in "long-form": it uses more variables than needed for illustration |; (defun SelPoly ( / MainEnt SubEnt EntData EntyType PointList) ;create a 3dpoly (which lets us use entnext for points) (command "_3DPOLY") (while (= 1 (getvar "cmdactive")) (command Pause)) ;get the entity that we just created (setq MainEnt (entlast)) ;get the first vertice of the 3dpoly (setq SubEnt (entnext MainEnt)) ;get the entity definition data of the vertice (setq EntData (entget SubEnt)) ;get the entity type from the data to test against (setq EntyType (cdr (assoc 0 EntData))) ;start our point list using the "point" value form the "10" group (setq PointList (list (cdr (assoc 10 EntData)))) (while (/= "SEQEND" EntyType) ;get the next vertice of the 3dpoly (setq SubEnt (entnext SubEnt)) ;get the entity definition data of the vertice (setq EntData (entget SubEnt)) ;get the entity type from the data (setq EntyType (cdr (assoc 0 EntData))) ;add to our point list if it's a vertice (if (/= "SEQEND" EntyType) (setq PointList (append (list (cdr (assoc 10 EntData))) PointList)) ) ) ;get rid of the temp 3dpoly (entdel MainEnt) ;send our list back to the calling function (princ PointList) )
This version uses a LWpolyline (it also includes the "trans" function that I omitted on the last version to ensure that the points translate to the active coordinate system):
;| define reusable point list with a user defined LW polyline This is in "long-form": it uses more variables than needed for illustration lwpolylines store points in multiple "10" group codes so we will parse through the data |; (defun SelPoly ( / MainEnt EntData EntyPoint PointList) ;create a 3dpoly (which lets us use entnext for points) (command "_pline") (while (= 1 (getvar "cmdactive")) (command Pause)) ;get the entity that we just created (setq MainEnt (entlast)) ;get the entity definition data (setq EntData (entget MainEnt)) ;get the point value from the first "10" group (setq EntyPoint (cdr (assoc 10 EntData))) ;translate the point from object Coordinate to current coordinate system (setq EntyPoint (trans EntyPoint MainEnt 1)) ;start our point list using the point (setq PointList (list EntyPoint)) ;shorten our list for the next point by cutting out the previous point (setq EntData (cdr (member (assoc 10 EntData) EntData))) ;create a test value for the assoc group (setq TestAssoc (member (assoc 10 Entdata) EntData)) ;start a loop for the remaining points (while (/= nil TestAssoc) ;get the point value from the first "10" group (setq EntyPoint (cdr (assoc 10 EntData))) ;translate the point from object Coordinate to current coordinate system (setq EntyPoint (trans EntyPoint MainEnt 1)) ;start our point list using the point (setq PointList (append (list EntyPoint) PointList)) ;shorten our list for the next point by cutting out the previous point (setq EntData (cdr (member (assoc 10 EntData) EntData))) ;reset test value for the assoc group (setq TestAssoc (member (assoc 10 Entdata) EntData)) ) ;get rid of the temp LWpoly (entdel MainEnt) ;send our list back to the calling function (princ PointList) )
Though I recommend using ssget, rather than the select command, I realize that the interactivity of the blue and green regions is lacking. To run the select command within a lisp program requires flexibility in input. Managing a generalized select command would mean running a loop of pauses and checking the status of CMDACTIVE or CMDNAMES. By limiting the selection to CP, only user points need to be captured. Within the point selection loop, the points can be stored in a list. There is no need to set the list to nil if the symbol that stores the list is local. The problem is that to access the selection set created by the select command, you need to use (ssget "p"). But that is a very unreliable method. It may select the objects just selected but if none were selected, it will use the last selection that returned objects. That would be a difficult to check logic flaw in the program. (I know of no way to delete the previous selection set, BTW )So even though one uses select command for input feedback, the ssget command still needs to be used to create a reliable selection set.
The function below should work reliably. If the selection fails, a nil result is returned. If the selection succeeds, it returns a 2 element list (selection set <list of points>)
;;D. C. Broad, Jr. ;;Return a Crossing Polygon set and the list of points where interactivity ;;simulates the select command. ;;Disadvantage. Adds a command to the undo stack.
;;Call with (ssgetcp "My selection prompt") (defun ssgetcp (prmpt / lst pt ss) (prompt prmpt) (command "._select" "_cp") (while (and (setq pt (getpoint)) (listp pt)) (command pt) (setq lst (cons pt lst))) (command "" "");finish selection (if (setq ss (ssget "cp" lst)) (progn (sssetfirst nil ss) (list ss lst))) )
I leave it up to you to figure out how to integrate this into your larger application.
I'm going to try these suggestions out with the list not localized so I can pull it in the main routine when i need. I'll get back with you on results. Sorry for all the frustration, and thanks for bearing with me!
There is no reason to change the function to remove lst localization.
(setq ssc (ssnamec "my prompt")) will pass the selection set and the list of points to the calling function. Always localize and pass via return value, (which in this case is a list with the selection set and the list of points. The calling function would only need to use (setq pts (cadr ssc)) to get the list of points.
About the localization, I wanted to integrate what you had into the current routine and localize it there. I don't think i need the selection set from the select command (or do I?), so I revised it so i get just the point list and pass the point list on to the refedit command. Sorry if it seems like I'm being stubbon, maybe I'm just misunderstanding?
So here is what I've got so far, and I'm getting an error "; error: bad argument type: lselsetp nil"
(defun c:XCOPY (/ SS PNT POINTLIST INC XR XREF XRLIST CMD BNDY XROBJECT)
; (setq CMD (getvar "cmdecho"))
; (setvar "cmdecho" 0)
(STARTUNDO (ACADDOC))
(princ "\nCommand: Select copy area:")
(progn (command "._select" "_cp")
(while (and (setq PNT (getpoint))
(listp PNT)
) ;AND
(command PNT)
(setq POINTLIST (cons PNT POINTLIST))
) ;WHILE
(command "" ""))
(princ "\nCommand: Select X-Refs or Blocks to copy objects from:")
(setq XR (ssget '((-4 . "<or") (0 . "BLOCK") (0 . "INSERT") (-4 . "or>"))))
(repeat (setq INC (sslength XRLIST))
(setq XRLIST (cons (ssname XR (setq INC (1- INC))) 1))
) ;REPEAT
; (if (= null XRLIST)
; (progn (princ "\nCommand: No XREF/BLOCK selected:")
; (setq XR (ssget '((-4 . "<or") (0 . "BLOCK") (0 . "INSERT") (-4 . "or>")))
; (repeat (setq INC (sslength XRLIST))
; (setq XRLIST (cons (ssname XR (setq INC (1- INC))) 1))
; ) ;REPEAT
; ) ;PROGN
(foreach XREF XRLIST
(progn (command "refedit" XREF "O" "N" "WP" POINTLIST "N")
(command "_copy" "ALL" "" "0,0,0" "0,0,0")
(command "_refset" "R" "ALL" "")
(command "_refclose" "D" "Y")
) ;PROGN
) ; FOREACH
; ) ;IF
(ENDUNDO (ACADDOC))
(princ "\nCommand: Done")
; (setvar "cmdecho" CMD)
(princ)
)
Hi Andrew,
perhaps something like this
(defun c:XCOPY (/ CMD INC OLD_PIK PNT POINTLIST SS SS1 XR XRLIST) ; (setq CMD (getvar "cmdecho")) ; (setvar "cmdecho" 0) (setq old_pik (getvar 'PICKFIRST)) (setvar 'PICKFIRST 1) ;(STARTUNDO (ACADDOC)) (princ "\nCommand: Select copy area:") (progn (command "._select" "_cp") (while (and (setq PNT (getpoint)) (listp PNT) ) ;AND (command PNT) (setq POINTLIST (cons PNT POINTLIST)) ) ;WHILE (command "" "")) (princ "\nCommand: Select X-Refs or Blocks to copy objects from:") (setq XR (ssget '((-4 . "<or") (0 . "BLOCK") (0 . "INSERT") (-4 . "or>"))));;(sslength XRLIST) (repeat (setq INC (sslength XR)) (setq XRLIST (cons (ssname XR (setq INC (1- INC)))XRLIST)) ) ;REPEAT ; (if (= null XRLIST) ; (progn (princ "\nCommand: No XREF/BLOCK selected:") ; (setq XR (ssget '((-4 . "<or") (0 . "BLOCK") (0 . "INSERT") (-4 . "or>"))) ; (repeat (setq INC (sslength XRLIST)) ; (setq XRLIST (cons (ssname XR (setq INC (1- INC))) 1)) ; ) ;REPEAT ; ) ;PROGN (foreach XREF XRLIST (setq ss (ssadd XREF)) (sssetfirst nil ss) (command "-refedit" "O" "N" "WP" (foreach pt POINTLIST (command pt)) "" "N") (setq ss1 (ssget "_WP" POINTLIST)) (command "_copy" ss1 "" "0,0,0" "0,0,0") (command "_refset" "R" "ALL" "") (command "_refclose" "D");;"Y" (setq ss nil) ) ; FOREACH ; ) ;IF ;(ENDUNDO (ACADDOC)) (princ "\nCommand: Done") (setvar 'PICKFIRST old_pik) ; (setvar "cmdecho" CMD) (princ) )
HTH
Henrique
It does work, but when testing, it's not selecting all the objects in the window like it's moving too fast for the selection process in the refedit command or it's throwing some point out in the middle of nowhere. Infact, the close point of the refedit window appears to be the culprit. Also, when there are multiple refedit instances (XREFS & BLOCKS) it's creating duplicate objects of the already copied object sets even though I have "Lock objects not in working set" selected (by the way is this a variable that I can manipulate?).
For the duplicating of objects I have found that turning your Osnaps off (setvar "osmode" 0) resolves it.
for the ability to ensure that you have the "Lock Objects..." set... There is no variable that I can find, therefore you will have to consider the registry:
;;; here is what you are targeting ;;;[HKEY_CURRENT_USER\Software\Autodesk\AutoCAD\R18.2\ACAD-A001:409\Profiles\<<Unnamed Profile>>\Dialogs\RefeditDialog] ;;;"UniqueSymbolNames"=dword:00000000 ;;;"DisplayAttDefs"=dword:00000000 ;;;"LockObjects"=dword:00000001 ;;;"SelectNested"=dword:00000000 ;;;"X"=dword:000002ee ;;;"Y"=dword:00000191 ;;;"Width"=dword:000001a4 ;;;"Height"=dword:00000171 ;required for any "VLxxx" function (vl-load-com) ;to set, first define the regkey string to the dialog (setq CURUSERSTR (strcat "HKEY_CURRENT_USER\\" (vlax-product-key) "\\Profiles\\" (getvar "cprofile") "\\Dialogs\\RefeditDialog")) ;as with everything we store the value to reset later ( (setq oldLock (vl-registry-read CURUSERSTR "LockObjects")) ;set the desired value, it's a "DWORD" object so it needs a number (vl-registry-write CURUSERSTR "LockObjects" 0) ;to reset (vl-registry-write CURUSERSTR "LockObjects" oldLock)
-Gary
I already added the osmode variable to the mix, but I can definately use this registry stuff over and over as I'm familiar with regedit. I replied to your e-mail with current code and a lengthy explaination...lol.
Seeing as the selection window issue has been solved I'm going to go ahead and mark this thread as solved. Thanks to everyone for their input. I will continue to keep posting here to resolve the rest of the problems though, and will post final working code when completed for anyone that wants it.
Hey, I found a work around for the "Lock Objects" setting not being initialized and posted it to the thread that I started to try to get Adesk's attention (if I leave it "unsolved" they will eventually look at it:
Here is a working product Gary and I worked on. Feel free to modify, rewrite, change, append, or revise as you see fit. I would ask that you post any of these modifications so others learning lisp can grow and hone their skills as well. Thanks to ALL that responded, you've all been a great help!
Andrew,
by switching to the direct processing of the selection set over the earlier "foreach..." version it no longer needs the:
(repeat (setq INC (sslength XR))
(setq XRLIST (cons (ssname XR (setq INC (1- INC)))XRLIST)))
that generates the list to process.
also: I left out another "cleanup issue" in the "while" loop to reset for the next run and to ensure that we dump the overhead early on the last run (should be added right after the (setq XREF nil) line:
(setq REMSET nil)
And I still like my polyline version (well, the abbreviated version that was in-line) better for gathering the points since you can see the whole of the working area that you are generating.
But hey, this was your concept so the end result should be yours 😉
I'm glad that you finally have a working version.
-G