Search code examples
pythonpyqtpyqt4qtguiqtcore

Have textbox update every 5 seconds PyQt


So here is my problem, I have data from a serial cable being read every 5 seconds and being stored on a CSV file. I am also taking that data and making it into a list. What I would like to do is take variables 5, 7, and 9 and have them be displayed in my GUI where I have Qtextboxes... how do I do that?

The list of variables will be in a value known as listvalues. I want to call 5, 7, and 9 and have them display in their respective text boxes in my PyQt window.

here is my code:

from PyQt4 import QtGui
import sys
import masimo
import csv
import time
import datetime as DT
import threading
from threading import Thread
import serial
import os

os.chdir(r"C:\Users\SpO2\Desktop\Data")
time = time.strftime("%d %b %Y %H%M%S")
location = r'%s.csv' % time
outputfile = open(location, mode='x', newline='')
outputWriter = csv.writer(outputfile)
outputWriter.writerow(["start"])
outputfile.close()
port = "COM4"


class ExampleApp(QtGui.QMainWindow, masimo.Ui_MainWindow):
    def __init__(self, parent=None):
        super(self.__class__, self).__init__()
        self.setupUi(self)

def SerialRead():
    delay1 = DT.datetime.now()                
    ser = serial.Serial(port, baudrate=9600, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS)   
    out = ser.read(167)
    reading = str(out)
    plaintext1 = reading.replace(' ', ', ')
    plaintext = plaintext1.replace('=', ', ')
    listvalue = plaintext.split(", ")
    ser.close()

    outputfile = open(location, mode='a', newline='')
    outputWriter = csv.writer(outputfile)
    outputWriter.writerow([plaintext])
    outputfile.close()

    delay2 = DT.datetime.now()
    differencetime = (delay2 - delay1).total_seconds()
    writedelay = int(5)
    restart = (writedelay - differencetime)
    threading.Timer(restart, SerialRead).start() 

def main():
    app = QtGui.QApplication(sys.argv)
    form = ExampleApp()
    QtGui.QApplication.processEvents()
    form.show()
    app.exec_()    

if __name__ == '__main__':
    Thread(target = SerialRead).start()
    Thread(target = main).start()

Solution

  • I would think you could do one of two things:

    1. Use a QTimer in your window, set its interval to 5 seconds and connect a method to its timeout signal, and update your text fields within that method.
    2. Use a threading event that is shared between your window and your read process and use a QTimer in your window class that checks more frequently to see if the event is set and do the update when it is.

    I would probably use an event so that you know that the thread is sleeping and you aren't trying to read values while the thread is writing them.

    In your ExampleApp class, you would need to store the event and handle the timeouts:

    class ExampleApp(QtGui.QMainWindow, masimo.Ui_MainWindow):
        def __init__(self, event, parent=None):
            super(self.__class__, self).__init__()
            self.setupUi(self) 
            self.dataWasReadEvent = event
    
            self.checkThreadTimer = QtCore.QTimer(self)
            self.checkThreadTimer.setInterval(500) #.5 seconds
    
            self.checkThreadTimer.timeout.connect(self.readListValues)
    
        def readListValues(self):
            if self.dataWasReadEvent.is_set():
                #Read your events from the list and update your fields
    
                self.dataWasReadEvent.clear() #Clear the event set flag so that nothing happens the next time the timer times out
    

    Your SerialRead function would need to take an argument that is the threading event and the event would need to be set after the serial read but before the restart:

    def SerialRead(dataReadEvent):
        ...
        dataReadEvent.set()
        threading.Timer(restart, SerialRead, args=(dataReadEvent,)).start()
    

    Your main function will also need to accept an event argument to be passed to the initializer for ExampleApp:

    def main(dataReadEvent):
        ...
        form = ExampleApp(dataReadEvent)
    

    And finally, in your if __name__ == '__main__': section, the threading event would need to be created and passed to the Thread calls:

    if __name__ == '__main__':
        dataReadEvent = threading.Event()
        Thread(target = SerialRead, args=(dataReadEvent,) ).start()
        Thread(target = main, args=(dataReadEvent,) ).start()