itemChange() problem



  • I am using pyqt and Python 3. I want to prevent a QGraphicsRectItem to cross the horizontal axis (y=0) in a QGraphicsScene when dragged with the mouse. I am using the following code (employing height() because the rectangle is in the upper half of the screen). See below a full example of the code.

    ######################################################

    import sys
    from PyQt4.QtCore import Qt, QPointF
    from PyQt4.QtGui import QGraphicsRectItem, QGraphicsLineItem, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem
    
    class MyRect(QGraphicsRectItem):
        def __init__(self, w, h):
            super().__init__(0, 0, w, h)
            self.setFlag(QGraphicsItem.ItemIsMovable, True)
            self.setFlag(QGraphicsItem.ItemIsSelectable, True)
            self.setFlag(QGraphicsItem.ItemIsFocusable, True)
            self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
    
        def itemChange(self, change, value):
            if change == QGraphicsItem.ItemPositionChange:
                if self.y() + self.rect().height() > 0:
                    return QPointF(self.x(), -self.rect().height())
            return value
    
    def main():
        # Set up the framework.
        app = QApplication(sys.argv)
        gr_view = QGraphicsView()
        scene = QGraphicsScene()
        scene.setSceneRect(-100, -100, 200, 200)
        gr_view.setScene(scene)
    
        # Add an x-axis
        x_axis = QGraphicsLineItem(-100, 0, 100, 0)
        scene.addItem(x_axis)
    
        # Add the restrained rect.
        rect = MyRect(50, 50)
        rect.setPos(-25, -100) # <--- not clear to me why I have to do this twice to get the 
        rect.setPos(-25, -100) # item positioned. I know it has to do with my itemChanged above...
        scene.addItem(rect)
    
        gr_view.fitInView(0, 0, 200, 200, Qt.KeepAspectRatio)    
        gr_view.show()
        sys.exit(app.exec_())
    
    if __name__ == '__main__':
        main()
    

    ##################################################

    In principle this is working, but when I drag the mouse below the horizontal axis (y=0), the rectangle flickers and jumps back and forth between the mouse position and its restrained position in the upper hemiplane. So it looks like the drag first moves it to the mouse cursor, and only then the position is adjusted retroactively. I would like the adjustment to happen before the item is moved (visibly) at all. What is the recommended approach to do this right?

    Using mouseMoveEvent towards the same end works very well, by the way. It's just that this fails when I select a group of items, because then the action only applies to the item under the mouse cursor.


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Not a direct answer but did you took a look at the Colliding Mice Example ? It shows a way to do collision detection.

    Hope it helps



  • @SGaist
    SGaist, thank you for the interesting example, but it didn't get me any further in answering the question what I'm doing wrong in my itemChange() approach above. The colliding mice don't make use of itemChange().



  • @Oatmeal
    Please bear in mind that I know nothing about QGraphicsScene, but....

    Clearly from what you say you observe it's "too late" by the time the itemChange event handler is called --- I presume Qt has already moved the item, and returning a new position works but causes a flicker-move-redraw. Correct?

    If so, it seems to me you'll need to handle the situation earlier, before the undesired move has happened. Can you instead intercept/handle the "drag" event, calculate the fact that it would move the rectangle to an undesired position, and deal with there? (Assumes that comes before the item is actually moved.)

    Else you'll have to handle it at the MouseMoveEvent stage. And I think https://stackoverflow.com/a/13102984/489865 might give you enough idea to alter for what you need? Also perhaps look at https://stackoverflow.com/a/4161542/489865 ?



  • I do something similar (although in C++), and it works for me. The only difference I see between our approaches is that, instead of directly returning the corrected value, I call the base class itemChange method with it.



  • @JNBarchan
    Thanks for your suggestions. Yes, you are correct. And yes, I would like to handle the situation before the actual move has happened. And it does work like a charm when I handle the mouseEvent. The problem with that, though, is that when I select two items and drag them, mouseEvent() is only called for the one that actually is under the mouse cursor. So that one gets constrained, but the other one still can happily move wherever it wants.

    I took this this restraining method from the QT documentation (http://doc.qt.io/qt-4.8/qgraphicsitem.html#itemChange). I'm now wondering if it has something to do with the fact that I'm using Python, because it is inherently slower than C++.



  • @Oatmeal
    Although I don't know what your exact code is, I should be most surprised if it were any noticeably slower than the corresponding C++. Python is not bad at speed, but in any case a few lines of code at the Python side is neither here nor there: the time will be spent in the Qt code it calls/is called from in a UI context.



  • @JNBarchan
    I posted my exact code at the beginning (between the lines of hash tags), but I admit I didn't do a good job at getting the web page to display it well.



  • @Oatmeal

    but I admit I didn't do a good job at getting the web page to display it well.

    :) When posting a block of code here, use the </> "toolbar" button to cause to surround the whole code. It puts in:
    ```
    (triple-backtick) markers on a line of their own at the start & end of the whole block, and does not rely on you indenting each line with spaces!



  • @JNBarchan
    Thanks. I edited my post to make the code look prettier.



  • Come on, guys, I wouldn't have thought that I have discovered an unsolvable problem with Qt or that I am trying to do something so far out of the ordinary. I'm sure that if somebody versed in Qt threw the above code in an editor and ran it, they would find out in the blink of an eye what I am doing wrong.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.