简体   繁体   English

无法使用线程化 FlaskAPI 显示 PyQt5 GUI

[英]Can't display PyQt5 GUI with threading FlaskAPI

I have a PyQt5 app.我有一个 PyQt5 应用程序。 The app is bigger than the example which I am sharing now.该应用程序比我现在分享的示例要大。 I am sharing the following code for convenience.为方便起见,我共享以下代码。

Example application consisting of a GUI class, a FlaskAPI class, a class that provides a video stream.示例应用程序由 GUI class、FlaskAPI class、class 组成,提供视频 ZF7B44CFAFD5C62223DB549A2E。 The GUI class uses the class that inherits QLabel class and called ImageLabel and this part is not related to my question. GUI class 使用继承 QLabel class 的 class 并称为 ImageLabel ,这部分与我的问题无关。 While displaying the video stream on the GUI, I want to enable a C# application to interact with my Python application.在 GUI 上显示视频 stream 时,我想启用 C# 应用程序与我的 Python 应用程序交互。 So I decided to use Flask to provide a REST interface for the C# application.所以我决定使用 Flask 为 C# 应用程序提供一个 REST 接口。 However, I have a problem now.但是,我现在有一个问题。 Because of the collision of the PyQt5 main thread and the FlaskAPI, I am trying to apply multithreading but I faced with a problem.由于 PyQt5 主线程和 FlaskAPI 的冲突,我试图应用多线程,但我遇到了一个问题。

My problem is that I can't display GUI if I start FlaskAPI threading.我的问题是,如果我启动 FlaskAPI 线程,我将无法显示 GUI。 How can I run the Flask API with GUI and communicate a method in GUI class?如何使用 GUI 运行 Flask API 并在 GUI class 中通信方法?

main.py主文件

import sys

from PyQt5.QtCore import Qt, pyqtSlot, QThread
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QPushButton, QHBoxLayout

from FlaskAPI import FlaskAPI
from ImageLabel import ImageLabel
from StreamThread import StreamThread


class MainWindow(QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.init_UI()


    def init_UI(self):
        self.setFixedSize(690, 530)
        self.image_lbl = ImageLabel()

        th = StreamThread()
        th.changePixmap.connect(self.setImage, Qt.QueuedConnection)
        th.start()


        btn_cnt = QPushButton("Continue")
        btn_pa = QPushButton("Pause")

        hbox = QHBoxLayout()
        hbox.addWidget(btn_cnt)
        hbox.addWidget(btn_pa)

        vbox = QVBoxLayout()
        vbox.addWidget(self.image_lbl)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

        btn_pa.clicked.connect(lambda: self.btn_pa_clicked(th))

        self.show()
        self.start_api()

    def start_api(self):
        """
        Start Flask API
        """
        self.thread = QThread()
        self.api = FlaskAPI()
        self.api.moveToThread(self.thread)

        self.thread.start()

    @pyqtSlot(QImage)
    def setImage(self, image):
       """
       Set frame on the ImageLabel instance (inherits QLabel to display video stream)
       """
       self.image_lbl.pixmap = QPixmap.fromImage(image).scaled(720, 540)
       self.image_lbl.setPixmap(QPixmap.fromImage(image).scaled(720, 540))

    def btn_pa_clicked(self, th):
        th.terminate()                                                    # terminates the video stream

        image = QImage("img/default.jpg")  # self.image_lbl.pixmap
        image = image.convertToFormat(QImage.Format_RGB888)
        # image = image.toImage()
        if image is not None:
            #(h, w, c) = image.shape
            #qimage = QImage(image.data, h, w, 3 * h, QImage.Format_RGB888)
            self.image_lbl.pixmap = QPixmap.fromImage(image)
            self.image_lbl.repaint()


def main():
    app = QApplication(sys.argv)
    main_form = MainWindow()
    sys.exit(app.exec_())



if __name__ == '__main__':
    main()

FlaskAPI.py FlaskAPI.py

from PyQt5.QtCore import QObject

from flask import Flask

class FlaskAPI(QObject):
    def __init__(self):
        """
        Run Flask API and set the example route.
        """
        self.app = Flask(__name__)
        self.app.add_url_rule('/get_value', 'get_value', self.get_value)

        self.app.run(debug=True)

    def get_value(self):
        """
        Return example data.
        """
        data = {"id": "value"}

        return data

StreamThread.py流线程.py

import cv2
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtGui import QImage

#https://stackoverflow.com/a/44404713/13080899
class StreamThread(QThread):
    changePixmap = pyqtSignal(QImage)

    def __init__(self):
        super(StreamThread, self).__init__()


    def run(self):
        """
        Start video stream on thread and emit the captured frame
        """
        self.capt = cv2.VideoCapture(0, cv2.CAP_DSHOW)

        while(True):
            ret, frame = self.capt.read()

            if ret:
                rbgImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                h, w, ch = rbgImage.shape
                bytesPerLine = ch*w
                convertToQtFormat = QImage(rbgImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
                p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
                self.changePixmap.emit(p)

ImageLabel.py图像标签.py

from PyQt5 import QtGui, QtCore
from PyQt5.QtCore import Qt, QSize, pyqtSignal, QObject, QPoint, pyqtSlot
from PyQt5.QtWidgets import QLabel

class Communicate(QObject):
    cor_update = pyqtSignal()
    cor_curr = pyqtSignal()

class ImageLabel(QLabel):
    def __init__(self, width=720, height=540):
        super(ImageLabel, self).__init__()
        self.setMouseTracking(True)
        self.pixmap = QtGui.QPixmap("img/im.jpg")

        #### initialize coordinates ####
        self.x1 = 0
        self.y1 = 0

        self.x_curr = 0
        self.y_curr = 0

        self.x2 = 0
        self.y2 = 0

        ##### enable state to allow user for drawing
        self.enable_labelling = False

        ##### enable state to track coordinates for drawing
        self.enable_cor = False

        ################################
        self.source = Communicate()

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        qp.drawPixmap(self.rect(), self.pixmap)
        if self.enable_labelling == True:
            self.paintRect(event, qp)
            qp.end()

    def paintRect(self, event, qp):
        br = QtGui.QBrush(QtGui.QColor(50, 255, 255, 40))
        qp.setBrush(br)

        if self.enable_cor == True:
            qp.drawRect(QtCore.QRect(QPoint(self.x1, self.y1), QSize(self.x_curr - self.x1, self.y_curr - self.y1)))
        else:
            qp.drawRect(QtCore.QRect(QPoint(self.x1, self.y1), QSize(self.x2 - self.x1, self.y2 - self.y1)))

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton and self.enable_labelling == True:
            self.x1 = event.x()
            self.y1 = event.y()

            self.enable_cor = True

            self.source.cor_update.emit()

    def mouseMoveEvent(self, event):
        self.x_curr = event.x()
        self.y_curr = event.y()

        self.source.cor_curr.emit()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton and self.enable_labelling == True:
            self.x2 = event.x()
            self.y2 = event.y()

            self.source.cor_update.emit()

            self.enable_cor = False

According to @musicamante feedback I removed the self.app.run() in __init__ in FlaskAPI.py.根据@musicamante 的反馈,我在 FlaskAPI.py 的__init__中删除了self.app.run() I created a new method then I start it when thread started.我创建了一个新方法,然后在线程启动时启动它。

Here is how I start the FlaskAPI in main.py on a new thread.这是我在新线程上的 main.py 中启动 FlaskAPI 的方法。 main.py主文件

class MainWindow(QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.init_UI()
        self.start_api()

    def start_api(self):
        """
        Start Flask API
        """
        self.thread = QThread()
        self.api = FlaskAPI()
        self.api.moveToThread(self.thread)
        self.thread.started.connect(self.api.start)
        self.thread.start()

The updated FlaskAPI module.更新的 FlaskAPI 模块。

FlaskAPI.py FlaskAPI.py

from PyQt5.QtCore import QObject

from flask import Flask

class FlaskAPI(QObject):
    def __init__(self):
        """
        Run Flask API and set the example route.
        """
        super(FlaskAPI, self).__init__()
        self.app = Flask(__name__)
        self.app.add_url_rule('/get_value/', 'get_value', self.get_value)


    def start(self):
        self.app.run()

    def get_value(self):
        """
        Return example data.
        """
        data = {"id": "value"}

        return data

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM