Tutorial for Blocks

If you are not familiar with the concept of blocks, please read this first: Concept of Blocks

Create a Block

Blocks are managed as BlockLayout objects by the BlocksSection object, every drawing has only one blocks section referenced by attribute Drawing.blocks.

import ezdxf
import random  # needed for random placing points


def get_random_point():
    """Returns random x, y coordinates."""
    x = random.randint(-100, 100)
    y = random.randint(-100, 100)
    return x, y


# Create a new drawing in the DXF format of AutoCAD 2010
doc = ezdxf.new('R2010')

# Create a block with the name 'FLAG'
flag = doc.blocks.new(name='FLAG')

# Add DXF entities to the block 'FLAG'.
# The default base point (= insertion point) of the block is (0, 0).
flag.add_lwpolyline([(0, 0), (0, 5), (4, 3), (0, 3)])  # the flag symbol as 2D polyline
flag.add_circle((0, 0), .4, dxfattribs={'color': 2})  # mark the base point with a circle

Block References (Insert)

A block reference can be created by adding an Insert entity to any of these layout types:

A block reference can be scaled and rotated individually. Lets add some random flags to the modelspace:

# Get the modelspace of the drawing.
msp = doc.modelspace()

# Get 50 random placing points.
placing_points = [get_random_point() for _ in range(50)]

for point in placing_points:
    # Every flag has a different scaling and a rotation of -15 deg.
    random_scale = 0.5 + random.random() * 2.0
    # Add a block reference to the block named 'FLAG' at the coordinates 'point'.
    msp.add_blockref('FLAG', point, dxfattribs={
        'xscale': random_scale,
        'yscale': random_scale,
        'rotation': -15
    })

# Save the drawing.
doc.saveas("blockref_tutorial.dxf")

Query all block references of block FLAG:

for flag_ref in msp.query('INSERT[name=="FLAG"]'):
    print(str(flag_ref))

When adding a block reference to a layout with different units, the scaling factor between these units should be applied as scaling attributes (xscale, …) e.g. modelspace in meters and block in centimeters, xscale has to be 0.01.

Block Attributes

A block attribute (Attrib) is a text annotation attached to a block reference with an associated tag. Attributes are often used to add information to blocks which can be evaluated and exported by CAD applications. An attribute can be added to a block reference by the Insert.add_attrib() method, the ATTRIB entity is geometrically not related to the block reference, so insertion point, rotation and scaling of the attribute have to be calculated by the user, but helper tools for that do exist.

Using Attribute Definitions

Another way to add attributes to block references is using attribute templates (AttDef). First create the attribute definition in the block definition, then add the block reference by add_blockref() and attach and fill attributes automatically by the add_auto_attribs() method to the block reference. This method has the advantage that all attributes are placed relative to the block base point with the same rotation and scaling as the block reference, but non-uniform scaling is not handled very well.

The add_auto_blockref() method handles non-uniform scaling better by wrapping the block reference and its attributes into an anonymous block and let the CAD application do the transformation work. This method has the disadvantage of a more complex evaluation of attached attributes

Using attribute definitions (AttDef templates):

# Define some attributes for the block 'FLAG', placed relative
# to the base point, (0, 0) in this case.
flag.add_attdef('NAME', (0.5, -0.5), dxfattribs={'height': 0.5, 'color': 3})
flag.add_attdef('XPOS', (0.5, -1.0), dxfattribs={'height': 0.25, 'color': 4})
flag.add_attdef('YPOS', (0.5, -1.5), dxfattribs={'height': 0.25, 'color': 4})

# Get another 50 random placing points.
placing_points = [get_random_point() for _ in range(50)]

for number, point in enumerate(placing_points):
    # values is a dict with the attribute tag as item-key and
    # the attribute text content as item-value.
    values = {
        'NAME': "P(%d)" % (number + 1),
        'XPOS': "x = %.3f" % point[0],
        'YPOS': "y = %.3f" % point[1]
    }

    # Every flag has a different scaling and a rotation of +15 deg.
    random_scale = 0.5 + random.random() * 2.0
    blockref = msp.add_blockref('FLAG', point, dxfattribs={
        'rotation': 15
    }).set_scale(random_scale)
    blockref.add_auto_attribs(values)

# Save the drawing.
doc.saveas("auto_blockref_tutorial.dxf")

Get/Set Attributes of Existing Block References

See the howto: Get/Set Block Reference Attributes

Evaluate Wrapped Block References

As mentioned above the evaluation of block references wrapped into anonymous blocks is complex:

# Collect all anonymous block references starting with '*U'
anonymous_block_refs = modelspace.query('INSERT[name ? "^\*U.+"]')

# Collect the references of the 'FLAG' block
flag_refs = []
for block_ref in anonymous_block_refs:
    # Get the block layout of the anonymous block
    block = doc.blocks.get(block_ref.dxf.name)
    # Find all block references to 'FLAG' in the anonymous block
    flag_refs.extend(block.query('INSERT[name=="FLAG"]'))

# Evaluation example: collect all flag names.
flag_numbers = [
    flag.get_attrib_text("NAME")
    for flag in flag_refs
    if flag.has_attrib("NAME")
]

print(flag_numbers)

Exploding Block References

This is an advanced feature and the results may not be perfect. A non-uniform scaling lead to incorrect results for text entities (TEXT, MTEXT, ATTRIB) and some other entities like HATCH with circular- or elliptic path segments. The “exploded” entities are added to the same layout as the block reference by default.

for flag_ref in msp.query('INSERT[name=="FLAG"]'):
    flag_ref.explode()

Examine Entities of Block References

To just examine the content entities of a block reference use the virtual_entities() method. This methods yields “virtual” entities with properties identical to “exploded” entities but they are not stored in the entity database, have no handle and are not assigned to any layout.

for flag_ref in msp.query('INSERT[name=="FLAG"]'):
    for entity in flag_ref.virtual_entities():
        if entity.dxftype() == "LWPOLYLINE":
            print(f"Found {str(entity)}.")