예제 #1
0
    def addChild(self,
                 branch: TreeBase,
                 index=None,
                 force=False,
                 emitDelta=None) -> TreeBase:
        """check if a delta is necessary"""
        #print("addChild")
        emitDelta = emitDelta if emitDelta is not None else self.trackingDeltas
        #print("emitStackDelta", emitStackDelta, self.trackingDeltas)
        if emitDelta:
            # check if tree is "known" to this delta tracking scope
            try:
                relPath = branch.relAddress(fromBranch=self.deltaTrackingRoot)
                print("existing branch delta")
                structDelta = TreeStructureDelta(
                    branchRef=TreeReference(branch,
                                            relParent=self.deltaTrackingRoot),
                    oldParentRef=TreeReference(
                        branch.parent, relParent=self.deltaTrackingRoot),
                    parentRef=TreeReference(
                        branch=self,
                        relParent=self.deltaTrackingRoot,
                    ),
                    oldIndex=branch.index(),
                    newIndex=index or -1,
                    eventCode=self.StructureEvents.branchAdded)
                self.emitStackDelta(structDelta)
            except LookupError:
                print("new branch delta")
                # branch not known to delta root, make creation delta
                createDelta = TreeCreationDelta(
                    TreeReference(branch,
                                  relParent=None,
                                  mode=TreeReference.Mode.Uid),
                    parentRef=TreeReference(self,
                                            relParent=self.deltaTrackingRoot),
                    name=branch.name,
                    value=branch.value,
                    index=index or -1,
                    treeUid=branch.uid,
                    treeCls=Tree)
                self.emitStackDelta(createDelta)

            # print("readonly", self.readOnly)
            if self.readOnly:
                print("read only returning")
                return None

        # check for read only
        if self.readOnly:
            raise PermissionError(
                f"tree {self.address()} is read only, cannot set value")

        return super(Tree, self).addChild(branch, index=index)
예제 #2
0
파일: transformer.py 프로젝트: edart76/tree
    def __init__(self, name="transform", uid=""):
        """TREES ALL THE WAY DOWN"""
        super(Transformation, self).__init__(uid)
        self.name = name

        # from tree.main import Tree
        self.settings = TreeBase(name + "Settings")
예제 #3
0
 def __copy__(self):
     from tree.core import TreeBase
     newRef = TreeReference(TreeBase("temp"),
                            relParent=None,
                            mode=self.mode)
     newRef.uid = self.uid
     newRef.address = self.address
     return newRef
예제 #4
0
 def __init__(self, branch: TreeBase = None, relParent=None, mode=Mode.Uid):
     self.mode = mode
     self.uid = branch.uid
     relParent = relParent or branch.root
     try:
         self.address = branch.relAddress(fromBranch=relParent)
     except:
         self.address = []
예제 #5
0
    def __get__(self, instance: TreeBase, owner):
        """look up property on given tree """
        if self.inherited:
            if self.breakTags:
                result = instance.getInherited(self.key,
                                               self.breakTags,
                                               returnBranch=self.returnBranch,
                                               default=FailToFind)
            else:
                result = instance.getInherited(self.key,
                                               returnBranch=self.returnBranch,
                                               default=FailToFind)
        else:
            result = instance.getProperty(self.key, default=FailToFind)
        if result is FailToFind:
            result = self._evalDefault(instance, self.key)

            # optionally set the value on this branch to the default result
            if self.defaultSetsValue:
                instance.setProperty(self.key, result)
        return result
예제 #6
0
class Tree(TreeBase):
	"""implement more complex systems beyond
	basic structure"""

	# system for finding required trees from a global scope
	globalTree = TreeBase("globalRoots")
	#globalTree.default = lambda branch: {} # each namespace a dict

	# import reference modes for TreeReference
	RefMode = TreeReference.Mode

	# optionally set up a parametre object for UI widgets in this tree
	uiParams = TreeBase.TreePropertyDescriptor(
		UI_PROPERTY_KEY, inherited=False,
		desc="Optional parametre object to inform UI elements generated from this branch"
	)

	@classmethod
	def defaultBranchCls(cls):
		return Tree

	@classmethod
	def proxyForBranch(cls, branch:Tree, mode=RefMode.Uid):
		"""return a TreeProxy for the given branch"""
		return TreeProxy.getProxy(branch, shared=True, refMode=mode)

	def __init__(self, name: str = None, val: T = None, default=None,
	             treeUid=None,
	             properties: dict = None
	             ):

		# for deltas signature: branch, delta, deltaTracker
		self.deltasChanged = Signal()

		super(Tree, self).__init__(name=name, val=val,
		                           default=default,
		                           treeUid=treeUid,
		                           properties=properties)



	@classmethod
	def addGlobalRoot(cls,
	                  branch:TreeBase,
	                  namespace=("main",)):
		"""add the given branch as a global tree root -
		in the given namespace address (which of course
		also corresponds to a tree branch
		branch is added by the given key
		"""

		globalBranch = cls.globalTree(namespace, create=True)
		#print(globalBranch)
		globalBranch.value = branch
		#print(globalBranch)


	@property
	def readOnly(self)->bool:
		"""read-only if explicitly set, OR if tree is reference
		and deltas are not tracked"""
		return self.getInherited("readOnly")

	@readOnly.setter
	def readOnly(self, val):
		self.setProperty("readOnly", val)

	# def _branchFromToken(self, token):
	# 	return super(Tree, self)._branchFromToken(token)

	def uidTie(self):
		return (self.uidRepr(), self._coreDataUid[:4] + "-", self.coreData().uidRepr())

	def uidView(self):
		return pprint.pformat([i.uidTie() for i in self.allBranches()])

	@classmethod
	def _copyAndXorUids(cls, targetTree:Tree, saltUid:str,
	                    andCoreData=True,
	                    sourceUidPropKey="_sourceUid")->Tree:
		"""copies given branch, replaces each uid with
		xor( base branch uid, salt uid )
		and adds original uid as property with sourceUidPropKey key"""
		copyTree = targetTree.copy(deep=True)
		for targetBranch, copyBranch in zip(
			targetTree.allBranches(includeSelf=True),
			copyTree.allBranches(includeSelf=True),
		):
			baseUid = targetBranch.uid
			newUid = cls.xorUids(baseUid, saltUid)
			if andCoreData:
				copyBranch.coreData().setUid(newUid)
				copyBranch._coreDataUid = newUid
			copyBranch.setUid(newUid, replace=False)
			newBranch = cls.byUid(newUid)
			newBranch.setProperty(sourceUidPropKey, baseUid)
		return copyTree


	# region ui integration
	def setUiData(self, widgetType:AtomicWidgetType,
	              widgetMin=None, widgetMax=None,
	              defaultPath:T.Union[str, PurePath]=None):
		uiData = BranchUiData(widgetType, widgetMin,
		                      widgetMax, defaultPath)
		self.setProperty("uiData", uiData)
예제 #7
0
 def __set__(self, instance: TreeBase, value):
     # do not set value if equal to default
     if value == self._evalDefault(instance, self.key):
         return
     instance.setProperty(self.key, value)
예제 #8
0
class Tree(TreeBase):
    """implement more complex systems beyond
	basic structure"""

    # system for finding required trees from a global scope
    globalTree = TreeBase("globalRoots")
    #globalTree.default = lambda branch: {} # each namespace a dict

    # import reference modes for TreeReference
    RefMode = TreeReference.Mode

    # what kind of UI widget should this branch show
    uiType = TreeBase.TreePropertyDescriptor("uiType")

    @classmethod
    def defaultBranchCls(cls):
        return Tree

    @classmethod
    def proxyForBranch(cls, branch: Tree, mode=RefMode.Uid):
        """return a TreeProxy for the given branch"""
        return TreeProxy.getProxy(branch, shared=True, refMode=mode)

    def __init__(self,
                 name: str = None,
                 val: T = None,
                 default=None,
                 treeUid=None,
                 properties: dict = None):
        #print("_init tree")
        self.liveInput: TransformChain = None
        self._liveInputChanged = False
        self._directLiveInputRef = None  # for direct access without hierarchy
        self._liveInputRoot = None  # for direct access without hierarchy

        # for deltas signature: branch, delta, deltaTracker
        self.deltasChanged = Signal()
        self._deltaTracker: TreeDeltaTracker = None
        self._deltaTrackingActive = False

        super(Tree, self).__init__(name=name,
                                   val=val,
                                   default=default,
                                   treeUid=treeUid,
                                   properties=properties)

        self.deltasChanged.connect(self.treeChanged)

    @classmethod
    def addGlobalRoot(cls, branch: TreeBase, namespace=("main", )):
        """add the given branch as a global tree root -
		in the given namespace address (which of course
		also corresponds to a tree branch
		branch is added by the given key
		"""

        globalBranch = cls.globalTree(namespace, create=True)
        #print(globalBranch)
        globalBranch.value = branch
        #print(globalBranch)

    @property
    def liveInputRoot(self):
        """return the closest direct reference above this branch
		or None if there aren't any references"""
        if self.liveInput:
            return self
        if self.parent:
            return self.parent.liveInputRoot
        return None

    @property
    def readOnly(self) -> bool:
        """read-only if explicitly set, OR if tree is reference
		and deltas are not tracked"""
        return self.getInherited("readOnly")

    @readOnly.setter
    def readOnly(self, val):
        self.setProperty("readOnly", val)

    # def _branchFromToken(self, token):
    # 	return super(Tree, self)._branchFromToken(token)

    def signals(self):
        return (*super(Tree, self).signals(), self.deltasChanged)

    def setLiveInput(self, liveInput: T.Union[Tree, TransformChain]):
        """set this tree to reflect the result of a transform chain,
		drawing from another branch"""
        if isinstance(liveInput, TreeBase):
            liveInput = TransformChain(liveInput)
        print("set live input")
        self.liveInput = liveInput
        self.readOnly = True
        self.onLiveInputChanged()

    def coreData(self) -> TreeCoreData:
        """check if live input is dirty"""
        if self._liveInputRoot:
            # if self._liveInputRoot._liveInputChanged:
            if self._liveInputRoot.liveInput.dirty:
                self._liveInputRoot._liveInputChanged = False
                self._liveInputRoot.onLiveInputChanged()

        return super(Tree, self).coreData()

    def uidTie(self):
        return (self.uidRepr(), self._coreDataUid[:4] + "-",
                self.coreData().uidRepr())

    def uidView(self):
        return pprint.pformat([i.uidTie() for i in self.allBranches()])

    @classmethod
    def _copyAndXorUids(cls,
                        targetTree: Tree,
                        saltUid: str,
                        andCoreData=True,
                        sourceUidPropKey="_sourceUid") -> Tree:
        """copies given branch, replaces each uid with
		xor( base branch uid, salt uid )
		and adds original uid as property with sourceUidPropKey key"""
        copyTree = targetTree.copy(deep=True)
        for targetBranch, copyBranch in zip(
                targetTree.allBranches(includeSelf=True),
                copyTree.allBranches(includeSelf=True),
        ):
            baseUid = targetBranch.uid
            newUid = cls.xorUids(baseUid, saltUid)
            if andCoreData:
                copyBranch.coreData().setUid(newUid)
                copyBranch._coreDataUid = newUid
            copyBranch.setUid(newUid, replace=False)
            newBranch = cls.byUid(newUid)
            newBranch.setProperty(sourceUidPropKey, baseUid)
        return copyTree

    def onLiveInputChanged(self, *args, **kwargs):
        """simplest, slowest solution - match this tree and its
		branches to result on change

		sourceUid is vital, used for delta retargeting back to
		transform result tree
		"""

        self.liveInput.resultTree()
        baseReadOnly = self.readOnly
        self.setProperty("_sourceUid", self.liveInput.resultTree().uid)

        # copy result tree
        resultTree = self._copyAndXorUids(self.liveInput.resultTree(),
                                          saltUid=self.uid,
                                          andCoreData=True)

        #print("result branches", self.liveInput.resultTree().branches)

        baseCoreData = self.coreData()
        self.setCoreData(resultTree.coreData())
        self.coreData().setUid(self.uid)
        self._coreDataUid = self.uid

        # set direct reference to this branch on all children
        for i in self.allBranches(includeSelf=True):
            i._liveInputRoot = self

        self.coreDataChanged.emit(self, baseCoreData, self.coreData())
        self.readOnly = baseReadOnly

        print("after input change")
        print(self.displayStr())
        print(self.deltaData(), self.deltaBaseName())

    def addReferenceBranch(self, otherBranch: Tree) -> Tree:
        """test without any other objects
		connect all signals of other tree's branches
		this one's sync functions
		"""
        newBranch = otherBranch.copy(deep=True)
        self.addChild(newBranch)
        newBranch.setLiveInput(otherBranch)
        return newBranch

    def setName(
        self,
        name,
        allowMerging=False,
        emitDelta=None,
    ):
        """ if name is current name, do nothing
		if emit delta, emit a delta
			if this is read only, then return
		if read-only, raise a permission error
		if read-only overridden to False AND live input AND no deltas
			just do nothing, this is super edge case
		"""

        oldName = self.name
        if name == oldName:
            return name

        if self.parent:
            if not allowMerging:
                name = self.parent.getUniqueBranchName(name)

        emitDelta = emitDelta if emitDelta is not None else self.trackingDeltas
        if emitDelta:
            delta = TreeNameDelta(
                TreeReference(self, relParent=self.deltaTrackingRoot), oldName,
                name)
            self.emitStackDelta(delta)
            if self.readOnly:
                # print("post emit read only, returning")
                return oldName

        # check for read only
        if self.readOnly:
            raise PermissionError(
                f"tree {self.address()} is read only, cannot set name")

        super(Tree, self).setName(name, allowMerging=allowMerging)
        return name

    def setValue(self, val, emitDelta=None):
        """set this tree's value - optionally emitting a delta object"""
        oldValue = self.value

        # if value hasn't actually changed, ignore
        # this often happens when linked to uis -
        # less elegant, but way less cde
        if val == oldValue:
            #print("new value is old")
            return
        emitDelta = emitDelta if emitDelta is not None else self.trackingDeltas
        if emitDelta:
            delta = TreeValueDelta(
                TreeReference(self, relParent=self.deltaTrackingRoot),
                oldValue, val)
            self.emitStackDelta(delta)
            if self.readOnly:
                return
        # check for read only
        if self.readOnly:
            raise PermissionError(
                f"tree {self.address()} is read only, cannot set value")

        return super(Tree, self).setValue(val)

    def addChild(self,
                 branch: TreeBase,
                 index=None,
                 force=False,
                 emitDelta=None) -> TreeBase:
        """check if a delta is necessary"""
        #print("addChild")
        emitDelta = emitDelta if emitDelta is not None else self.trackingDeltas
        #print("emitStackDelta", emitStackDelta, self.trackingDeltas)
        if emitDelta:
            # check if tree is "known" to this delta tracking scope
            try:
                relPath = branch.relAddress(fromBranch=self.deltaTrackingRoot)
                print("existing branch delta")
                structDelta = TreeStructureDelta(
                    branchRef=TreeReference(branch,
                                            relParent=self.deltaTrackingRoot),
                    oldParentRef=TreeReference(
                        branch.parent, relParent=self.deltaTrackingRoot),
                    parentRef=TreeReference(
                        branch=self,
                        relParent=self.deltaTrackingRoot,
                    ),
                    oldIndex=branch.index(),
                    newIndex=index or -1,
                    eventCode=self.StructureEvents.branchAdded)
                self.emitStackDelta(structDelta)
            except LookupError:
                print("new branch delta")
                # branch not known to delta root, make creation delta
                createDelta = TreeCreationDelta(
                    TreeReference(branch,
                                  relParent=None,
                                  mode=TreeReference.Mode.Uid),
                    parentRef=TreeReference(self,
                                            relParent=self.deltaTrackingRoot),
                    name=branch.name,
                    value=branch.value,
                    index=index or -1,
                    treeUid=branch.uid,
                    treeCls=Tree)
                self.emitStackDelta(createDelta)

            # print("readonly", self.readOnly)
            if self.readOnly:
                print("read only returning")
                return None

        # check for read only
        if self.readOnly:
            raise PermissionError(
                f"tree {self.address()} is read only, cannot set value")

        return super(Tree, self).addChild(branch, index=index)

    def remove(self,
               address: T.Union[TreeBase.ADDRESS_TYPES, "TreeBase",
                                None] = None,
               delete=False,
               emitDelta=None):
        # print(address, type(address))
        parent, child = self._getRemoveTarget(address)
        # print(address, parent, child)
        if parent is None:
            return
        if parent is not self:
            return parent.remove(child, delete=delete, emitDelta=emitDelta)

        emitDelta = emitDelta if emitDelta is not None else self.trackingDeltas
        if emitDelta:
            deletionDelta = TreeDeletionDelta(
                TreeReference(child,
                              relParent=self.deltaTrackingRoot,
                              mode=TreeReference.Mode.Uid),
                parentRef=TreeReference(self,
                                        relParent=self.deltaTrackingRoot,
                                        mode=TreeReference.Mode.Uid),
                name=child.name,
                value=child.value,
                treeUid=child.uid,
                index=child.index(),
                treeCls=Tree)
            self.emitStackDelta(deletionDelta)
            if self.readOnly:
                #print("read only returning")
                return None

        # check for read only
        if self.readOnly:
            raise PermissionError(
                f"tree {self.address()} is read only, cannot set value")

        return super(Tree, self).remove(address, delete=delete)

    #region deltas
    def deltaTracker(self):
        if self.liveInputRoot:
            return self.liveInputRoot.liveInput.getDeltaTransform()
        return self._deltaTracker

    def setTrackingDeltas(self, state: bool):

        if state:
            self._deltaTracker = TreeDeltaTracker()
            self._deltaTrackingActive = True
        else:
            self._deltaTracker = None
            self._deltaTrackingActive = False

        self.deltasChanged(self, None, self._deltaTracker)

    def clearDeltas(self):
        self.deltaTracker().clearDeltas()
        self.deltasChanged(self, None, self.deltaTracker())

    @property
    def deltaTrackingRoot(self):
        if self._deltaTracker:
            return self
        if self.parent:
            return self.parent.deltaTrackingRoot
        return None

    @property
    def trackingDeltas(self):
        if not self.deltaTrackingRoot:
            return False
        return self.deltaTrackingRoot._deltaTrackingActive

    def undo(self):
        """rolls back a single delta"""
        self.deltaTrackingRoot.deltaTracker().undo(self)

    def redo(self):
        """redo a single delta"""
        self.deltaTrackingRoot.deltaTracker().redo(self)

    def deltaAddress(self):
        """return relative address from delta tracking root"""
        return self.relAddress(self.deltaTrackingRoot)

    def emitStackDelta(self, delta: TreeDeltaAtom):
        print("emit delta", delta)
        if not self.deltaTrackingRoot:
            print("tree ", self, "is not tracking deltas")
            return
        # patch any tree references in this delta to input tree
        if self.deltaTrackingRoot.liveInputRoot:
            for ref in delta.references():
                if ref.resolve() is self.deltaTrackingRoot:
                    ref.uid = self.deltaTrackingRoot.liveInput.resultTree().uid
                elif ref.resolve().getProperty("_sourceUid"):
                    # reverse xor operation to recover original tree uid
                    reverseUid = bitwiseXor(ref.uid,
                                            self.deltaTrackingRoot.uid)
                    ref.uid = reverseUid

        self.deltaTrackingRoot.deltaTracker().addDelta(delta)
        self.deltasChanged(self, delta, self.deltaTrackingRoot.deltaTracker())

    def deltaData(self) -> dict:
        """return any record of what has been done to this tree
		by a delta"""
        if self.getProperty(self.deltaPropKey) is None:
            self.setProperty(self.deltaPropKey, {})
        return self.getProperty(self.deltaPropKey)

    def deltaBaseName(self):
        return self.deltaData().get("name")

    def deltaBaseValue(self):
        return self.deltaData().get("value")

    def deltaBaseBranchNames(self):
        return self.deltaData().get("branchNames")

    def isDeltaCreated(self):
        """check if this branch is created by a delta"""
        return self.deltaData().get("created")

    def isDeltaCreatedChild(self):
        """check if this branch has a parent which is created
		by a delta"""
        state = self.isDeltaCreated()
        if self.parent:
            state = state or self.parent.isDeltaCreatedChild()
        return state

    #endregion

    # region ui integration
    def setUiData(self,
                  widgetType: AtomicWidgetType,
                  widgetMin=None,
                  widgetMax=None,
                  defaultPath: T.Union[str, PurePath] = None):
        uiData = BranchUiData(widgetType, widgetMin, widgetMax, defaultPath)
        self.setProperty("uiData", uiData)