pythonpython-2.7pysideqgraphicsitemqgraphicspixmapitem

How do I reimplement the itemChange and mouseMoveEvent of a QGraphicsPixmapItem?


I'm building a T Shirt Designer using PySide. For this, I've set up a QGraphicsScene with the image of the T Shirt as a QPixmapItem on the scene. To overlay the design on the T Shirt, I'm getting the design image PNG from the user and setting that up as another QPixmapItem. I align them up using the setPos() method and then use the setZValue() method to ensure that the Design PNG shows up on top of the T Shirt image.

I've enabled the flags ItemIsMovable, ItemIsSelectable, ItemSendsScenePositionChanges, ItemIsFocusable for the Design image QPixmapItem. So I am able to move the design around over the T Shirt image. Next, I want to restrict this movement to only where the printing is possible. To achieve this, I've followed this question to derive a new QGraphicsPixmapItem class and have tried to reimplement both the itemChange() and the mouseMoveEvent() methods.

Inside these methods, I've tried calling the same methods of the original QPixmapItem class using both super() as well as the regular way QGraphicsPixmapItem.itemChange(change, event). However, nothing seems to be happening. The design moves just fine but it's not getting restricted. To see if the method gets called I added print statements inside these methods but they don't get executed.

I've tried adding setSceneRect() in the scene as well. I've also enabled setMouseTracking on the QGraphicsView. However, none of those things triggers either the itemChanged() or mouseMoveEvent().

There are other questions where people have explained how to do this in C++. However, I'm not able to replicate it in python.

# -*- coding: utf-8 -*-

from PySide import QtCore, QtGui
from os import path

class Pixmap(QtGui.QGraphicsPixmapItem):

    def __init__(self, pix):
        super(Pixmap, self).__init__()
        self.pixmap_item = QtGui.QGraphicsPixmapItem(pix)
        self.pixmap_item.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
        self.pixmap_item.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
        self.pixmap_item.setFlag(QtGui.QGraphicsItem.ItemSendsScenePositionChanges, True)
        self.pixmap_item.setFlag(QtGui.QGraphicsItem.ItemIsFocusable, True)

    def itemChange(self, change, event):
        QtGui.QGraphicsPixmapItem.itemChange(change, event)
        print "Item Changed"
        #Code to restrict to a rectangular area goes here
        return QtGui.QGraphicsPixmapItem.itemChange(self, change, event)

    def mouseMoveEvent(self, event):
        super(Pixmap, self).mouseMoveEvent(event)
        print "Mouse Moved"
        #Code to restrict to a rectangular area goes here

class Ui_frmSelectRoundNeckHalfSleeve(QtCore.QObject):

    def setupUi(self, frmSelectRoundNeckHalfSleeve):
        frmSelectRoundNeckHalfSleeve.setObjectName("frmSelectRoundNeckHalfSleeve")
        frmSelectRoundNeckHalfSleeve.resize(842, 595)
        self.imgRoundNeckTShirt = QtGui.QGraphicsView(frmSelectRoundNeckHalfSleeve)
        self.imgRoundNeckTShirt.setGeometry(QtCore.QRect(20, 20, 500, 500))
        self.imgRoundNeckTShirt.setObjectName("imgRoundNeckTShirt")
        self.imgRoundNeckTShirt.setMouseTracking(True)

        self.tShirtScene = QtGui.QGraphicsScene(frmSelectRoundNeckHalfSleeve)
        self.tShirtScene.setSceneRect(20, 20, 480, 480)
        self.TShirtImage = QtGui.QGraphicsPixmapItem(QtGui.QPixmap("./Images/black-t-shirt.jpg").scaled(480, 480, QtCore.Qt.KeepAspectRatio))
        self.designImagePixmap = QtGui.QPixmap("./Designs/test.png")
        self.designImagePng = Pixmap(self.designImagePixmap.scaledToWidth(135,QtCore.Qt.SmoothTransformation))
        self.TShirtImage.setZValue(10)
        self.designImagePng.pixmap_item.setZValue(40)
        self.designImagePng.pixmap_item.setPos(167,90)
        self.tShirtScene.addItem(self.TShirtImage)
        self.tShirtScene.addItem(self.designImagePng.pixmap_item)
        self.imgRoundNeckTShirt.setScene(self.tShirtScene)

if __name__ == "__main__":

    path = r"E:\\Documents\\T Shirt Designer\\"
    QtGui.QApplication.addLibraryPath(path)

    app = QtGui.QApplication(sys.argv)
    testFile = QtGui.QWidget()
    ui = Ui_frmSelectRoundNeckHalfSleeve()
    ui.setupUi(testFile)

    testFile.show()
    sys.exit(app.exec_())

Solution

  • The error you have is that you are not overwriting the pixmap_item itemChange but a Pixmap. It seems you are confusing inheritance with composition.

    An improvement is that the base item (T-shirt) is the father of the design item so that the coordinates of the design item are relative to the base item.

    Considering the previous thing I have implemented the logic to restrict the movement of the design item to the space of the T-shirt Item.

    # -*- coding: utf-8 -*-
    
    import sys
    from os import path
    from PySide import QtCore, QtGui
    
    
    class Pixmap(QtGui.QGraphicsPixmapItem):
        def __init__(self, pix, parent=None):
            super(Pixmap, self).__init__(pix, parent)
            self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
            self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
            self.setFlag(QtGui.QGraphicsItem.ItemSendsScenePositionChanges, True)
            self.setFlag(QtGui.QGraphicsItem.ItemIsFocusable, True)
    
        def itemChange(self, change, value):
            if change == QtGui.QGraphicsItem.ItemPositionChange:
                parent = self.parentItem()
                if parent is not None:
                    r = self.mapToParent(self.boundingRect()).boundingRect()
                    R = parent.boundingRect()
                    rR = QtCore.QRectF(R.topLeft(), R.size() - r.size())
                    if not rR.contains(value):
                        x = min(max(rR.left(), value.x()), rR.right())
                        y = min(max(rR.top(), value.y()), rR.bottom())
                        return QtCore.QPointF(x, y)
            return QtGui.QGraphicsPixmapItem.itemChange(self, change, value)
    
    
    class Ui_frmSelectRoundNeckHalfSleeve(QtCore.QObject):
        def setupUi(self, frmSelectRoundNeckHalfSleeve):
            frmSelectRoundNeckHalfSleeve.setObjectName(
                "frmSelectRoundNeckHalfSleeve"
            )
            frmSelectRoundNeckHalfSleeve.resize(842, 595)
            self.imgRoundNeckTShirt = QtGui.QGraphicsView(
                frmSelectRoundNeckHalfSleeve
            )
            self.imgRoundNeckTShirt.setGeometry(QtCore.QRect(20, 20, 500, 500))
            self.imgRoundNeckTShirt.setObjectName("imgRoundNeckTShirt")
    
            self.tShirtScene = QtGui.QGraphicsScene(frmSelectRoundNeckHalfSleeve)
            self.tShirtScene.setSceneRect(20, 20, 480, 480)
            self.TShirtImage = QtGui.QGraphicsPixmapItem(
                QtGui.QPixmap("./Images/black-t-shirt.jpg").scaled(
                    480, 480, QtCore.Qt.KeepAspectRatio
                )
            )
            designImagePixmap = QtGui.QPixmap("./Designs/test.png").scaledToWidth(
                135, QtCore.Qt.SmoothTransformation
            )
            self.designImagePng = Pixmap(designImagePixmap, self.TShirtImage)
            self.designImagePng.setZValue(1)
            self.designImagePng.setPos(167, 90)
            self.tShirtScene.addItem(self.TShirtImage)
            self.imgRoundNeckTShirt.setScene(self.tShirtScene)
    
    
    if __name__ == "__main__":
    
        path = r"E:\\Documents\\T Shirt Designer\\"
        QtGui.QApplication.addLibraryPath(path)
        app = QtGui.QApplication(sys.argv)
        testFile = QtGui.QWidget()
        ui = Ui_frmSelectRoundNeckHalfSleeve()
        ui.setupUi(testFile)
        testFile.show()
        sys.exit(app.exec_())