LISP FOR BLOCK DATA BASE

LISP FOR BLOCK DATA BASE

Kyle.para
Advocate Advocate
5,436 Views
22 Replies
Message 1 of 23

LISP FOR BLOCK DATA BASE

Kyle.para
Advocate
Advocate

 

I have a lisp from @hencoop that I am using to write 100's of blocks that I currently have sitting in a bunch of different drawings. I can get the lisp to work no problem to write the blocks to a central location.


The problem is i need a lisp to update and replace blocks from the database that I am writing to.  That way I can just update the blocks in the database's library and then either update or replace the blocks in there respective drawings.  I tried to get 2 programs to work from @hencoop I get errors on both of them.  Does anyone have anything similar or who can help me figure out how to get the programs working properly?

UBLK

http://paracadd.com/lisp/ublk.lsp

 

RBLK

http://paracadd.com/lisp/lisp_lst.htm#rblk

http://paracadd.com/lisp/RBRKL.LSP

 

0 Likes
Accepted solutions (3)
5,437 Views
22 Replies
Replies (22)
Message 2 of 23

hencoop
Advisor
Advisor

Sounds like a new custom routine is needed.

1) Construct a list of blocks in your database

    (setq blk_db_list (dos_dir "<path>*.dwg")); DOS_DIR is a function defined within DOSLIB from Robert McNeel & Associates (free) here: https://wiki.mcneel.com/doslib/home

2) Construct a list of blocks in your drawing.

    (progn

      (setq dwg_blk_list (list (cdr (assoc 2 (tblnext "block" T)))))

      (while (setq next_blk (tblnext "block"))

        ; (if

        ;   (you will likely want to filter out some types of blocks here and only ad those that remain to your dwg_blk_list)

        ;   nil

            (setq dwg_blk_list (append dwg_blk_list (list (cdr (assoc 2 next_blk)))))

        ; )

      )

    )

3) execute the insert command using the "=" method to redefine the blocks in the drawing that are also in your block database list.

    (foreach n dwg_blk_list

      (if (member n blk_db_list

        (COMMAND ".INSERT" (STRCAT n "=") "Y" nil)

      )

    )

Like this:

(DEFUN c:updblks ()
    (setq dwg_db_list (dos_dir "L:\\Util\\*.dwg")); put the location of your block database (folder) here.
    (setq blk_db_list (mapcar '(lambda (x) (strcase (nth 2 (dos_splitpath x)))) dwg_db_list))
    (progn
      (setq dwg_blk_list (list (strcase (cdr (assoc 2 (tblnext "block" T))))))
      (while (setq next_blk (tblnext "block"))
        ; (if
        ;   (you may want to filter out some types of blocks here and only add those that remain to your dwg_blk_list)
        ;   nil
            (setq dwg_blk_list (append dwg_blk_list (list (strcase (cdr (assoc 2 next_blk))))))
        ; )
      )
    )
    (foreach n dwg_blk_list
      (if (member n blk_db_list)
	(progn
	  (princ "\n")
	  (PRINC (STRCAT n "="))
	  (PRINC)
          (COMMAND ".INSERT" (STRCAT n "=") "Y" nil)
	)
	(PROGN
	  (princ "\n")
	  (PRINC (STRCAT n "= Not updated! "))
	  (PRINC)
	)
      )
    )
)

 

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
Message 3 of 23

Anonymous
Not applicable

@hencoop, I was able to get the dos lib loaded and I added the search path as follows.


    (setq dwg_db_list (dos_dir "S:\Kyle\Symbols\WASH BAY\Blocks\\*.dwg")); put the location of your block database (folder) here.

 

The program runs and it works ok but it's not updating the block. I took the block directly from the folder.

 

Command: UPDBLKS

RW111W-1= Not updated!

 

Im pretty sure the doslib part is working because I got a different error before I loaded it earlier.

 

Thanks,

0 Likes
Message 4 of 23

Kyle.para
Advocate
Advocate

@hencoop

 

Just checking to see if you were around to answer my last post.  I am still having no luck getting it to update.

 

Also what do you mean by filtering out certain types of blocks?  Don't think that's the problem though.

 

Thanks!

0 Likes
Message 5 of 23

hencoop
Advisor
Advisor
Accepted solution

Hi Kyle_Para

 

You need to modify your code If this is what you have verbatim:

 (setq dwg_db_list (dos_dir "S:\Kyle\Symbols\WASH BAY\Blocks\\*.dwg")); 

Modify it to this:

 (setq dwg_db_list (dos_dir "S:\\Kyle\\Symbols\\WASH BAY\\Blocks\\*.dwg"));

Each backslash must be a double backslash.  Alternatively you may use a single forward slash in place of each double backslash.

 

Before you modify the code, run it and then type "dwg_db_list" at the VLIDE console prompt (without quotes).

It should return the list of drawings found in folder "S:\Kyle\Symbols\WASH BAY\Blocks\".

If it returns NIL then your current code does not find any of the drawings in your database folder and modifying it as mentioned should fix that.

 

The comment about filtering is that you can exclude specific blocks in the current drawing from being updated if you need to.

 

         (if
           (WCMATCH (CDR (ASSOC 2 next_blk)) "<an exact name or wildcard name to match, separate multiple match strings by commas without padding spaces>")
           nil ; If it matches then do nothing, else add it to dwg_blk_list for use
           (setq dwg_blk_list (append dwg_blk_list (list (strcase (cdr (assoc 2 next_blk))))))
         )

 

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
Message 6 of 23

Kyle.para
Advocate
Advocate

Thanks so much man it works now.

 

I also learned what VLIDE was today. Ha

 

One other question if I have another directory how do I add that into the code as well.

 

Thanks again.

0 Likes
Message 7 of 23

hencoop
Advisor
Advisor
Accepted solution

You generally should only use one folder at a time otherwise there may be duplicate names and version issues.  To be able to select any folder you should replace the following (your current version of this line)

 

    (setq dwg_db_list (dos_dir "L:\\Util\\*.dwg")); put the location of your block database (folder) here.

with

    (SETQ db_folder 
      (dos_getdir 
        "Select Database Folder"
        (IF db_folder db_folder (GETVAR "DWGPREFIX"))
        "Select the folder containing updated block definitions to use."
      )
    )
    (setq dwg_db_list (dos_dir (strcat db_folder "*.dwg")))

 (As with other (DOS_...) functions this assumes that you have DOSLIB loaded)

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
Message 8 of 23

Kyle.para
Advocate
Advocate

I have different blocks that are completely not a like so, it will be easier to have them separated in the folder.  In case they needed edited later on.

 

Were you worried that there might be 2 different blocks with the same names in different folders?

 

That won't be a problem for us, but thanks I appreciate all of your help.

0 Likes
Message 9 of 23

hencoop
Advisor
Advisor

In that case you can replace 

(IF db_folder db_folder (GETVAR "DWGPREFIX"))

or just replace (GETVAR "DWGPREFIX") inside the above line with the fixed path that contains the various subfolders  you are using.  In the first case it will always open to the parent directory of your subfolders.  In the second case it will default to the parent folder but will open the last folder selected if the function has already been run during the current session.

 

You can always browse to wherever you want.

 

You may never plan on having duplicates but if you happen to that would cause confusion that may not be readily apparent to the user.  Besides, Collecting the drawing lists from multiple folders requires much more code as you cannot select multiple folders by any method that I know of.

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
Message 10 of 23

Kyle.para
Advocate
Advocate

One thing I am trying to do is auto load your blkxport lisp with acaddoc and for some reason it's not letting me load it.

 

Do you know what's wrong?

 

;;; Example
(load "autoatt")
(load "AttRenList")
(load "addatribs")
(load "UPDBLKS")
(autoload "less_used_func" '("foo"))
(verify_arxapp_loaded "doslib21")
(verify_arxapp_loaded "doslib21x64")
(load "blkxport")

 

Also what's the difference between the command local and global on the blkxport?

 

Thanks

0 Likes
Message 11 of 23

hencoop
Advisor
Advisor

I'm not sure what the problem may be; however, if you add your files to the "Contents..." of your "Startup Suite" using the APPLOAD dialog you may have better success.

 

 StartupSuite.jpg

 

P.S. I almost always use SDI=1 (Single Document Interface) to avoid having my session memory wiped clean when a new drawing is opened.  It is necessary for my purposes to also ensure that LISPINIT is always set to 0, otherwise the memory will be wiped upon opening a new drawing in any case.

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
Message 12 of 23

Kyle.para
Advocate
Advocate

I have managed to get the browse function to work, but is it not possible for it to search through the parent folder and then find the specific .dwg in the various sub folders? Or is that what you were saying when you said that, that would cause confusion and too much programming?

 

EDIT: Sorry I re-read what you wrote and I believe you answered that question up above that you cannot do it easily.  It's ok.

 

Like if I had "S:\\Kyle\\Symbols\\

And then subfolder, subfolder, subfolder?

 

You must be doing civil work of some sort with all of those handrails etc in your program?

I use to do the same stuff before also.

 

It will not let me change SDI to 1.

0 Likes
Message 13 of 23

hencoop
Advisor
Advisor

As written, the routine expects only a folder from which it will always gather ALL of the blocks (drawings) and then update every block in that collection that is also defined in the current drawing.

 

To update single block definitions I use ULBK.LSP which depends upon the Support File Search Path (under Options>Files Tab) to find my block definitions.  Specifically, I add the path to my block library to the Support File Search Paths.  It is also important to never put copies of those blocks anywhere else in the Support File Search Path or in the current drawing folder to ensure that my external block definitions (drawings) can only be found in my block library folder.

 

It is possible to construct the routine to select an individual block (drawing) to update.  I thought your purpose was to update all blocks at once.  You cannot do both of these things in the same routine.

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
0 Likes
Message 14 of 23

Kyle.para
Advocate
Advocate

No you're correct I want it to update all of the blocks at once. That's working perfectly fine.

 

For instance I have all of my blocks in kyle//symbols.

Then I was hoping to have it update all of the blocks in the subfolders of that symbols folder that are currently being used in the DWG that I am updating.

 

IE:

kyle//symbols//

subfolders blocks, blocks1, blocks2 etc...

 

Then update all of the dwg's found in the drawings in those subfolders?

0 Likes
Message 15 of 23

hencoop
Advisor
Advisor
Accepted solution

I think I see what you are asking.

 

Try this version:

(DEFUN c:blkupdateall ()
; Select the parent folder
    (SETQ db_folder 
      (dos_getdir 
        "Select Database Folder"
        (IF db_folder db_folder (GETVAR "DWGPREFIX"))
        "Select the folder containing updated block definitions to use."
      )
    )
; get its subfolders
    (setq db_subs (dos_subdir db_folder))
; process each subfolder adding the drawings in it to blk_db_list (include the full path to each file)
    (foreach n db_subs
      (if blk_db_list
        (setq blk_db_list (append blk_db_list (mapcar '(lambda (x) (strcat db_folder n "\\" x)) (dos_dir (strcat db_folder n "\\*.dwg")))))
	(setq blk_db_list (mapcar '(lambda (x) (strcat db_folder n "\\" x)) (dos_dir (strcat db_folder n "\\*.dwg"))))
      )
    )
    (progn
; list all of the bocks defined in the drawing
      (setq dwg_blk_list (list (cdr (assoc 2 (tblnext "block" T)))))
      (while (setq next_blk (tblnext "block"))
        (if
; except exclude blocks that are from xreferenced drawings
          (WCMATCH (CDR (ASSOC 2 next_blk)) "*|*")
          nil
          (setq dwg_blk_list (append dwg_blk_list (list (cdr (assoc 2 next_blk)))))
        )
      )
    )
; extract a list of just the drawing (block) names without the extension from drawings listed in the blk_db_list
    (setq name_db_list (mapcar '(lambda (x) (strcase (nth 1 (reverse (dos_splitpath x))))) blk_db_list))
; process each block listed in dwg_blk_list updating any that have a matching name in name_db_list while ignoring case
    (foreach n dwg_blk_list
      (if (setq member_name_list (member (STRCASE n) name_db_list))
	(progn
	  (princ "\n")
	  (PRINC (STRCAT n "=" (NTH (-(LENGTH blk_db_list)(LENGTH member_name_list)) blk_db_list)))
	  (PRINC)
          (COMMAND ".INSERT" (STRCAT n "=" (NTH (-(LENGTH blk_db_list)(LENGTH member_name_list)) blk_db_list)) "Y" nil)
	)
	(PROGN
	  (princ "\n")
	  (PRINC (STRCAT n "= Not updated! "))
	  (PRINC)
	)
      )
    )
)

I also changed the string concatenated for the INSERT <name>=<drawing> because as it was it could have used a block of the same name found anywhere on the support file search path.  I realized that from discussing the Support File Search Path in a prior post in this thread.

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
Message 16 of 23

Kyle.para
Advocate
Advocate

That's exactly what I was looking for.

 

Thank you very much!

0 Likes
Message 17 of 23

hencoop
Advisor
Advisor

During my testing it would fail with certain drawing name instances.  I excluded them by name to continue with my tests.  That is the kind of thing that could happen if your subfolders have duplicate named drawings as my subfolders do.  It would take even more code at the point the drawing list is made to ensure that only one instance of any drawing name is added to the list.

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
0 Likes
Message 18 of 23

Kyle.para
Advocate
Advocate

@hencoop

 

I am running into one problem with the updating and was hoping that you might have the answer.

 

When I run the program it updates everything in the block no problem, but it does not sync the attributes the same in the editor.

When I insert the extracted block the attributes come out as they should with the correct data.

 

Anything that had the attribute editor with wrong data is not working.  I know this is a limitation in CAD in general, but is there any way to force it through?

 

Thanks, Kyle

0 Likes
Message 19 of 23

hencoop
Advisor
Advisor

I accomplished that for myself in my RBLK.LSP routine.  It uses attribute values from the old block in the new block if they have the same tag.  It will only include the default attribute value for any new attributes.  How it is done is in that routine.  If it does not work for you I'm sorry.  I don't have time right now to try to make it work for you.  You should be able to use the method I used as a starting place to make it work for you.  Note: I have specific code for replacing my custom profile grid blocks with names matching "##PPGRID*".  These have attributes for Station (100's), +, Station (10's1's) labels as well as right and left elevation labels.  (Stations are split like this so the '+' is always centered on the station line.)  The point is there is a lot of code that you may not need.  There are very few comments as it is written for my use and I don't need very many comments, sorry.

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
0 Likes
Message 20 of 23

Kyle.para
Advocate
Advocate

I'm wondering if RBLK isn't working cause I didn't download all of the required LSP.  Do you have a zip of those lisp files on that paracad site?

 

I keep copying and pasting them but it's taking forever to copy all of the required LSP for each of the routines.

 

I understand you're busy.  I am going to try and get something working lol.

0 Likes