123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- # Created: 22.03.2011
- # Copyright (c) 2011-2018, Manfred Moitzi
- # License: MIT-License
- from typing import TYPE_CHECKING, cast, KeysView, ItemsView, Any, Union
- from ezdxf.lldxf.const import DXFKeyError
- from ezdxf.lldxf.packedtags import TagDict
- from ezdxf.lldxf import loader
- from .dxfobjects import DXFObject, DefSubclass, DXFAttributes, DXFAttr, ExtendedTags
- from .dxfobjects import none_subclass
- if TYPE_CHECKING:
- from collections import OrderedDict
- from ezdxf.eztypes import Tags, DXFEntity, Auditor
- @loader.register('ACDBDICTIONARYWDFLT', legacy=False)
- @loader.register('DICTIONARY', legacy=False)
- def tag_processor(tags: ExtendedTags) -> ExtendedTags:
- subclass = tags.get_subclass('AcDbDictionary')
- d = TagDict.from_tags(subclass)
- d.replace_tags(subclass)
- return tags
- _DICT_TPL = """0
- DICTIONARY
- 5
- 0
- 330
- 0
- 100
- AcDbDictionary
- 281
- 1
- """
- dictionary_subclass = DefSubclass('AcDbDictionary', {
- 'hard_owned': DXFAttr(280, default=0), # Hard-owner flag.
- # If set to 1, indicates that elements of the dictionary are to be treated as hard-owned
- 'cloning': DXFAttr(281, default=1), # Duplicate record cloning flag (determines how to merge duplicate entries):
- # 0 = not applicable
- # 1 = keep existing
- # 2 = use clone
- # 3 = <xref>$0$<name>
- # 4 = $0$<name>
- # 5 = Unmangle name
- # 3: entry name
- # 350: entry handle, some DICTIONARY objects have 360 as handle group code, this is accepted by AutoCAD but not
- # documented by the DXF reference!!! ezdxf replaces 360 codes by 350.
- })
- class DXFDictionary(DXFObject):
- __slots__ = ('_cached_dict',)
- """
- AutoCAD maintains items such as mline styles and group definitions as objects in dictionaries.
- Other applications are free to create and use their own dictionaries as they see fit. The prefix "ACAD_" is reserved
- for use by AutoCAD applications.
- DXFDictionary entries are (key, handle) values, so it can only store handles and nothing else, to store other
- values, you have to create a DXFDictionaryVar object, and store its handle.
- """
- TEMPLATE = tag_processor(ExtendedTags.from_text(_DICT_TPL))
- DXFATTRIBS = DXFAttributes(
- none_subclass,
- dictionary_subclass,
- )
- @property
- def AcDbDictinary(self) -> 'Tags':
- return self.tags.subclasses[1]
- @property
- def is_hard_owner(self) -> bool:
- return bool(self.get_dxf_attrib('hard_owned', False))
- @property
- def data(self) -> 'OrderedDict':
- try:
- return self._cached_dict
- except AttributeError:
- self._cached_dict = cast('OrderedDict', self.AcDbDictinary.get_first_value(TagDict.code))
- return self._cached_dict
- def keys(self) -> KeysView:
- """
- Generator for the dictionary's keys.
- """
- return self.data.keys()
- def items(self) -> ItemsView:
- """
- Generator for the dictionary's items as (key, value) pairs.
- """
- return self.data.items()
- def __getitem__(self, key: str) -> str:
- """
- Return the value for `key` if key is in the dictionary, else raises a `KeyError`.
- """
- return self.get(key)
- def __setitem__(self, key: str, value: str) -> None:
- """
- Add item `(key, value)` to dictionary.
- """
- return self.add(key, value)
- def __delitem__(self, key: str) -> None:
- """
- Remove element `key` from the dictionary. Raises `KeyError` if key is not contained in the dictionary.
- """
- return self.remove(key)
- def __contains__(self, key: str) -> bool:
- """
- Return True if the dictionary has `key`, else False.
- """
- return key in self.data
- def __len__(self) -> int:
- """
- Return the number of items in the dictionary.
- """
- return len(self.data)
- count = __len__
- def get(self, key: str, default: Any = DXFKeyError) -> str:
- """
- Return the value (handle) for `key` if `key` is in the dictionary, else `default` or raises a `DXFKeyError`
- for `default`=`DXFKeyError`.
- """
- try:
- return self.data[key]
- except KeyError:
- if default is DXFKeyError:
- raise DXFKeyError("KeyError: '{}'".format(key))
- else:
- return default
- get_handle = get # synonym
- def get_entity(self, key: str) -> Union['DXFEntity', str]:
- """
- Get object referenced by handle associated by `key` as wrapped entity, raises a `KeyError` if *key* not exists.
- """
- handle = self.get(key)
- if self.drawing is not None:
- return self.dxffactory.wrap_handle(handle)
- else:
- return handle
- def add(self, key: str, value: str) -> None:
- """
- Add item `(key, value)` to dictionary.
- """
- self.data[key] = value
- def remove(self, key: str) -> None:
- """
- Remove element `key` from the dictionary. Raises `DXFKeyError` if `key` not exists. Deletes hard owned DXF
- objects from OBJECTS section.
- """
- data = self.data
- if key not in data:
- raise DXFKeyError(key)
- if self.is_hard_owner:
- entity = self.get_entity(key)
- # Presumption: hard owned DXF objects always reside in the OBJECTS section
- self.drawing.objects.delete_entity(entity)
- del data[key]
- def discard(self, key: str) -> None:
- """
- Remove `key` from dictionary, if exists. Does NOT delete hard owned entities!
- """
- try:
- del self.data[key]
- except KeyError:
- pass
- def clear(self) -> None:
- """
- Removes all entries from DXFDictionary, and also deletes all hard owned DXF objects from OBJECTS section.
- """
- if self.is_hard_owner:
- self.delete_hard_owned_entries()
- self.data.clear()
- def delete_hard_owned_entries(self) -> None:
- # Presumption: hard owned DXF objects always reside in the OBJECTS section
- objects = self.drawing.objects
- wrap = self.dxffactory.wrap_handle
- for key, handle in self.items():
- objects.delete_entity(wrap(handle))
- def add_new_dict(self, key: str) -> 'DXFDictionary':
- """
- Create a new sub dictionary.
- Args:
- key: name of the sub dictionary
- """
- dxf_dict = self.drawing.objects.add_dictionary(owner=self.dxf.handle)
- self.add(key, dxf_dict.dxf.handle)
- return dxf_dict
- def get_required_dict(self, key: str) -> 'DXFDictionary':
- """
- Get DXFDictionary `key`, if exists or create a new DXFDictionary.
- """
- try:
- dxf_dict = self.get_entity(key)
- except DXFKeyError:
- dxf_dict = self.add_new_dict(key)
- return dxf_dict
- def audit(self, auditor: 'Auditor') -> None:
- auditor.check_handles_exists(self, handles=self.data.values())
- def destroy(self) -> None:
- if self.get_dxf_attrib('hard_owned', False):
- self.delete_hard_owned_entries()
- _DICT_WITH_DEFAULT_CLS = """0
- CLASS
- 1
- ACDBDICTIONARYWDFLT
- 2
- AcDbDictionaryWithDefault
- 3
- ObjectDBX Classes
- 90
- 0
- 91
- 0
- 280
- 0
- 281
- 0
- """
- _DICT_WITH_DEFAULT_TPL = """0
- ACDBDICTIONARYWDFLT
- 5
- 0
- 330
- 0
- 100
- AcDbDictionary
- 281
- 1
- 100
- AcDbDictionaryWithDefault
- 340
- 0
- """
- class DXFDictionaryWithDefault(DXFDictionary):
- TEMPLATE = tag_processor(ExtendedTags.from_text(_DICT_WITH_DEFAULT_TPL))
- CLASS = (ExtendedTags.from_text(_DICT_WITH_DEFAULT_CLS))
- DXFATTRIBS = DXFAttributes(
- none_subclass,
- dictionary_subclass,
- DefSubclass('AcDbDictionaryWithDefault', {
- 'default': DXFAttr(340),
- }),
- )
- def get(self, key: str, default: Any = DXFKeyError) -> str:
- """
- Return the value for `key` if exists else returns the predefined dictionary wide `default` value. Parameter
- `default` is always ignored!
- """
- return super(DXFDictionaryWithDefault, self).get(key, default=self.dxf.default)
- _DICTIONARYVAR_CLS = """0
- CLASS
- 1
- DICTIONARYVAR
- 2
- AcDbDictionaryVar
- 3
- ObjectDBX Classes
- 90
- 0
- 91
- 0
- 280
- 0
- 281
- 0
- """
- _DICTIONARYVAR_TPL = """0
- DICTIONARYVAR
- 5
- 0
- 330
- 0
- 102
- DictionaryVariables
- 280
- 0
- 1
- """
- class DXFDictionaryVar(DXFObject):
- """
- DICTIONARYVAR objects are used by AutoCAD as a means to store named values in the database for setvar / getvar
- purposes without the need to add entries to the DXF HEADER section. System variables that are stored as
- DICTIONARYVAR objects are the following:
- - DEFAULTVIEWCATEGORY
- - DIMADEC
- - DIMASSOC
- - DIMDSEP
- - DRAWORDERCTL
- - FIELDEVAL
- - HALOGAP
- - HIDETEXT
- - INDEXCTL
- - INDEXCTL
- - INTERSECTIONCOLOR
- - INTERSECTIONDISPLAY
- - MSOLESCALE
- - OBSCOLOR
- - OBSLTYPE
- - OLEFRAME
- - PROJECTNAME
- - SORTENTS
- - UPDATETHUMBNAIL
- - XCLIPFRAME
- - XCLIPFRAME
- """
- TEMPLATE = ExtendedTags.from_text(_DICTIONARYVAR_TPL)
- CLASS = ExtendedTags.from_text(_DICTIONARYVAR_CLS)
- DXFATTRIBS = DXFAttributes(
- none_subclass,
- DefSubclass('DictionaryVariables', {
- 'schema': DXFAttr(280, default=0), # Object schema number (currently set to 0)
- 'value': DXFAttr(1),
- }),
- )
|