1
votes

Flask versus PyQt5 - Flask contrôle le déroulement du programme?

Flask semble empêcher la mise à jour de l'interface utilisateur de PyQt5.

Le code respectif fonctionne correctement pour PyQt5 ou Flask - mais pas ensemble. Je comprends que cela peut avoir à voir avec la façon dont le threading est configuré.

Toute aide serait grandement appréciée. 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_())

`

Il faut une interface utilisateur pour contrôler l'analyse des données à afficher sur le site Web.


0 commentaires

3 Réponses :


1
votes

La meilleure façon de gérer les processus (éventuellement en attente) est d'utiliser les propres threads de Qt. Dans cet exemple, j'ai créé une sous-classe QObject qui effectue tout le traitement et envoie finalement un signal chaque fois que la condition est valide. Je ne peux pas installer flask pour le moment, donc je n'ai pas testé tout le code, mais vous aurez l'idée.

L'astuce consiste à utiliser un QObject "worker" qui effectue le traitement. Une fois que l'objet est créé, il est déplacé vers un nouveau QThread, où il effectue tout son traitement sans bloquer la boucle d'événements (donc, l'interface graphique). Vous pouvez également créer d'autres signaux pour cet objet et vous connecter à vos slots (qui peuvent également être des fonctions / méthodes python standard en dehors de QtApplication) qui seront appelées chaque fois que nécessaire.

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

p >


1 commentaires

Merci 'musicamante' d'avoir pris le temps de nous aider. J'explorerai votre solution plus en détail.



1
votes

Je pense que le problème vient de la façon dont Flask est activé. Si la commande app.run reçoit des paramètres (même si dans un thread), cela bloque les autres commandes.

La seule façon dont j'ai pu faire fonctionner Flask et PyQt5 en même temps, était d'activer Flask dans un Thread dédié SANS aucun paramètre - VOIR CI-DESSOUS pour les différentes combinaisons.

Question: S'agit-il d'un bug ou d'une fonctionnalité Flask / Python ou d'une autre explication liée au déploiement de développement vs production?

Dans tous les cas, j'aimerais avoir de l'aide pour trouver un moyen de déployer flask dans un port autre que 5000 - SANS Flask Blocking autre 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_())


0 commentaires

2
votes

[RÉSOLU]

Tous les paramètres de Flask peuvent être définis comme:

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

et Flask ne se bloquera PAS et ne fonctionnera PAS avec les paramètres définis dans kwargs.


1 commentaires

oh mec merci beaucoup ... C'était juste génial :) fonctionne parfaitement