Flask versus PyQt5 - Flask controls program flow?

2019-08-23 05:34发布

问题:

Flask appears to prevent PyQt5 UI from updating.

The respective code works properly for either PyQt5 or Flask - but not together. I understand that it may need to do with the way threading is set up.

Any assistance would be greatly appreciated. TIA.

`

import sys
import serial
import threading

from PyQt5.QtWidgets import QWidget, QLabel, QApplication
from flask import Flask, render_template, request, redirect, url_for

app1 = Flask(__name__)

ser = serial.Serial ("/dev/ttyS0", 57600,timeout=3)    #Open port with baud rate

count=0
temp = []

class Example(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        global count
        count = 1

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('PyQt5 vs Flask')
        self.lbl1 = QLabel('Count '+str(count), self)
        self.lbl1.move(100, 50) 
        self.show()

        threading.Timer(5,self.refresh).start()

    def refresh(self):
        global count
        count +=1
        print("UI ",count)

        self.lbl1.setText('Count'+str(count))
        threading.Timer(5,self.refresh).start()

def get_uart():
    global temp
    if ser.inWaiting()>0:
        temp =[str(float(x.decode('utf-8'))) for x in ser.read_until().split(b',')]
        print(temp)
    threading.Timer(1,get_uart).start()

@app1.route("/")
def index():
    global temp  
    templateData = {'temp1' : temp[1] ,'temp2' : temp[2]}
    return render_template('index.html',**templateData)


if __name__ == "__main__":   
    app = QApplication(sys.argv)
    pyqt5 = Example()

    threading.Timer(1,get_uart).start()    
    ser.flushInput()

    #app1.run(host='0.0.0.0',threaded=True, port=5000) # ,debug=True)    

    sys.exit(app.exec_())

`

Need to have a UI to control the data analysis to be displayed on Website.

回答1:

The better way deal with (possibly waiting) processes, is to use Qt's own threads. In this example I've created a QObject subclass that does all processing and eventually sends a signal whenever the condition is valid. I can't install flask right now, so I've not tested the whole code, but you'll get the idea.

The trick is to use a "worker" QObject that does the processing. Once the object is created is moved to a new QThread, where it does all its processing without blocking the event loop (thus, the GUI). You can also create other signals for that object and connect to your slots (which might also be standard python functions/methods outside the QtApplication) which will be called whenever necessary.

class Counter(QtCore.QObject):
    changed = QtCore.pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.count = 0

    def run(self):
        while True:
            self.thread().sleep(1)
            if ser.inWaiting() > 0:
                self.changed.emit('{}: {}'.format(self.count, [str(float(x.decode('utf-8'))) for x in ser.read_until().split(b',')]))
            self.count += 1

class Example(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.counter = Counter()
        self.counterThread = QtCore.QThread()
        self.counter.moveToThread(self.counterThread)
        self.counterThread.started.connect(self.counter.run)
        self.initUI()

    def initUI(self):
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('PyQt5 vs Flask')
        self.lbl1 = QtWidgets.QLabel('Count {}'.format(self.counter.count), self)
        self.lbl1.move(100, 50)
        self.counter.changed.connect(self.lbl1.setText)
        self.counterThread.start()

if __name__ == "__main__":   
    app = QtWidgets.QApplication(sys.argv)
    pyqt5 = Example()
    pyqt5.show()


回答2:

I think the problem stems from how Flask is activated. If the app.run command is given any parameters (even if in a Thread), then it blocks other commands.

The only way I was able to make Flask and PyQt5 work at the same time, was to activate Flask in a dedicated Thread WITHOUT any parameters - SEE BELOW for the various combinations.

Question: Is this a Flask/Python Bug or Feature or some other explanation related to Development vs Production deployment??

In any case, I would like any help with finding a way to deploy flask in a Port other than 5000 - WITHOUT Flask Blocking other code.

import sys
import serial
import threading
import atexit

from PyQt5.QtWidgets import QWidget, QLabel, QApplication
from flask import Flask, render_template, request, redirect, url_for

ser = serial.Serial ("/dev/ttyS0", 57600,timeout=3)    #Open port with baud rate

app = Flask(__name__)

count=0
temp = []

class Example(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        global count
        count = 1       
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('PyQt5 vs Flask')
        self.lbl1 = QLabel("Count "+str(count)+"  ", self)
        self.lbl1.move(100, 50) 
        self.show()

        threading.Timer(5,self.refresh).start()

    def refresh(self):
        global count
        global str_data
        count +=1
        self.lbl1.setText("Count "+str(count)+"  ")

        threading.Timer(0.5,self.refresh).start()

def get_uart():
    global temp
    if ser.inWaiting()>0:
        temp =[str(float(x.decode('utf-8'))) for x in ser.read_until().split(b',')]
        print(temp)

    threading.Timer(1,get_uart).start()

@app.route("/")
def blank():
    global count
    data="Count "+str(count)
    return data

if __name__ == "__main__":
    threading.Timer(5,get_uart).start()

    #app.run                                                 ## Does not block further execution. Website IS NOT available
    #app.run()                                               ## Blocks further execution. Website available at port 5000 without Refresh value
    #app.run(port=5123)                                      ## Blocks further execution. Website available at port 5123 without Refresh value
    #app.run(threaded=True)                                  ## Blocks further execution. Website available at port 5000 without Refresh value
    #threading.Thread(target=app.run()).start()              ## Blocks further execution. Website available at port 5000 without Refresh value
    #threading.Thread(target=app.run(port=5123)).start()     ## Blocks further execution. Website available at port 5123 without Refresh value
    #threading.Thread(target=app.run(threaded=True)).start() ## Blocks further execution. Website available at port 5000 without Refresh value

    threading.Thread(target=app.run).start()                ## Flask DOES NOT block.  Website is available at port 5000 with Refresh value

    print("Flask does not block")

    app1 = QApplication(sys.argv)
    pyqt5 = Example()
    sys.exit(app1.exec_())


回答3:

[SOLVED]

All Flask parameters can be defined as:

port = int(os.environ.get('PORT', local_port))
kwargs = {'host': '127.0.0.1', 'port': port , 'threaded' : True, 'use_reloader': False, 'debug':False}
threading.Thread(target=app.run, daemon = True, kwargs=kwargs).start()

and Flask will NOT Block and run with the parameters defined in kwargs.