[英]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.