from weakref import ref cdef inline void observable_set_dispatch(object self) except *: cdef Property prop = self.prop obj = self.obj() if obj is not None: prop.dispatch(obj) class ObservableSet(set): # Internal class to observe changes inside a native python set. def __init__(self, *largs): self.prop = largs[0] self.obj = ref(largs[1]) super(ObservableSet, self).__init__(*largs[2:]) def __iand__(self, *largs): set.__iand__(self, *largs) observable_set_dispatch(self) def __ior__(self, *largs): set.__ior__(self, *largs) observable_set_dispatch(self) def __isub__(self, *largs): set.__isub__(self, *largs) observable_set_dispatch(self) def __ixor__(self, *largs): set.__ixor__(self, *largs) observable_set_dispatch(self) def add(self, *largs): set.add(self, *largs) observable_set_dispatch(self) def clear(self): set.clear(self) observable_set_dispatch(self) def difference_update(self, *largs): set.difference_update(self, *largs) observable_set_dispatch(self) def discard(self, *largs): set.discard(self, *largs) observable_set_dispatch(self) def intersection_update(self, *largs): set.intersection_update(self, *largs) observable_set_dispatch(self) def pop(self, *largs): cdef object result = set.pop(self, *largs) observable_set_dispatch(self) return result def remove(self, *largs): set.remove(self, *largs) observable_set_dispatch(self) def symmetric_difference_update(self, *largs): set.symmetric_difference_update(self, *largs) observable_set_dispatch(self) def update(self, *largs): set.update(self, *largs) observable_set_dispatch(self) cdef class SetProperty(Property): '''Property that represents a set. :Parameters: `defaultvalue`: set, defaults to set() Specifies the default value of the property. .. warning:: When assigning a set to a :class:`SetProperty`, the set stored in the property is a shallow copy of the set and not the original set. This can be demonstrated with the following example:: >>> class MyWidget(Widget): >>> my_set = SetProperty([]) >>> widget = MyWidget() >>> my_set = {1, 5, {'hi': 'hello'}} >>> widget.my_set = my_set >>> print(my_set is widget.my_set) False >>> my_set.add(10) >>> print(my_set, widget.my_set) {1, 5, {'hi': 'hello'}, 10} {1, 5, {'hi': 'hello'}} However, changes to nested levels will affect the property as well, since the property uses a shallow copy of my_set. ''' def __init__(self, defaultvalue=0, **kw): defaultvalue = set() if defaultvalue == 0 else defaultvalue super(SetProperty, self).__init__(defaultvalue, **kw) cpdef PropertyStorage link(self, EventDispatcher obj, str name): Property.link(self, obj, name) cdef PropertyStorage ps = obj.__storage[self._name] if ps.value is not None: ps.value = ObservableSet(self, obj, ps.value) cdef check(self, EventDispatcher obj, value, PropertyStorage property_storage): if Property.check(self, obj, value, property_storage): return True if type(value) is not ObservableSet: raise ValueError('%s.%s accept only ObservableSet' % ( obj.__class__.__name__, self.name)) cpdef set(self, EventDispatcher obj, value): if value is not None: value = ObservableSet(self, obj, value) Property.set(self, obj, value)