4

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()
NRav
  • 407
  • 1
  • 6
  • 18

1 Answers1

4

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()
sid16rgt
  • 736
  • 5
  • 14
  • OK... I sort of follow... how might I integrate this if my textbox objects name is "SPO2text" and the value I want printed in this box is variable #5 in 'Listvalues' – NRav Oct 02 '15 at 19:32
  • Is your text box a [QLineEdit](http://pyqt.sourceforge.net/Docs/PyQt4/qlineedit.html)? QLineEdit has a method `setText` that is used to set the text in the box. You would set the text to the fifth variable (index 4) with a call like: `self.SPO2text.setText(str(Listvalues[4]))` – sid16rgt Oct 02 '15 at 19:42
  • They are QLineEdits. Awesome. I will report back on when I test it out! – NRav Oct 02 '15 at 19:45
  • I am getting this callback when I try to run script: TypeError: __init__() missing 1 required positional argument: 'event' ... thoughts? @sid16rgt – NRav Oct 04 '15 at 16:32
  • My mistake. You will need to change the signature of the main() function to take a positional argument for the threading event and change the form = ExampleApp call to pass the event to the initializer. I will update my answer when I get on a computer. – sid16rgt Oct 04 '15 at 16:54
  • I figured that was the case... can't quite seem to figure out how to put a 'pass' argument in properly. ... I Also have 2 buttons in my UI (start and stop) how easy would it be to code them to start/stop these threads? – NRav Oct 04 '15 at 17:06
  • I've foraged the internet and come up dry... How do I implement a pass and initiate with multthreading? Thanks for all your help thus far! – NRav Oct 05 '15 at 12:40
  • Read up on thread synchronization. I read [this post](http://effbot.org/zone/thread-synchronization.htm) many years ago and it got me started. Essentially your thread would skip reading until an event were set or it was able to acquire a lock or semaphore. Threading in Python is similar to other languages with the caveat being the Global Interpreter Lock which will only allow one thread to execute simultaneously. – sid16rgt Oct 05 '15 at 16:49
  • thanks for the forward! I am rather new to this multi threading... Do you happen to know how to fix the issue I am having? I'm at a loss :( – NRav Oct 05 '15 at 16:59
  • You could make another event for the read thread and set or clear it from the buttons on the GUI by attaching methods to their clicked signal. – sid16rgt Oct 05 '15 at 17:01
  • hahaha sorry, I meant for the 'missing 1 required positional argument' ... You had mentioned you would fix your answer to have working code hopefully :) – NRav Oct 05 '15 at 17:05
  • Ha. Yes, I just fixed the answer a few minutes ago and I think it should work. – sid16rgt Oct 05 '15 at 17:06
  • awesome! I'll try in a bit... I'll also try to configure the buttons, might fail but at least I tried! – NRav Oct 05 '15 at 17:12
  • Tried the code... its recording fine to CSV every 5 seconds, starting the GUI fine, but still not updating the textboxes... – NRav Oct 05 '15 at 17:40
  • My bad, I put a capital letter where I shouldnt have. It all works now! Thanks! – NRav Oct 05 '15 at 18:49