Describe an algorithm using signals/slots together with ItemChildAdded/RemovedChange that will know when to clear its memo (complex graph) i.e. when it's done?
-
I have a quite complicated question to ask here. Node labeled C is a DirectedGraph. The arrow labeled G is a Directed graph morphism.
Now I'm currently doing this using signals/slots insided of GraphMorphism:
def take_image(self): if self.dom() is None or self.cod() is None: raise Exception("Both domain and codomain must be set before calling GraphMorphism.take_image().") for X in self.dom().nodes(): if id(X) not in self._domainNodes: self._domainNodes[id(X)] = X FX = X.copy() self.cod().add_node(FX) FX.setPos(X.pos()) self._codomainNodes[id(FX)] = FX self._imageMap[id(X)] = id(FX) X.position_changing_memoized.connect(lambda delta, memo: self._onItemPositionChange(FX, delta, memo)) FX.position_changing_memoized.connect(lambda delta, memo: self._onItemPositionChange(X, delta, memo)) for A in self.dom().arrows(): if id(A) not in self._domainArrows: FA = A.copy() self.cod().add_arrow(FA) FA.setPos(A.pos()) FA.label_item().setPos(A.label_item().pos()) FA.set_label(A.label()) FA.set_source(self._codomainNodes[self._imageMap[id(A.source())]]) FA.set_target(self._codomainNodes[self._imageMap[id(A.target())]]) self._domainArrows[id(FA)] = FA self._imageMap[id(A)] = id(FA) for k, point in enumerate(A.control_points()): FA.setPos(point.pos()) if k in {1, 2}: point.position_changing_memoized.connect( lambda delta, memo, point=FA.control_points()[k]: self._onItemPositionChange( point, delta, memo)) FA.update() self._imageTaken = True
The result is that one can reflect position changes of the corresponding nodes arrows (between the graphs) in either direction, without the obvious bug of infinite recursion. Infinite recursion occurs naturally because a setPos() on a domain node (a node in C) will setPos() on its corresponding node in D, and in turn that will cause a setPos() in C again, and so on... But there is also infinite loops formed when a loop in the graph of GraphMorphisms is formed. That is because I am allowing loops in these graphs (they are not DAG's in general).
However, I am determining when to clear the memo (held in each item: _posChangingMemo : set) which holds each visited item's id() (like it's instance pointer in Python), solely when the user does a mouseReleaseEvent() on the item being moved. Obviously this will cause the bug of if the scene gets cludgy, then mouseReleaseEvent() might be called too early, clearing the memo before the chain of signal/slot calls has propogated to everywhere it needs to go.
However, it is currently working with the depicted example. Another thing I want reflected by GraphMorphisms/Functors is when I add/remove a node, it will automatically propogate, but I cut off this propogation when when the GraphMorphisms form a loop. Ie. I propogate just one time around. I can see bugs forming with this as well. My first question is where would be a good spot to clear the memo? Again, each involved item holds a reference to a common _addingChildMemo (resp. _removingChildMemo:set). They are assigned like so:
def setPos(self, pos: QPointF, memo: set = None): if pos != self.pos(): if memo is None: memo = set() if id(self) not in memo: if len(self._posChangingMemo) > 0: self._clearPosChangingMemos() self._posChangingMemo = memo delta = pos - self.pos() memo.add(id(self)) self.position_changing_memoized.emit(delta, memo) super().setPos(pos)
The signals/slots approach seems very flimsy/buggy. Can you suggest another approach? Thanks.
-
I think I've got it, I look at the parent container's arrows (BigCat's arrows). Anything connected to the nodes I'm moving might trigger updates (in the case of setPos). This is a nice way, because I can be sure I have everything and it's iterative / without signals/slots. For the child added / removed it's the same principal.
-
E enjoysmath has marked this topic as solved