Files
stepanalyser/.venv/lib/python3.12/site-packages/ezdxf/entities/objectcollection.py
Christian Anetzberger a197de9456 initial
2026-01-22 20:23:51 +01:00

194 lines
5.9 KiB
Python

# Copyright (c) 2018-2023 Manfred Moitzi
# License: MIT License
from __future__ import annotations
from typing import (
TYPE_CHECKING,
Iterator,
cast,
Optional,
TypeVar,
Generic,
)
from ezdxf.lldxf.const import (
DXFValueError,
DXFKeyError,
INVALID_NAME_CHARACTERS,
)
from ezdxf.lldxf.validator import make_table_key, is_valid_table_name
if TYPE_CHECKING:
from ezdxf.document import Drawing
from ezdxf.entities import DXFObject, Dictionary
def validate_name(name: str) -> str:
name = name[:255]
if not is_valid_table_name(name):
raise DXFValueError(
f"table name '{name}' contains invalid characters: {INVALID_NAME_CHARACTERS}"
)
return name
T = TypeVar("T", bound="DXFObject")
class ObjectCollection(Generic[T]):
"""
Note:
ObjectCollections may contain entries where the name stored in the entity as
"name" attribute diverges from the key in the DICTIONARY object e.g. MLEADERSTYLE
collection may have entries for "Standard" and "Annotative" but both MLEADERSTYLE
objects have the name "Standard".
"""
def __init__(
self,
doc: Drawing,
dict_name: str = "ACAD_MATERIAL",
object_type: str = "MATERIAL",
):
self.doc: Drawing = doc
self.object_dict_name = dict_name
self.object_type: str = object_type
self.object_dict: Dictionary = doc.rootdict.get_required_dict(dict_name)
def update_object_dict(self) -> None:
self.object_dict = self.doc.rootdict.get_required_dict(self.object_dict_name)
def create_required_entries(self) -> None:
pass
def __iter__(self) -> Iterator[tuple[str, T]]:
return self.object_dict.items()
def __len__(self) -> int:
return len(self.object_dict)
def __contains__(self, name: str) -> bool:
return self.has_entry(name)
def __getitem__(self, name: str) -> T:
entry = self.get(name)
if entry is None:
raise DXFKeyError(name)
return entry
@property
def handle(self) -> str:
"""Returns the DXF handle of the DICTIONARY object."""
return self.object_dict.dxf.handle
@property
def is_hard_owner(self) -> bool:
"""Returns ``True`` if the collection is hard owner of entities.
Hard owned entities will be destroyed by deleting the dictionary.
"""
return self.object_dict.is_hard_owner
def has_entry(self, name: str) -> bool:
return self.get(name) is not None
def is_unique_name(self, name: str) -> bool:
name = make_table_key(name)
for entry_name in self.object_dict.keys():
if make_table_key(entry_name) == name:
return False
return True
def get(self, name: str, default: Optional[T] = None) -> Optional[T]:
"""Get object by name. Object collection entries are case-insensitive.
Args:
name: object name as string
default: default value
"""
name = make_table_key(name)
for entry_name, obj in self.object_dict.items():
if make_table_key(entry_name) == name:
return obj
return default
def new(self, name: str) -> T:
"""Create a new object of type `self.object_type` and store its handle
in the object manager dictionary. Object collection entry names are
case-insensitive and limited to 255 characters.
Args:
name: name of new object as string
Returns:
new object of type `self.object_type`
Raises:
DXFValueError: if object name already exist or is invalid
(internal API)
"""
name = validate_name(name)
if not self.is_unique_name(name):
raise DXFValueError(f"{self.object_type} entry {name} already exists.")
return self._new(name, dxfattribs={"name": name})
def duplicate_entry(self, name: str, new_name: str) -> T:
"""Returns a new table entry `new_name` as copy of `name`,
replaces entry `new_name` if already exist.
Raises:
DXFValueError: `name` does not exist
"""
entry = self.get(name)
if entry is None:
raise DXFValueError(f"entry '{name}' does not exist")
new_name = validate_name(new_name)
# remove existing entry
existing_entry = self.get(new_name)
if existing_entry is not None:
self.delete(new_name)
entitydb = self.doc.entitydb
if entitydb:
new_entry = entitydb.duplicate_entity(entry)
else: # only for testing!
new_entry = entry.copy()
if new_entry.dxf.is_supported("name"):
new_entry.dxf.name = new_name
self.object_dict.add(new_name, new_entry) # type: ignore
owner_handle = self.object_dict.dxf.handle
new_entry.dxf.owner = owner_handle
new_entry.set_reactors([owner_handle])
return new_entry # type: ignore
def _new(self, name: str, dxfattribs: dict) -> T:
objects = self.doc.objects
assert objects is not None
owner = self.object_dict.dxf.handle
dxfattribs["owner"] = owner
obj = objects.add_dxf_object_with_reactor(
self.object_type, dxfattribs=dxfattribs
)
self.object_dict.add(name, obj)
return cast(T, obj)
def delete(self, name: str) -> None:
objects = self.doc.objects
assert objects is not None
obj = self.get(name) # case insensitive
if obj is not None:
# The underlying DICTIONARY is not case-insensitive implemented,
# get real object name if available
if obj.dxf.is_supported("name"):
name = obj.dxf.get("name", name)
self.object_dict.discard(name)
objects.delete_entity(obj)
def clear(self) -> None:
"""Delete all entries."""
self.object_dict.clear()