123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- # Created: 25.03.2011
- # Copyright (c) 2011-2018, Manfred Moitzi
- # License: MIT License
- from typing import TYPE_CHECKING, Iterable, Tuple, Union, Optional, cast
- from ezdxf.lldxf.const import DXFValueError, DXFKeyError
- from .graphics import GraphicEntity, ExtendedTags, make_attribs, DXFAttr, XType
- if TYPE_CHECKING:
- from ezdxf.eztypes import Attrib, Attdef, Vertex
- _INSERT_TPL = """0
- INSERT
- 5
- 0
- 8
- 0
- 2
- BLOCKNAME
- 10
- 0.0
- 20
- 0.0
- 30
- 0.0
- 41
- 1.0
- 42
- 1.0
- 43
- 1.0
- 50
- 0.0
- """
- # IMPORTANT: Bug in AutoCAD 2010
- # attribsfollow = 0, for NO attribsfollow, does not work with ACAD 2010
- # if no attribs attached to the INSERT entity, omit attribsfollow tag
- class Insert(GraphicEntity):
- __slots__ = ()
- TEMPLATE = ExtendedTags.from_text(_INSERT_TPL)
- DXFATTRIBS = make_attribs({
- 'attribs_follow': DXFAttr(66, default=0),
- 'name': DXFAttr(2),
- 'insert': DXFAttr(10, xtype=XType.any_point),
- 'xscale': DXFAttr(41, default=1.0),
- 'yscale': DXFAttr(42, default=1.0),
- 'zscale': DXFAttr(43, default=1.0),
- 'rotation': DXFAttr(50, default=0.0),
- 'column_count': DXFAttr(70, default=1),
- 'row_count': DXFAttr(71, default=1),
- 'column_spacing': DXFAttr(44, default=0.0),
- 'row_spacing': DXFAttr(45, default=0.0),
- })
- def attribs(self) -> Iterable['Attrib']:
- """
- Iterate over all appended ATTRIB entities, yields Attrib() objects.
- """
- if self.dxf.attribs_follow == 0:
- return
- dxffactory = self.dxffactory
- handle = self.tags.link
- while handle is not None:
- entity = dxffactory.wrap_handle(handle)
- next_entity = entity.tags.link
- if next_entity is None: # found SeqEnd
- return
- else:
- yield entity
- handle = next_entity
- def place(self, insert: 'Vertex' = None,
- scale: Tuple[float, float, float] = None,
- rotation: float = None) -> 'Insert':
- """
- Set placing attributes of the INSERT entity.
- Args:
- insert: insert position as (x, y [,z]) tuple
- scale: (scale_x, scale_y, scale_z) tuple
- rotation (float): rotation angle in degrees
- Returns:
- Insert object (fluent interface)
- """
- if insert is not None:
- self.dxf.insert = insert
- if scale is not None:
- if len(scale) != 3:
- raise DXFValueError("Parameter scale has to be a (x, y, z)-tuple.")
- x, y, z = scale
- self.dxf.xscale = x
- self.dxf.yscale = y
- self.dxf.zscale = z
- if rotation is not None:
- self.dxf.rotation = rotation
- return self
- def grid(self, size: Tuple[int, int] = (1, 1), spacing: Tuple[float, float] = (1, 1)) -> 'Insert':
- """
- Set grid placing attributes of the INSERT entity.
- Args:
- size: grid size as (row_count, column_count) tuple
- spacing: distance between placing as (row_spacing, column_spacing) tuple
- Returns:
- Insert object (fluent interface)
- """
- if len(size) != 2:
- raise DXFValueError("Parameter size has to be a (row_count, column_count)-tuple.")
- if len(spacing) != 2:
- raise DXFValueError("Parameter spacing has to be a (row_spacing, column_spacing)-tuple.")
- self.dxf.row_count = size[0]
- self.dxf.column_count = size[1]
- self.dxf.row_spacing = spacing[0]
- self.dxf.column_spacing = spacing[1]
- return self
- def get_attrib(self, tag: str, search_const: bool = False) -> Optional[Union['Attrib', 'Attdef']]:
- """
- Get attached ATTRIB entity by *tag*.
- Args:
- tag: tag name
- search_const: search also const ATTDEF entities
- Returns:
- Attrib or Attdef object
- """
- for attrib in self.attribs():
- if tag == attrib.dxf.tag:
- return attrib
- if search_const and self.drawing is not None:
- block = self.drawing.blocks[self.dxf.name] # raises KeyError() if not found
- for attdef in block.get_const_attdefs():
- if tag == attdef.dxf.tag:
- return attdef
- return None
- def get_attrib_text(self, tag: str, default: str = None, search_const: bool = False) -> str:
- """
- Get content text of attached ATTRIB entity *tag*.
- Args:
- tag: tag name
- default: default value if tag is absent
- search_const: search also const ATTDEF entities
- Returns:
- content text as str
- """
- attrib = self.get_attrib(tag, search_const)
- if attrib is None:
- return default
- return attrib.dxf.text
- def has_attrib(self, tag: str, search_const: bool = False) -> bool:
- """
- Check if ATTRIB for *tag* exists.
- Args:
- tag: tag name
- search_const: search also const ATTDEF entities
- """
- return self.get_attrib(tag, search_const) is not None
- def add_attrib(self, tag: str, text: str, insert: 'Vertex' = (0, 0), dxfattribs: dict = None) -> 'Attrib':
- """
- Add new ATTRIB entity.
- Args:
- tag: tag name
- text: content text
- insert: insert position as tuple (x, y[, z])
- dxfattribs: additional DXF attributes
- Returns:
- Attrib object
- """
- dxfattribs = dxfattribs or {}
- dxfattribs['tag'] = tag
- dxfattribs['text'] = text
- dxfattribs['insert'] = insert
- attrib_entity = cast('Attrib', self._new_entity('ATTRIB', dxfattribs))
- self._append_attrib_entity(attrib_entity)
- return attrib_entity
- def _append_attrib_entity(self, entity: 'Attrib') -> None:
- has_no_attribs_attached = self.tags.link is None
- if has_no_attribs_attached or self.dxf.attribs_follow == 0:
- prev = self
- seqend = self._new_entity('SEQEND', {})
- else:
- attribs = list(self.attribs())
- prev = attribs[-1]
- seqend = self.dxffactory.wrap_handle(prev.tags.link)
- prev.tags.link = entity.dxf.handle
- entity.tags.link = seqend.dxf.handle
- self.dxf.attribs_follow = 1
- def delete_attrib(self, tag: str, ignore=False) -> None:
- """
- Delete attached ATTRIB entity `tag`, raises a KeyError exception if `tag` does not exist, set `ignore` to True,
- to ignore not existing ATTRIB entities.
-
- Args:
- tag: ATTRIB name
- ignore: False -> raise KeyError exception if `tag` does not exist
- """
- if self.dxf.attribs_follow == 0:
- if ignore:
- return
- else:
- raise DXFKeyError(tag)
- dxffactory = self.dxffactory
- handle = self.tags.link
- prev = self
- while handle is not None:
- entity = dxffactory.wrap_handle(handle)
- next_entity = entity.tags.link
- if next_entity is None: # found SeqEnd
- break
- else:
- if entity.dxf.tag == tag:
- prev.tags.link = next_entity # remove entity from linked list
- self.entitydb.delete_entity(entity)
- self._fix_attribs()
- return
- prev = entity
- handle = next_entity
- if not ignore:
- raise DXFKeyError(tag)
- def delete_all_attribs(self) -> None:
- """
- Delete all ATTRIB entities attached to the INSERT entity and the following SEQEND entity. Ignores the value
- of dxf.attribs_follow.
-
- """
- db = self.entitydb
- handle = self.tags.link
- while handle is not None:
- entity_tags = db[handle]
- db.delete_handle(handle)
- handle = entity_tags.link
- entity_tags.link = None
- self.tags.link = None
- self.dxf.attribs_follow = 0
- def _fix_attribs(self) -> None:
- if self.dxf.attribs_follow == 0:
- self.delete_all_attribs()
- else:
- handle = self.tags.link
- if handle is None:
- self.dxf.attribs.follow = 0
- return
- entity = self.dxffactory.wrap_handle(handle)
- if entity.dxftype() == 'SEQEND':
- # last attrib was deleted, only the SEQEND entity remains
- self.entitydb.delete_entity(entity)
- self.dxf.attribs_follow = 0
- self.tags.link = None
- return
- def destroy(self) -> None:
- """
- Delete all attached ATTRIB entities from entity database.
- Caution: this method is meant for internal usage.
- """
- self.delete_all_attribs()
|