# # Copyright (c) 2003, 2004, 2005, 2006 Art Haas # # This file is part of PythonCAD. # # PythonCAD is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # PythonCAD is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with PythonCAD; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # This file contains simple classes meant to be used as base classes. # from PythonCAD.Generic import entity class ModObject(object): """A base class for objects that store a modification state variable. There are several methods for the modobject class: isModified(): Test the state of the modified flag modified(): Set the modified flag to True reset(): Set the modified flag to False This class is meant to be used as a base class for more complex classes. """ def __init__(self): """Initialize a modobject instance. There are no arguments needed for this method. """ self.__modified = False def isModified(self): """Tests the modified state flag of the modobject. isModified() """ return self.__modified def modified(self): """Set the modified state flag value to True. modified() """ self.__modified = True def reset(self): """Set the modified state flag value to False. reset() """ self.__modified = False class SListObject(object): """Base class for objects emulating unidirectional linked lists. A SListObject has the following attributes: next: The next object in the list. A SListObject has the following methods: {get/set/del}Next(): Get/Set the next object in the list. """ def __init__(self): """Initialize a SListObject object. """ self.__next = None def getNext(self): """Get the object following this object. getNext() """ return self.__next def setNext(self, obj): """Set the object that will follow this object. setNext(obj) The object must be an instance of a ListObject. """ if obj is not None: if not isinstance(obj, SListObject): raise TypeError, "Invalid SListObject: " + str(obj) self.__next = obj def delNext(self): """Delete the object that follows this object. delNext() This method returns the deleted object. """ _next = self.__next if _next is not None: self.__next = _next.getNext() _next.setNext(None) return _next next = property(getNext, setNext, delNext, "Accessor to the next object.") class DListObject(SListObject): """Base class for objects emulating doubly linked lists. A DListObject is derived from a SListObject, so it shares the attributes and methods of that class. Addtionally it has the following attributes prev: The previous object in the list A DListObject has the following addtional methods: {get/set/del}Prev(): Get/Set the previous object in the list. """ def __init__(self): """Initialize a DListObject object. """ SListObject.__init__(self) self.__prev = None def getPrev(self): """Get the object preceeding this object. getPrev() """ return self.__prev def setPrev(self, obj): """Set the object that will precede this object. setPrev(obj) The object must be an instance of a DListObject. """ if obj is not None: if not isinstance(obj, DListObject): raise TypeError, "Invalid DListObject: " + str(obj) obj.setNext(self) self.__prev = obj def delPrev(self): """Delete the object preceding this object. delPrev(obj) This method returns the previous object. """ _prev = self.__prev if _prev is not None: _new_prev = _prev.getPrev() self.__prev = _new_prev if _new_prev is not None: _new_prev.setNext(self) _prev.__prev = None _prev.setNext(None) return _prev prev = property(getPrev, setPrev, delPrev, "Accessor to preceding object.") # # base class for objects that are components of other # objects # class Subpart(entity.Entity): """A base class for objects that store references to other objects. The Subpart class is meant to be a base class for other classes defining simple objects that will be used in other objects but are not subclasses of those other objects. The Subpart objects that are in those classes can be used to store references to the larger object. The Subpart class has the following methods: storeUser(): Save a reference to some object. freeUser(): Release a reference to some object getUsers(): Return the list of objects that have been stored. hasUsers(): Test if the subpart has any """ __messages = { 'added_user' : True, 'removed_user' : True, } def __init__(self, **kw): super(Subpart, self).__init__(**kw) self.__users = None def finish(self): if self.__users is not None: print "%d refs in users" % len(self.__users) for _user in self.__users: print "stray object reference to: " + `_user` super(Subpart, self).finish() def storeUser(self, obj): """Save a reference to another object. storeObject(obj) Argument 'obj' can be any type of object. """ if self.__users is None: self.__users = [] _users = self.__users _seen = False for _user in _users: if _user is obj: _seen = True break if not _seen: self.startChange('added_user') _users.append(obj) self.endChange('added_user') self.sendMessage('added_user', obj) def freeUser(self, obj): """Release a reference to another object. freeObject(obj) This method does nothing if the Component object has not stored references to any object or if the argument 'obj' had not been stored with storeObject(). """ if self.__users is not None: _users = self.__users for _i in range(len(_users)): if obj is _users[_i]: self.startChange('removed_user') del _users[_i] self.endChange('removed_user') self.sendMessage('removed_user', obj) break if not len(_users): self.__users = None def getUsers(self): """Return the list of stored objects. getObjects() This method returns a list of references stored by calling the storeObject() method. """ if self.__users is not None: return self.__users[:] return [] def countUsers(self): """Return the number of stored objects. countUsers() """ _count = 0 if self.__users is not None: _count = len(self.__users) return _count def hasUsers(self): """Test if the Subpart has any users. hasUsers() This method returns True if there are any users of this Subpart, otherwise this method returns False. """ return self.__users is not None def canParent(self, obj): """Test if an Entity can be the parent of another Entity. canParent(obj) This method overrides the Entity::canParent() method """ return False def getValues(self): """Return values comprising the Subpart. getValues() This method extends the Entity::getValues() method. """ return super(Subpart, self).getValues() def sendsMessage(self, m): if m in Subpart.__messages: return True return super(Subpart, self).sendsMessage(m) # # TypedDict Class # # The TypedDict class is built from the dict object. A TypedDict # instance has a defined object type for a key and value and will # only allow objects of that type to be used for these dictionary # fields. # class TypedDict(dict): def __init__(self, keytype=None, valtype=None): super(TypedDict, self).__init__() self.__keytype = keytype self.__valtype = valtype def __setitem__(self, key, value): _kt = self.__keytype if _kt is not None: if not isinstance(key, _kt): raise TypeError, "Invalid key type %s" % type(key) _vt = self.__valtype if _vt is not None: if not isinstance(value, _vt): raise TypeError, "Invalid value type %s" % type(value) super(TypedDict, self).__setitem__(key, value) # # ConstDict class # # The ConstDict class is a TypedDict based class that allows # the setting of a key only once and does not permit the # deletion of the key. The idea with a ConstDict class is to # store a non-modifiable set of key/value pairs. # class ConstDict(TypedDict): def __init__(self, keytype=None, valtype=None): super(ConstDict, self).__init__(keytype, valtype) def __setitem__(self, key, value): if key in self: raise KeyError, "Key already used: " + key super(ConstDict, self).__setitem__(key, value) def __delitem__(self, key): pass # raise an exception? # # LockedDict class # # The LockedDict class is a TypedDict based class that allows # the setting of keys/values until the dictionary is locked. # After then the dictionary is cannot be altered. There is # not an unlock method to allow changing the LockedDict object # once it has been locked. # class LockedDict(TypedDict): def __init__(self, keytype=None, valtype=None): super(LockedDict, self).__init__(keytype, valtype) self.__locked = False def __setitem__(self, key, value): if self.__locked: raise KeyError, "LockedDict object is locked: " + `self` super(LockedDict, self).__setitem__(key, value) def __delitem__(self, key): if self.__locked: raise KeyError, "LockedDict object is locked: " + `self` super(LockedDict, self).__delitem__(key) def lock(self): self.__locked = True # # TypedList Class # # # The TypedList class is built from the list object. A TypedList # instance has a defined object type for a occupant in the list and # will only allow objects of that type to be stored. # class TypedList(list): def __init__(self, listtype=None): super(TypedList, self).__init__() self.__listtype = listtype def __setitem__(self, key, value): _lt = self.__listtype if _lt is not None: if not isinstance(value, _lt): raise TypeError, "Invalid list member %s" % type(value) super(TypedList, self).__setitem__(key, value) def append(self, obj): _lt = self.__listtype if _lt is not None: if not isinstance(obj, _lt): raise TypeError, "Invalid list member %s" % type(obj) super(TypedList, self).append(obj) def insert(self, idx, obj): _lt = self.__listtype if _lt is not None: if not isinstance(obj, _lt): raise TypeError, "Invalid list member %s" % type(obj) super(TypedList, self).insert(idx, obj)