TheSwamp
Code Red => AutoLISP (Vanilla / Visual) => Topic started by: Cawaugh on January 08, 2013, 04:41:35 PM
-
I have a situation where I would like to run a lisp routine whenever someone tries to edit a field in a particular attribute.
I do not want the field to be manually updated but only updated when the user runs the program.
Any thoughts?
-
... Perhaps an Object Reactor (http://exchange.autodesk.com/autocad/enu/online-help/browse#WS1a9193826455f5ff1a32d8d10ebc6b7ccc-6797.htm)? :?
-
I don't know much about reactors. in doing some research on them I can see how this could work. This is what I was trying to no avail: (vlr-object-reactor e1 "Run Code Eval" '((:vlr-modified . code_eval1))). e1 was set by doing an ssget on my title block but I do not believe this is the correct "Owner" to go here. I'm trying to run the code_eval1 program every time someone finishes editing the title block.
-
Perhaps one of these will help.
http://goo.gl/0NswR
No time this morning to look further.
-
I don't know much about reactors. in doing some research on them I can see how this could work. This is what I was trying to no avail: (vlr-object-reactor e1 "Run Code Eval" '((:vlr-modified . code_eval1))). e1 was set by doing an ssget on my title block but I do not believe this is the correct "Owner" to go here. I'm trying to run the code_eval1 program every time someone finishes editing the title block.
Object reactors can be a little tricky. For one, you can't use the code called by the reactor to modify the object which fired the reactor (insert "Who's on First?" sketch here! ;-) ). The second problem is the reactor must be loaded when the drawing is opened, which can present certain logistical problems. Fallout from that, is that you can't do anything about a drawing that leaves your control, either.
What the object reactor would have to do is log the object ID which called the reactor (typically as an entity reference or handle), enable a second reactor to handle the command-ended event which would process the logged items to restore the field(s) then disable the second reactor (PITA - I've investigated this). In terms of work involved, you *might* be better off putting those attributes on a locked layer and requiring users to use your own code to modify the attribute information.
-
Thanks for jumping in Dgorsman... I'm short on time, but wanted to clarify further... nice job, Sir.
The only think I can think to add (quickly), is that further adding to the complexity, the OP must avoid Command calls as well (within the Callback function(s)). :-)
-
The comments have gone over my head. I have not worked with reactors but the idea was given to me to check them out for what I need to do. Unfortunately, several sample codes I found and tried worked up until I wanted to run a lisp routine. The lisp routine works just fine on it's own but the total package (reactor code plus the eval code) crashes. All I wanted to do was run the eval code any time the title block was edited manually. I'll keep looking, thanks for the help. If anyone comes up with something, i'm listening! ^-^
-
Perhaps you could post the 'eval code' so that we might better suggest a solution?
-
I'm heading out but will post it tomorrow. Thanks!
-
The callback code_eval2 works just fine when I tested it against a command reactor on a save, tsee below code:
(vl-load-com)
(if (not *caw-CommandReactors*)
(setq *caw-CommandReactors*
(vlr-command-reactor nil '((:vlr-commandWillStart . StrtCMD)))
);end setq
);end if
(defun StrtCMD (calling-reactor StrtCMDInfo / theCMDStrt)
(setq theCMDStrt (strcase (nth 0 StrtCMDInfo) t))
(cond
((wcmatch theCMDStrt "*save*")
(code_eval2)
);end wcmatch
);end cond
);end defun
When I try and use an object reactor, testing for a change in the title block with the below code, nothing happens.
What am I missing? The best scenerio would be to test to see if a particular attribute was change then run the eval instead of just a random change to the title block.
(vl-load-com)
(setq ss (ssget "X" '((-4 . "<AND") (0 . "INSERT") (2 . "3M-BORDER*")
(-4 . "<NOT") (2 . "3M-BORDER-DIVISION") (-4 . "NOT>")
(-4 . "<NOT") (2 . "3M-BORDER-FACILITIES") (-4 . "NOT>")
(-4 . "<NOT") (2 . "3M-BORDER-LABORATORY") (-4 . "NOT>")
(-4 . "<NOT") (2 . "3M-BORDER-PICS") (-4 . "NOT>")
(-4 . "<NOT") (2 . "3M-BORDER-SME") (-4 . "NOT>")
(-4 . "AND>")))
);end setq ss
(if ss
(setq vlr-object
(vlr-object-reactor
(list (vlax-ename->vla-object (ssname ss 0.0))) "Title Block Updated!" '((:vlr-modified . TBmod)))
);end setq
);end if
(defun TBmod (notifier reactor parameter)
(vl-load-com)
(code_eval2)
);end defun
Thanks!
-
Remember that an object with an object reactor can not modify itself within the reactor.
-
I'm not modifing the title block. I want to run a eval (which doesn't modify the tb) after the title block has been modified.
Does that make sense how i want to do it?
-
Here is something to get you started. Not fool-proof or anything, but works with the basic idea.
(defun c:TestReact ( / Ent Obj )
(defun reactObjChange ( obj react notSure )
(if (not (wcmatch (getvar "CmdNames") "U,UNDO,REDO,OOPS"))
(progn
(setq GlbVarAttributeChanged (cons obj GlbVarAttributeChanged))
(if (not GlbReactorAttributeCommandEnd)
(setq GlbReactorAttributeCommandEnd
(vlr-command-reactor
"tempAttributeCommandReactor"
'((:vlr-commandEnded . reactAttributeChange))
)
)
)
)
)
(princ)
)
;-------------------------------------------------------
(defun reactAttributeChange ( react cmdList )
(if (not (member "CMDNAME" (mapcar (function strcase) cmdList)))
(foreach i GlbVarAttributeChanged
(if (setq tempList (assoc i GlbVarAttributeValueList))
(vla-put-TextString (car tempList) (cdr tempList))
)
)
)
(setq GlbVarAttributeChanged nil)
(vlr-remove GlbReactorAttributeCommandEnd)
(setq GlbReactorAttributeCommandEnd nil)
(princ)
)
;-----------------------------------------------------------
(defun reactObjErased ( obj react notSure )
(vlr-pers-release react)
(vlr-remove react)
)
;--------------------------------------------------------------
(if
(and
(setq Ent (nentsel "\n Select attribute: "))
(= (cdr (assoc 0 (entget (car Ent)))) "ATTRIB")
(setq Obj (vlax-ename->vla-object (car Ent)))
)
(progn
(foreach i (cdar (vlr-reactors :vlr-object-reactor))
(if
(and
(vl-position Obj (vlr-owners i))
(= (vlr-data i) "AttributeReactorChanged")
)
(progn
(vlr-pers-release i)
(vlr-remove i)
)
)
)
(vlr-pers
(vlr-object-reactor
(list Obj)
"AttributeReactorChanged"
'(
(:vlr-modified . reactObjChange)
(:vlr-erased . reactObjErased)
)
)
)
(if GlbVarAttributeValueList
(if (setq tempList (assoc Obj GlbVarAttributeValueList))
(setq GlbVarAttributeValueList (subst (cons Obj (vla-get-TextString Obj)) tempList GlbVarAttributeValueList))
(setq GlbVarAttributeValueList (cons (cons Obj (vla-get-TextString Obj)) GlbVarAttributeValueList))
)
(setq GlbVarAttributeValueList (list (cons Obj (vla-get-TextString Obj))))
)
)
)
(princ)
)
-
If you want to know "why" this happens: An object reactor fires when the object is changed. The change has already placed a lock on the object, thus until the command which changed the object completes nothing else may change the object. So nothing in the reactor is allowed to change its object.
That's why dgorsman suggested the 2 tiered reactor approach: I.e. the object reactor simply saves the object(s) into a global list. A second command-ended reactor checks this list and then performs the eval on each object in that. That way the eval happens after the original changing command has finished.
BTW, I'm not sure if such would work if the user changes the attribute value through the Properties Palette. I mean: seeing as there's not actually a command which runs. So it's quite possible that the eval will only execute after the user issues another command.
-
Good morning, Tim. It's been a while. i'm heading into a meeting but when i get back could i give you a call? here is my email so you can send me your telephone number (if thats ok). charles.waugh@wmeng.com.
Thanks!