python-2.7pyqtsignals-slotsqgraphicsviewqgraphicsitem

Emit signals from QgraphicsItem


Hi this is a follow on question from my earlier query Getting events from Context Menu action on QGraphicsItems I am now trying to emit a signal from myQgraphicsItem, the goal being when a user right clicks and selects action1 the notifyaction1 function will tell the scene to do something (Add more items to the graphicsitem that sent the signal) After doing some research i find that the QgraphicsItem cannot emit, see *Stack Overflow: Events and signals in Qt's QGraphicsItem: How is this supposed to work?*

So I added a signaling_object (QObject) to my class, but I am not sure how to send/receive the signal so that the parent scene can get the notification of the user's action.

class Node(QtGui.QGraphicsItem):
Type = QtGui.QGraphicsItem.UserType + 1

def __init__(self, Parent=None):
    super(Node, self).__init__()


    self.edgeList = []
    self.newPos = QtCore.QPointF()

    self.setFlag(QtGui.QGraphicsItem.ItemIsMovable)
    self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable)
    self.setFlag(QtGui.QGraphicsItem.ItemSendsGeometryChanges)
    self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
    self.setZValue(1)
    self.signaling_object=QtCore.QObject()

@QtCore.pyqtSlot()    
def notifyaction(self):
    #print "action1"
    message="action1"

    QtCore.QObject.emit(self.signaling_object,QtCore.SIGNAL('action_update(QString)'),str(message))
    print self.signaling_object.signalsBlocked()
    child_items=self.childItems()
    for item in child_items:
        #print item
        item.hide()
    self.hide()


def contextMenuEvent(self, contextEvent):
    object_cntext_Menu = QtGui.QMenu()
    object_cntext_Menu.addAction("action1")        
    object_cntext_Menu.addAction("action2", self.notifyaction)
    object_cntext_Menu.addAction("action3")
    position=QtGui.QCursor.pos() 
    object_cntext_Menu.exec_(position)

Is there anyway to send notifcations to other qt objects from a qgraphicsItem's context menu. I am using PyQt/Python on windows Many thanks


Solution

  • If you want signal/slot support for graphics items, you could use QGraphicsObject instead of QGraphicsItem. This would allow you to emit customs signals, like this:

    class Node(QtGui.QGraphicsObject):
        customSignal = QtCore.pyqtSignal(str)
        ...
    
        @QtCore.pyqtSlot()    
        def notifyaction(self):
            message = 'action1'
            self.customSignal.emit(message)
    

    To receive these custom signals, just connect an appropriate handler:

     item.customSignal.connect(scene.handleCustomSignal)
    

    However, it may be simpler to avoid signals altogether and just call the scene directly. Every graphics item can access the scene its been added to via its scene method. So you could just do something as simple as this:

        @QtCore.pyqtSlot()    
        def notifyaction(self):
            message = 'action1'
            scene = self.scene()
            if scene is not None:
                scene.handleItemAction(self, message)
    

    And doing things this way means you don't have to connect up signals for every graphics item that is created.