У объекта 'QWidget' нет атрибута 'set_status_message' - PullRequest
0 голосов
/ 13 ноября 2018

Я использую PyQt5 and Python2.7.

У меня есть четыре класса. App, UIWidget, PlayStreaming and Thread.

App является родителем UIWidget.

UIWidget является родителем PlayStreaming.

PlayStreaming является родителем Thread.

Мне нравится передавать Statusbar message из Thread Class to App Class, чтобы я мог обновлять статус.

Я использую self.parent().set_status_message('') из дочерних классов впоследствии для отображения сообщения с self.statusBar().showMessage('') в классе приложения.

Но у меня ошибка в UIWidget как

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

Как мне обновить строку состояния в 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 голосов
/ 13 ноября 2018

Всякий раз, когда вы добавляете виджет в макет, он будет автоматически повторно связан с виджетом, который содержит макет.Поэтому этот код:

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

Тем не менее, это плохая практика, чтобы пытаться получить прямой доступ к методам графического интерфейса из потока, поэтому лучшим решением будет испускать пользовательский сигнал, который отправляет сообщение в строке состояния:

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) 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...