コード例 #1
0
ファイル: entitydb.py プロジェクト: billhu0228/ezdxf
class EntityDB:
    """ A simple key/entity database.

    Every entity/object, except tables and sections, are represented as
    DXFEntity or inherited types, this entities are stored in the
    DXF document database, database-key is the `handle` as string.

    """
    def __init__(self):
        self._database: Dict[str, DXFEntity] = {}
        # dxf entities to delete as set of handles
        self._trashcan: Set[str] = set()
        self.handles = HandleGenerator()
        self.locked = False  # for debugging

    def __getitem__(self, handle: str) -> DXFEntity:
        """ Get entity by `handle`, does not filter destroyed entities nor
        entities in the trashcan.
        """
        return self._database[handle]

    def __setitem__(self, handle: str, entity: DXFEntity) -> None:
        """ Set `entity` for `handle`. """
        assert isinstance(handle, str), type(handle)
        assert isinstance(entity, DXFEntity), type(entity)
        assert entity.is_alive, 'Can not store destroyed entity.'
        if self.locked:
            raise DXFInternalEzdxfError('Locked entity database.')

        if handle == '0' or not is_valid_handle(handle):
            raise ValueError(f'Invalid handle {handle}.')
        self._database[handle] = entity

    def __delitem__(self, handle: str) -> None:
        """ Delete entity by `handle`. Removes entity only from database, does
        not destroy the entity.
        """
        if self.locked:
            raise DXFInternalEzdxfError('Locked entity database.')
        del self._database[handle]

    def __contains__(self, handle: str) -> bool:
        """ ``True`` if database contains `handle`. """
        if handle is None:
            return False
        assert isinstance(handle, str), type(handle)
        return handle in self._database

    def __len__(self) -> int:
        """ Count of database items. """
        return len(self._database)

    def __iter__(self) -> Iterable[str]:
        """ Iterable of all handles, does filter destroyed entities but not
        entities in the trashcan.
        """
        return self.keys()

    def get(self, handle: str) -> Optional[DXFEntity]:
        """ Returns entity for `handle` or ``None`` if no entry for `handle`
        exist, does not filter destroyed entities nor entities in the trashcan.
        """
        return self._database.get(handle)

    def next_handle(self) -> str:
        """ Returns next unique handle."""
        while True:
            handle = self.handles.next()
            if handle not in self._database:  # you can not trust $HANDSEED value
                return handle

    def keys(self) -> Iterable[str]:
        """ Iterable of all handles, does filter destroyed entities but not
        entities in the trashcan.
        """
        return (handle for handle, entity in self.items())

    def values(self) -> Iterable[DXFEntity]:
        """ Iterable of all entities, does filter destroyed entities but not
        entities in the trashcan.
        """
        return (entity for handle, entity in self.items())

    def items(self) -> Iterable[Tuple[str, DXFEntity]]:
        """ Iterable of all (handle, entities) pairs, does filter destroyed
        entities but not entities in the trashcan.  """
        return ((handle, entity) for handle, entity in self._database.items()
                if entity.is_alive)

    def add(self, entity: DXFEntity) -> None:
        """ Add `entity` to database, assigns a new handle to the `entity`
        if :attr:`entity.dxf.handle` is ``None``. Adding the same entity
        multiple times is possible, but creates only a single entry.

        """
        if entity.dxftype() in DATABASE_EXCLUDE:
            if entity.dxf.handle is not None:
                # store entities with handles (TABLE, maybe others) to avoid
                # reassigning of its handle
                self[entity.dxf.handle] = entity
            return
        handle: str = entity.dxf.handle
        if handle is None:
            handle = self.next_handle()
            # update_handle() requires the objects section to update the owner
            # handle of the extension dictionary, but this is no problem at
            # file loading, all entities have handles, and DXF R12 (without handles)
            # have no extension dictionaries.
            entity.update_handle(handle)
        self[handle] = entity

        # add sub entities like ATTRIB, VERTEX and SEQEND to database
        if isinstance(entity, LinkedEntities):
            entity.add_sub_entities_to_entitydb(self)

    def delete_entity(self, entity: DXFEntity) -> None:
        """ Removes `entity` from database and destroys the `entity`. """
        if entity.is_alive:
            del self[entity.dxf.handle]
            entity.destroy()

    def discard(self, entity: 'DXFEntity') -> None:
        """ Discard entity from database without destroying the entity.
        """
        if entity.is_alive:
            if isinstance(entity, LinkedEntities):
                entity.process_sub_entities(lambda e: self.discard(e))

            handle = entity.dxf.handle
            try:
                del self._database[handle]
                entity.dxf.handle = None
            except KeyError:
                pass

    def duplicate_entity(self, entity: DXFEntity) -> DXFEntity:
        """
        Duplicates `entity` and its sub entities (VERTEX, ATTRIB, SEQEND) and
        store them with new handles in the entity database. Graphical entities
        have to be added to a layout by :meth:`~ezdxf.layouts.BaseLayout.add_entity`,
        for other DXF entities: DON'T DUPLICATE THEM.

        To import DXF entities into another drawing use the
        :class:`~ezdxf.addons.importer.Importer` add-on.

        An existing owner tag is not changed because this is not the domain of
        the :class:`EntityDB` class, will be set by adding the duplicated entity
        to a layout.

        This is not a deep copy in the meaning of Python, because handles and
        links are changed.

        """
        new_entity: DXFEntity = entity.copy()
        new_entity.dxf.handle = self.next_handle()
        factory.bind(new_entity, entity.doc)
        return new_entity

    def audit(self, auditor: 'Auditor'):
        """ Restore database integrity:

        - restore database entries with modified handles (key != entity.dxf.handle)
        - remove entities with invalid handles
        - empty trashcan - destroy all entities in the trashcan
        - removes destroyed database entries (purge)

        """
        assert self.locked is False, 'Database is locked!'
        add_entities = []
        # Destroyed entities already filtered in self.items()!
        for handle, entity in self.items():
            if not is_valid_handle(handle):
                auditor.fixed_error(
                    code=AuditError.INVALID_ENTITY_HANDLE,
                    message=f'Removed entity {entity.dxftype()} with invalid '
                    f'handle "{handle}" from entity database.',
                )
                self.trash(handle)
            if handle != entity.dxf.get('handle'):
                # database handle != stored entity handle
                # prevent entity from being destroyed:
                self._database[handle] = None
                self.trash(handle)
                add_entities.append(entity)

        # Destroy entities in trashcan:
        self.empty_trashcan()
        # Remove all destroyed entities from database:
        self.purge()

        for entity in add_entities:
            handle = entity.dxf.get('handle')
            if handle is None:
                auditor.fixed_error(
                    code=AuditError.INVALID_ENTITY_HANDLE,
                    message=f'Removed entity {entity.dxftype()} without handle '
                    f'from entity database.',
                )
                continue
            if not is_valid_handle(handle) or handle == '0':
                auditor.fixed_error(
                    code=AuditError.INVALID_ENTITY_HANDLE,
                    message=f'Removed entity {entity.dxftype()} with invalid '
                    f'handle "{handle}" from entity database.',
                )
                continue
            self[handle] = entity

    def trash(self, handle: str) -> None:
        """ Put handle into trashcan to delete an entity later, required while
        iterating the database.
        """
        self._trashcan.add(handle)

    def empty_trashcan(self) -> None:
        """ Remove handles in trashcan from database and destroy entities if
        still alive.
        """
        # Important: operate on underlying data structure:
        db = self._database
        for handle in self._trashcan:
            entity = db.get(handle)
            if entity and entity.is_alive:
                entity.destroy()

            if handle in db:
                del db[handle]

        self._trashcan.clear()

    def purge(self) -> None:
        """ Remove all destroyed entities from database, but does not empty the
        trashcan.
        """
        # Important: operate on underlying data structure:
        db = self._database
        dead_handles = [
            handle for handle, entity in db.items() if not entity.is_alive
        ]
        for handle in dead_handles:
            del db[handle]

    def dxf_types_in_use(self) -> Set[str]:
        return set(entity.dxftype() for entity in self.values())
コード例 #2
0
ファイル: test_018_handle.py プロジェクト: Rahulghuge94/ezdxf
def test_init_and_reset_rejects_invalid_ints():
    with pytest.raises(ValueError):
        HandleGenerator("XXXX")
    handles = HandleGenerator()
    with pytest.raises(ValueError):
        handles.reset("xyz")
コード例 #3
0
ファイル: test_018_handle.py プロジェクト: Rahulghuge94/ezdxf
def test_next():
    handles = HandleGenerator("100")
    assert "100" == handles.next()
コード例 #4
0
class EntityDB:
    """ A simple key/entity database.

    Every entity/object, except tables and sections, are represented as DXFEntity or inherited types, this entities are
    stored in the drawing-associated database, database-key is the `handle` as string (group code == 5 or 105).

    """

    def __init__(self):
        self._database = {}
        self.handles = HandleGenerator()

    def __getitem__(self, handle: str) -> DXFEntity:
        """ Get entity by `handle`. """
        return self._database[handle]

    def __setitem__(self, handle: str, entity: DXFEntity) -> None:
        """ Set `entity` for `handle`. """
        self._database[handle] = entity

    def __delitem__(self, handle: str) -> None:
        """ Delete entity by `handle`. Removes entity only from database, does not destroy the entity. """
        del self._database[handle]

    def __contains__(self, item: Union[str, DXFEntity]) -> bool:
        """ ``True`` if database contains `item`, `item` can be a handle or an entity. """
        if isinstance(item, str):
            handle = item
        else:
            handle = item.dxf.handle
        return handle in self._database

    def __len__(self) -> int:
        """ Count of database items. """
        return len(self._database)

    def __iter__(self) -> Iterable[str]:
        """ Iterable of all handles. """
        return iter(self._database.keys())

    def get(self, handle: str) -> Optional[DXFEntity]:
        """ Returns entity for `handle` or ``None`` if no entry for `handle` exist. """
        try:
            return self.__getitem__(handle)
        except KeyError:  # internal exception
            return None

    def next_handle(self) -> str:
        """ Returns next unique handle."""
        while True:
            handle = self.handles.next()
            if handle not in self._database:  # you can not trust $HANDSEED value
                return handle

    def keys(self) -> Iterable[str]:
        """ Iterable of all handles. """
        return self._database.keys()

    def values(self) -> Iterable[DXFEntity]:
        """ Iterable of all entities. """
        return self._database.values()

    def items(self) -> Iterable[Tuple[str, DXFEntity]]:
        """ Iterable of all (handle, entities) pairs. """
        return self._database.items()

    def add(self, entity: DXFEntity) -> None:
        """ Add `entity` to database, assigns a new handle to the `entity` if :attr:`entity.dxf.handle` is ``None``. """
        if entity.dxftype() in DATABASE_EXCLUDE:
            if entity.dxf.handle is not None:
                # store entities with handles (TABLE, maybe others) to avoid reassigning of its handle
                self[entity.dxf.handle] = entity
            return
        handle = entity.dxf.handle  # type: str
        if handle is None:
            handle = self.next_handle()
            # update_handle() requires the objects section to update the owner handle of the extension dictionary,
            # but this is no problem at file loading, all entities have handles, and DXF R12 (without handles) have no
            # extension dictionaries.
            entity.update_handle(handle)
        self[handle] = entity

        # add sub entities like ATTRIB, VERTEX and SEQEND to database
        # only INSERT and POLYLINE using this feature
        if hasattr(entity, 'add_sub_entities_to_entitydb'):
            entity.add_sub_entities_to_entitydb()

    def delete_entity(self, entity: DXFEntity) -> None:
        """ Removes `entity` from database and destroys the `entity`. """
        del self[entity.dxf.handle]
        entity.destroy()

    def duplicate_entity(self, entity: DXFEntity) -> DXFEntity:
        """
        Duplicates `entity` and its sub entities (VERTEX, ATTRIB, SEQEND) and store them with new handles in the
        drawing database. This is the recommend method to duplicate DXF entities in a drawing. Graphical entities
        have to be added to a layout by :meth:`~ezdxf.layouts.BaseLayout.add_entity`, for other DXF entities:
        DON'T DUPLICATE THEM.

        To import DXF entities into another drawing use the :class:`~ezdxf.addons.importer.Importer` add-on.

        An existing owner tag is not changed because this is not the domain of the :class:`EntityDB` class, will be set
        by adding the duplicated entity to a layout.

        This is not a deep copy in the meaning of Python, because handles and links are changed.

        """
        new_entity = entity.copy()  # type: DXFEntity
        new_entity.dxf.handle = self.next_handle()
        self.add(new_entity)
        return new_entity
コード例 #5
0
ファイル: test_018_handle.py プロジェクト: yening2020/ezdxf
def test_seed():
    handles = HandleGenerator('200')
    handles.next()
    assert '201' == str(handles)
コード例 #6
0
ファイル: entitydb.py プロジェクト: yanbin-ha/ezdxf
class EntityDB:
    """ A simple key/entity database.

    Every entity/object, except tables and sections, are represented as
    DXFEntity or inherited types, this entities are stored in the
    DXF document database, database-key is the `handle` as string.

    """

    class Trashcan:
        """ Store handles to entities which should be deleted later. """

        def __init__(self, db: 'EntityDB'):
            self._database = db._database
            self._handles: Set[str] = set()

        def add(self, handle: str):
            """ Put handle into trashcan to delete the entity later, this is
            required for deleting entities while iterating the database.
            """
            self._handles.add(handle)

        def clear(self):
            """ Remove handles in trashcan from database and destroy entities if
            still alive.
            """
            db = self._database
            for handle in self._handles:
                entity = db.get(handle)
                if entity and entity.is_alive:
                    entity.destroy()

                if handle in db:
                    del db[handle]

            self._handles.clear()

    def __init__(self):
        self._database: Dict[str, DXFEntity] = {}
        # DXF handles of entities to delete later:
        self.handles = HandleGenerator()
        self.locked: bool = False  # used only for debugging

    def __getitem__(self, handle: str) -> DXFEntity:
        """ Get entity by `handle`, does not filter destroyed entities nor
        entities in the trashcan.
        """
        return self._database[handle]

    def __setitem__(self, handle: str, entity: DXFEntity) -> None:
        """ Set `entity` for `handle`. """
        assert isinstance(handle, str), type(handle)
        assert isinstance(entity, DXFEntity), type(entity)
        assert entity.is_alive, 'Can not store destroyed entity.'
        if self.locked:
            raise DXFInternalEzdxfError('Locked entity database.')

        if handle == '0' or not is_valid_handle(handle):
            raise ValueError(f'Invalid handle {handle}.')
        self._database[handle] = entity

    def __delitem__(self, handle: str) -> None:
        """ Delete entity by `handle`. Removes entity only from database, does
        not destroy the entity.
        """
        if self.locked:
            raise DXFInternalEzdxfError('Locked entity database.')
        del self._database[handle]

    def __contains__(self, handle: str) -> bool:
        """ ``True`` if database contains `handle`. """
        if handle is None:
            return False
        assert isinstance(handle, str), type(handle)
        return handle in self._database

    def __len__(self) -> int:
        """ Count of database items. """
        return len(self._database)

    def __iter__(self) -> Iterable[str]:
        """ Iterable of all handles, does filter destroyed entities but not
        entities in the trashcan.
        """
        return self.keys()

    def get(self, handle: str) -> Optional[DXFEntity]:
        """ Returns entity for `handle` or ``None`` if no entry exist, does
        not filter destroyed entities.
        """
        return self._database.get(handle)

    def next_handle(self) -> str:
        """ Returns next unique handle."""
        while True:
            handle = self.handles.next()
            if handle not in self._database:
                return handle

    def keys(self) -> Iterable[str]:
        """ Iterable of all handles, does filter destroyed entities.
        """
        return (handle for handle, entity in self.items())

    def values(self) -> Iterable[DXFEntity]:
        """ Iterable of all entities, does filter destroyed entities.
        """
        return (entity for handle, entity in self.items())

    def items(self) -> Iterable[Tuple[str, DXFEntity]]:
        """ Iterable of all (handle, entities) pairs, does filter destroyed
        entities.
        """
        return (
            (handle, entity) for handle, entity in self._database.items()
            if entity.is_alive
        )

    def add(self, entity: DXFEntity) -> None:
        """ Add `entity` to database, assigns a new handle to the `entity`
        if :attr:`entity.dxf.handle` is ``None``. Adding the same entity
        multiple times is possible and creates only a single database entry.

        """
        if entity.dxftype() in DATABASE_EXCLUDE:
            if entity.dxf.handle is not None:
                # Mark existing entity handle as used to avoid
                # reassigning the same handle again.
                self[entity.dxf.handle] = entity
            return
        handle: str = entity.dxf.handle
        if handle is None:
            handle = self.next_handle()
            entity.update_handle(handle)
        self[handle] = entity

        # Add sub entities ATTRIB, VERTEX and SEQEND to database:
        # Add linked MTEXT columns to database:
        if hasattr(entity, 'add_sub_entities_to_entitydb'):
            entity.add_sub_entities_to_entitydb(self)

    def delete_entity(self, entity: DXFEntity) -> None:
        """ Remove `entity` from database and destroy the `entity`. """
        if entity.is_alive:
            del self[entity.dxf.handle]
            entity.destroy()

    def discard(self, entity: DXFEntity) -> None:
        """ Discard `entity` from database without destroying the `entity`. """
        if entity.is_alive:
            if hasattr(entity, 'process_sub_entities'):
                entity.process_sub_entities(lambda e: self.discard(e))

            handle = entity.dxf.handle
            try:
                del self._database[handle]
                entity.dxf.handle = None
            except KeyError:
                pass

    def duplicate_entity(self, entity: DXFEntity) -> DXFEntity:
        """ Duplicates `entity` and its sub entities (VERTEX, ATTRIB, SEQEND)
        and store them with new handles in the entity database.
        Graphical entities have to be added to a layout by
        :meth:`~ezdxf.layouts.BaseLayout.add_entity`.

        To import DXF entities from another drawing use the
        :class:`~ezdxf.addons.importer.Importer` add-on.

        A new owner handle will be set by adding the duplicated entity to a
        layout.

        """
        new_entity: DXFEntity = entity.copy()
        new_entity.dxf.handle = self.next_handle()
        factory.bind(new_entity, entity.doc)
        return new_entity

    def audit(self, auditor: 'Auditor'):
        """ Restore database integrity:

        - restore database entries with modified handles (key != entity.dxf.handle)
        - remove entities with invalid handles
        - empty trashcan - destroy all entities in the trashcan
        - removes destroyed database entries (purge)

        """
        assert self.locked is False, 'Database is locked!'
        add_entities = []

        with self.trashcan() as trash:
            for handle, entity in self.items():
                # Destroyed entities are already filtered!
                if not is_valid_handle(handle):
                    auditor.fixed_error(
                        code=AuditError.INVALID_ENTITY_HANDLE,
                        message=f'Removed entity {entity.dxftype()} with invalid '
                                f'handle "{handle}" from entity database.',
                    )
                    trash.add(handle)
                if handle != entity.dxf.get('handle'):
                    # database handle != stored entity handle
                    # prevent entity from being destroyed:
                    self._database[handle] = None
                    trash.add(handle)
                    add_entities.append(entity)

        # Remove all destroyed entities from database:
        self.purge()

        for entity in add_entities:
            handle = entity.dxf.get('handle')
            if handle is None:
                auditor.fixed_error(
                    code=AuditError.INVALID_ENTITY_HANDLE,
                    message=f'Removed entity {entity.dxftype()} without handle '
                            f'from entity database.',
                )
                continue
            if not is_valid_handle(handle) or handle == '0':
                auditor.fixed_error(
                    code=AuditError.INVALID_ENTITY_HANDLE,
                    message=f'Removed entity {entity.dxftype()} with invalid '
                            f'handle "{handle}" from entity database.',
                )
                continue
            self[handle] = entity

    def new_trashcan(self) -> 'EntityDB.Trashcan':
        """ Returns a new trashcan, empty trashcan manually by: :
        func:`Trashcan.clear()`.
        """
        return EntityDB.Trashcan(self)

    @contextmanager
    def trashcan(self) -> 'EntityDB.Trashcan':
        """ Returns a new trashcan in context manager mode, trashcan will be
        emptied when leaving context.
        """
        trashcan_ = self.new_trashcan()
        yield trashcan_
        # try ... finally is not required, in case of an exception the database
        # is maybe already in an unreliable state.
        trashcan_.clear()

    def purge(self) -> None:
        """ Remove all destroyed entities from database, but does not empty the
        trashcan.
        """
        # Important: operate on underlying data structure:
        db = self._database
        dead_handles = [
            handle for handle, entity in db.items()
            if not entity.is_alive
        ]
        for handle in dead_handles:
            del db[handle]

    def dxf_types_in_use(self) -> Set[str]:
        return set(entity.dxftype() for entity in self.values())
コード例 #7
0
ファイル: entitydb.py プロジェクト: vshu3000/ezdxf
class EntityDB:
    """ A simple key/entity database.

    Every entity/object, except tables and sections, are represented as DXFEntity or inherited types, this entities are
    stored in the drawing-associated database, database-key is the `handle` as string (group code == 5 or 105).

    """
    def __init__(self):
        self._database = {}
        self.handles = HandleGenerator()
        self.locked = False  # for debugging

    def __getitem__(self, handle: str) -> DXFEntity:
        """ Get entity by `handle`. """
        return self._database[handle]

    def __setitem__(self, handle: str, entity: DXFEntity) -> None:
        """ Set `entity` for `handle`. """
        assert isinstance(handle, str), type(handle)
        assert isinstance(entity, DXFEntity), type(entity)
        if self.locked:
            raise DXFInternalEzdxfError('Locked entity database.')

        if handle == '0' or not is_valid_handle(handle):
            raise ValueError(f'Invalid handle {handle}.')
        self._database[handle] = entity

    def __delitem__(self, handle: str) -> None:
        """ Delete entity by `handle`. Removes entity only from database, does not destroy the entity. """
        del self._database[handle]

    def __contains__(self, handle: str) -> bool:
        """ ``True`` if database contains `handle`. """
        assert isinstance(handle, str), type(handle)
        return handle in self._database

    def __len__(self) -> int:
        """ Count of database items. """
        return len(self._database)

    def __iter__(self) -> Iterable[str]:
        """ Iterable of all handles. """
        return iter(self._database.keys())

    def get(self, handle: str) -> Optional[DXFEntity]:
        """ Returns entity for `handle` or ``None`` if no entry for `handle` exist. """
        return self._database.get(handle)

    def next_handle(self) -> str:
        """ Returns next unique handle."""
        while True:
            handle = self.handles.next()
            if handle not in self._database:  # you can not trust $HANDSEED value
                return handle

    def keys(self) -> Iterable[str]:
        """ Iterable of all handles. """
        return self._database.keys()

    def values(self) -> Iterable[DXFEntity]:
        """ Iterable of all entities. """
        return self._database.values()

    def items(self) -> Iterable[Tuple[str, DXFEntity]]:
        """ Iterable of all (handle, entities) pairs. """
        return self._database.items()

    def add(self, entity: DXFEntity) -> None:
        """ Add `entity` to database, assigns a new handle to the `entity` if :attr:`entity.dxf.handle` is ``None``. """
        if entity.dxftype() in DATABASE_EXCLUDE:
            if entity.dxf.handle is not None:
                # store entities with handles (TABLE, maybe others) to avoid reassigning of its handle
                self[entity.dxf.handle] = entity
            return
        handle = entity.dxf.handle  # type: str
        if handle is None:
            handle = self.next_handle()
            # update_handle() requires the objects section to update the owner handle of the extension dictionary,
            # but this is no problem at file loading, all entities have handles, and DXF R12 (without handles) have no
            # extension dictionaries.
            entity.update_handle(handle)
        self[handle] = entity

        # add sub entities like ATTRIB, VERTEX and SEQEND to database
        # only INSERT and POLYLINE using this feature
        if hasattr(entity, 'add_sub_entities_to_entitydb'):
            entity.add_sub_entities_to_entitydb()

    def delete_entity(self, entity: DXFEntity) -> None:
        """ Removes `entity` from database and destroys the `entity`. """
        if entity.is_alive:
            del self[entity.dxf.handle]
            entity.destroy()

    def duplicate_entity(self, entity: DXFEntity) -> DXFEntity:
        """
        Duplicates `entity` and its sub entities (VERTEX, ATTRIB, SEQEND) and store them with new handles in the
        drawing database. This is the recommend method to duplicate DXF entities in a drawing. Graphical entities
        have to be added to a layout by :meth:`~ezdxf.layouts.BaseLayout.add_entity`, for other DXF entities:
        DON'T DUPLICATE THEM.

        To import DXF entities into another drawing use the :class:`~ezdxf.addons.importer.Importer` add-on.

        An existing owner tag is not changed because this is not the domain of the :class:`EntityDB` class, will be set
        by adding the duplicated entity to a layout.

        This is not a deep copy in the meaning of Python, because handles and links are changed.

        """
        new_entity = entity.copy()  # type: DXFEntity
        new_entity.dxf.handle = self.next_handle()
        self.add(new_entity)
        return new_entity

    def audit(self, auditor: 'Auditor'):
        """ Restore database integrity:

        - removes deleted database entries (purge)
        - restore database entries with modified handles (key != entity.dxf.handle)
        - remove entities with invalid handles

        """
        db = self._database
        remove_handles = []
        add_entities = []
        for handle, entity in db.items():
            if not is_valid_handle(handle):
                auditor.fixed_error(
                    code=AuditError.INVALID_ENTITY_HANDLE,
                    message=
                    f'Removed entity {entity.dxftype()} with invalid handle "{handle}" from entity database.',
                )
                remove_handles.append(handle)
            if not entity.is_alive:
                remove_handles.append(handle)
            elif handle != entity.dxf.get('handle'):
                remove_handles.append(handle)
                add_entities.append(entity)

        for handle in remove_handles:
            del db[handle]

        for entity in add_entities:
            handle = entity.dxf.get('handle')
            if handle is None:
                auditor.fixed_error(
                    code=AuditError.INVALID_ENTITY_HANDLE,
                    message=
                    f'Removed entity {entity.dxftype()} without handle from entity database.',
                )
                continue
            if not is_valid_handle(handle) or handle == '0':
                auditor.fixed_error(
                    code=AuditError.INVALID_ENTITY_HANDLE,
                    message=
                    f'Removed entity {entity.dxftype()} with invalid handle "{handle}" from entity database.',
                )
                continue
            self[handle] = entity
コード例 #8
0
 def test_reset(self):
     handles = HandleGenerator('200')
     handles.reset('300')
     self.assertEqual('300', str(handles))
コード例 #9
0
ファイル: test_018_handle.py プロジェクト: Rahulghuge94/ezdxf
def test_next_function():
    handles = HandleGenerator("100")
    assert "100" == next(handles)
コード例 #10
0
 def test_next_function(self):
     handles = HandleGenerator('100')
     self.assertEqual('100', next(handles))
コード例 #11
0
 def test_seed(self):
     handles = HandleGenerator('200')
     handles.next()
     self.assertEqual('201', str(handles))
コード例 #12
0
 def test_next(self):
     handles = HandleGenerator('100')
     self.assertEqual('100', handles.next())
コード例 #13
0
ファイル: test_018_handle.py プロジェクト: yening2020/ezdxf
def test_next():
    handles = HandleGenerator('100')
    assert '100' == handles.next()
コード例 #14
0
ファイル: test_018_handle.py プロジェクト: yening2020/ezdxf
def test_reset():
    handles = HandleGenerator('200')
    handles.reset('300')
    assert '300' == str(handles)
コード例 #15
0
ファイル: entitydb.py プロジェクト: billhu0228/ezdxf
 def __init__(self):
     self._database: Dict[str, DXFEntity] = {}
     # dxf entities to delete as set of handles
     self._trashcan: Set[str] = set()
     self.handles = HandleGenerator()
     self.locked = False  # for debugging
コード例 #16
0
ファイル: test_018_handle.py プロジェクト: Rahulghuge94/ezdxf
def test_seed():
    handles = HandleGenerator("200")
    handles.next()
    assert "201" == str(handles)
コード例 #17
0
class EntityDB:
    """ A simple key/value database a.k.a. dict(), but can be replaced by other
    classes that implements all of the methods of `EntityDB`. The entities
    have no order.

    The Data Model

    Every entity/object, except tables and sections, are represented as
    tag-list (see ExtendedTags Class), this lists are stored in the drawing-associated
    database, database-key is the 'handle' tag (code == 5 or 105).

    For the entity/object manipulation this tag-list will be wrapped into
    separated classes, which are generated by the dxffactory-object.
    The dxffactory-object generates DXF-Version specific wrapper classes.

    """

    def __init__(self):
        self._database = {}
        self.handles = HandleGenerator()

    def __delitem__(self, handle: str) -> None:
        del self._database[handle]

    def __getitem__(self, handle: str) -> ExtendedTags:
        return self._database[handle]

    def get(self, handle: str) -> Optional[ExtendedTags]:
        try:
            return self.__getitem__(handle)
        except KeyError:  # internal exception
            return None

    def __setitem__(self, handle: str, tags: ExtendedTags) -> None:
        self._database[handle] = tags

    def __contains__(self, handle: str) -> bool:
        """ Database contains handle? """
        return handle in self._database

    def __len__(self) -> int:
        """ Count of database items. """
        return len(self._database)

    def __iter__(self) -> Iterable[str]:
        """ Iterate over all handles. """
        return iter(self._database.keys())

    def keys(self) -> Iterable[str]:
        """ Iterate over all handles. """
        return self._database.keys()

    def values(self) -> Iterable[ExtendedTags]:
        """ Iterate over all entities. """
        return self._database.values()

    def items(self) -> Iterable[Tuple[str, ExtendedTags]]:
        """ Iterate over all (handle, entities) pairs. """
        return self._database.items()

    def add_tags(self, tags: ExtendedTags) -> str:
        try:
            handle = tags.get_handle()
        except DXFValueError:  # create new handle
            handle = self.get_unique_handle()
            handle_code = 105 if tags.dxftype() == 'DIMSTYLE' else 5  # legacy shit!!!
            tags.noclass.insert(1, DXFTag(handle_code, handle))  # handle should be the 2. tag

        self.__setitem__(handle, tags)
        return handle

    def delete_entity(self, entity: DXFEntity) -> None:
        entity.destroy()
        self.delete_handle(entity.dxf.handle)

    def delete_handle(self, handle: str) -> None:
        del self._database[handle]

    def get_unique_handle(self)-> str:
        while True:
            handle = self.handles.next()
            if handle not in self._database:  # you can not trust $HANDSEED value
                return handle

    def duplicate_tags(self, tags: ExtendedTags) -> ExtendedTags:
        """
        Deep copy of tags with new handle and duplicated linked entities (VERTEX, ATTRIB, SEQEND) with also new handles.
        An existing owner tag is not changed because this is not the domain of the EntityDB() class.
        The new entity tags are added to the drawing database.

        This is not a deep copy in the meaning of Python, because handle and link is changed.
        
        """
        new_tags = tags.clone()
        new_tags.noclass.replace_handle(self.get_unique_handle())  # set new handle
        self.add_tags(new_tags)  # add new tags to database
        source_link = tags.link  # follow link structure of original entity
        parent_copy = new_tags
        while source_link is not None:  # duplicate linked entities (VERTEX, ATTRIB, SEQEND)
            source_linked_entity = self.get(source_link)  # extended tags
            linked_entity_copy = source_linked_entity.clone()
            new_handle = self.get_unique_handle()
            linked_entity_copy.noclass.replace_handle(new_handle)  # set new handle
            self.add_tags(linked_entity_copy)  # add new tags to database
            parent_copy.link = new_handle
            source_link = source_linked_entity.link  # follow link structure of original entity
            parent_copy = linked_entity_copy
        return new_tags
コード例 #18
0
ファイル: test_018_handle.py プロジェクト: Rahulghuge94/ezdxf
def test_returns_not_zero():
    handles = HandleGenerator("0")
    assert handles.next() != "0"
コード例 #19
0
ファイル: entitydb.py プロジェクト: yanbin-ha/ezdxf
 def __init__(self):
     self._database: Dict[str, DXFEntity] = {}
     # DXF handles of entities to delete later:
     self.handles = HandleGenerator()
     self.locked: bool = False  # used only for debugging
コード例 #20
0
ファイル: test_018_handle.py プロジェクト: Rahulghuge94/ezdxf
def test_returns_not_negative():
    handles = HandleGenerator("-2")
    assert int(handles.next(), 16) > 0
コード例 #21
0
ファイル: entitydb.py プロジェクト: vshu3000/ezdxf
 def __init__(self):
     self._database = {}
     self.handles = HandleGenerator()
     self.locked = False  # for debugging
コード例 #22
0
ファイル: test_018_handle.py プロジェクト: Rahulghuge94/ezdxf
def test_reset():
    handles = HandleGenerator("200")
    handles.reset("300")
    assert "300" == str(handles)
コード例 #23
0
 def __init__(self):
     self._database = {}
     self.handles = HandleGenerator()
コード例 #24
0
ファイル: test_018_handle.py プロジェクト: yening2020/ezdxf
def test_next_function():
    handles = HandleGenerator('100')
    assert '100' == next(handles)