PySide2 QPushButton.setText() 在停止视频输入后使我的应用程序崩溃

PySide2 QPushButton.setText() crashes my application after halting video feed

我正在创建一个小部件,它在从 QLabel 派生的自定义小部件上显示来自机器视觉相机的实时摄像头源。

但是,当我停止实时查看提要时,我的程序无声地崩溃了。
当我使用调试器时,最后执行的行是 self.ui.btn_prev_st.setText('Start') 在我停止提要的按钮上。如果我越过它,我将进入 CameraControl 的 update_image(),但应用程序会关闭,而调试器仍处于暂停状态。
在那之后,app.exec_() 似乎 return 并且应用程序关闭。
对于图像采集,我使用 pymba。我在 setText() 之前调用 self.cam.disarm() 函数,但如果我单步执行,它会正常执行。 (它在最后调用 gc.collect(),但那不应该做任何事情吗?)
所以要么是我在 Qt 中遇到了一些问题,要么是 pymba disarm 函数是原因。

有人有想法吗?我提供了一个最小的工作示例。
提前致谢!

main.py代码:

import sys
import os
import atexit

from PySide2.QtWidgets import QApplication, QMainWindow
from PySide2.QtCore import QFile, Signal
from PySide2.QtUiTools import QUiLoader
from ui_main import Ui_main
from camera_control import CameraControl

class main(QMainWindow):
    start_acquisition_signal = Signal()
    stop_acquisition_signal = Signal()
    def __init__(self):
        super(main, self).__init__()
        self.ui = Ui_main()
        self.ui.setupUi(self)
        atexit.register(self.cleanup)
        self.ui.btn_prev_st.clicked.connect(self.prev_start_pushed)
        self.ui.btn_set_roi.clicked.connect(self.ui.camera_prev.apply_roi)
        self.ui.btn_reset_roi.clicked.connect(self.ui.camera_prev.reset_roi)
        self.start_acquisition_signal.connect(self.ui.camera_prev.start_preview)
        self.stop_acquisition_signal.connect(self.ui.camera_prev.stop_preview)

    def __del__(self):
        del self.ui.camera_prev

    def cleanup(self):
        del self

    def load_ui(self):
        loader = QUiLoader()
        path = os.path.join(os.path.dirname(__file__), "form.ui")
        ui_file = QFile(path)
        ui_file.open(QFile.ReadOnly)
        loader.load(ui_file, self)
        ui_file.close()

    def prev_start_pushed(self, event):
        if self.ui.btn_prev_st.text() != 'Stop':
            self.start_acquisition_signal.emit()
            self.ui.btn_prev_st.setText('Stop')
        else:
            self.stop_acquisition_signal.emit()
            self.ui.btn_prev_st.setText('Start')

if __name__ == "__main__":
    app = QApplication([])
    widget = main()
    widget.show()
    sys.exit(app.exec_())

camera_control.py:

from typing import Optional
import cv2
import numpy as np
from pymba import Vimba, Frame

from PySide2 import QtGui
from PySide2.QtWidgets import QLabel
from PySide2.QtCore import Signal, Slot, Qt, QPoint, QRect, QSize
from PySide2.QtGui import QPixmap

class CameraControl(QLabel):
    change_pixmap_signal = Signal(np.ndarray)
    def __init__(self, parent=None):
        super(CameraControl, self).__init__(parent)
        self._first_show = True # whether form is shown for the first time
        self.is_running = False
        self.change_pixmap_signal.connect(self.update_image)
        self.vimba = Vimba()
        self.vimba.startup()
        self.cam = self.vimba.camera(0)
        try:
            self.cam.close()
        except:
            pass
        self.cam.open()
        self.setup_camera()
        self.update()

    def __del__(self):
        try:
            self.cam.disarm()
            self.cam.close()
        except:
            pass
        del self.vimba

    @Slot()
    def stop_preview(self):
        self.is_running = False
        #self.cam.stop_frame_acquisition()
        self.cam.disarm()

    @Slot()
    def start_preview(self):
        self.cam.arm('Continuous', self.frame_handler)
        self.cam.start_frame_acquisition()
        self.is_running = True

    def frame_handler(self, frame: Frame, delay: Optional[int] = 1) -> None:
        img = frame.buffer_data_numpy()
        self.change_pixmap_signal.emit(img)

    @Slot(np.ndarray)
    def update_image(self, cv_img):
        """ Updates the image_label with a new opencv image"""
        qt_img = self.convert_cv_qt(cv_img)
        self.setPixmap(qt_img)

ui_main.py:

from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *

from camera_control import CameraControl


class Ui_main(object):
    def setupUi(self, main):
        if not main.objectName():
            main.setObjectName(u"main")
        main.resize(1192, 752)
        self.centralwidget = QWidget(main)
        self.centralwidget.setObjectName(u"centralwidget")
        self.box_preview = QGroupBox(self.centralwidget)
        self.box_preview.setObjectName(u"box_preview")
        self.box_preview.setGeometry(QRect(10, 10, 601, 391))
        self.btn_prev_st = QPushButton(self.box_preview)
        self.btn_prev_st.setObjectName(u"btn_prev_st")
        self.btn_prev_st.setGeometry(QRect(20, 20, 75, 23))
        self.camera_prev = CameraControl(self.box_preview)
        self.camera_prev.setObjectName(u"camera_prev")
        self.camera_prev.setGeometry(QRect(110, 20, 480, 360))
        self.camera_prev.setMinimumSize(QSize(0, 0))
        self.camera_prev.setBaseSize(QSize(640, 480))
        self.camera_prev.setMouseTracking(True)
        self.camera_prev.setFrameShape(QFrame.Panel)
        self.camera_prev.setScaledContents(False)
        self.camera_prev.setAlignment(Qt.AlignCenter)
        main.setCentralWidget(self.centralwidget)
        self.menubar = QMenuBar(main)
        self.menubar.setObjectName(u"menubar")
        self.menubar.setGeometry(QRect(0, 0, 1192, 21))
        main.setMenuBar(self.menubar)
        self.statusbar = QStatusBar(main)
        self.statusbar.setObjectName(u"statusbar")
        main.setStatusBar(self.statusbar)

        self.retranslateUi(main)

        QMetaObject.connectSlotsByName(main)
    # setupUi

    def retranslateUi(self, main):
        main.setWindowTitle(QCoreApplication.translate("main", u"main", None))
        self.box_preview.setTitle(QCoreApplication.translate("main", u"Preview", None))
        self.btn_prev_st.setText(QCoreApplication.translate("main", u"Start", None))
    # retranslateUi

我决定改用官方 VimbyPython 库。这解决了问题。