Hi all,
I have been avoiding this for a looong time. It is finally here. I need to begin working with reactors. Currently we are using none and I need to address some issues that reactors are likely the only solution.
Just now, I'm asking for advice on Reactors, Dos and Don'ts.
The two reactors that I'll have to tackle first:
Most of all I have fingers crossed that someone can shed some light in this darkness.
Thank you for any information, but usefull examples are greatly appreciated.
Solved! Go to Solution.
Solved by Lee_Mac. Go to Solution.
Solved by dbroad. Go to Solution.
Reactors only react, they don't actually postpone or cancel the event that call them (like, say, a drawing close). If the save operation takes longer than it takes to close the drawing, then AutoCAD will throw an error as its trying to close a drawing while the save (or supporting) operations are still in progress.
Do you have any suggestions? Can I just undefine CLOSE? Maybe if I cannot undefine, I can set all drawings as read only. Then once my command is triggered I can save a copy as temporary, delete the previous and save the copy to the original folder. There's a vlisp reference to temp, I believe.
Hi, mid-awe!
I think we have to use Reactors if there are ABSOLUTELY NO WAY to get what we want without them ; ))
How about this simply code for CLOSE with SAVE:
(defun c:close () (if (= 0 (getvar "dbmod")) (command "_.close") (progn (command "_.qsave" (strcat (getvar "dwgprefix") (getvar "dwgname")) "_y") (princ "\nDWG save forced.") (command "_.close") ) ; - 'progn' ) ; - 'if' (princ) )
@mid-awe wrote:
Just now, I'm asking for advice on Reactors, dos and don'ts.
The main priority when working with Reactors (and to a degree, when writing any other program) is to account for every possible eventuality that the reactor may encounter - this is especially apparent when working with Object Reactors, since a modification event can be caused by numerous operations.
Furthermore, because Reactors operate autonomously in the background, monitoring for specific events and automatically evaluating the corresponding callback functions, code that is evaluated within a Reactor callback function must be absolutely watertight.
Callback functions which are not completely bulletproof risk crashing or locking up the AutoCAD application, since the callback function is in effect shoe-horning an operation into the standard flow of the application.
As far as dos & don'ts: it is quite well known that you cannot issue command calls from within a Reactor callback function, but you should also avoid any user input from the callback function since, as noted above, this is interupting the standard flow of the application.
@mid-awe wrote:
I need a reactor to force save on drawing close. (also nice to have an alert before the save takes place)
I would agree with the sentiments shared by those above in that if there is an alternative method to accomplishing a certain task that does not involve the use of a Reactor, in 9 out of 10 cases I would likely opt for the alternative (within reason) since Reactors can open a can of worms (or should I say, bugs) and can be a headache to manipulate.
With that said, to provide a simple example demonstrating how to accomplish the task in question, consider the following code:
(defun c:autosave-on ( ) (if (= 'vlr-command-reactor (type autosave:reactor)) (if (vlr-added-p autosave:reactor) (princ "\nAutosave reactor already running.") (progn (vlr-add autosave:reactor) (princ "\nAutosave reactor enabled.") ) ) (progn (setq autosave:reactor (vlr-command-reactor "autosave-reactor" '( (:vlr-commandwillstart . autosave:callback) ) ) ) (princ "\nAutosave reactor enabled.") ) ) (princ) ) (defun c:autosave-off ( ) (if (= 'vlr-command-reactor (type autosave:reactor)) (progn (vlr-remove autosave:reactor) (setq autosave:reactor nil) (princ "\nAutosave reactor disabled.") ) (princ "\nAutosave reactor not running.") ) (princ) ) (defun autosave:callback ( obj arg ) (if (and (wcmatch (strcase (car arg)) "QUIT,CLOSE,CLOSEALL") (= 1 (getvar 'dwgtitled)) ) (vla-save (vla-get-activedocument (vlax-get-acad-object))) ) (princ) ) (vl-load-com) (princ)
To test the above application, load the code in the normal way and type autosave-on to enable the Command Reactor. The Command Reactor can be subsequently disabled at any time by typing autosave-off at the command-line.
The Command Reactor will evaluate the callback function for every command issued, before the command operation is initiated. The callback function will then test the name of the issued command against the wildcard pattern specified in the code, and, if a match is found, the callback function will automatically save the drawing.
When monitoring for a drawing close event, you could use either a Drawing Reactor (vlr-dwg-reactor) or Editor Reactor (vlr-editor-reactor), however, the events pertaining to drawing close for these reactors will fire after AutoCAD has issued the prompt for whether or not to save changes to the drawing, hence, a Command Reactor (vlr-command-reactor) must be used as the :vlr-commandwillstart event will be triggered before the 'Save Changes' dialog is issued.
@mid-awe wrote:
Also, I need a reactor to monitor changes to global / system variable. (for now I'd like to be alerted to changes)
For this task you could utilise a vlr-sysvar-reactor, however, note that not all system variables are monitored by this reactor. I don't have a complete reference for those that are, but it would be a simple matter of testing which system variable changes trigger evaluation of the Reactor callback function.
@mid-awe wrote:
Thank you for any information, but useful examples are greatly appreciated.
Since you are looking for some examples, here are a selection of my programs which are driven by Visual LISP Reactors:
Associative Textbox - (also available at the Exchange App Store)
I hope this provides some food for thought.
Lee
Others who have replied to this thread have offered sound advice. In general, I would avoid the autosave temptation. There are times that you want to open a drawing, make changes and NOT save. Even though that sounds crazy, it isn't. Building in autosave takes the judgement away from the drafter.
The autosaveon and autosaveoff proposals that Lee Mac offers are a good compromise but require that someone deliberately turn the autosave off. Accidentally saving is as much of a problem as accidentally not saving.
As far as sysvars, I have had this tracking my changes for the past several weeks. I eventually turned it off to avoid the annoyance but you may use it to build yours.
;;initial settings (acad-push-dbmod) ;don't let anything in this file affect drawing status (vl-load-com) (vlr-sysvar-reactor nil '((:vlr-sysvarchanged . notifysysvarchange))) (defun notifysysvarchange (r l) (if (member (car l) '("CANNOSCALE" "CANNOSCALEVALUE" "DIMSCALE")) (alert (strcat (car l) "changed."))) ) ;.......other stuff at startup (acad-pop-dbmod)
You're welcome mid-awe
Though, be careful with the solution that I have provided, the other members raise a good point about it being dangerous to automatically save a drawing without intervention from the user.
Lee
For your application, I would recommend something like that proposed here:
http://www.afralisp.net/visual-lisp/tutorials/reactors-part-2.php
It saves the drawing as part of the plot command. To be complete, you should also track the publish command.
Goodmorning
my 2 cents,
the afralisp website, shows a method to force a save upon -plot command being called. This code also uses a reactor, but is probably much safer than one that responds to every single command. Visible response or not, that seems like alot of processing.....as an alternative method to save with plotting, would be to redefine the plot command to include a qsave just before or just after the plot command, depending on your needs (some companies like the most current time/date stamp on their most recent plots, some don't). Or even better to use locally defined functions...
I know this pretty much takes it full circle but if you're still attempting to monitor system variable changes then hopefully you would see the light in not requiring a reactor to save your drawings as well- and just use the reactor on the system variable changes.
Here is a quickly adapted example using (verbatim at first only a few modifications) the examples given on the afralisp website, and kenny ramage's autosave function, also on the same webpage. This should put an autosave into drawings that are plotted, so that nothing gets plotted that is not saved.
(command "undefine" "-plot")
(defun C:PLOT () ;define the function (autosave) ;call the Autosave function (command ".plot") ;call the plot command (princ) );defun
Next we need to write our Autosave function :
(defun AUTOSAVE ( / T1 ECC) ;define the function (setq ECC (getvar "CMDECHO")) ;get the value of the CMDECHO system variable (setvar "CMDECHO" 0) ;switch it off (if (not T3) (setq T3 (getvar "TDUSRTIMER"))) ;check if we have the value of the drawing timer ;if we haven't got it, then get it. (if (not T2) (setq T2 (getreal "\nHow many minutes between saves? : "))) ;check if we have an AutoSave time. ;if we haven't got it, then get it. (setq T1 (getvar "TDUSRTIMER")) ;get the drawing timer again for comparison purposes. (if (> (- T1 T3) (/ T2 60.0 24.0)) ;compare the drawing timer values (progn ;if it is time to save (prompt "\nAutoSaving Drawing, Please Wait…") ;inform the user (command "QSAVE") ;save the drawing (setq T3 (getvar "TDUSRTIMER")) ;reset the timer );progn );if (setvar "CMDECHO" ECC) ;reset CMDECHO (princ) );defun
I'm not all too familiar with his autosave function but it looks helpful, and I don't think mr. Ramage minds people adapting his code for their needs, isn't that the entire point of the afralisp website, to teach and provide example? Anyhow...hope this glaring simple method is somewhat useful , even if already considered or similar methods were already considered...
Why not just alert the drafter if the drawing is read only? If they ignore all notices then it's their time wasted and perhaps they'll pay closer attention next time.
BTW, the sysvar is "WRITESTAT" : 0 means the drawing read-only.
@mid-awe wrote:Why not just alert the drafter if the drawing is read only? If they ignore all notices then it's their time wasted and perhaps they'll pay closer attention next time.
BTW, the sysvar is "WRITESTAT" : 0 means the drawing read-only.
I agree - a simple
(if (zerop (getvar 'writestat)) (alert "Drawing is read-only") )
Placed in the acaddoc.lsp would probably suffice for this.
Though, the reactor route is also pretty simple:
(defun c:roreactoron nil (if (= 'vlr-command-reactor (type readonly:reactor)) (if (not (vlr-added-p readonly:reactor)) (vlr-add readonly:reactor) ) (setq readonly:reactor (vlr-command-reactor nil '((:vlr-commandwillstart . readonly:callback)) ) ) ) (princ "\nRead Only Reactor enabled.") (princ) ) (defun c:roreactoroff nil (if (= 'vlr-command-reactor (type readonly:reactor)) (vlr-remove readonly:reactor) ) (setq readonly:reactor nil) (princ "\nRead Only Reactor disabled.") (princ) ) (defun readonly:callback ( obj arg ) (if (zerop (getvar 'writestat)) (alert "WARNING: Drawing is read-only.") ) (princ) ) (vl-load-com) (princ)
The reactor can be enabled or disabled using the roreactoron & roreactoroff commands respectively.
Lee
Thank you Lee! It now also makes quite a bit more sense to me 🙂
If I use the writestat method, I'll just create a double popup (saying the same) upon opening... besides the "Read-only" at the top of the screen, after the name... the lock on the drawing tab and all these other functions that AutoCAD already offers to show something is read-only.
The reactor method seems more effective... If only if I could find the right spot to start it from:
- When I put it through the startup suite, the drafter needs to press Ok 4 times besides the one time AutoCAD already asks for (the regular read-only warning).
- This is because upon opening I have a Standards script that makes sure some settings are correct, which apparently activates the reactor 4 times.
so:
I'll pass through my standards script and see if I vl-cmd or command anything in there and if there's other methods to accomplish the same. 🙂