问题
I have two texts associated to a line. Because the texts represent some data of the line they are always considered children of the line and visible next to it. Through some lisp routines, if the data of the line change, the text entities reflect the change by changing their text. For that I have stored the handle of the line to each text as xdata and vice versa, e.g. the handles of the texts into the line.
The problem arises when I copy the line with the texts where each one gets a new handle but the stored xdata are giving the old handles which leads to further problems. I thought the vlr-copied
reactor could solve my problem but since I am not very proficient with reactors I cant seem to make it work.
Could someone point me to the right direction? I found this
http://www.theswamp.org/index.php?topic=42654.0
but I cannot understand when I make a selection set of lines but also including non relevant other entities, how to pass the correct selection set to the reactor and get the handles updated.
Any suggestion appreciated. Thank you.
回答1:
Firstly, you need to decide on the behaviour that you want the objects to exhibit assuming that either object (text or line) is copied independently of the other. Since the two objects are linked, you may need to decide which object is the 'master' and which is the 'slave'.
For example, if the text object is copied into empty space, you might decide that the resulting copy should be deleted, since there is no line to which it could refer. Whereas, if the line is copied into empty space, you might decide to replicate the associated text object and position it relative to the new line.
This is the approach that I followed when developing my Associative Textbox application (which is essentially solving the same problem of associating two objects in a drawing - in my case, a text object and a bounding frame).
In my application, I use a separate Object Reactor to handle the modification events for the text object and textbox respectively:
(vlr-object-reactor txt "tbox-textreactor"
'(
(:vlr-modified . tbox:textcallback)
(:vlr-copied . tbox:textcopied)
)
)
(vlr-object-reactor box "tbox-tboxreactor"
'(
(:vlr-modified . tbox:tboxcallback)
(:vlr-copied . tbox:tboxcopied)
)
)
Similar to your setup, these are built & configured on program load using Extended Entity Data (xData) attached to both the text and textbox.
When the Copy event for the texbox is fired (evaluating the tbox:tboxcopied
callback function), I decide that a textbox cannot live without the text it encloses, and so I delete the orphan textbox from the drawing.
However, the most important point that you have to remember when working with object reactors is that you cannot modify the owner of an object reactor within its own callback function.
As such, for all modification events in which I need to modify the owner of the event, I generate a temporary Command Reactor which will fire after the object has been modified, so as to ensure that the object is not locked for modification.
For example, for the textbox copy event, I use the following:
(defun tbox:tboxcopied ( owner reactor params )
(if (/= 0 (car params))
(progn
(setq tbox:owner (append tbox:owner (list (car params))))
(vlr-command-reactor "tbox-tboxcopiedcommreactor"
'(
(:vlr-commandended . tbox:tboxcopiedcommandended)
(:vlr-commandcancelled . tbox:tboxcopiedcommandcancelled)
(:vlr-commandfailed . tbox:tboxcopiedcommandcancelled)
)
)
)
)
(princ)
)
I then remove this temporary Command Reactor within any of its own callback functions so as to prevent the propagation of redundant reactors in the drawing:
(defun tbox:tboxcopiedcommandended ( reactor params / ent )
(vlr-remove reactor) ;; <----- Remove temporary Command Reactor
(if
(and
(setq ent (car tbox:owner))
(member (cdr (assoc 0 (entget ent))) '("CIRCLE" "LWPOLYLINE"))
)
(entdel ent) ;; <----- Delete orphan textbox
)
(setq tbox:owner (cdr tbox:owner))
(princ)
)
Whereas, when the text is copied, I recreate the surrounding textbox and build the new association (again, generating a temporary Command Reactor to facilitate modification of the text object itself):
(defun tbox:textcopied ( owner reactor params )
(if (/= 0 (car params))
(progn
(setq tbox:owner (append tbox:owner (list (car params))))
(vlr-command-reactor "tbox-textcopiedcommreactor"
'(
(:vlr-commandended . tbox:textcopiedcommandended)
(:vlr-commandcancelled . tbox:textcopiedcommandcancelled)
(:vlr-commandfailed . tbox:textcopiedcommandcancelled)
)
)
)
)
(princ)
)
...and recreate the appropriate textbox as part of the callback function for the temporary Command Reactor:
(defun tbox:textcopiedcommandended ( reactor params / box ent enx val )
(vlr-remove reactor) ;; <----- Remove temporary Command Reactor
(if
(and
(setq ent (car tbox:owner))
(setq enx (entget ent (list tbox:app)))
(member (cdr (assoc 0 enx)) '("TEXT" "MTEXT"))
(setq val (cdadr (assoc -3 enx)))
(setq box (tbox:createbox enx (cdr (assoc 1000 val)) (cdr (assoc 1040 val))))
)
(progn
(entmod
(append (vl-remove (assoc 40 enx) (entget ent))
(list
(list -3
(list tbox:app
'(1002 . "{")
(cons 1005 (cdr (assoc 5 (entget box))))
(assoc 1000 val)
(assoc 1040 val)
'(1002 . "}")
)
)
)
)
)
(if (= 'vlr-object-reactor (type tbox:textreactor))
(vlr-owner-add tbox:textreactor (vlax-ename->vla-object ent))
)
(if (= 'vlr-object-reactor (type tbox:tboxreactor))
(vlr-owner-add tbox:tboxreactor (vlax-ename->vla-object box))
)
)
)
(setq tbox:owner (cdr tbox:owner))
(princ)
)
And the methods I have described above is the approach that I would recommend for your scenario:
When the text is copied, delete the resulting orphaned text object; when the line is copied, create a corresponding text object and build the association between the copied line & new text object.
来源:https://stackoverflow.com/questions/56840278/how-to-update-xdata-information-of-an-entity-when-it-is-copied