-->

Connecting signal to slot between classes in PyQt

2020-06-29 07:40发布

问题:

Aim is to connect a signal of the top class TicTacToe with the QMainWindow class.

It throws an error: TicTacToe cannot be converted to PyQt5.QtCore.QObject in this context

#!/usr/bin/env python


from PyQt5.QtCore import (QLineF, QPointF, QRectF, pyqtSignal)
from PyQt5.QtGui import (QIcon, QBrush, QColor, QPainter, QPixmap)
from PyQt5.QtWidgets import (QAction, QMainWindow, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem,
                             QGridLayout, QVBoxLayout, QHBoxLayout,
                             QLabel, QLineEdit, QPushButton)

class TicTacToe(QGraphicsItem):
    def __init__(self):
        super(TicTacToe, self).__init__()

    def paintEvent(self, painter, option, widget):
        painter.setPen(Qt.black)
        painter.drawLine(0,100,300,100)


    def boundingRect(self):
        return QRectF(0,0,300,300)

    def mousePressEvent(self, event):
        pos = event.pos()
        self.select(int(pos.x()/100), int(pos.y()/100))
        self.update()
        super(TicTacToe, self).mousePressEvent(event)

    messageSignal = pyqtSignal(int)


class MyGraphicsView(QGraphicsView):
    def __init__(self):
        super(MyGraphicsView, self).__init__()
        scene = QGraphicsScene(self)
        self.tic_tac_toe = TicTacToe()
        scene.addItem(self.tic_tac_toe)

        scene.addPixmap(QPixmap("exit.png"))

        self.setScene(scene)

    def keyPressEvent(self, event):
        key = event.key()
        if key == Qt.Key_R:
            self.tic_tac_toe.reset()
        super(MyGraphicsView, self).keyPressEvent(event)

class Example(QMainWindow):    
    def __init__(self):
        super(Example, self).__init__()

        self.y = MyGraphicsView()
        self.setCentralWidget(self.y)

        self.y.tic_tac_toe.messageSignal.connect (self.messageSlot)

        self.initUI()

    def messageSlot(self, val):
        self.statusBar().showMessage(val)


    def initUI(self):               
        self.toolbar = self.addToolBar('Tools')


        self.setGeometry(30, 30, 30, 20)
        self.setWindowTitle('Menubar')    
        self.show()        


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    mainWindow = Example()

    mainWindow.showFullScreen()
    sys.exit(app.exec_())

回答1:

Only classes that inherit from QObject have the ability to create signals, for example QWidget, QMainWIndow, QGraphicsView inherit from QObject so they can have signals. But QGraphicsItem does not inherit from QObject because of efficiency issues so they do not have the ability to create signals. If you want an item that is a QObject you must use QGraphicsObject. In addition, the items have the paint() method, not paintEvent().

class TicTacToe(QGraphicsObject):
    def paint(self, painter, option, widget):
        painter.setPen(Qt.black)
        painter.drawLine(0,100,300,100)

    def boundingRect(self):
        return QRectF(0,0,300,300)

    def mousePressEvent(self, event):
        pos = event.pos()
        # self.select(int(pos.x()/100), int(pos.y()/100))
        self.update()
        super(TicTacToe, self).mousePressEvent(event)

    messageSignal = pyqtSignal(int)

If you still want to use QGraphicsItem, a possible work around is to create a class that is in charge of the communication and that inherits from QObject:

class Helper(QObject):
    messageSignal = pyqtSignal(int)

class TicTacToe(QGraphicsObject):
    def __init__(self, helper):
        super(TicTacToe, self).__init__()
        self.helper = helper

    def paint(self, painter, option, widget):
        painter.setPen(Qt.black)
        painter.drawLine(0,100,300,100)

    def boundingRect(self):
        return QRectF(0,0,300,300)

    def mousePressEvent(self, event):
        pos = event.pos()
        self.helper.emit(10)
        # self.select(int(pos.x()/100), int(pos.y()/100))
        self.update()
        super(TicTacToe, self).mousePressEvent(event)


class MyGraphicsView(QGraphicsView):
    def __init__(self):
        super(MyGraphicsView, self).__init__()
        scene = QGraphicsScene(self)
        self.helper = Helper(self)
        self.tic_tac_toe = TicTacToe(self.helper)
        scene.addItem(self.tic_tac_toe)
        scene.addPixmap(QPixmap("exit.png"))
        self.setScene(scene)

    def keyPressEvent(self, event):
        key = event.key()
        if key == Qt.Key_R:
            self.tic_tac_toe.reset()
        super(MyGraphicsView, self).keyPressEvent(event)

class Example(QMainWindow):    
    def __init__(self):
        super(Example, self).__init__()

        self.y = MyGraphicsView()
        self.setCentralWidget(self.y)
        self.helper.messageSignal.connect(self.messageSlot)
        self.initUI()

    def messageSlot(self, val):
        self.statusBar().showMessage(val)


    def initUI(self):               
        self.toolbar = self.addToolBar('Tools')
        self.setGeometry(30, 30, 30, 20)
        self.setWindowTitle('Menubar')    
        self.show()  


回答2:

The problem is that your class TicTacToe doesn't inherit -- directly or otherwise -- from QObject meaning it can't be used by Qt as a signal source.

Try inheriting from QGraphicsObject instead...

class TicTacToe(QGraphicsObject):
def __init__(self):
    super(TicTacToe, self).__init__()