TheSwamp

Code Red => AutoLISP (Vanilla / Visual) => Topic started by: Cawaugh on January 08, 2013, 04:41:35 PM

Title: Run a lisp routine when a field is selected for editing.
Post 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?
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: BlackBox on January 08, 2013, 05:48:16 PM
... Perhaps an Object Reactor (http://exchange.autodesk.com/autocad/enu/online-help/browse#WS1a9193826455f5ff1a32d8d10ebc6b7ccc-6797.htm)?  :?
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: Cawaugh on January 09, 2013, 07:45:58 AM
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.
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: CAB on January 09, 2013, 09:01:23 AM
Perhaps one of these will help.
http://goo.gl/0NswR

No time this morning to look further.
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: dgorsman on January 09, 2013, 11:08:43 AM
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.
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: BlackBox on January 09, 2013, 11:33:04 AM
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)).  :-)
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: Cawaugh on January 09, 2013, 03:13:20 PM
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! ^-^
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: BlackBox on January 09, 2013, 03:47:21 PM
Perhaps you could post the 'eval code' so that we might better suggest a solution?
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: Cawaugh on January 09, 2013, 04:43:36 PM
I'm heading out but will post it tomorrow. Thanks!
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: Cawaugh on January 10, 2013, 01:39:10 PM
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!
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: CAB on January 10, 2013, 01:54:42 PM
Remember that an object with an object reactor can not modify itself within the reactor.
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: Cawaugh on January 10, 2013, 02:00:25 PM
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?
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: T.Willey on January 10, 2013, 11:21:00 PM
Here is something to get you started.  Not fool-proof or anything, but works with the basic idea.

Code: [Select]
(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)
)
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: irneb on January 11, 2013, 12:00:50 AM
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.
Title: Re: Run a lisp routine when a field is selected for editing.
Post by: Cawaugh on January 11, 2013, 09:06:39 AM
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!