PyQt程序崩溃QPixmap问题
PyQt program crashes QPixmap issue
我有一个 PyQt 代码,它不断崩溃并给我错误消息 QPixmap::fromImage: QPixmap cannot be created without a QGuiApplication QPixmap: Must construct a QGuiApplication before a QPixmap
这是一个相当简单的应用程序,我从一个名为 CameraWidget
的 class 读取帧并应用函数 def transform_perspective(self, frame, points)
来获取该帧的变换透视图。我将我的屏幕分成两个相等的部分,右半部分显示相机看到的框架,左半部分显示它的透视变换(为此我从另一个名为 class 的坐标14=]).
还有一个问题:左半部分没有占据整个区域。很多部分只是显得黑色。这是一张图片供您参考
这是一个相当长的程序,所以下面我包括了我认为问题所在的 class。
class TopView(QtWidgets.QWidget):
def __init__(self, parent=None):
super(TopView, self).__init__(parent)
self.original_frame = CameraWidget('Abc.ts')
# Layouts and frames
self.frame = QtWidgets.QFrame()
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.frame)
layout.setContentsMargins(0,0,0,0)
layout.setSpacing(0)
self.setLayout(layout)
# frame left
self.frame_left = QtWidgets.QFrame()
self.get_frame_thread = Thread(target=self.transform_frame, args=())
self.get_frame_thread.daemon = True
self.get_frame_thread.start()
self.top_view_label = QtWidgets.QLabel()
self.top_view_label.setScaledContents(True)
self.layout_left = QtWidgets.QVBoxLayout()
self.layout_left.addWidget(self.top_view_label)
self.layout_left.setContentsMargins(0,0,0,0)
self.layout_left.setSpacing(0)
self.frame_left.setLayout(self.layout_left)
# frame right
self.frame_right = QtWidgets.QFrame()
self.frame_right.setStyleSheet("background-color: rgb(153, 187, 255)")
self.video_frame_1 = self.original_frame
# Create camera widgets
print('Creating Camera Widgets...')
self.layout_right = QtWidgets.QVBoxLayout()
self.layout_right.addWidget(self.video_frame_1)
self.layout_right.setContentsMargins(5,5,5,5)
self.frame_right.setLayout(self.layout_right)
self.layout_inner = QtWidgets.QHBoxLayout()
self.layout_inner.addWidget(self.frame_left, 50)
self.layout_inner.addWidget(self.frame_right, 50)
self.layout_inner.setContentsMargins(0,0,0,0)
self.layout_inner.setSpacing(0)
self.frame.setLayout(self.layout_inner)
self.setLayout(layout)
sizeObject = QtWidgets.QDesktopWidget().screenGeometry(0)
self.screen_width = int(0.7*sizeObject.width())
self.screen_height = int(0.7*sizeObject.height())
def event(self, e):
if e.type() in (QtCore.QEvent.Show, QtCore.QEvent.Resize):
print('')
return QtWidgets.QWidget.event(self, e)
def transform_frame(self):
while True:
try:
self.top_view_frame = self.transform_perspective(self.original_frame.get_video_frame(), self.original_frame.canvas.mapped_list)
h, w, ch = self.top_view_frame.shape
bytesPerLine = ch * w
self.img = QtGui.QImage(self.top_view_frame, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
self.pix = QtGui.QPixmap.fromImage(self.img)
if not sip.isdeleted(self.top_view_label):
self.top_view_label.setPixmap(self.pix)
except Exception as e:
print(e)
def transform_perspective(self, frame, points):
points = np.float32(points)
p2 = np.float32([[0, 0], [600, 0], [0, 600], [600, 600]])
self.matrix = cv2.getPerspectiveTransform(points, p2)
frame = cv2.warpPerspective(frame, self.matrix, (400, 600))
return frame
尽管OP不提供MRE,但很容易注意到错误是它在禁止的辅助线程中创建QPixmap。相反,您应该将 QImage 发送到 GUI 线程,并在 GUI 线程中将其转换为 QPixmap:
class ImageProcessor(QtCore.QObject):
imageChanged = QtCore.pyqtSignal(QtGui.QImage)
def process(self, video_frame, mapped_list):
thread = Thread(
target=self._execute,
args=(
video_frame,
mapped_list,
),
)
thread.daemon = True
thread.start()
def _execute(self, video_frame, mapped_list):
top_view_frame = self.transform_perspective(video_frame, mapped_list)
qimage = self.convert_np_to_qimage(top_view_frame)
self.imageChanged.emit(qimage.copy())
def transform_perspective(self, frame, points):
points = np.float32(points)
p2 = np.float32([[0, 0], [600, 0], [0, 600], [600, 600]])
matrix = cv2.getPerspectiveTransform(points, p2)
frame = cv2.warpPerspective(frame, matrix, (400, 600))
return frame
def convert_np_to_qimage(self, array):
h, w, ch = array.shape
bytesPerLine = ch * w
img = QtGui.QImage(array.data, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
return img.copy()
class TopView(QtWidgets.QWidget):
def __init__(self, parent=None):
super(TopView, self).__init__(parent)
self.original_frame = CameraWidget("Abc.ts")
# Layouts and frames
self.frame = QtWidgets.QFrame()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.frame)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
# frame left
self.frame_left = QtWidgets.QFrame()
self.top_view_label = QtWidgets.QLabel()
self.top_view_label.setScaledContents(True)
self.layout_left = QtWidgets.QVBoxLayout(self.frame_left)
self.layout_left.addWidget(self.top_view_label)
self.layout_left.setContentsMargins(0, 0, 0, 0)
self.layout_left.setSpacing(0)
# frame right
self.frame_right = QtWidgets.QFrame()
self.frame_right.setStyleSheet("background-color: rgb(153, 187, 255)")
self.video_frame_1 = self.original_frame
# Create camera widgets
print("Creating Camera Widgets...")
self.layout_right = QtWidgets.QVBoxLayout(self.frame_right)
self.layout_right.addWidget(self.video_frame_1)
self.layout_right.setContentsMargins(5, 5, 5, 5)
self.layout_inner = QtWidgets.QHBoxLayout(self.frame)
self.layout_inner.addWidget(self.frame_left, 50)
self.layout_inner.addWidget(self.frame_right, 50)
self.layout_inner.setContentsMargins(0, 0, 0, 0)
self.layout_inner.setSpacing(0)
sizeObject = QtWidgets.QDesktopWidget().screenGeometry(0)
self.screen_width = int(0.7 * sizeObject.width())
self.screen_height = int(0.7 * sizeObject.height())
self.processor = ImageProcessor()
self.processor.imageChanged.connect(self.handle_imageChanged)
self.launch_processor()
def launch_processor(self):
self.processor.process(
self.original_frame.get_video_frame(),
self.original_frame.canvas.mapped_list,
)
@QtCore.pyqtSlot(QtGui.QImage)
def handle_imageChanged(self, qimage):
qpixmap = QtGui.QPixmap.fromImage(qimage)
self.top_view_label.setPixmap(qpixmap)
QtCore.QTimer.singleShot(0, self.launch_processor)
我有一个 PyQt 代码,它不断崩溃并给我错误消息 QPixmap::fromImage: QPixmap cannot be created without a QGuiApplication QPixmap: Must construct a QGuiApplication before a QPixmap
这是一个相当简单的应用程序,我从一个名为 CameraWidget
的 class 读取帧并应用函数 def transform_perspective(self, frame, points)
来获取该帧的变换透视图。我将我的屏幕分成两个相等的部分,右半部分显示相机看到的框架,左半部分显示它的透视变换(为此我从另一个名为 class 的坐标14=]).
还有一个问题:左半部分没有占据整个区域。很多部分只是显得黑色。这是一张图片供您参考
这是一个相当长的程序,所以下面我包括了我认为问题所在的 class。
class TopView(QtWidgets.QWidget):
def __init__(self, parent=None):
super(TopView, self).__init__(parent)
self.original_frame = CameraWidget('Abc.ts')
# Layouts and frames
self.frame = QtWidgets.QFrame()
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.frame)
layout.setContentsMargins(0,0,0,0)
layout.setSpacing(0)
self.setLayout(layout)
# frame left
self.frame_left = QtWidgets.QFrame()
self.get_frame_thread = Thread(target=self.transform_frame, args=())
self.get_frame_thread.daemon = True
self.get_frame_thread.start()
self.top_view_label = QtWidgets.QLabel()
self.top_view_label.setScaledContents(True)
self.layout_left = QtWidgets.QVBoxLayout()
self.layout_left.addWidget(self.top_view_label)
self.layout_left.setContentsMargins(0,0,0,0)
self.layout_left.setSpacing(0)
self.frame_left.setLayout(self.layout_left)
# frame right
self.frame_right = QtWidgets.QFrame()
self.frame_right.setStyleSheet("background-color: rgb(153, 187, 255)")
self.video_frame_1 = self.original_frame
# Create camera widgets
print('Creating Camera Widgets...')
self.layout_right = QtWidgets.QVBoxLayout()
self.layout_right.addWidget(self.video_frame_1)
self.layout_right.setContentsMargins(5,5,5,5)
self.frame_right.setLayout(self.layout_right)
self.layout_inner = QtWidgets.QHBoxLayout()
self.layout_inner.addWidget(self.frame_left, 50)
self.layout_inner.addWidget(self.frame_right, 50)
self.layout_inner.setContentsMargins(0,0,0,0)
self.layout_inner.setSpacing(0)
self.frame.setLayout(self.layout_inner)
self.setLayout(layout)
sizeObject = QtWidgets.QDesktopWidget().screenGeometry(0)
self.screen_width = int(0.7*sizeObject.width())
self.screen_height = int(0.7*sizeObject.height())
def event(self, e):
if e.type() in (QtCore.QEvent.Show, QtCore.QEvent.Resize):
print('')
return QtWidgets.QWidget.event(self, e)
def transform_frame(self):
while True:
try:
self.top_view_frame = self.transform_perspective(self.original_frame.get_video_frame(), self.original_frame.canvas.mapped_list)
h, w, ch = self.top_view_frame.shape
bytesPerLine = ch * w
self.img = QtGui.QImage(self.top_view_frame, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
self.pix = QtGui.QPixmap.fromImage(self.img)
if not sip.isdeleted(self.top_view_label):
self.top_view_label.setPixmap(self.pix)
except Exception as e:
print(e)
def transform_perspective(self, frame, points):
points = np.float32(points)
p2 = np.float32([[0, 0], [600, 0], [0, 600], [600, 600]])
self.matrix = cv2.getPerspectiveTransform(points, p2)
frame = cv2.warpPerspective(frame, self.matrix, (400, 600))
return frame
尽管OP不提供MRE,但很容易注意到错误是它在禁止的辅助线程中创建QPixmap。相反,您应该将 QImage 发送到 GUI 线程,并在 GUI 线程中将其转换为 QPixmap:
class ImageProcessor(QtCore.QObject):
imageChanged = QtCore.pyqtSignal(QtGui.QImage)
def process(self, video_frame, mapped_list):
thread = Thread(
target=self._execute,
args=(
video_frame,
mapped_list,
),
)
thread.daemon = True
thread.start()
def _execute(self, video_frame, mapped_list):
top_view_frame = self.transform_perspective(video_frame, mapped_list)
qimage = self.convert_np_to_qimage(top_view_frame)
self.imageChanged.emit(qimage.copy())
def transform_perspective(self, frame, points):
points = np.float32(points)
p2 = np.float32([[0, 0], [600, 0], [0, 600], [600, 600]])
matrix = cv2.getPerspectiveTransform(points, p2)
frame = cv2.warpPerspective(frame, matrix, (400, 600))
return frame
def convert_np_to_qimage(self, array):
h, w, ch = array.shape
bytesPerLine = ch * w
img = QtGui.QImage(array.data, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
return img.copy()
class TopView(QtWidgets.QWidget):
def __init__(self, parent=None):
super(TopView, self).__init__(parent)
self.original_frame = CameraWidget("Abc.ts")
# Layouts and frames
self.frame = QtWidgets.QFrame()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.frame)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
# frame left
self.frame_left = QtWidgets.QFrame()
self.top_view_label = QtWidgets.QLabel()
self.top_view_label.setScaledContents(True)
self.layout_left = QtWidgets.QVBoxLayout(self.frame_left)
self.layout_left.addWidget(self.top_view_label)
self.layout_left.setContentsMargins(0, 0, 0, 0)
self.layout_left.setSpacing(0)
# frame right
self.frame_right = QtWidgets.QFrame()
self.frame_right.setStyleSheet("background-color: rgb(153, 187, 255)")
self.video_frame_1 = self.original_frame
# Create camera widgets
print("Creating Camera Widgets...")
self.layout_right = QtWidgets.QVBoxLayout(self.frame_right)
self.layout_right.addWidget(self.video_frame_1)
self.layout_right.setContentsMargins(5, 5, 5, 5)
self.layout_inner = QtWidgets.QHBoxLayout(self.frame)
self.layout_inner.addWidget(self.frame_left, 50)
self.layout_inner.addWidget(self.frame_right, 50)
self.layout_inner.setContentsMargins(0, 0, 0, 0)
self.layout_inner.setSpacing(0)
sizeObject = QtWidgets.QDesktopWidget().screenGeometry(0)
self.screen_width = int(0.7 * sizeObject.width())
self.screen_height = int(0.7 * sizeObject.height())
self.processor = ImageProcessor()
self.processor.imageChanged.connect(self.handle_imageChanged)
self.launch_processor()
def launch_processor(self):
self.processor.process(
self.original_frame.get_video_frame(),
self.original_frame.canvas.mapped_list,
)
@QtCore.pyqtSlot(QtGui.QImage)
def handle_imageChanged(self, qimage):
qpixmap = QtGui.QPixmap.fromImage(qimage)
self.top_view_label.setPixmap(qpixmap)
QtCore.QTimer.singleShot(0, self.launch_processor)