“ QWidget”对象没有属性“ set_status_message”

时间:2018-11-13 06:56:27

标签: python-2.7 pyqt pyqt5

我使用PyQt5 and Python2.7

我有四个班级。 App, UIWidget, PlayStreaming and Thread

AppUIWidget的父级。

UIWidgetPlayStreaming的父级。

PlayStreamingThread的父级。

我喜欢从Statusbar message传递Thread Class to App Class,以便更新状态。

我随后使用子类的self.parent().set_status_message('')在App类中显示带有self.statusBar().showMessage('')的消息。

但是我在UIWidget中的错误是

AttributeError: 'QWidget' object has no attribute 'set_status_message'
Aborted (core dumped)

如何从Child类更新到MainWindow中的状态栏?

我的代码如下。

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
import cv2
import time
import face_recognition.api as face_recognition

class Thread(QtCore.QThread):
    changePixmap = QtCore.pyqtSignal(QtGui.QImage)
    scaled_size = QtCore.QSize(640, 480)          
    curScale=1.0
    def run(self):
        cap = cv2.VideoCapture(-1)
        cap.set(3,1280);
        cap.set(4,1024);
        time.sleep(2)
        self.maxHeight=cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
        self.maxScale=self.maxHeight/480.0
        while True:
            ret, frame = cap.read()                  
            if ret:
                r=1
                face_locations=[]
                rescaleSize=int(480*self.curScale)
                if(frame.shape[0] > 480 and frame.shape[1] > 640):
                    r = rescaleSize / float(frame.shape[0])
                    dim = (int(frame.shape[1] * r), rescaleSize)
                    face_locations = face_recognition.face_locations(cv2.resize(frame, dim, fx=0.0, fy=0.0))
                else:
                    face_locations = face_recognition.face_locations(frame)
                for face_location in face_locations:  
                    top, right, bottom, left = face_location
                    cv2.rectangle(frame,(int(right/r),int(top/r)),(int(left/r),int(bottom/r)),(0,255,0),2)
                rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                convertToQtFormat = QtGui.QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0], QtGui.QImage.Format_RGB888)
                p = convertToQtFormat.scaled(self.scaled_size, QtCore.Qt.KeepAspectRatio)
                self.changePixmap.emit(p)

    @QtCore.pyqtSlot(QtCore.QSize)
    def scaled(self, scaled_size):
        self.scaled_size = scaled_size 

    @QtCore.pyqtSlot()
    def scaleup(self):
        self.curScale = self.curScale + 0.1
        if self.curScale > self.maxScale:
            self.curScale = self.maxScale
        self.parent().set_status_message('Cur scale:'+str(self.curScale))

    @QtCore.pyqtSlot()
    def scaledown(self):
        self.curScale = self.curScale - 0.1
        if self.curScale < 1.0:
            self.curScale = 1.0
        self.parent().set_status_message('Cur scale:'+str(self.curScale))


class PlayStreaming(QtWidgets.QLabel):
    reSize = QtCore.pyqtSignal(QtCore.QSize)
    scaleupSignal = QtCore.pyqtSignal()
    scaledownSignal = QtCore.pyqtSignal()
    def __init__(self):
        super(PlayStreaming, self).__init__()
        self.initUI()

    @QtCore.pyqtSlot(QtGui.QImage)
    def setImage(self, image):
        self.label.setPixmap(QtGui.QPixmap.fromImage(image))

    def initUI(self):
        self.setWindowTitle("Image")
        # create a label
        self.label = QtWidgets.QLabel(self)
        th = Thread(self)
        th.changePixmap.connect(self.setImage)
        self.scaleupSignal.connect(th.scaleup)
        self.scaledownSignal.connect(th.scaledown)
        self.reSize.connect(th.scaled)
        th.start()
        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)

    def resizeEvent(self, event):
        self.reSize.emit(self.size())
    def set_status_message(self, message):
        return self.parent().set_status_message(message) 

class UIWidget(QtWidgets.QWidget):

    def __init__(self, parent=None):
        super(UIWidget, self).__init__(parent)

        # Initialize tab screen
        self.tabs = QtWidgets.QTabWidget()
        self.tab1 = QtWidgets.QWidget()
        self.tab2 = QtWidgets.QWidget()
        self.tab3 = QtWidgets.QWidget()

        # Add tabs
        self.tabs.addTab(self.tab1, "Face")
        self.tabs.addTab(self.tab2, "Human")
        self.tabs.addTab(self.tab3, "Vehicle")

        self.display = PlayStreaming()
        # Create first tab
        self.createGridLayout()
        self.tab1.layout = QtWidgets.QVBoxLayout()
        self.tab1.layout.addWidget(self.display, stretch=1)
        self.tab1.layout.addWidget(self.horizontalGroupBox)
        self.tab1.setLayout(self.tab1.layout)

        # Add tabs to widget
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.tabs)

    def createGridLayout(self):
        self.horizontalGroupBox = QtWidgets.QGroupBox("")
        self.horizontalGroupBox.setStyleSheet("QGroupBox{ background-color: red; border: none;}")  
        hlay1 = QtWidgets.QHBoxLayout()
        self.TestButton=QtWidgets.QPushButton('Test')
        hlay1.addWidget(self.TestButton) 
        self.RunButton=QtWidgets.QPushButton('Run')
        hlay1.addWidget(self.RunButton) 
        self.ScaleUpButton=QtWidgets.QPushButton('ScaleUp')
        self.ScaleUpButton.clicked.connect(self.display.scaleupSignal)
        hlay1.addWidget(self.ScaleUpButton) 
        self.ScaleDownButton=QtWidgets.QPushButton('ScaleDown')
        self.ScaleDownButton.clicked.connect(self.display.scaledownSignal)
        hlay1.addWidget(self.ScaleDownButton) 

        hlay2 = QtWidgets.QHBoxLayout()
        hlay2.addWidget(QtWidgets.QPushButton('Set Faces')) 
        hlay2.addWidget(QtWidgets.QPushButton('FacePose'))
        hlay2.addWidget(QtWidgets.QPushButton('Gender')) 
        hlay2.addWidget(QtWidgets.QPushButton('Age'))
        hlay2.addWidget(QtWidgets.QPushButton('Recognize'))

        layout = QtWidgets.QVBoxLayout()        
        layout.addLayout(hlay1)
        layout.addLayout(hlay2)
        self.horizontalGroupBox.setLayout(layout)

    def set_status_message(self, message):
        return self.statusBar().set_status_message(message) 

class App(QMainWindow): 
    def __init__(self):
        super(App,self).__init__()
        self.title = 'FaceHumanVehicle'
        self.left = 10
        self.top = 10
        self.width = 1000
        self.height = 800   
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.form_widget = UIWidget(self) 
        self.statusBar().showMessage('') 
        self.setCentralWidget(self.form_widget) 
        self.show()

    def set_status_message(self, message):
        return self.statusBar().showMessage(message) 

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

1 个答案:

答案 0 :(得分:1)

每当您将小部件添加到布局时,它将自动重新成为包含该布局的小部件的父级。所以这段代码:

    self.display = PlayStreaming()
    # Create first tab
    self.createGridLayout()
    self.tab1.layout = QtWidgets.QVBoxLayout()
    self.tab1.layout.addWidget(self.display, stretch=1)

表示self.display.parent()返回self.tab1,显然它没有set_status_message方法(因为它只是普通的QWidget)。

但是,尝试直接从线程访问gui方法是一个坏习惯,因此更好的解决方案是发出自定义信号来发送状态栏消息:

class Thread(QtCore.QThread):
    statusMessage = QtCore.pyqtSignal(str)
    ...
    def scaleup(self):
        ...
        self.statusMessage.emit('Cur scale:'+str(self.curScale))

然后您可以将其连接到您的PlayStreaming类中,并使用其window()方法访问顶级主窗口:

class PlayStreaming(QtWidgets.QLabel):
    ...
    def initUI(self):
        ...
        th = Thread(self)
        th.statusMessage.connect(self.handle_status_message)

    def handle_status_message(self, message):
        self.window().set_status_message(message)