I have a problem regarding the real-time update in a Q Label, because when I click the "start" button, the screen stays static for a few seconds while it performs the calculations, but what I need is that in the QLabel the status messages are displayed while advancing with the simulation. I have tried with the use of threads, but I do not understand it very well since it does not work out, if someone could help me with this I would appreciate it, since I do not handle much the Thread issue in Pyqt5. (I do not understand) Thank you very much
I attach both the code and the images of the interface:
INTERFACE
Before execution:
During execution:
After execution:
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_())
Here I show the separate code of the call of 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_())
This part of the code is where I describe the functions that will be used within the Monte Carlo algorithm. where the texts that are specified should be shown in the QTextEdit It is important to mention that in the Montecarlo function, the data list is generated, which is what keeps all the simulations performed. This variable is necessary so that the save function in the simulation class can be executed
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)
The appropriate solution is to execute the blocking task in another thread, and send the data to the GUI that is in the main thread through signals, Qt prohibits updating the GUI from another than the main one, using processEvents
is forcing the GUI to update some parameters which does not guarantee a correct operation, you can read more about this topic in the following link: Should I use QCoreApplication::processEvents() or QApplication::processEvents()?.
In the following example I will use the native python threads next to 2 signals, one will send the text and the other the data.
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_())