我使用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('')
在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_())
答案 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)