[EDIT]
I've added an incremental counter to the data, to see if I'm missing some data, I don't miss data. These are the times between packets in nanoseconds:
650255.8786692759 (Agrees with the oscilloscope)
500667.7866927593 (wrong)
491091.92954990215 (wrong)
484505.240704501 (wrong)
488062.3091976517 (wrong)
487157.1350293542 (wrong)
485264.33268101764 (wrong)
I have a Python Program that reads the serial port. And draws it on a Chart. Using Qt.
The data comes in every 300 microseconds (Confirmed with an Oscilloscope).
Problem is after the first run through of the worker Thread (traad()), it says the time is 100 microseconds between reads. (which is clearly wrong)
I just can't figure out what's wrong!
import serial
from PyQt5 import QtWidgets
from pyqtgraph.Qt import QtCore
import pyqtgraph as pg
import sys
from scipy.fft import fft, fftfreq
import numpy as np
import threading
import time
import re
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.lock = threading.Lock()
self.Voltage = []
self.tid = []
self.xdata = []
self.ydata = []
self.dataReady = False
self.dataRead = True
self.samples = 512
self.x = threading.Thread(target=self.traad, args=(self.samples,))
self.graphWidget = pg.PlotWidget()
self.setCentralWidget(self.graphWidget)
self.x.start()
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updater)
self.timer.start(100)
def traad(self, s):
ser = serial.Serial('/dev/ttyUSB0', baudrate=576000, stopbits=2, bytesize=8)
while True:
while self.dataRead:
self.dataRead = False
self.lock.acquire()
self.xdata = []
self.ydata = []
self.Voltage = []
self.tid = []
counter = 0
self.lock.release()
while counter < s:
if ser.read(1) == b'?':
data = ser.readline().strip()
stid = time.time_ns()
data = re.search(b"\\d+\\.\\d+", data)
if data:
self.lock.acquire()
self.Voltage.append(data.group(0))
self.tid.append(stid)
self.lock.release()
print(data.group(0))
counter += 1
tot = self.get_samplerate(self.tid)
xf, yf = self.CalcFft(self.Voltage, tot)
self.lock.acquire()
self.xdata = xf
self.ydata = yf
self.dataReady = True
self.lock.release()
def CalcFft(self, volt, deltatid):
N = len(volt)
T = deltatid / 1000000000
yf = fft(volt)
xf = fftfreq(N, T)[:N // 2]
return xf, yf
def get_samplerate(self, tider):
total = []
delta = 0
test = 0
for x in tider:
total.append(x-test)
test = x
total.pop(0)
for e in total:
delta += e
l = len(total)
d = delta / l
print(d)
return d
def updater(self):
if self.dataReady:
self.graphWidget.plot(self.xdata, 2.0 / self.samples * np.abs(self.ydata[0:self.samples // 2]))
self.lock.acquire()
self.dataReady = False
self.dataRead = True
self.lock.release()
def main():
app = QtWidgets.QApplication(sys.argv)
m = MainWindow()
m.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Related
Hi :) I am programming an independent multi-metronome and need to run the funciont beat() on a while loop for every instances of my class Metronome(), starting at the same time.
import time
import mido
from numpy import interp
from IPython.display import clear_output
inport = mido.open_input() #my MIDI is a KORG nanoKontrol 2
class Metronome():
#A dict for my MIDI controller
metronomes_controls = {
'inst_number':[n + 0 for n in range(0,7)],
'vol_slide':[n + 0 for n in range(0,7)],
'tempo_knob': [n + 16 for n in range(0,7)],
'play_stop': [n + 32 for n in range(0,7)],
'sync_selected': [n + 48 for n in range(0,7)],
'tap_button': [n + 64 for n in range(0,7)]}
def __init__(self, inport, inst_number = 0, tempo=60, active=True,
on_off_list = ['ON','OFF','ON','OFF'], selector = 0):
self.inport = inport
self.inst_number = inst_number
self.tempo = tempo
self.active = active
self.on_off_list = on_off_list #The controller is not so precise
self.selector = selector
self.controls = dict(zip(list(metronomes_controls.keys()),
[val[self.inst_number] for val in metronomes_controls.values()]))
def beat(self):
if self.active == True:
print('Tick', self.tempo) #this is going to be a sound
time.sleep(int(round(60/self.tempo)))
clear_output()
self.update()
else:
self.update()
def update(self):
msg = self.inport.receive(block=False)
for msg in inport.iter_pending():
if msg.control == self.controls['tempo_knob']:
self.tempo = int(interp(msg.value,[0,127],[20,99]))
if msg.control == self.controls['play_stop']:
self.selector += 1
if self.selector >3:
self.selector = 0
if 'ON' in self.on_off_list[self.selector]:
print('ON')
self.active = True
if 'OFF' in self.on_off_list[self.selector]:
print('OFF')
self.active = False
#Creating two instances of my class
m0 = Metronome(inport = inport, inst_number = 0)
m1 = Metronome(inport = inport,inst_number = 1)
m2 = Metronome(inport = inport,inst_number = 1)
m3 = Metronome(inport = inport,inst_number = 1)
#They run on a while loop. All should start at the same time.
while True:
m0.beat()
m1.beat()
m2.beat()
m3.beat()
I read about threading but it seems to create some starting delay. Then I got into barries, but I couldn't imagine how to implement it :/ or maybe should I try something with multiprocess? I got really lost! Any advice is highly appreciated
Thanks for the help!!!
Create one thread per metronome and start them at (almost) the same time:
from threading import Thread
# Previous code...
...
def create_thread(inport, inst_number):
# The function the thread is going to execute
def inner():
m = Metronome(inport, inst_number)
m.beat()
return Thread(target=inner)
if __name__ == "__main__":
inport = mido.open_input()
# Create the threads
threads = [
create_thread(inport, i) for i in (0, 1, 1, 1)
]
# Start them at (almost) the same time
for t in threads:
t.start()
# Wait for them to finish execution
for t in threads:
t.join()
I have a job to create a stm mcu downloader using uart communication.
I used to python and pyqt5.
Results were bad because of the program GUI freezing when starting the download. (download progress is well)
So I changed the code to the way I use Qthread.
But nothing change.
I have 2 Questions.
Am I use Qthread correctly?
How can I fix GUI freezing when start download?
Attach the code below
from PyQt5.QtWidgets import QMainWindow, QApplication, QFileDialog, QTextBrowser
from PyQt5 import uic
from PyQt5.QtCore import *
import sys, serial, time, struct, os
form_class = uic.loadUiType('USART_Downloader.ui')[0]
class Worker(QObject):
ports=['COM%s'%(i+1)for i in range(256)]
check_connect = 0
serial = serial.Serial()
time_instance = 0.00008
finished = pyqtSignal(list)
finished2 = pyqtSignal(str)
finished3 = pyqtSignal(str)
finished4 = pyqtSignal(int)
#pyqtSlot()
def Find_Com(self):
result = []
for i in self.ports:
try:
temp = serial.Serial(i)
temp.close()
result.append(i)
except serial.SerialException:
pass
self.finished.emit(result)
#pyqtSlot(str)
def Apply_Com(self, COM_Port):
self.check_connect=0
self.serial.close()
try :
self.serial = serial.Serial(COM_Port, 115200, timeout = 1)
self.check_connect = 1
self.finished2.emit('Successfully Port Connection')
except serial.SerialException:
self.finished2.emit('Failure Port Connection')
if self.check_connect :
self.serial.write(b'\x7F')
time.sleep(self.time_instance)
res = self.serial.read()
if res == b'\x79' :
self.finished2.emit('Successfully Enter Boot Mode')
else:
self.finished2.emit('Failure Enter Boot Mode')
#pyqtSlot(str)
def Download_Flash(self, fileName):
if self.check_connect:
self.serial.write(b'\x44')
time.sleep(self.time_instance)
self.serial.write(b'\xBB')
time.sleep(self.time_instance)
res = self.serial.read()
if res == b'\x79' :
self.finished3.emit('Erasing')
else:
self.finished3.emit('Failure Enter Erase Flash')
return
self.serial.write(b'\xFF')
time.sleep(self.time_instance)
self.serial.write(b'\xFF')
time.sleep(self.time_instance)
self.serial.write(b'\x00')
time.sleep(self.time_instance)
res = self.serial.read()
if res == b'\x79' :
self.finished3.emit('Successfully Erase Flash')
else:
self.finished3.emit('Failure Enter Erase Flash')
return
else :
self.finished3.emit('Need USART Connection')
Bit_Num = 256
f = open(fileName, 'rb')
download_binary = f.read()
download_binary_len = len(download_binary)
f.close()
start_address = b'\x00\x00\x00\x08'
total_data = []
while(True):
if(download_binary_len > Bit_Num):
total_data.append(Bit_Num)
download_binary_len -= Bit_Num
elif(download_binary_len > 0):
total_data.append(download_binary_len)
download_binary_len = 0
else:
break
self.finished3.emit('Downloading')
for i, k in enumerate(total_data):
self.serial.write(b'\x31')
time.sleep(self.time_instance)
self.serial.write(b'\xCE')
time.sleep(self.time_instance)
res = self.serial.read()
if res == b'\x79' :
pass
else:
self.finished3.emit('Failure Download1')
return
self.serial.write(start_address[3:4])
time.sleep(self.time_instance)
self.serial.write(start_address[2:3])
time.sleep(self.time_instance)
self.serial.write(start_address[1:2])
time.sleep(self.time_instance)
self.serial.write(start_address[0:1])
time.sleep(self.time_instance)
temp2 = struct.unpack('4B', start_address)
check_sum = temp2[0] ^ temp2[1] ^ temp2[2] ^ temp2[3]
check_sum = struct.pack('B', check_sum)
self.serial.write(check_sum)
time.sleep(self.time_instance)
res = self.serial.read()
if res == b'\x79' :
pass
else:
self.finished3.emit('Failure Download2')
return
check_sum = (k-1)
self.serial.write(struct.pack('B', check_sum))
time.sleep(self.time_instance)
for j in range(k):
self.serial.write(download_binary[(i*Bit_Num)+j:(i*Bit_Num)+j+1])
time.sleep(self.time_instance)
for j in download_binary[(i*Bit_Num):(i*Bit_Num)+j+1] :
check_sum = check_sum ^ j
check_sum = struct.pack('B', check_sum)
self.serial.write(check_sum)
time.sleep(self.time_instance)
res = self.serial.read()
if res == b'\x79' :
pass
else:
self.finished3.emit('Failure Download3')
return
temp3 = struct.unpack('i', start_address)[0]
temp3 = temp3 + Bit_Num
start_address = struct.pack('i', temp3)
self.finished4.emit(i/(len(total_data)-1)*100)
self.finished3.emit('Success Download')
class MyWindowClass(QMainWindow, form_class):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.setupUi(self)
self.fileName = ''
self.worker_thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.worker_thread)
self.worker_thread.start()
self.worker.finished.connect(self.Refresh_Com)
self.worker.finished2.connect(self.Print_textBrowser)
self.worker.finished3.connect(self.Print_textBrowser_3)
self.worker.finished4.connect(self.Set_ProgressBar)
self.pushButton.clicked.connect(self.Find_Binary)
self.pushButton_2.clicked.connect(lambda:self.worker.Download_Flash(self.fileName))
self.pushButton_3.clicked.connect(lambda:self.worker.Apply_Com(self.comboBox.currentText()))
self.pushButton_4.clicked.connect(self.worker.Find_Com)
self.textBrowser.setAcceptRichText(True)
self.textBrowser.setOpenExternalLinks(True)
self.textBrowser_3.setAcceptRichText(True)
self.textBrowser_3.setOpenExternalLinks(True)
self.progressBar.reset()
self.worker.Find_Com()
#pyqtSlot(list)
def Refresh_Com(self, result):
self.comboBox.clear()
for i in result:
self.comboBox.addItem(i)
self.textBrowser.append('COM Port Refresh Done')
#pyqtSlot(str)
def Print_textBrowser(self, text):
self.textBrowser.append(text)
#pyqtSlot(str)
def Print_textBrowser_3(self, text):
self.textBrowser_3.append(text)
#pyqtSlot(int)
def Set_ProgressBar(self, percent):
self.progressBar.setValue(percent)
def Find_Binary(self):
options = QFileDialog.Options()
self.fileName, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "", "Binary Files (*.bin)", options=options)
self.lineEdit.setText(self.fileName)
if __name__ == "__main__":
app = QApplication(sys.argv)
myWindow = MyWindowClass()
myWindow.show()
app.exec_()
You're using time.sleep() in your code which blocks your GUI. I would recommend you to use QTimer.singleShot() instead. according from this source, you can use time.sleep equivalent like this QtTest.QTest.qWait(msecs)
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_())
Below I have an example program. When the button is pressed, it takes a second before it can calculate the value to show. If the user presses the button in rapid succession they end up waiting a long time to see the last answer, which is the only answer they care about. In the code, you can see that the _dataCruncher function needs to know self._count, but self._count does not depend on the output of _dataCruncher.
My question, therefore, is how can I interrupt the normal execution of _dataCruncher on subsequent calls in order to keep the GUI free to do other stuff, and to not waste processing time when it is not needed? I realize that I will likely need to use a thread to run _dataCruncher and some sort of Queue to get the appropriate val to display, but I do not understand how to put this all together.
from PyQt4 import QtGui, QtCore
import sys
import time
import random
import random
class MainWindow(QtGui.QMainWindow):
def __init__(self):
self.app = QtGui.QApplication(sys.argv)
super(MainWindow, self).__init__()
self.count = 0
self.initUI()
def initUI(self):
# Layouts
central = QtGui.QWidget()
layout = QtGui.QVBoxLayout()
self.button = QtGui.QPushButton('Press Me')
self.text = QtGui.QLabel('?')
layout.addWidget(self.button)
layout.addWidget(self.text)
central.setLayout(layout)
self.setCentralWidget(central)
self.button.clicked.connect(self._buttonClicked)
def _dataCruncher(self, val):
time.sleep(1) # takes a long time to process data using val
return val * random.randint(1,10)
def _buttonClicked(self):
self.count += 1
val = self._dataCruncher(self.count)
self.text.setText('Value {}'.format(val))
def startup(self):
self.show()
result = self.app.exec_()
sys.exit(result)
if __name__ == '__main__':
random.seed()
myWindow = MainWindow()
myWindow.startup()
So, finding an answer to this was more complicated than I thought. As #MTset mentions in one of the comments, python does not offer any means by which to cancel the execution of a Thread. So, what I did was create a 'threadHandler' class that, well, handles thread. It keeps track of the last thread that was created and offers a means by which to get the result from the execution of the last thread.
I am posting an modified version of the test code from the original post as well as the threadHandler code in full in case anyone has use for it.
File 1 here
# tester.py, run this file
from PyQt4 import QtGui, QtCore
import random, sys, time
from threadHandler import MyThreadHandler
class MyModel(object):
def dataCruncher(self, val):
delay = random.randint(1,5)
print('{} sleeping for {}'.format(val, delay))
time.sleep(delay) # takes a long time to process data using val
print('{} done sleeping'.format(val))
return val
class MainWindow(QtGui.QMainWindow):
def __init__(self, threadHandler):
self.app = QtGui.QApplication(sys.argv)
super(MainWindow, self).__init__()
self.count = 0
self.initUI()
self.button_clicked_events = Event()
self.threadHandler = threadHandler
def initUI(self):
# Layouts
central = QtGui.QWidget()
layout = QtGui.QVBoxLayout()
self.button = QtGui.QPushButton('Press Me')
self.text = QtGui.QLabel('?')
layout.addWidget(self.button)
layout.addWidget(self.text)
central.setLayout(layout)
self.setCentralWidget(central)
self.button.clicked.connect(self._buttonClicked)
def _buttonClicked(self):
self.count += 1
self.button_clicked_events(self.count)
def setLabel(self, val):
self.text.setText(str(val))
def startup(self):
self.show()
result = self.app.exec_()
return result
class Event(list):
"""Event subscription.
A list of callable objects. Calling an instance of this will cause a
call to each item in the list in ascending order by index.
Example Usage:
>>> def f(x):
... print 'f(%s)' % x
>>> def g(x):
... print 'g(%s)' % x
>>> e = Event()
>>> e()
>>> e.append(f)
>>> e(123)
f(123)
>>> e.remove(f)
>>> e()
>>> e += (f, g)
>>> e(10)
f(10)
g(10)
>>> del e[0]
>>> e(2)
g(2)
"""
def __init__(self):
self.output = {}
def __call__(self, *args, **kwargs):
for f,key in self:
output = f(*args, **kwargs)
self.output[key] = output
return self.output
def __repr__(self):
return "Event({})".format(list.__repr__(self))
if __name__ == '__main__':
def checker(handler, window):
if handler.isLastDone():
val = handler.getLastResult()
window.setLabel(val)
else:
window.setLabel('calculating...')
random.seed()
model = MyModel()
threadHandler = MyThreadHandler()
myWindow = MainWindow(threadHandler)
threadHandler.createTimer(1, checker, threadHandler, myWindow)
def getData(count):
threadHandler.createOneShot(model.dataCruncher, count)
myWindow.button_clicked_events.append((getData, 'dt'))
result = myWindow.startup()
print('ending')
threadHandler.end()
print('ended')
sys.exit(result)
File 2 below
#threadHandler.py, save this file in the same folder as tester.py
import threading, time
class MyThreadHandler(object):
def __init__(self):
self.oneShots = []
self.timers = []
self.oldOneShots = []
self.latest = None
self.cleaning = False
self._startCleaner()
def _startCleaner(self):
print('-'*20+'Starting cleaner'+'-'*20)
self.cleaner = self.createTimer(1, self._cleanupThreads)
def _stopCleaner(self):
print('-'*20+'Stopping cleaner'+'-'*20)
self.cleaner.stop()
def getNumThreads(self):
return len(self.oneShots)
def getNumOldThreads(self):
return len(self.oldOneShots)
def end(self):
for i,timer in enumerate(self.timers):
timer.stop()
self.timers.pop(i)
def createTimer(self, interval, func, *args, **kwargs):
timer = myTimer(interval, func, args, kwargs)
self.timers.append(timer)
return timer
def createOneShot(self, func, *args, **kwargs):
oneshot = myOneShot(func, args, kwargs)
self.oneShots.append(oneshot)
self.latest = oneshot
def isLastDone(self):
if not self.latest is None:
return not self.latest.running()
else:
return None
def getLastResult(self):
if self.latest is None:
raise ValueError('There have not been any oneshots created.')
while self.latest.running():
pass
result = self.latest.getResult()
if len(self.oneShots) > 0:
self.oldOneShots.append(myOneShot(self._cleanAll, (self.oneShots,)))
self.oneShots = []
return result
def _cleanAll(self, toClean):
# loop through toClean and pop up anything that's done. this DOES lock
while len(toClean) > 0:
toClean = self._cleanup(toClean)
def _cleanup(self, toCleanup):
while not self.cleaning:
self.cleaning = True
for i, thread in enumerate(toCleanup):
if not thread.running():
toCleanup.pop(i)
self.cleaning = False
return toCleanup
def _cleanupThreads(self):
# check each of these lists and pop out any threads that are done. This
# does not lock. This function should really only be called by the
# cleaner, which is set up in __init__
self.oneShots = self._cleanup(self.oneShots)
self.timers = self._cleanup(self.timers)
self.oldOneShots = self._cleanup(self.oldOneShots)
class myTimer(object):
def __init__(self, delay, func, args=tuple(), kwargs={}):
self.delay = delay
self.func = func
self.loop = True
self.args = args
self.kwargs = kwargs
self.thread = threading.Thread(target=self.run, daemon=True)
self.thread.start()
self.output = None
def run(self):
while self.loop:
self.output = self.func(*self.args, **self.kwargs)
if self.delay > 0.1:
count = 0
while count <= self.delay:
count += 0.1
time.sleep(0.1)
else:
time.sleep(self.delay)
def stop(self):
self.loop = False
def running(self):
return self.loop
def getResult(self):
return self.output
class myOneShot(object):
def __init__(self, func, args=tuple(), kwargs={}):
self.func = func
self.args = args
self.kwargs = kwargs
self.thread = threading.Thread(target=self.run, daemon=True)
self.thread.start()
self.output = None
def run(self):
self.output = self.func(*self.args, **self.kwargs)
def running(self):
return self.thread.is_alive()
def getResult(self):
return self.output
if __name__ == '__main__':
import random
random.seed()
def longFunc(num):
delay = random.randint(5,8)
if num in (3, 6):
delay = 2
print('-'*30+'func {} has sleep {}'.format(num, delay))
time.sleep(delay)
print('-'*30+'func {} is done'.format(num))
return num
def checker(handler):
if handler.isLastDone():
return handler.getLastResult()
else:
return None
myHandler = MyThreadHandler()
# The 'checker' function simulates something in my program that uses the
# data generated by the 'longFunc'. It waits until there are no more threads
# in the threadHandler, as that would indicate that the user is done
# switching back-and-forth between different values
checkTimer = myHandler.createTimer(1, checker, myHandler)
# create 10 one-shot threads that take a 'long' time. The delay is to keep
# them in order, as this loop is meant to simulate a user switching between
# items using a keyboard or mouse, which I imagine they couldn't do any
# faster than every 1/10th of a second
start = time.time()
for i in range(4):
myHandler.createOneShot(longFunc, i)
time.sleep(0.1)
# wait until there are no more threads executing
last = myHandler.getLastResult()
print('result from last = {}'.format(last))
for i in range(4, 7):
myHandler.createOneShot(longFunc, i)
time.sleep(0.1)
last = myHandler.getLastResult()
print('result from last = {}'.format(last))
while myHandler.getNumOldThreads() >0 or myHandler.getNumThreads() > 0:
pass
myHandler.end()
print('done ending')
You could disable the button after it's pressed until an answer is ready using:
setEnabled(False)
Then reset it just before providing the result.
I want to display CPU using mat plot library. My python code sends cpu load for every 1 sec through serial port to target board and output i need to display it using plot. i came across some examples but I'm not getting how to integrate in my code. I'm new to python. This is the code I've written.
import serial, time, threading, multiprocessing,sys
import numpy as np
from matplotlib import pyplot as plt
from collections import deque
import csv
ser = serial.Serial(
port='COM4',
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS
)
class analogData:
# constr
def __init__(self, maxLen):
self.ax = deque([0.0]*maxLen)
self.ay = deque([0.0]*maxLen)
self.maxLen = maxLen
# ring buffer
def addToBuf(self, buf, val):
if len(buf) < self.maxLen:
buf.append(val)
else:
buf.pop()
buf.appendleft(val)
# add data
def add(self, data):
assert(len(data) == 2)
self.addToBuf(self.ax, data[0])
self.addToBuf(self.ay, data[1])
# plot class
class analogPlot:
# constr
def __init__(self, analogData):
# set plot to animated
plt.ion()
self.axline, = plt.plot(analogData.ax)
self.ayline, = plt.plot(analogData.ay)
plt.ylim([0, 1023])
plt.show()
# update plot
def update(self, analogData):
self.axline.set_ydata(analogData.ax)
self.ayline.set_ydata(analogData.ay)
plt.draw()
def serialWrite(cmd: str):
l=0
ser.flushOutput()
time.sleep(0.04)
while l < len(cmd):
ser.write(cmd[l].encode())
time.sleep(0.02)
ser.flushInput()
l=l+1
ser.write('\r'.encode())
def serialRead():
time.sleep(0.065)
out=""
while ser.inWaiting() > 0:
out += ser.read().decode("utf-8")
return out
def read_from_serial():
#pool_sema.acquire()
out=""
serialWrite("cpu")
out=serialRead()
data = [float(val) for val in out.split()]
analogData.add(analogData,data)
analogPlot.update(analogData)
print (data)
#pool_sema.release()
threading.Timer(1, read_from_serial).start()
def commandInput():
while True:
out=""
out=input()
serialWrite(out)
out=""
out=serialRead()
print (out)
if __name__ == "__main__":
serial_thread = threading.Thread(target = read_from_serial())
main_thread=threading.Thread(target = commandInput())
analogData = analogData(100)
analogPlot = analogPlot(analogData)
serial_thread.start()
main_thread.start()
Please give me suggestions what are things I'm doing wrong.