-2

I've a PyQt code which keeps on crashes giving me error message QPixmap::fromImage: QPixmap cannot be created without a QGuiApplication QPixmap: Must construct a QGuiApplication before a QPixmap

It's a fairly simple application in which I read frames from one class named CameraWidget and apply a function def transform_perspective(self, frame, points) to get the transform perspective of that frame. I've divided my screen in two equal haves, the right half section displays the frame as seen by the camera and the left half displays the perspective transformation of it (for which I get the coordinates from another class named Canvas).

There is one more issue: the left half doesn't occupy it's entire region. A lot of portion just appears black. Here is a pic for your reference

reference

It's a fairly long program so below I'm including the class where I believe the problem lies.

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
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Voldemort
  • 175
  • 5
  • 20
  • please provide a [mre] – eyllanesc Mar 04 '21 at 20:02
  • @eyllanesc can't you identify the problem from this much code only? The code is quite long and I'm afraid it's quite difficult for me to trim down that code further. Infact, that code itself is **MRE** ...link https://pastebin.com/Mg5Rjhzu – Voldemort Mar 04 '21 at 20:08
  • Are you able to reproduce the issue *just with the code you provided*? I doubt so, so it's **not** reproducible. – musicamante Mar 04 '21 at 20:13
  • @musicamante are you refering to the code in the question or the link I've provided? – Voldemort Mar 04 '21 at 20:18
  • @Voldemort It refers to the code, the link does not interest us (I have not even seen it) since in a while it can be broken making it useless for future visitors. – eyllanesc Mar 04 '21 at 20:20
  • @Voldemort Do not make information as important as the MRE dependent on an external resource that can be broken at any time. The MRE must be in your own post since in SO the posts are self-contained – eyllanesc Mar 04 '21 at 20:21

1 Answers1

1

Although the OP does not provide an MRE it is easy to notice that the error is that it is creating a QPixmap in a secondary thread which is prohibited. Instead you should send the QImage to the GUI thread, and in the GUI thread just convert it to 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)
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Could you also explain why am I seeing so much black space in the left hand side frame despite the fact that I've provided ```setContentsMargins(0,0,0,0)```? – Voldemort Mar 04 '21 at 20:29
  • @Voldemort I don't know sir, that code is not tested because the OP has not provided an MRE – eyllanesc Mar 04 '21 at 20:30