Author Topic: Width of block multiline attribute  (Read 831 times)

0 Members and 1 Guest are viewing this topic.

Spainenins

  • Mosquito
  • Posts: 4
Width of block multiline attribute
« on: April 17, 2024, 11:22:44 AM »
Hello!
I have made a lisp routine that does the following:
1)adds a block reference to the model space;
2)changes the value (text string) of an attribute;
3)gets the real width (through bounding box) of the attribute;
4)changes a dynamic block property so that the width of the block reference fits the new text.

It works beautifully, when the attribute is a single-line one. But sadly my colleagues requested I make it a multi-line attribute, so they could use subscripts and superscripts in it.
The problem seems to be that Bounding box of a multi-line attribute is always as if it had it's default text string.
Maybe there is another way to get the width of a multi-line attribute?
Code below:
Code - Auto/Visual Lisp: [Select]
  1. (defun c:ko-addmyblock ()
  2.   (setq mspace (vla-get-modelspace (vla-get-activedocument (vlax-get-acad-object))))    ;get modelspace of active document
  3.   (setq blkname "MyBlock")      ;name of my block
  4.   (setq attrname "NR")  ;block attribute name (TagString) that I want to change
  5.   (setq newattrvalue "MyNewValue")      ;the new value I want to give to the attribute
  6.   (setq propname "Width")       ;block property name that is supposed to change depending on width of the attribute
  7.   (setq pt (getpoint "Insertion point for block"))      ;get insertion point for the block
  8.   (setq blkobj (vla-insertblock mspace (vlax-3d-point pt) blkname 1 1 1 0))     ;insert block
  9.  
  10.   ;;get the relevant attribute as vla-object
  11.   (setq attrobj
  12.          (car
  13.            (vl-member-if
  14.              '(lambda (blkattrobj) (= (vlax-get-property blkattrobj "TagString") attrname));lambda
  15.              (vlax-safearray->list
  16.                (vlax-variant-value
  17.                  (vlax-invoke-method blkobj "GetAttributes")))
  18.              );vl-member-if
  19.            );car
  20.         );setq
  21.  
  22.   (vla-put-textstring attrobj newattrvalue)     ;change text of the relevant attribute
  23.   (vla-getboundingbox attrobj 'll 'ur)  ;get bounding box of attribute
  24.   (setq attrwidth (apply '- (mapcar 'car (mapcar 'safearray-value (list ur ll)))))      ;get width of the bounding box
  25.  
  26.   ;;get the relevant property as vla-object
  27.   (setq propobj
  28.          (car
  29.            (vl-member-if
  30.              '(lambda (blkpropobj) (= (vlax-get-property blkpropobj "PropertyName") propname));lambda
  31.              (vlax-invoke blkobj "GetDynamicBlockProperties")
  32.              );vl-member-if
  33.            );car
  34.         );setq
  35.  
  36.   (vla-put-value propobj (+ 2.0 attrwidth))     ;change it's value
  37.   (princ)
  38. );defun

The block is a really simple one with one attribute called "NR", and one custom property called "Width".


EDIT (John): Fixed code tag.
« Last Edit: April 17, 2024, 02:26:43 PM by JohnK »

BIGAL

  • Swamp Rat
  • Posts: 1471
  • 40 + years of using Autocad
Re: Width of block multiline attribute
« Reply #1 on: April 17, 2024, 09:53:02 PM »
I just made a simple mtext with multi line, picked 2 points for the mtext, the bounding box returned the 2 points which was way wider than the actual text.

Which leads me to maybe 2 ideas the 1st is need mtext to be with extents pulled back to just touch right side. The other way is to use mtext pt pt which will force an auto width.

Ok but the good news try this simple mod before running bounding box.

Code: [Select]
(setq ent (entget (car (entsel "\nPick object"))))
(entmod (subst (cons 41 0.0) (assoc 41 ent) ent))
A man who never made a mistake never made anything

Spainenins

  • Mosquito
  • Posts: 4
Re: Width of block multiline attribute
« Reply #2 on: June 27, 2024, 05:21:13 AM »
TLDR at last code snippet.

Oh wow, it's been 2 months already. I got a little sidetracked, as programming isn't my main job.

I have to say big Thanks to BIGAL! You helped me get to the root of this.
I'm not working with mtext (DXF code 0 = "MTEXT"), but with multiline block attribute (DXF code 0 = "ATTRIB"), which is it's own thing. But you made me think about DXF codes, which I often forget.
So I looked at DXF 41:
*For mtext it is "Reference rectangle width" Which means - the rectangle that you drew when manually creating the mtext. It is what you get from (getpropertyvalue ename "Width") or from the bounding box width of mtext.
*Attributes have 2 DXF codes 41:
  • "Relative X scale factor (width) (optional; default = 1). This value is also adjusted when fit-type text is used". Haven't really tested how exactly this works.
  • "Reference rectangle width". For single-line attributes that is the same as the actual width taken by the text string and matches the bounding box width. For multi-line attributes it is 0 and doesn't match bounding box width.

So code 41 doesn't work for getting width of attributes.
Luckily, there is DXF code 42:
*For mtext it is "Horizontal width of the characters that make up the mtext entity. This value will always be equal to or less than the value of group code 41 (read-only, ignored if supplied)".
This gives the exact width that the mtext takes up.
*For attributes it is "Horizontal width of the characters that make up the mtext entity. This value will always be equal to or less than the value of group code 41 (read-only, ignored if supplied)".
This gives the exact width that the attribute takes up. The second sentence is actually wrong for multi-line attributes, as code 41 is 0.

So I can get the width of my attribute with a simple line like this:
Code - Auto/Visual Lisp: [Select]
  1. (setq attrwidth (cdr (assoc 42 (entget (vlax-vla-object->ename attrobj)))))
right?

When trying it out step by step, it seemed to work fine. But when launching the updated function as a whole, it seemed to think that the width of my attribute was 0.
Turns out, after changing the attribute text from "" (the default value when inserting the block) to the desired one, you need to specifically say that you changed the drawing.
Code - Auto/Visual Lisp: [Select]
This works, but creates an ugly flicker if you do it multiple times (my function is called repeatedly automatically). And it may take a long time for large drawings.

Turns out you only need to update the changed block, not the whole drawing.
Code - Auto/Visual Lisp: [Select]
  1. (vla-update blkobj)
Now it works perfectly.

Full code from original post with corrections:
Code - Auto/Visual Lisp: [Select]
  1. (defun c:ko-addmyblock ()
  2.   (setq mspace (vla-get-modelspace (vla-get-activedocument (vlax-get-acad-object))));get modelspace of active document
  3.   (setq blkname "MyBlock");name of my block
  4.   (setq attrname "NR");block attribute name (TagString) that I want to change
  5.   (setq newattrvalue "MyNewValue");the new value I want to give to the attribute
  6.   (setq propname "Width");block property name that is supposed to change depending on width of the attribute
  7.   (setq pt (getpoint "Insertion point for block"));get insertion point for the block
  8.   (setq blkobj (vla-insertblock mspace (vlax-3d-point pt) blkname 1 1 1 0));insert block reference
  9.  
  10.   ;;get the relevant attribute as vla-object
  11.   (setq attrobj
  12.          (car
  13.            (vl-member-if
  14.              '(lambda (blkattrobj) (= (vlax-get-property blkattrobj "TagString") attrname));lambda
  15.              (vlax-safearray->list
  16.                (vlax-variant-value
  17.                  (vlax-invoke-method blkobj "GetAttributes")))
  18.              );vl-member-if
  19.            );car
  20.         );setq
  21.  
  22.   (vla-put-textstring attrobj newattrvalue);change text of the relevant attribute
  23. ;;2 lines deleted:
  24. ;;;  (vla-getboundingbox attrobj 'll 'ur);get bounding box of attribute
  25. ;;;  (setq attrwidth (apply '- (mapcar 'car (mapcar 'safearray-value (list ur ll)))));get width of the bounding box
  26. ;;2 new lines added:
  27.   (vla-update blkobj);update block reference to the database to get width of the newly changed text
  28.   (setq attrwidth (cdr (assoc 42 (entget (vlax-vla-object->ename attrobj)))));DXF 42
  29.  
  30.   ;;get the relevant property as vla-object
  31.   (setq propobj
  32.          (car
  33.            (vl-member-if
  34.              '(lambda (blkpropobj) (= (vlax-get-property blkpropobj "PropertyName") propname));lambda
  35.              (vlax-invoke blkobj "GetDynamicBlockProperties")
  36.              );vl-remove-if-not
  37.            );car
  38.         );setq
  39.  
  40.   (vla-put-value propobj (+ 2.0 attrwidth));change it's value
  41.   (princ)
  42. );defun

Hope this helps someone sometime :)
« Last Edit: June 27, 2024, 05:29:08 AM by Spainenins »