1
votes

I want to emit a signal from a QGraphicsItem when it is doubled-clicked, in order to change a widget in the main window. The graphics-scene/-item does not provide an emit() method, but I was just wondering if there is an alternate way to do this. The code below has a function within a QGraphicsView class that will print to the terminal when an item is double-clicked. How can I make that into a slot/signal instead (if QGraphicsItem does not support signal/slots)?

import sys
from PySide.QtCore import *
from PySide.QtGui import *

class MyFrame(QGraphicsView):
    def __init__( self, parent = None ):
        super(MyFrame, self).__init__(parent)

        scene = QGraphicsScene()
        self.setScene(scene)
        self.setFixedSize(500, 500)

        pen = QPen(QColor(Qt.green))
        brush = QBrush(pen.color().darker(150))

        item = scene.addEllipse(0, 0, 45, 45, pen, brush)
        item.setPos(0,0)

    def mouseDoubleClickEvent(self, event):
        print("Circle Clicked!")
        # this double click event prints to terminal but how to setup
        # signal/slot to update the QWidget QLabel text instead?

class Example(QWidget):   
    def __init__(self):
        super(Example, self).__init__()
        self.initUI()

    def initUI(self):        
        hbox = QHBoxLayout(self)
        top = QLabel("Double Click Green Circle (Howto change this QWidget Label with signals?)")
        bottom = MyFrame()

        splitter = QSplitter(Qt.Vertical)
        splitter.addWidget(top)
        splitter.addWidget(bottom)

        hbox.addWidget(splitter)
        self.setLayout(hbox)
        self.setGeometry(0, 0, 500, 600)
        self.show()

def main():
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
1
Define a custom itemDoubleClicked signal on a subclass of QGraphicsScene, then do self.scene().itemDoubleClicked.emit() from the item.ekhumoro
do you have an example?cvw76
class GS(QGraphicsScene): itemDoubleClicked = QtCore.pyqtSignal().ekhumoro
hi, yes, doesn't work. Errors with "does not have emit() attribute". Making a separate QGraphicsObject subclass and calling func there doesnt error but doesnt emit either. I've seen this issue of signal/slots on QGraphicsItem raised many times on searches but nobody can ever provide an example or evidence this can actually be done without totally rewriting the QGraphicsScene. That is what most folk do, so guessing its impossible otherwise to signal from QGraphis to QWidget. Shame, renders QGraphics classes pretty useless in real applications. Time to develop my own QGraphics I guess.cvw76
The code I posted works perfectly fine for me. Not sure what you're doing wrong. I would guess you didn't create an instance of the QGraphicsScene subclass and set it on the view.ekhumoro

1 Answers

3
votes

Below is a simple example showing one way to emit signals from a graphics-item. This defines a custom signal on a subclass of QGraphicsScene and then uses the scene() method of graphics-items to emit it:

import sys
from PySide import QtCore, QtGui

class GraphicsScene(QtGui.QGraphicsScene):
    itemDoubleClicked = QtCore.Signal(object)

class GraphicsRectangle(QtGui.QGraphicsRectItem):
    def mouseDoubleClickEvent(self, event):
        self.scene().itemDoubleClicked.emit(self)

class Window(QtGui.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.view = QtGui.QGraphicsView()
        self.scene = GraphicsScene(self)
        self.view.setScene(self.scene)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.view)
        for i in range(1, 4):
            self.scene.addItem(GraphicsRectangle(50 * i, 50 * i, 20, 20))
        self.scene.itemDoubleClicked.connect(self.handleItemDoubleClicked)

    def handleItemDoubleClicked(self, item):
        print(item.boundingRect())

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 100, 300, 200)
    window.show()
    sys.exit(app.exec_())

UPDATE:

Below is a an example based on the code in your question. The basic idea is the same: define a custom signal on an available QObject (the graphics-view in this case), and use that to emit the double-click notification.

import sys
from PySide.QtCore import *
from PySide.QtGui import *

class MyFrame(QGraphicsView):
    itemDoubleClicked = Signal(object)

    def __init__(self, parent=None):
        super(MyFrame, self).__init__(parent)
        scene = QGraphicsScene()
        self.setScene(scene)
        self.setFixedSize(500, 500)
        for i, color in enumerate('red blue green'.split()):
            pen = QPen(QColor(color))
            brush = QBrush(pen.color().darker(150))
            item = scene.addEllipse(i * 50, i * 50, 45, 45, pen, brush)
            item.setData(0, color.upper())

    def mouseDoubleClickEvent(self, event):
        item = self.itemAt(event.pos())
        if item is not None:
            self.itemDoubleClicked.emit(item)

class Example(QWidget):
    def __init__(self):
        super(Example, self).__init__()
        self.initUI()

    def initUI(self):
        hbox = QHBoxLayout(self)
        top = QLabel('Double Click a Circle')
        bottom = MyFrame()
        bottom.itemDoubleClicked.connect(
            lambda item, top=top:
                top.setText('Double Clicked: %s' % item.data(0)))
        splitter = QSplitter(Qt.Vertical)
        splitter.addWidget(top)
        splitter.addWidget(bottom)
        hbox.addWidget(splitter)
        self.setLayout(hbox)
        self.setGeometry(0, 0, 500, 600)
        self.show()

def main():
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()