pythoncountblockautocadezdxf

INSERT Block and Dynamically used content access in DXF with ezdxf


I want to make a table in python like the COUNT LIST command used in the drawing. I'm pulling the number of blocks used in the drawing, but I can't pull the number of dynamic blocks. What I want to do is to count the dynamic and normal blocks I have determined such as "DOOR, WINDOW, TOILET" and print them in the table. Thank you from now.

import ezdxf
doc = ezdxf.readfile("Python.dxf")
blocks = doc.blocks
msp = doc.modelspace()

block_usage_count = {}

inserts = msp.query('INSERT')

for insert in inserts:
    block_name = insert.dxf.name
    block_usage_count[block_name] = block_usage_count.get(block_name, 0) + 1


print("Block Name | Usage Count")
print("------------------------")
for block_name, usage_count in block_usage_count.items():
    print(f"{block_name:10} | {usage_count:11}")


Solution

  • I ended up having a similar problem and could not find any solutions. After a lot of digging I found one. I am sharing it here to help future people with the same issue.

    The thing with dynamic block in autocad is that block created from them have different names (usually starting with *). ezdxf is just a dxf reader and do not provide out of the box handling for it. But all the information to do it is actually available in ezdxf

    In the case of dynamic blocks the information about the original block is going to be saved as xdata in the created block.

    So the step needed is:

    Here how it would like:

    import ezdxf
    from collections import defaultdict
    
    doc = ezdxf.readfile("Python.dxf")
    msp = doc.modelspace()
    
    block_usage_count = defaultdict(int)
    
    inserts = msp.query('INSERT')
    
    def get_xdata_name(name):
        try:
            xdata = doc.blocks[name].block_record.get_xdata('AcDbBlockRepBTag')
        except Exception as exc:
            print(f"Error reading xdata for {name}")
            print(exc)
            return name
        for tag, value in xdata:
            if tag == 1005:
                new_name = doc.entitydb[value].dxf.name
                print(f"new name {new_name}")
        return new_name
    
    for insert in inserts:
        block_name = insert.dxf.name
        if block_name.startswith('*'):
            print(f"anonymous name {block_name}")
            name = get_xdata_name(block_name)
        else:
            name = block_name
        block_usage_count[name] += 1
    
    
    print("Block Name | Usage Count")
    print("------------------------")
    for block_name, usage_count in block_usage_count.items():
        print(f"{block_name} | {usage_count}")
    

    It is not foolproof but would handle most cases. In cases of error when trying to retrieve AcDbBlockRepBTag I advice to look in details about the xdata and see what APPID are present. Or look more in detail about the nature of the object and how it was created.

    If you are curious about why tag == 1005 check here:
    https://help.autodesk.com/view/ACD/2024/ENU/?guid=GUID-8243079C-B44F-493A-BAF7-1D11A6E6C78C