如何在QLabel中进行计算时实时更新文本,而不会使界面冻结?

时间:2018-04-05 22:07:14

标签: python python-3.x pyqt pyqt5 qthread

我对Q标签中的实时更新有疑问,因为当我点击“开始”按钮时,屏幕会在执行计算时保持静态几秒钟,但我需要的是在QLabel在模拟前进时显示状态消息。我已尝试使用线程,但我不太了解它,因为它没有成功,如果有人可以帮助我,我会很感激,因为我没有处理Pyqt5中的线程问题。 (我不明白) 非常感谢你

我附上了界面的代码和图片:

接口

执行前:

enter image description here

执行期间:

enter image description here

执行后:

enter image description here

Codigo

import sys
from PyQt5.QtWidgets import QStyleFactory,QApplication, QMainWindow,QFileDialog
from PyQt5.uic import loadUi
from PyQt5.QtGui import QTextCursor
from PyQt5.QtCore import *
import random
import time
import pandas as pd


parametros = [['Area', 0, 5]]

class simulacion(QMainWindow):

    def __init__(self, parent=None):

        QMainWindow.__init__(self, parent)
        loadUi('simulacion.ui', self)
        self.setStyle(QStyleFactory.create('Fusion'))
        self.numsim = 10000000

        self.pushButton.clicked.connect(self.calibracion)
        self.pushButton_2.clicked.connect(self.save)

    def cerrar(self):
        self.close()

    def calibracion(self):
        self.montecarlo(self.numsim)

    def generar_aleatorio(self):
        aleatorio = []
        for i in range(len(parametros)):
            aleatorio.append(random.uniform(parametros[i][1],parametros[i][2]))
        return aleatorio

    def area(self,x1):
        area = 3.1416 * x1**2
        return area

    def estado(self,starttime,last_print,contador, n, area):
        global ult_print
        acttime = time.time()
        if acttime - last_print >= 2:
            avg_time_per_run = (acttime - starttime) / (contador + 1)
            timestr = time.strftime("%H:%M:%S", time.gmtime(round(avg_time_per_run * (n - (contador + 1)))))

            text = ('Simulacion %i de %i - Tiempo estimado restante: %s\n' % (contador, n,timestr)+'Area = %5.3f' % (area)+'\n\n')

            self.textEdit.moveCursor(QTextCursor.End)
            self.textEdit.insertPlainText(text)
            self.textEdit.moveCursor(QTextCursor.End)

            ult_print = time.time()
            return text

    def montecarlo(self,n):
        QApplication.processEvents()
        global ult_print
        text='Iniciando iteraciones con {} repeticiones...\n\n'.format(n)
        self.textEdit.setText(text)
        self.textEdit.moveCursor(QTextCursor.End)
        ult_print = time.time()
        starttime = time.time()
        contador = 0
        self.data=[]
        self.num_sim=[]

        QApplication.setOverrideCursor(Qt.WaitCursor)

        while contador < n:
            contador +=1
            #Generar el numero aleatorio
            z = self.generar_aleatorio()
            #Simulacion del modelo con el numero aleatorio
            y = self.area(z[0])
            #Calculo de la funcion objetivo
            self.estado(starttime,ult_print,contador,n,y)

        QApplication.setOverrideCursor(Qt.CustomCursor)


    def save(self):
        file,_=QFileDialog.getSaveFileName(self,'Guardar Archivo de Simulación','','(*.csv)')

        if file:
            columns= []
            for valor in self.num_sim:
                columns.append('Simulación '+str(valor))
            #print(columns)

            df = pd.DataFrame(self.data,index=columns)
            a = df.transpose()
            a.to_csv(file,sep=';',index=False,encoding='utf-8')

if __name__ == "__main__":
    app = QApplication(sys.argv)
    widget = simulacion()
    widget.show()
    sys.exit(app.exec_())

这里我展示了simulacion.ui

的单独调用代码

Codigo

import sys
from PyQt5.QtWidgets import QStyleFactory,QApplication, 
QMainWindow,QFileDialog
from PyQt5.uic import loadUi
from PyQt5.QtGui import QTextCursor
from PyQt5.QtCore import *
import random
import time
import pandas as pd

'''This part of the code calls the window designed in QTDesigner'''

class simulacion(QMainWindow):

    def __init__(self, parent=None):

        QMainWindow.__init__(self, parent)
        loadUi('simulacion.ui', self)
        self.setStyle(QStyleFactory.create('Fusion'))
        self.numsim = 10000000

        self.pushButton.clicked.connect(self.calibracion)
        self.pushButton_2.clicked.connect(self.save)

    def cerrar(self):
        self.close()

    def calibracion(self):
        self.montecarlo(self.numsim)

    def save(self, data):
        file,_=QFileDialog.getSaveFileName(self,'Guardar Archivo de Simulación','','(*.csv)')
        if file:
            df = pd.DataFrame(data)
            df.to_csv(file,sep=';',index=False,encoding='utf-8')
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = simulacion()
widget.show()
sys.exit(app.exec_())

这部分代码是我描述将在蒙特卡罗算法中使用的函数的地方。其中指定的文本应该显示在QTextEdit中重要的是要提到在Montecarlo函数中,生成数据列表,这是保持所有模拟执行的内容。此变量是必需的,以便可以执行模拟类中的保存功能

parametros = [['Area', 0, 5]]

def generar_aleatorio():
    aleatorio = []
    for i in range(len(parametros)):
        aleatorio.append(random.uniform(parametros[i][1],parametros[i][2]))
        return aleatorio

def area(x1):
    area = 3.1416 * x1**2
    return area

def estado(starttime,last_print,contador, n, area):
    global ult_print
    acttime = time.time()
    if acttime - last_print >= 2:
        avg_time_per_run = (acttime - starttime) / (contador + 1)
        timestr = time.strftime("%H:%M:%S", time.gmtime(round(avg_time_per_run * (n - (contador + 1)))))

        text = ('Simulacion %i de %i - Tiempo estimado restante: %s\n' % (contador, n,timestr)+'Area = %5.3f' % (area)+'\n\n')

        self.textEdit.moveCursor(QTextCursor.End)
        self.textEdit.insertPlainText(text)
        self.textEdit.moveCursor(QTextCursor.End)

        ult_print = time.time()
        return text

def montecarlo(n):
        global ult_print
        text='Iniciando iteraciones con {} repeticiones...\n\n'.format(n)
        #self.textEdit.setText(text)
        #self.textEdit.moveCursor(QTextCursor.End)
        ult_print = time.time()
        starttime = time.time()
        contador = 0
        data=[]
        num_sim=[]
        #QApplication.setOverrideCursor(Qt.WaitCursor)

        while contador < n:
            contador +=1
            #Generar el numero aleatorio
            z = generar_aleatorio()
            #Simulacion del modelo con el numero aleatorio
            y = area(z[0])
            #Calculo de la funcion objetivo
            estado(starttime,ult_print,contador,n,y)
            data.append(list(z+y))

        #QApplication.setOverrideCursor(Qt.CustomCursor)

2 个答案:

答案 0 :(得分:0)

适当的解决方案是在另一个线程中执行阻塞任务,并通过信号将数据发送到主线程中的GUI,Qt禁止使用processEvents从主要线程更新GUI。强制GUI更新一些不保证正确操作的参数,您可以在以下链接中阅读有关此主题的更多信息:Should I use QCoreApplication::processEvents() or QApplication::processEvents()?

在下面的示例中,我将使用2个信号旁边的本机python线程,一个将发送文本,另一个将发送数据。

import random
import sys
import time
from threading import Thread
import pandas as pd

from PyQt5.QtCore import QObject, pyqtSignal, Qt
from PyQt5.QtWidgets import QApplication, QFileDialog, QStyleFactory, QMainWindow
from PyQt5.uic import loadUi

parametros = [['Area', 0, 5]]


def generar_aleatorio():
    return random.uniform(*parametros[0][1:])


def area(x1):
    area = 3.1416 * x1 ** 2
    return area


class Helper(QObject):
    send_signal = pyqtSignal(str)
    data_signal = pyqtSignal(list)


helper = Helper()


def estado(starttime, last_print, contador, n, area):
    acttime = time.time()
    if acttime - last_print <= 2:
        avg_time_per_run = (acttime - starttime) / (contador + 1)
        timestr = time.strftime("%H:%M:%S", time.gmtime(round(avg_time_per_run * (n - (contador + 1)))))

        text = 'Simulacion %i de %i - Tiempo estimado restante: %s\n' % (contador, n, timestr) \
               + 'Area = %5.3f\n\n' % area
        helper.send_signal.emit(text)


def montecarlo(n):
    data = []
    text = 'Iniciando iteraciones con {} repeticiones...\n\n'.format(n)
    helper.send_signal.emit(text)
    ult_print = time.time()
    starttime = time.time()

    for contador in range(n):
        z = generar_aleatorio()
        y = area(z)
        estado(starttime, ult_print, contador + 1, n, y)
        ult_print = time.time()
        time.sleep(0.001)
        data.append([z, y])
    helper.data_signal.emit(data)


class simulacion(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        loadUi('simulacion.ui', self)
        self.setStyle(QStyleFactory.create('Fusion'))
        self.numsim = 10000000

        self.pushButton.clicked.connect(self.calibracion)
        self.pushButton_2.clicked.connect(self.save)

    def calibracion(self):
        thread = Thread(target=montecarlo, args=(self.numsim,))
        helper.send_signal.connect(self.textEdit.append, Qt.QueuedConnection)
        helper.data_signal.connect(self.obtener_resultados)
        thread.start()

    def obtener_resultados(self, data):
        self.data = data

    def save(self, data):
        file, _ = QFileDialog.getSaveFileName(self, 'Guardar Archivo de Simulación', '', '(*.csv)')
        if file:
            df = pd.DataFrame(self.data)
            df.to_csv(file, sep=';', index=False, encoding='utf-8')


app = QApplication(sys.argv)
w = simulacion()
w.show()
sys.exit(app.exec_())

答案 1 :(得分:-2)

QApplication.processEvents()方法移到while contador <n:

不太好,但确实有效。

def montecarlo(self, n):
    #----QApplication.processEvents()                                   # ---
    global ult_print
    text = 'Iniciando iteraciones con {} repeticiones...\n\n'.format(n)
    self.textEdit.setText(text)
    self.textEdit.moveCursor(QTextCursor.End)
    ult_print = time.time()
    starttime = time.time()
    contador    = 0
    self.data   = []
    self.num_sim= []

    QApplication.setOverrideCursor(Qt.WaitCursor)

    while contador < n:
        contador += 1
        #Generar el numero aleatorio
        z = self.generar_aleatorio()
        #Simulacion del modelo con el numero aleatorio
        y = self.area(z[0])
        #Calculo de la funcion objetivo
        self.estado(starttime,ult_print,contador,n,y)

        QApplication.processEvents()                                    # +++

    QApplication.setOverrideCursor(Qt.CustomCursor)

enter image description here