Visual LISP, AutoLISP and General Customization

Reply
Distinguished Contributor
msarqui
Posts: 114
Registered: ‎09-14-2010
Message 11 of 20 (382 Views)

Re: Change block layer after insertion

03-10-2013 12:41 PM in reply to: pbejse

Hi,

 

After a full day of hard work, mistakes, research, learning and more, I got something that works for what I need. I used the base of the second Kent's routine and added some things to fit in what I needed.

I tested it and it works!!!!!!!!
What do you think?

 

(defun C:IBL (/ currentlayer list1 list2 list3 L1 L2 L3 LT1 LT2 LT3 lt bname); = Insert Block on appropriate Layer
(setq currentlayer (getvar "clayer"))
(initdia)
(command "_.insert")
(while (> (getvar 'cmdactive) 0) (command pause)); allowing use of options

 

(setq
list1 '("BLOCK A" "BLOCK B")
list2 '("BLOCK C" "BLOCK D")
list3 '("BLOCK E" "BLOCK F")
;; do all capitalized in blist; (strcase) below will make
;; sure User's entry, if typed, is not case-sensitive
bname (strcase (getvar 'insname))
);setq

 

;I think this way it's easer to add and manager new layers
;If the layer name needs to be change, I will do here only
(setq
L1 "Architecture"
L2 "Telecom"
L3 "Structure"
);setq

 

;I think this way it's easer to add and manager new ltypes;
;If the ltypes needs to be change, I will do here only
(foreach lt '("Center" "Hidden" "Phantom")
(if (not (tblsearch "ltype" lt)) ;if the lltype does not exists
(command "_.-linetype" "_l" lt "acadiso.lin" "") ;create it
); end if
); end foreach

 

(setq
LT1 "Center"
LT2 "Hidden"
LT3 "Phantom"
);setq

 

(cond
((member bname list1)
(if (not (tblsearch "layer" L1)) ;if the layer does not exists
(command "-layer" "m" L1 "lt" LT1 "" "c" "1" "" "") ;create it with my standard configuration
);end if;
(command "_.chprop" (entlast) "" "_layer") ;put this block
(command L1 "_color" "bylayer" "_ltype" "bylayer" "") ;in this layer with my standard configuration for it
);end list1 condition

((member bname list2)
(if (not (tblsearch "layer" L2))
(command "-layer" "m" L2 "lt" LT2 "" "c" "2" "" "")
);end if;
(command "_.chprop" (entlast) "" "_layer")
(command L2 "_color" 3 "_ltype" "bylayer" "")
);end list2 condition

((member bname list3)
(if (not (tblsearch "layer" L3))
(command "-layer" "m" L3 "lt" LT3 "" "c" "3" "" "")
);end if;
(command "_.chprop" (entlast) "" "_layer")
(command L3 "_color" 6 "_ltype" "hidden" "")
);end list3 condition
);cond

 

(setvar "clayer" currentlayer)
(princ)
); defun

*Expert Elite*
Posts: 2,391
Registered: ‎12-17-2004
Message 12 of 20 (369 Views)

Re: Change block layer after insertion

03-10-2013 04:29 PM in reply to: msarqui

msarqui,
you want to change the blocks after inserting, to a specific layer, and want to have the ability to modify the block color and line type, to do this, your blocks must have already color and LType set to ByBlock, if not, you'll have to redefine the block Color and LType to ByBlock.
As you wrote:
"because of the large number of blocks and possibilities that I have."
I think the code could be huge to load, and maybe allocate too much memory, without need.
One hypothesis is to create a main code, that only test the block name and calls a second code that will make the changes you want, layer, ltype and color...

 

(defun C:IBL (/ list1 list2 list3 bname); = Insert Block on appropriate Layer
(initdia)
(command "_.insert")
(while (> (getvar 'cmdactive) 0) (command pause)); allowing use of options
(setq
list1 '("BLOCK A" "BLOCK B")
list2 '("BLOCK C" "BLOCK D")
list3 '("BLOCK E" "BLOCK F")
;; do all capitalized in blist; (strcase) below will make
;; sure User's entry, if typed, is not case-sensitive
bname (strcase (getvar 'insname))
);setq
(cond
((member bname list1)(c:blk001))
((member bname list2)(c:blk002))
((member bname list3)(c:blk003))
);cond
);defun - IBL

;;first sub-routine
(defun c:blk001 (/ currentlayer)
  (setq currentlayer (getvar "clayer"))
  (if (not (tblsearch "ltype" "Center")) ;if the lltype does not exists, only loads the necessary ltype
(command "_.-linetype" "_l" "Center" "acadiso.lin" "") ;create it
); end if
  (if (not (tblsearch "layer" "Architecture")) ;if the layer does not exists
(command "-layer" "m" "Architecture" "lt" "Center" "" "c" "1" "" "") ;create it with my standard configuration
);end if
(command
  "_.layer" "_thaw" "Architecture" "_make" "Architecture" "_unlock" "Architecture" ""
  "_.chprop" (entlast) ""
  "_layer" "Architecture" "_color" "bylayer" "_ltype" "bylayer" ""
);command - put this block in this layer with my standard configuration for it
(setvar "clayer" currentlayer)
);defun - blk001

;; blk002 blk003 and so on

 To load the sub-routines You can use the acaddoc.lsp with the autoload function

 

(autoload "C:/MyLisp/blk001.LSP" '("blk001")); change to your customization directory

 

with the autoload function will only load the sub-routine if it is not loaded, if is, just run it. I think this way you will not have to load so much code without need.

 

hope that helps

Henrique

*Expert Elite*
Kent1Cooper
Posts: 4,982
Registered: ‎09-13-2004
Message 13 of 20 (348 Views)

Re: Change block layer after insertion

03-11-2013 10:57 AM in reply to: msarqui

msarqui wrote:

.... I got something that works for what I need. I used the base of the second Kent's routine and added some things to fit in what I needed.

I tested it and it works!!!!!!!!
What do you think?

....


If it works, that's most important, but I do think some things....

 

At the least, I would consolidate some of your redundant (command) functions, e.g. change this:

(command "_.chprop" (entlast) "" "_layer") ;put this block
(command L1 "_color" "bylayer" "_ltype" "bylayer" "")

to this:

(command "_.chprop" (entlast) "" "_layer") L1 "_color" "bylayer" "_ltype" "bylayer" "")

and likewise in the other similar places.

 

Also, if you assign a linetype to either a Layer or an object from within a Layer or Chprop command, it is not necessary for that linetype to be loaded into the drawing already -- AutoCAD will find it.  [It won't if you try to do it by (subst)/(entmod), or (entmake), or, if I remember rightly, VLA Properties].  So you don't need your linetype loading portion.

 

But the more I consider your statement "...because of the large number of blocks and possibilities that I have...", the more I think you're better off using multiple lists and (nth) functions to get pieces of information out of them, as in my third suggested approach [and the subsequent adjustment to it].  Here's why:

 

If you really have considerable numbers of Block names and classifications/groupings with their associated Layers and colors and linetypes, the amount of code you need to do what you want could get pretty cumbersome [hmsilva's concern].  As you have it, if there are, say, 20 classifications, then you will need variables list1 through list20, L1 through L20, and LT1 through LT20, with the correspondingly bloated localized variables list, and all the added lines of code to define them [three added lines for every one, for its Block name list, Layer name, and linetype], and the whole Layer and Chprop portion spelled out separately for every one of them [whether in a lengthy version of a single routine, or pulled out into separate ones as hmsilva suggested].

 

By contrast, the following suggested routine [which is set up to replicate what your version does, though I imagine you really use different Block names] will expand by much less with each additional classification/grouping needed.  Each will add a sublist in the bclasslist variable, and just one item to each of the five following list variables.  No matter how many more Block names and classifications are added, there will be no added variables needed.  And the Layer and Chprop operations are spelled out only once, covering all classifications, and remain unaffected by any amount of increase in the number of Block names and classifications.

 

Also, since it creates any new Layer(s) needed with the New option rather than the Make option, it does not change the current Layer at any time, so it's not necessary to save the current Layer at the beginning or return to it at the end.  That also means there's no need to consider adding error handling [as you probably should in your latest version], since there would be nothing for an error handler to restore to the way it should be, because nothing like that is changed by the routine.

 

So here's how I'd do it [tested, but lightly]:

 

(defun C:IBL ; = Insert Block and move to appropriate Layer
  (/ bname bclasslist blaylist bclist bltlist lclist lltlist bci bclass blay)
  (initdia)
  (command "_.insert")
  (while (> (getvar 'cmdactive) 0) (command pause)); allowing use of options
  (setq
    bname (strcase (getvar 'insname)); NAME of just-inserted Block
    bclasslist ; list of Block CLASSification LISTs
      ;; do all capitalized in these lists; (strcase) above will
      ;; ensure User's entry, if typed, is not case-sensitive
      (list
        '("BLOCK A" "BLOCK B")
        '("BLOCK C" "BLOCK D")
        '("BLOCK E" "BLOCK F")
        ;;; add classifications/groupings here as needed
      ); list & classlist
    ;;; add items as needed to the following 5 lists:
    blaylist '("Architecture" "Telecom" "Structure"); = Block LAYer LIST
    bclist '("bylayer" 3 6); = Block assigned Color LIST
    bltlist '("bylayer" "bylayer" "hidden"); = Block assigned LineType LIST
    lclist '(1 2 3); = Layer Color LIST
    lltlist '("Center" "Hidden" "Phantom"); = Layer LineType LIST
  ); setq
  (while
    (and
      (not bclass); hasn't found it yet
      (< bci (length bclasslist)); hasn't run out of lists to look in
    ); and
    (if (member bname (nth (setq bci (if bci (1+ bci) 0)) bclasslist)); = Block Classification Index
      (setq bclass bci); then
    ); if
  ); while
  (if bclass ; won't exist if not in any list
    (progn ; then
      (setq blay (nth bclass blaylist)); find LAYer name to put Block on
      (if (tblsearch "layer" blay); Layer is in drawing
        (command "_.layer" "_thaw" blay "_unlock" blay ""); then -- ensure it's ready [could omit unlocking]
        (command "_.layer" "_new" blay "_color" (nth bclass lclist) blay "_ltype" (nth bclass lltlist) blay ""); else -- create it
      ); if
      (command
        "_.chprop" (entlast) ""
          "_layer" blay
          "_color" (nth bclass bclist); find that category's assigned color
          "_ltype" (nth bclass bltlist); find that category's assigned linetype
        "" ; finish CHPROP
      ); command
    ); progn -- then [no else, if Block is not in any list]
  ); if
  (princ)
); defun

Kent Cooper
*Expert Elite*
Posts: 2,391
Registered: ‎12-17-2004
Message 14 of 20 (334 Views)

Re: Change block layer after insertion

03-11-2013 01:56 PM in reply to: Kent1Cooper

Kent,

as usual,

a very well structured, simple and clean code.

Well done!

I have learned a few more things!

 

Henrique

*Expert Elite*
Kent1Cooper
Posts: 4,982
Registered: ‎09-13-2004
Message 15 of 20 (317 Views)

Re: Change block layer after insertion

03-14-2013 06:16 AM in reply to: hmsilva

hmsilva wrote:

Kent,

as usual,

a very well structured, simple and clean code.

Well done!

I have learned a few more things!

 

Henrique


Thank you.  I've since been pondering whether I'd want to actually redefine the Insert command to do this with a limited number of Blocks that I know I will always want on particular Layers.  [In my case, I would not use the color or linetype overrides, but would have those be characteristics of the Layers, which would shorten the code, but the concept is the same.]  If I ever have any reason to put one of them on some other Layer, I could use .insert to do it, so I don't think it locks me into anything I might regret.  But if I do it in a system-wide way, I'd need to educate other Users about it.  I could also do it just on my computer.  I'll have to think about that some more....

Kent Cooper
*Expert Elite*
Posts: 2,370
Registered: ‎11-24-2009
Message 16 of 20 (309 Views)

Re: Change block layer after insertion

03-14-2013 07:52 AM in reply to: hmsilva

hmsilva wrote:

Kent,....

a very well structured, simple and clean code.

...


It sure is. kudos to Kent and  Henrique :smileyhappy:

 

Im off to revisit command function approach.

 

Cheers

 

pBe

 

//Off topic

*Expert Elite*
Kent1Cooper
Posts: 4,982
Registered: ‎09-13-2004
Message 17 of 20 (304 Views)

Re: Change block layer after insertion

03-14-2013 08:38 AM in reply to: Kent1Cooper

Kent1Cooper wrote:

....

      (if (tblsearch "layer" blay); Layer is in drawing
        (command "_.layer" "_thaw" blay "_unlock" blay ""); then -- ensure it's ready [could omit unlocking]
        (command "_.layer" "_new" blay "_color" (nth bclass lclist) blay "_ltype" (nth bclass lltlist) blay ""); else -- create it
      ); if

....


Come to think of it, the Layer might be in the drawing, and turned off rather than [or in addition to] being frozen, so the 'then' argument would be better this way:

...

        (command "_.layer" "_thaw" blay "_on" blay "_unlock" blay ""); then -- ensure it's ready [could omit unlocking]

 

I probably didn't think of that because I don't usually set things up to test whether a Layer is there, but just have them Make it regardless, and the Make option will turn it on if it's off.  [So will the Set option, but (setvar 'clayer "LayerName") will not.]  I think I did it this way this time to avoid having to save and restore the current Layer.  But if, whether or not the Layer exists, a Layer command is going to be invoked anyway [i.e. you're not avoiding the need for the command if the Layer already exists], and you don't mind its changing the current Layer, then maybe it does make just as much sense to use the Make approach.  You could replace the entire (if) function quoted above with:

 

      (command "_.layer" "_thaw" blay "_make" blay "_unlock" blay "_color" (nth bclass lclist) "" "_ltype" (nth bclass lltlist) "" ""); [could omit unlocking]

 

It needs to Thaw it before it Makes it, because Make sets it current, which it can't do with a Frozen Layer.  Thawing won't be bothered if the Layer doesn't exist yet -- a message will go by [if command echoing is on] that it didn't find a Layer like that, but it will just go back to the Layer sub-prompt and carry on.

 

 

The two circumstance under which you shouldn't do it that way are:

1) if you don't want that Layer to be the current Layer after you're done, and don't want to bother to save the current Layer at the beginning and restore it at the end, or

2) if the Layer might already exist but with non-standard color and/or linetype, and you want to let it keep those non-standard settings [the Make approach will force them to the standard].

Kent Cooper
*Expert Elite*
Posts: 2,391
Registered: ‎12-17-2004
Message 18 of 20 (290 Views)

Re: Change block layer after insertion

03-14-2013 02:10 PM in reply to: Kent1Cooper

Kent1Cooper wrote:

...

I've since been pondering whether I'd want to actually redefine the Insert command to do this with a limited number of Blocks that I know I will always want on particular Layers.  [In my case, I would not use the color or linetype overrides, but would have those be characteristics of the Layers, which would shorten the code, but the concept is the same.]  If I ever have any reason to put one of them on some other Layer, I could use .insert to do it, so I don't think it locks me into anything I might regret.  But if I do it in a system-wide way, I'd need to educate other Users about it.  I could also do it just on my computer.  I'll have to think about that some more....

 

Kent,

I'm currently rewriting part of the customization files (using your structure, message 13) for our topography department, without redefine the Insert command, because they already insert blocks using a lisp that after inserting, change the block to the correct layer (with many, many, many more lines of code, than your approach), I chose to use the "command" (message 17) instead of the "if", because when they are inserting blocks, they are only to insert blocks, so there is no problem in the layer of the last inserted block become the corrent layer. Regarding other departments, I have to think about it.

 

Thank you.

Henrique

Distinguished Contributor
msarqui
Posts: 114
Registered: ‎09-14-2010
Message 19 of 20 (277 Views)

Re: Change block layer after insertion

03-17-2013 08:05 AM in reply to: Kent1Cooper

Kent1Cooper wrote:

 

Come to think of it, the Layer might be in the drawing, and turned off rather than [or in addition to] being frozen, so the 'then' argument would be better this way:

...

        (command "_.layer" "_thaw" blay "_on" blay "_unlock" blay ""); then -- ensure it's ready [could omit unlocking]

 

Well, this one is the best for me until now. Thanks Kent.

 

I was wondering, because the large number of blocks, do you think it will be more pratical to put the list of blocks in an external txt file and load it?

I mean, something like this:

 

list 1 '(command "_.load" blocks1.txt)

list 2 '(command "_.load" blocks2.txt)

list 2 '(command "_.load" blocks3.txt)

 

Or it's not a good ideia? Just a curiosity...

 

I would like to thank you for taking your time about it. I am very glad to learn from you.

 

And yes 

 

*Expert Elite*
Kent1Cooper
Posts: 4,982
Registered: ‎09-13-2004
Message 20 of 20 (264 Views)

Re: Change block layer after insertion

03-18-2013 08:39 AM in reply to: msarqui

msarqui wrote:

....

I was wondering, because the large number of blocks, do you think it will be more pratical to put the list of blocks in an external txt file and load it?

I mean, something like this:

 

list 1 '(command "_.load" blocks1.txt)

list 2 '(command "_.load" blocks2.txt)

list 2 '(command "_.load" blocks3.txt)

 

Or it's not a good ideia? Just a curiosity...

....


I don't think there would be any real advantage, but if you would find it any easier to maintain such lists outside of the routine itself, you can certainly keep them in separate files.  But it would need to be by way of the AutoLISP (load) function, rather than the Load command, which is for Shape files.  [You could do it by way of the (open) and (read-line) functions instead, but that would be more cumbersome, because that would operate by text strings, and those would need to be converted somehow to make lists out of them.]

 

I would suggest, if you want to do it by separate file, that to work with the structure of my latest routine, the Blocks.lsp [or whatever you want to call it] file would contain something like this:

 

(setq bclasslist ; list of Block CLASSification LISTs
  (list
    '("BLOCK A" "BLOCK B")
    '("BLOCK C" "BLOCK D")
    '("BLOCK E" "BLOCK F")
    ;;; add classifications/groupings here as needed
  ); list & classlist

); setq

 

or better yet [and this simplification could be used in my routine]:

 

(setq bclasslist ; list of Block CLASSification LISTs
  '( ; "quoted" list rather than explicity (list) function
    ("BLOCK A" "BLOCK B") ; without preceding apostrophes
    ("BLOCK C" "BLOCK D")
    ("BLOCK E" "BLOCK F")
    ;;; add classifications/groupings here as needed
  ); list & classlist

); setq

 

and the main routine would contain something like this, anywhere prior to its (while) function:
 

(load "blocks.lsp")

 

You could also do it with a separate file for each list, if you really want to:

 

Blocks1.lsp would be:

(setq blocks1 '("BLOCK A" "BLOCK B"))

 

Blocks2.lsp would be:

(setq blocks2 '("BLOCK C" "BLOCK D"))

 

Blocks3.lsp would be:

(setq blocks3 '("BLOCK E" "BLOCK F"))

 

And the main routine would contain:
 

(load "blocks1.lsp")

(load "blocks2.lsp")

(load "blocks3.lsp")

(setq bclasslist (list blocks1 blocks2 blocks3))

 

But that way, if you ever need to add another classification, you would need to:

1) make an additional file for its Block name list;

2) add a (load) line in the main routine for that file;

3) add its list variable name to the (setq bclasslist...) function in the main routine.

 

And if you ever need to remove one other than the last one, you would need to re-number at least some of the remaining ones, both in their list variable names and in their file names, and you would need to delete a file and remove its (load) line and variable name in the main routine.

 

In contrast, doing it all in one combined list-of-lists Blocks.lsp file means that all you would need to do is to add [or remove] the single line for the classification/grouping in that one file -- no adjustments at all would be needed in the main routine except those added elements in the following lists that would be needed if the classification lists were internal to that file, and no files would be needed created or deleted.

Kent Cooper

You are not logged in.

Log into access your profile, ask and answer questions, share ideas and more. Haven't signed up yet? Register

Announcements
Are you interested in helping shape the Autodesk Community?
We’re looking at a few different ways to improve the “All Forums” landing page and need your feedback! If interested, please take a few minutes to fill out the following Usability Study. Thank you for your time!

Need installation help?

Start with some of our most frequented solutions to get help installing your software.

Ask the Community