簡體   English   中英

無法使用線程化 FlaskAPI 顯示 PyQt5 GUI

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

我有一個 PyQt5 應用程序。 該應用程序比我現在分享的示例要大。 為方便起見,我共享以下代碼。

示例應用程序由 GUI class、FlaskAPI class、class 組成,提供視頻 ZF7B44CFAFD5C62223DB549A2E。 GUI class 使用繼承 QLabel class 的 class 並稱為 ImageLabel ,這部分與我的問題無關。 在 GUI 上顯示視頻 stream 時,我想啟用 C# 應用程序與我的 Python 應用程序交互。 所以我決定使用 Flask 為 C# 應用程序提供一個 REST 接口。 但是,我現在有一個問題。 由於 PyQt5 主線程和 FlaskAPI 的沖突,我試圖應用多線程,但我遇到了一個問題。

我的問題是,如果我啟動 FlaskAPI 線程,我將無法顯示 GUI。 如何使用 GUI 運行 Flask API 並在 GUI class 中通信方法?

主文件

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

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

流線程.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)

圖像標簽.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

根據@musicamante 的反饋,我在 FlaskAPI.py 的__init__中刪除了self.app.run() 我創建了一個新方法,然后在線程啟動時啟動它。

這是我在新線程上的 main.py 中啟動 FlaskAPI 的方法。 主文件

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()

更新的 FlaskAPI 模塊。

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