PySide/PyQT5:如何从QGraphicsItem发出信号?

3
我想在QGraphicsItem被双击时发射一个信号,以便更改主窗口中的小部件。图形场景/-项不提供emit()方法,但我只是想知道是否有其他方法来实现这一目标。下面的代码在QGraphicsView类中有一个函数,当项目被双击时会打印到终端。如果QGraphicsItem不支持信号/插槽,我应该如何将其变成插槽/信号?
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()

QGraphicsScene的子类上定义一个自定义的itemDoubleClicked信号,然后从该项执行self.scene().itemDoubleClicked.emit() - ekhumoro
嗨,是的,不行。出现“没有emit()属性”的错误。创建一个单独的QGraphicsObject子类并在那里调用函数不会出错,但也不会发出信号。我在搜索中看到了关于QGraphicsItem上信号/槽问题的讨论很多次,但没有人能够提供一个例子或证据表明这可以在不完全重写QGraphicsScene的情况下实现。大多数人都是这样做的,所以猜测从QGraphis到QWidget发出信号是不可能的。遗憾的是,在实际应用中,QGraphics类几乎没什么用处。是时候开发自己的QGraphics了。 - cvw76
我发布的代码对我来说完全正常。不确定你做错了什么。我猜想你没有创建QGraphicsScene子类的实例并将其设置在视图上。 - ekhumoro
有人能回答这个问题吗?(举个例子,想象你在和阿甘谈话)如果有人能帮忙解决这个问题,我会非常感激的。这是我学习教程中遇到的最后一个问题了。就像我说的,我从来没有看到过关于QGraphicsScene和信号/插槽的工作代码示例(我大约看了100多个),除了抛弃QGraphicsScene框架并重写它之外。双击QGraphicsItem并将信号发射到QGraphics场景之外(例如QWidget)。 - cvw76
我已经发布了一个简单的工作示例。 - ekhumoro
显示剩余4条评论
1个回答

8

以下是一个简单的示例,展示了从图形项中发出信号的一种方式。这定义了一个自定义信号,该信号是在QGraphicsScene的子类上定义的,然后使用graphics-item的scene()方法来发出它:

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_())

更新:

下面是一个基于你问题中的代码的例子。基本思路是相同的:在可用的QObject(在这种情况下是图形视图)上定义自定义信号,并使用它来发出双击通知。

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()

嗨,谢谢,回答了一个问题,但我已经更新了上面的代码,以更好地说明我试图在QGraphicsEvents和QWidgets之间通信事件的方向。如何连接这里嵌套在QWindow中的QGraphicsScene之间的信号,以更新QLabel与当前打印到终端的文本?即,点击后将QLabel更改为“Circle Clicked!”? - cvw76
@cvw76。我根据你的示例添加了第二个示例。(PS:如果您现在将此答案标记为已接受 - 即单击勾号符号,我将不胜感激)。 - ekhumoro
太棒了!Lambda表达式非常优雅,我之前想得太复杂了,这完美地解决了我在小应用程序中尝试实现的问题。非常感谢!我将其标记为完美回答。 - cvw76

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接