2

I have a QGraphicsScene and its associated QGraphicsView. I let the user create some shapes in the form of derived QGraphicsItems into that scene. I also want them to be movable by mouse. Clicking one or more items select them, and moving them around while the mouse button is pressed works. I inherited QGraphicsView to do this, and overrided mousePressedEvent, mouseReleasedEvent & mouseMoveEvent to achieve this. When the user clicks, I am basically testing if an item (accessed through items() which returns the items of the associated scene) is under the mouse with contains(), and if it is then I am selecting it.

In the mouseMoveEvent, I am using setPos() on each item of the selection to move it relatively to the mouse move. It works and displays as expected.

This may not be the most efficient way, but that's what I achieved while discovering Qt. Now, the problem is : once I've moved my item or group of items with the mouse, if I want to deselect them (by clicking again on them), the contains() method supplied with the position of the input acts as if the item wasn't moved. Example : I draw a rectangle in the upper left corner, and move it around to, say, the center of the view. Clicking on it again doesn't work but clicking on where it was initially works. So I suspect it has something to do with local and global coordinates.

I've run through several problems today (most of them resolved) but I'm stuck on this one.

Here's my View class :

class CustomGraphicsView(QGraphicsView):


    def __init__(self, *args):
        super().__init__(*args)

        self.selection = []
        self.offsets = []
        self.select_point = None

    def mousePressEvent(self, event):
        pos = self.mapFromGlobal(event.globalPos())
        modifiers = event.modifiers()

        if event.button() == Qt.LeftButton:
            #do something else

        elif event.button() == Qt.RightButton:
            self.select_point = pos
            for s in self.selection:
                if s.contains(pos): # deselect or drag
                    for s in self.selection: # construct the offsets for dragging
                        self.offsets = [s.pos() - pos for s in self.selection]
                    break

    def mouseReleaseEvent(self, event):
        pos = self.mapFromGlobal(event.globalPos())
        modifiers = event.modifiers()

        if event.button() == Qt.LeftButton:
            #do something else

        elif event.button() == Qt.RightButton:
            if self.select_point == pos: # one click selection
                    self.update_selection(pos)
            if self.offsets:
                self.offsets.clear()

    def mouseMoveEvent(self, event):
        pos = self.mapFromGlobal(event.globalPos())
        modifiers = event.modifiers()

        if event.buttons() == Qt.RightButton:
            if not self.offsets:
                for s in self.selection:
                    self.offsets = [s.pos() - pos for s in self.selection]

            for s, off in zip(self.selection, self.offsets):
                s.set_pos(pos + off)

    def update_selection(self, pos):
        for item in self.items():
            if not item.contains(pos):
                continue
            if item.selected:
                self.selection.remove(item)
            else:
                self.selection.append(item)
            item.select()
            break

The scene rect is set at (0;0) so there's no concern about moving it or whatever.

  • Are you sure that you are not confused with `pos()` and `scenePos()` getters in your handlers? First one returns position in item coordinate if you handle it in graphics item subclass. Adding some code will help to answer your question a lot – dvvrd Oct 29 '13 at 21:40
  • @dvvrd I added my View class, hope it'll make things clearer. – teh internets is made of catz Oct 29 '13 at 21:58
  • It is not very clear where you actually deselect items. I see `item.select()` in every case in `update_selection()`. Maybe deselection works by clicking anywhere on scene? This is expected behaviour when ItemIsSelectable flag is set. – dvvrd Oct 29 '13 at 22:20
  • @dvvrd `item.select()` is just a method I wrote for my items, inheriting `QGraphicsItem`; that's some logic that is not relevant here (invert selection color, mainly) – teh internets is made of catz Oct 29 '13 at 23:42
  • 2
    usually when the `QGraphicsItem` got the flags set to `QGraphicsItem::itemIsMoveable` and `QGraphicsItem::itemIsSelectable` you should have no need to handle the item movement cause the scene/view will handle that for you. the only thing is, you could move the item out of the scenes rect and that has to be cought – Zaiborg Oct 30 '13 at 06:57
  • @Zaibord Oh great, I'll take a look. – teh internets is made of catz Oct 30 '13 at 10:44
  • Maybe you should compare items in self.items() and self.selection and check if their positions are the same. – tharibo Oct 30 '13 at 17:55

0 Answers0