I want to delete all attribute of block inside drawing using AutoLISP ObjectDBX method.
The below program works well: it deletes the attributes of all blocks inside the drawing, but when I edit this block in the Block Editor, I see all the attributes are still there.
I think I need to delete this attribute from definition of block.
;[dwgin]--input drawing file
;[dwgout]-- Output drawing fath with name
;function 'LM:GetDocumentObject' lee mac function to open drawing in ObjectDBX method
(defun DBXAttDelete ( dwgin dwgout / doc flg val )
(if (setq doc (LM:GetDocumentObject dwgin))
(progn
(vlax-for lyt (vla-get-layouts doc)
(vlax-for obj (vla-get-block lyt)
(if (and (= "AcDbBlockReference" (vla-get-objectname obj))
(= :vlax-true (vla-get-hasattributes obj))
)
(foreach att (vlax-invoke obj 'getattributes)
(if (vl-catch-all-error-p (setq err (vl-catch-all-apply 'vla-delete (list att))))
(princ (strcat "\nERROR: " (vl-catch-all-error-message err)))
)
)
)
)
)
(vla-saveas doc dwgout)
(vlax-release-object doc)
flg
)
(prompt (strcat "\nThe drawing \"" dwgin "\" was not found or could not be accessed."))
)
)
Can you help to find where I need to improve/correct this program.
Consider that the Block Definition is essentially the "blueprint" for the block, and each Block Reference is an instance displaying the objects found within the block definition, at a specific position, scale, rotation & orientation in the drawing.
Attributes also have Attribute Definitions within the Block Definition, and corresponding Attribute References attached to each Block Reference. Such attribute references may then hold different text content for each block reference inserted in the drawing.
Aside, interestingly, attribute references may also be programmatically attached to a block reference independently of the block definition, however, this is not permitted when operating AutoCAD using the standard out-of-the-box front end.
As such, to remove all attributes from the drawing, you'll need to delete the attribute references associated with all block references, and the attribute definitions from the corresponding block definitions.
You may also want to unlock locked layers prior to performing the delete operation, and relock the previously locked layers following this operation.
Since layouts are merely a type of block, iterating over the blocks collection will be sufficient to process all layouts, blocks, and nested blocks (excluding xrefs):
(defun DBXAttDelete ( dwgin dwgout / doc lck )
(if (setq doc (LM:GetDocumentObject dwgin))
(progn
(vlax-for lay (vla-get-layers doc)
(if (= :vlax-true (vla-get-lock lay))
(progn
(setq lck (cons lay lck))
(vla-put-lock lay :vlax-false)
)
)
)
(vlax-for blk (vla-get-blocks doc)
(if (= :vlax-false (vla-get-isxref blk))
(vlax-for obj blk
(cond
( (= "AcDbBlockReference" (vla-get-objectname obj))
(if (= :vlax-true (vla-get-hasattributes obj))
(foreach att (vlax-invoke obj 'getattributes)
(vla-delete att)
)
)
)
( (= "AcDbAttributeDefinition" (vla-get-objectname obj))
(vla-delete obj)
)
)
)
)
)
(foreach lay lck
(vla-put-lock lay :vlax-true)
)
(vla-saveas doc dwgout)
(vlax-release-object doc)
t
)
(prompt (strcat "\nThe drawing \"" dwgin "\" was not found or could not be accessed."))
)
)
Related
I need some help understanding completion-at-point.
I have this minimal example, where I want to:
activate when I type "#"
search/complete on candidates car ...
... but return cdr, so result at point is, for example "#doe" (though I may need to extend this later to drop the "#" in some cases, like with LaTeX).
The actual use case is to insert a citation key in a document, but search on author, title, etc. The intention is for this to be used with solutions like corfu and company-capf.
In that code, which is a front-end to bibtex-completion like helm-bibtex and ivy-bibtex, I have a core bibtex-actions-read function based on completing-read-multiple for minibuffer completion.
With this capf, I want to use the same cached data to complete against for at-point completion.
With this test example, I get 1 and 2, which is what I want on the UI end.
(defun test-capf ()
"My capf."
(when (looking-back "#[a-zA-Z]*")
(list
(save-excursion
(backward-word)
(point))
(point)
(lambda (str pred action)
(let ((candidates '(("a title doe" . "doe")
("different title jones" . "jones")
("nothing smith" . "smith"))))
(complete-with-action action candidates str pred))))))
But how do I adapt it to this to add 3? That is, if I type "#not", corfu or company should display "nothing smith", and if I select that item, it should return "#smith" at-point.
Note: my package pretty much depends on completion-styles like orderless, so order is of course not significant.
Do I need to use an :exit-function here?
For completeness, here's the current actual function, which now says "no matches" when I try to use it.
(defun bibtex-actions-complete-key-at-point ()
"Complete citation key at point.
When inserting '#' in a buffer the capf UI will present user with
a list of entries, from which they can narrow against a string
which includes title, author, etc., and then select one. This
function will then return the key 'key', resulting in '#key' at
point."
;; FIX current function only returns "no match"
;; TODO this regex needs to adapt for mode/citation syntax
(when (looking-back "#[a-zA-Z]+" 5)
(let* ((candidates (bibtex-actions--get-candidates))
(begin (save-excursion (backward-word) (point)))
(end (point)))
(list begin end candidates :exclusive 'no
;; I believe I need an exit-function so I can insert the key instead
;; of the candidate string.
:exit-function
(lambda (chosen status)
(when (eq status 'finished)
(cdr (assoc chosen candidates))))))))
Any other tips or suggestions?
This Q&A is related, but I can't figure out how to adapt it.
Why not just keep the completion candidates in your completion table, not conses?
There are some useful wrappers in minibuffer.el around completion tables. In this case you could use completion-table-dynamic, as a wrapper to use a function as the COLLECTION argument to complete-with-action.
I think the more efficient way would just collect the cdrs of your current candidates and allow the C implementations of all-completions to find matches
(complete-with-action action (mapcar #'cdr candidates) str pred)
Or, calling a function to return current candidates
(completion-table-dynamic
(lambda (_str)
(mapcar #'cdr (my-current-candidates))))
Or, filtering in elisp
(let ((candidates '((...)))
(beg '...)
(end '...))
;; ...
(list beg end
(completion-table-dynamic
(lambda (str)
(cl-loop for (a . b) in candidates
if (string-prefix-p str a)
collect b)))))
The solution was an exit-function, with body like this:
(delete-char (- (length str)))
(insert (cdr (assoc str candidates)))))
In Emacs f90 mode, there are two useful functions f90-beginning-of-block and f90-end-of-block, (bound to keys C-M-p and C-M-n,repsectively), which I often use to jump between beginning and end of code blocks (such as function/subroutine/module).
However I found there is weakness in these two functions. For example:
module a
contains
function f()
write(*,*)
end function
end module a
When placing the cursor at the beginning of module and press C-M-n, the cursor will jump to the end function line, rather than the end module a line. The correct behavior appears only after I modify the end function line to the end function f , i.e., adding back the function name. Since there are many existing codes that often omit function names at the end function, I am wondering whether there is an easy improvement to f90-end-of-block, so that it can correctly handle the above case.
The original interactive Lisp function f90-end-of-block is defined as:
(defun f90-end-of-block (&optional num)
"Move point forward to the end of the current code block.
With optional argument NUM, go forward that many balanced blocks.
If NUM is negative, go backward to the start of a block. Checks
for consistency of block types and labels (if present), and
completes outermost block if `f90-smart-end' is non-nil.
Interactively, pushes mark before moving point."
(interactive "p")
;; Can move some distance.
(if (called-interactively-p 'any) (push-mark (point) t))
(and num (< num 0) (f90-beginning-of-block (- num)))
(let ((f90-smart-end (if f90-smart-end 'no-blink)) ; for final match-end
(case-fold-search t)
(count (or num 1))
start-list start-this start-type start-label end-type end-label)
(end-of-line) ; probably want this
(while (and (> count 0) (re-search-forward f90-blocks-re nil 'move))
(beginning-of-line)
(skip-chars-forward " \t0-9")
(cond ((or (f90-in-string) (f90-in-comment)))
((setq start-this
(or
(f90-looking-at-do)
(f90-looking-at-select-case)
(f90-looking-at-type-like)
(f90-looking-at-associate)
(f90-looking-at-critical)
(f90-looking-at-program-block-start)
(f90-looking-at-if-then)
(f90-looking-at-where-or-forall)))
(setq start-list (cons start-this start-list) ; not add-to-list!
count (1+ count)))
((looking-at (concat "end[ \t]*" f90-blocks-re
"[ \t]*\\(\\(?:\\sw\\|\\s_\\)+\\)?"))
(setq end-type (match-string 1)
end-label (match-string 2)
count (1- count))
;; Check any internal blocks.
(when start-list
(setq start-this (car start-list)
start-list (cdr start-list)
start-type (car start-this)
start-label (cadr start-this))
(or (f90-equal-symbols start-type end-type)
(error "End type `%s' does not match start type `%s'"
end-type start-type))
(or (f90-equal-symbols start-label end-label)
(error "End label `%s' does not match start label `%s'"
end-label start-label)))))
(end-of-line))
(if (> count 0) (error "Missing block end"))
;; Check outermost block.
(when f90-smart-end
(save-excursion
(beginning-of-line)
(skip-chars-forward " \t0-9")
(f90-match-end)))))
A quick hack is to modify the line that checks the label matching from yielding error message to just yielding a warning message:
(or (f90-equal-symbols start-label end-label)
(message "Start label `%s' does not match end label `%s'"
There is a SO user who posted this solution as an answer, but deleted the answer before I can verify the solution. The reason she/he deleted the answer may be because I commented on the answer saying that another function f90-beginning-of-subprogram can handle the case with mismatched labels. But later I found I still need the additional functionalities provided by f90-beginning-of-block, which are not provided by f90-beginning-of-subprogram.
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.
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.
I'm trying to insert blocks with custom properties. How do I set those variables?
Within the Deciduous block there is a custom property called "visibility" with various different styles (Visibility 1, Visibility 2, Visibility 3, ...).
For instance, how would I insert the Deciduous block with Visibility 3.
(DEFUN C:TREE ( / DECIDUOUS CONIFER SHRUBMEDIUM SHRUBSMALL)
(INITGET 1 "DECIDUOUS CONIFER SHRUBMEDIUM SHRUBSMALL")
(OR
(SETQ RETKWORD (GETKWORD "\nSpecify tree type: [DECIDUOUS/CONIFER/SHRUBMEDIUM/SHRUBSMALL]:"))
(SETQ RETKWORD "DECIDUOUS")
)
(IF (= RETKWORD "DECIDUOUS")
(PROGN
(SETQ OLDLAYER (GETVAR "CLAYER"))
(SETQ FLAG (TBLSEARCH "LAYER" "L-PLNT-DECD"))
(IF FLAG
(SETVAR "CLAYER" "L-PLNT-DECD")
)
(INITGET 1 "Visibility1 Visibility2 Visibility3")
(OR
(SETQ CMDKWORD (GETKWORD "\nPick a command: [Visibility1/Visibility2/Visibility3]:"))
)
(IF (= CMDKWORD "Visibility3")
(PROGN
(COMMAND "INSERT"
"TT-L-TREE-DECIDUOUS"
)
)
)
(PRINC)
)
)
)
The answer to this question ultimately depends on whether or not you require the visual preview of the inserted block reference, as afforded by the AutoCAD INSERT command.
Since the standard AutoCAD INSERT command does not prompt for dynamic block parameter values during block insertion, you'll need to insert the block and then manipulate the visibility state using the ActiveX properties & methods of the inserted dynamic block reference, specifically, using the getdynamicblockproperties method.
If the visual preview of the inserted block is NOT required...
...then you can forego the INSERT command entirely, and use the insertblock method of the target container object (modelspace/paperspace/block definition), which will return a block reference vla-object.
Here is a basic example demonstrating how to use the insertblock method:
(defun c:test ( / dwg ins )
(if
(and
(setq dwg (getfiled "Select Block" "" "dwg" 16))
(setq ins (getpoint "\nSpecify insertion point: "))
)
(vla-insertblock
(vlax-get-property
(vla-get-activedocument (vlax-get-acad-object))
(if (= 1 (getvar 'cvport))
'paperspace
'modelspace
)
)
(vlax-3D-point (trans ins 1 0))
dwg
1.0 1.0 1.0 0.0
)
)
(princ)
)
(vl-load-com) (princ)
This method will return a block reference vla-object, whose dynamic block properties you can then manipulate using the array of dynamic block properties returned by the getdynamicblockproperties method.
Since you are looking to modify the Visibility State in particular, you may wish to consider the following set of functions that I have developed as part of my dynamic block library to modify the visibility state of a supplied block reference object:
;; Set Dynamic Block Visibility State - Lee Mac
;; Sets the Visibility Parameter of a Dynamic Block (if present) to a specific value (if allowed)
;; blk - [vla] VLA Dynamic Block Reference object
;; val - [str] Visibility State Parameter value
;; Returns: [str] New value of Visibility Parameter, else nil
(defun LM:SetVisibilityState ( blk val / vis )
(if
(and
(setq vis (LM:getvisibilityparametername blk))
(member (strcase val) (mapcar 'strcase (LM:getdynpropallowedvalues blk vis)))
)
(LM:setdynpropvalue blk vis val)
)
)
;; Get Visibility Parameter Name - Lee Mac
;; Returns the name of the Visibility Parameter of a Dynamic Block (if present)
;; blk - [vla] VLA Dynamic Block Reference object
;; Returns: [str] Name of Visibility Parameter, else nil
(defun LM:getvisibilityparametername ( blk / vis )
(if
(and
(vlax-property-available-p blk 'effectivename)
(setq blk
(vla-item
(vla-get-blocks (vla-get-document blk))
(vla-get-effectivename blk)
)
)
(= :vlax-true (vla-get-isdynamicblock blk))
(= :vlax-true (vla-get-hasextensiondictionary blk))
(setq vis
(vl-some
'(lambda ( pair )
(if
(and
(= 360 (car pair))
(= "BLOCKVISIBILITYPARAMETER" (cdr (assoc 0 (entget (cdr pair)))))
)
(cdr pair)
)
)
(dictsearch
(vlax-vla-object->ename (vla-getextensiondictionary blk))
"ACAD_ENHANCEDBLOCK"
)
)
)
)
(cdr (assoc 301 (entget vis)))
)
)
;; Get Dynamic Block Property Allowed Values - Lee Mac
;; Returns the allowed values for a specific Dynamic Block property.
;; blk - [vla] VLA Dynamic Block Reference object
;; prp - [str] Dynamic Block property name (case-insensitive)
;; Returns: [lst] List of allowed values for property, else nil if no restrictions
(defun LM:getdynpropallowedvalues ( blk prp )
(setq prp (strcase prp))
(vl-some '(lambda ( x ) (if (= prp (strcase (vla-get-propertyname x))) (vlax-get x 'allowedvalues)))
(vlax-invoke blk 'getdynamicblockproperties)
)
)
;; Set Dynamic Block Property Value - Lee Mac
;; Modifies the value of a Dynamic Block property (if present)
;; blk - [vla] VLA Dynamic Block Reference object
;; prp - [str] Dynamic Block property name (case-insensitive)
;; val - [any] New value for property
;; Returns: [any] New value if successful, else nil
(defun LM:setdynpropvalue ( blk prp val )
(setq prp (strcase prp))
(vl-some
'(lambda ( x )
(if (= prp (strcase (vla-get-propertyname x)))
(progn
(vla-put-value x (vlax-make-variant val (vlax-variant-type (vla-get-value x))))
(cond (val) (t))
)
)
)
(vlax-invoke blk 'getdynamicblockproperties)
)
)
You might call the above functions from the earlier example I provided in the following way (changing the name of the visibility state to suit your block of course):
(defun c:test ( / dwg ins )
(if
(and
(setq dwg (getfiled "Select Block" "" "dwg" 16))
(setq ins (getpoint "\nSpecify insertion point: "))
)
(LM:SetVisibilityState
(vla-insertblock
(vlax-get-property
(vla-get-activedocument (vlax-get-acad-object))
(if (= 1 (getvar 'cvport))
'paperspace
'modelspace
)
)
(vlax-3D-point (trans ins 1 0))
dwg
1.0 1.0 1.0 0.0
)
"YourVisibilityState"
)
)
(princ)
)
(vl-load-com) (princ)
If the visual preview of the inserted block IS required...
...Then aside from rolling your own version of the standard INSERT command using a grread loop (which would also need to imitate all drawing aids, such as Object Snap, and Orthomode), you would need to make use of the INSERT command.
However, since the visibility state of the block can only be changed following the insertion of the block reference, the visual preview displayed to the user will be inaccurate.
To get the best of both worlds, I have previously proposed the following possible solution (along with an accompanying function posted here):
;; Example demonstrating a method to insert a Dynamic Block with a Visibility State already set.
;; Lee Mac - 2013-12-24
(defun c:test ( / *error* att blk def doc ent new obj par spc tmp vis )
(defun *error* ( msg )
(if (= 'int (type att))
(setvar 'attreq att)
)
(foreach obj (list new def)
(if (and (= 'vla-object (type obj)) (not (vlax-erased-p obj)))
(vl-catch-all-apply 'vla-delete (list obj))
)
)
(if (not (wcmatch (strcase msg t) "*break,*cancel*,*exit*"))
(princ (strcat "\nError: " msg))
)
(princ)
)
(cond
( (= 4 (logand 4 (cdr (assoc 70 (tblsearch "layer" (getvar 'clayer))))))
(princ "\nCurrent layer locked.")
)
( (null (setq blk (getfiled "Select Dynamic Block with Visibility States" "" "dwg" 16)))
(princ "\n*Cancel*")
)
( (progn
(setq doc (vla-get-activedocument (vlax-get-acad-object))
spc (vlax-get-property doc (if (= 1 (getvar 'cvport)) 'paperspace 'modelspace))
obj (vlax-invoke spc 'insertblock '(0.0 0.0 0.0) blk 1.0 1.0 1.0 0.0)
)
(vla-put-visible obj :vlax-false)
(= :vlax-false (vla-get-isdynamicblock obj))
)
(princ "\nSelected block is not dynamic.")
(vla-delete obj)
)
( (null (setq par (LM:getvisibilityparametername obj)))
(princ "\nSelected block does not have a visibility parameter.")
(vla-delete obj)
)
( (null (setq vis (car (LM:listbox "Choose a Visibility State" (acad_strlsort (LM:getdynpropallowedvalues obj par)) 0))))
(princ "\n*Cancel*")
(vla-delete obj)
)
( t
(LM:setdynpropvalue obj par vis)
(setq tmp 0)
(while (tblsearch "block" (setq blk (strcat "tmp" (itoa (setq tmp (1+ tmp)))))))
(vla-put-visible
(car
(vlax-invoke doc
'copyobjects
(list obj)
(setq def (vlax-invoke (vla-get-blocks doc) 'add '(0.0 0.0 0.0) blk))
)
)
:vlax-true
)
(vla-delete obj)
(setq ent (entlast)
att (getvar 'attreq)
)
(setvar 'attreq 0)
(if
(and
(vl-cmdf "_.-insert" blk "_S" 1.0 "_R" 0.0 "\\")
(not (eq ent (setq ent (entlast))))
(= "AcDbBlockReference" (vla-get-objectname (setq new (vlax-ename->vla-object ent))))
)
(progn
(vla-explode new)
(vla-delete new)
)
)
(vl-catch-all-apply 'vla-delete (list def))
)
)
(princ)
)
;; Get Visibility Parameter Name - Lee Mac
;; Returns the name of the Visibility Parameter of a Dynamic Block (if present)
;; blk - [vla] VLA Dynamic Block Reference object
;; Returns: [str] Name of Visibility Parameter, else nil
(defun LM:getvisibilityparametername ( blk / vis )
(if
(and
(vlax-property-available-p blk 'effectivename)
(setq blk
(vla-item
(vla-get-blocks (vla-get-document blk))
(vla-get-effectivename blk)
)
)
(= :vlax-true (vla-get-isdynamicblock blk))
(= :vlax-true (vla-get-hasextensiondictionary blk))
(setq vis
(vl-some
'(lambda ( pair )
(if
(and
(= 360 (car pair))
(= "BLOCKVISIBILITYPARAMETER" (cdr (assoc 0 (entget (cdr pair)))))
)
(cdr pair)
)
)
(dictsearch
(vlax-vla-object->ename (vla-getextensiondictionary blk))
"acad_enhancedblock"
)
)
)
)
(cdr (assoc 301 (entget vis)))
)
)
;; Get Dynamic Block Property Allowed Values - Lee Mac
;; Returns the allowed values for a specific Dynamic Block property.
;; blk - [vla] VLA Dynamic Block Reference object
;; prp - [str] Dynamic Block property name (case-insensitive)
;; Returns: [lst] List of allowed values for property, else nil if no restrictions
(defun LM:getdynpropallowedvalues ( blk prp )
(setq prp (strcase prp))
(vl-some '(lambda ( x ) (if (= prp (strcase (vla-get-propertyname x))) (vlax-get x 'allowedvalues)))
(vlax-invoke blk 'getdynamicblockproperties)
)
)
;; Set Dynamic Block Property Value - Lee Mac
;; Modifies the value of a Dynamic Block property (if present)
;; blk - [vla] VLA Dynamic Block Reference object
;; prp - [str] Dynamic Block property name (case-insensitive)
;; val - [any] New value for property
;; Returns: [any] New value if successful, else nil
(defun LM:setdynpropvalue ( blk prp val )
(setq prp (strcase prp))
(vl-some
'(lambda ( x )
(if (= prp (strcase (vla-get-propertyname x)))
(progn
(vla-put-value x (vlax-make-variant val (vlax-variant-type (vla-get-value x))))
(cond (val) (t))
)
)
)
(vlax-invoke blk 'getdynamicblockproperties)
)
)
;; List Box - Lee Mac
;; Displays a DCL list box allowing the user to make a selection from the supplied data.
;; msg - [str] Dialog label
;; lst - [lst] List of strings to display
;; bit - [int] 1=allow multiple; 2=return indexes
;; Returns: [lst] List of selected items/indexes, else nil
(defun LM:listbox ( msg lst bit / dch des tmp rtn )
(cond
( (not
(and
(setq tmp (vl-filename-mktemp nil nil ".dcl"))
(setq des (open tmp "w"))
(write-line
(strcat "listbox:dialog{label=\"" msg "\";spacer;:list_box{key=\"list\";multiple_select="
(if (= 1 (logand 1 bit)) "true" "false") ";width=50;height=15;}spacer;ok_cancel;}"
)
des
)
(not (close des))
(< 0 (setq dch (load_dialog tmp)))
(new_dialog "listbox" dch)
)
)
(prompt "\nError Loading List Box Dialog.")
)
( t
(start_list "list")
(foreach itm lst (add_list itm))
(end_list)
(setq rtn (set_tile "list" "0"))
(action_tile "list" "(setq rtn $value)")
(setq rtn
(if (= 1 (start_dialog))
(if (= 2 (logand 2 bit))
(read (strcat "(" rtn ")"))
(mapcar '(lambda ( x ) (nth x lst)) (read (strcat "(" rtn ")")))
)
)
)
)
)
(if (< 0 dch)
(unload_dialog dch)
)
(if (and tmp (setq tmp (findfile tmp)))
(vl-file-delete tmp)
)
rtn
)
(vl-load-com) (princ)
My solution essentially involves temporarily inserting a block reference, configuring the visibility state appropriately, creating a temporary block definition containing the configured dynamic block, and then exploiting the visual preview offered by the standard INSERT command to insert the temporary block reference, which is then exploded & purged from the drawing.
Blocks with custom properties are called Dynamic blocks.
Details and samples You can find here
p.s.
Thank You #LeeMac
When selecting a point is there a way to filter OSNAP to only snap onto a specific entity type and not an entity of another type. eg
Snap only to lines.
setq startpt (*SNAP FILTER CODE* "LINE" (getpoint "\nChoose Start Line : "))
Snap only to arcs.
setq startpt (*SNAP FILTER CODE* "ARC" (getpoint "\nChoose Start Arc: "))
Snap only to polyline.
setq startpt (*SNAP FILTER CODE* "POLYLINE" (getpoint "\nChoose Start Polyline: "))
I hope the fake lisp above helps in understanding what I'm trying to ask.
Thanks in advance.
The AutoLISP osnap function can be used to return a point snapped to geometry using a supplied Object Snap modifier, however, this function will not filter the candidate geometry.
Therefore, you could alternatively supply the point returned by getpoint as the point argument for a filtered ssget selection, or test the entity returned by the nentselp function.
Here is a possible solution using ssget:
(defun c:test1 ( / pnt )
(while
(and
(setq pnt (getpoint "\nSelect start point on arc: "))
(not (ssget pnt '((0 . "ARC"))))
)
(princ "\nThe point does not lie on an arc.")
)
(if pnt
(princ (strcat "\nThe user picked (" (apply 'strcat (mapcar 'rtos pnt)) ")."))
(princ "\nThe user did not supply a point.")
)
(princ)
)
Here is a possible solution using nentselp:
(defun c:test2 ( / ent pnt )
(while
(and (setq pnt (getpoint "\nSelect start point on arc: "))
(not
(and
(setq ent (car (nentselp pnt)))
(= "ARC" (cdr (assoc 0 (entget ent))))
)
)
)
(princ "\nThe point does not lie on an arc.")
)
(if pnt
(princ (strcat "\nThe user picked (" (apply 'strcat (mapcar 'rtos pnt)) ")."))
(princ "\nThe user did not supply a point.")
)
(princ)
)
It is possible to handle this issue, but it's very complicated.
What I can tell is that You may use function (grread) to get user input (mouse move or keyboard pressed). Then You have to analyse returned value, considering osnaps. here You may filter for example like this:
(cond
( ( = (vlax-get-property curve 'ObjectName ) "AcDbMLeader" ) ( progn
...
) )
( ( = (vlax-get-property curve 'ObjectName ) "AcDbPolyline" ) ( progn
...
) )
( YOUR NEXT CASES ( progn
...
) )
( t (progn
(princ "\n*Error:NotImplementedYetForThisEntity\n" ) )
) )
)
You have to draw Your own osnap markers (shapes draw for example by (grvecs) size based on system variables "VIEWSIZE" "SCREENSIZE". You need to handle polar tracking, ortho mode, keys pressed on the keyboard.
I tried to do it some time ago, didn't handle every case, and my code was hundreds of lines of code. Sorry, but I can not share all the code.
So probably You will spend weeks if You are the beginner in AutoLISP maybe even months on this way of solving the problem. So consider if You can spend so many time on this issue. Maybe problem You met may be handled in another way than filtering osnaps.