Strange QGraphicsItem behaviour after node deletion
-
When I delete a QGraphicsItem (node from a graph) I am getting strange mouse behaviour on the next mouse move.
Using the example below:
run it and see:- four connected nodes on a QGraphicsView.
- each node can be moved. If one is moved in X past its immediate neighbours it is deleted.
- The very next mouse event on a node will snap that node to 0,0 instead of letting you drag it around.
The problem is the snap to 0,0 after deleting a node from the QGraphicsView.
It seems I am not cleaning up properly and the first mouse event is getting bogus x,y positioning.
But I can't work out what I have neglected to do, or how to fix it.
This is cut down code focusing on the structure and behaviour.Any ideas ?
Code is here: (too long for post)
Here is my delete node code which is causing the problem:
@ def delete_node(self, index):
""" delete node at index
- reindex nodes, edges
"""
nodes = self.get_ordered_nodes()
node = nodes[index-1]
# find preceding and next nodes
pn = False
nn = False
if index > 1:
pn = nodes[index-2]
if index < len(nodes):
nn = nodes[index]
# disconnect edges between pn->node and node->nn
if pn:
e_index = pn.edges().index(pn.get_next_edge())
print " removing edge",e_index,'from pn', pn.edges()
pn.edges().pop(e_index)
if nn:
e_index = nn.edges().index(nn.get_prev_edge())
print " removing edge",e_index,'from nn', nn.edges()
nn.edges().pop(e_index)
# add new edge between pn,nn -if node not an end
if pn and nn:
self.scene().addItem(Edge(pn, nn))
# force first node to time=0
if not pn:
nn.force_to_Tzero()
# remove edges belonging to node
for e in node.edges():
e().hide()
self.scene().removeItem(e())
del(e)
node.hide()
# remove node from scene
self.scene().removeItem(node)
del(node)
# start from fresh nodes list
# correct node numbering for this and following nodes and edges
nodes = self.get_ordered_nodes()
for i in range(len(nodes)):
n = nodes[i]
print "reindexing", n.index,i+1
#n.prepareGeometryChange()
n.set_index(i+1)
self.nodecount -= 1@ -
With respect, I don't have an answer, just comments about why you haven't gotten a response.
You are asking us to debug your code. Unless the code has some teaching value about Qt, it might not get a response.
Even if someone is willing, your code is very hard to read. Most code is read more than it is written (for example in this case to debug it) so it should be easy to read.
Your code is hard to read because it is not object oriented and full of extraneous stuff. For example:
@
remove edges belonging to node
for e in node.edges(): e().hide() self.scene().removeItem(e()) del(e)@
Could be better written as a method of a Node class:
@def removeEdges(self):
for e in self.edges():
self.scene().removeItem(e)
@You don't need to hide() because it won't be in the scene and thus not drawn. You don't need to del() because garbage collection will do that. You don't need the comment because the name of the method means what the comment says.
Finally, why does your code use e() instead of just e? That seems to imply that your iterator edges() returns a callable function instead of an object.
-
These are all good points.
The reason the code looks the way it does is because it is built upon an example supplied with QT: "elastic nodes" . The node removal is inside the function instead of in its own because it is only used here. [ I subscribe to the west coast (arguably more pragmatic) style of OO instead of the East coast style -which I'm sure you know - would have everything broken into objects regardless of usage ]. The e() is because the original example uses the weakref module. This module returns the information in a way that requires you to call it as a function.
The reason I have posted this code is - as you say - because I want someone to help me debug it. I am asking in this forum because the code is based on a published example (which does not have node deletion in it) and I think it would be useful for people to know how to free up nodes created in the way the example does. Also I can't work out what's wrong with it.
Which is also the reason why there are possibly extraneous calls such as the hide and del calls. However - the removal of these does not 'fix' the problem of what happens to the node structure and some internal dirty flag which cause the graph to behave in a way that is not acceptable to a user.It is unfortunate that your input seems to be to explain to others how smart you are and how stupid I am. Instead of looking at, what I deem to be, an interesting QT problem to do with refeshing the display after deleting a graphical structure...
YMMV but thanks for bumping the post. Seriously - all input is gratefully received. -
I didn't mean to offend you. I made a snap judgement that since you had few posts (but so do I) and since there was so much extraneous code, you were a beginning programmer. But from your reply, I was wrong. My point is that posted code should be condensed and self contained so that people will read it. Certain styles are more condensed.
That it is based on a published example is new information, and gives context that readers need.
Now just thinking out loud: I see that you commented out #n.prepareGeometryChange(). How are the nodes and edges parented to each other? If you delete one, what does that do to the retained (cumulative, current?) transform of its children? Is this a case where you need to call some Qt method that informs it to recompute geometry and repaint? I have found that generally Qt takes care of that without much effort, without needing to call update().
(Seriously, some of my recent posts are intended to be helpful, but also just to bump my points so I can add notes to the documentation, for the things I have experienced and solved. And where others can edit my posts to delete my obnoxiousness.)
-
Part of what obscures the code is your reindexing. So I downloaded the code and tested it. I thought it would be a better and simpler design if, instead of the graphics view deleting a node, a node removing itself from the scene when it gets mousePressEvent. But even that simpler code exhibits the strange behaviour.
I see that nodes and edges are all parented to the top, and that your code only calls setPos() on node creation.
Is it a problem with some Qt cache and Qt's implementation of dragging? When you click on a node, doesn't QGraphicsView get involved to dispatch to scene items, and to implement Qt drag events? The problem doesn't manifest itself until the first move event. The example doesn't have any special code to move a node when you drag it. Could that be where the problem is, in the hidden Qt magic to implement dragging?
-
Well that is why its not clear to me.
One thing that the user can do to avoid the problem is to click on a node but not move the mouse.
So no dragging (mouseMoveEvent) messages get sent - this seems to clear up the cache and subsequent mouse dragging does not incorrectly reposition the element at 0,0. So it seems mouse tracking is key to the fault. I agree it looks like bug in QT but it seems to me this bug must have been found by now if it really was a bug in QT. I think QtGui.QGraphicsItem is extensively used in QT apps. So I am reluctant to raise a bug report when it is more likely that my understanding of how QT works is flawed and incomplete.I have tried overriding the mouse drag event and stuffing a good position in there manually - but I have not succeeded at such a draconian method as keeping my own position and trying to determine when the new pos suddenly moves close to 0 (not exactly 0 mind - which would be easy - but 0 + the mouse delta).
@
def mouseMoveEvent(self,event):
print event.pos(), event.scenePos(), event.screenPos()
QtGui.QGraphicsItem.mouseMoveEvent(self, event)@The other area - but I can't see how it should affect it - would be to remove all weakrefs and see if there is some collision here with the mouse dragging behaviour. But I have not done this.
Thanks for looking.