Read and plot real time live self updating csv file - python
So, I have a .csv file which updates itself. I would like to do some things with it and am not sure how to approach it, hope you can help me.
The data in the csv looks like this:
There is no headers. I can join the date and time to be in same column without a delimiter too.
07/12/2017,23:50,113.179,113.182,113.168,113.180,113.187,113.189,113.176,113.186,144
07/12/2017,23:51,113.180,113.190,113.180,113.187,113.186,113.196,113.186,113.193,175
07/12/2017,23:52,113.187,113.188,113.174,113.186,113.193,113.194,113.181,113.192,340
07/12/2017,23:53,113.186,113.192,113.175,113.181,113.192,113.199,113.182,113.188,282
07/12/2017,23:54,113.181,113.183,113.170,113.171,113.188,113.188,113.176,113.179,74
07/12/2017,23:55,113.171,113.181,113.170,113.179,113.179,113.188,113.176,113.186,329
07/12/2017,23:56,113.179,113.189,113.174,113.181,113.186,113.195,113.181,113.187,148
07/12/2017,23:57,113.181,113.181,113.169,113.169,113.187,113.187,113.175,113.175,55
07/12/2017,23:58,113.169,113.183,113.169,113.182,113.175,113.188,113.175,113.187,246
07/12/2017,23:59,113.182,113.210,113.175,113.203,113.187,113.215,113.181,113.209,378
08/12/2017,00:00,113.203,113.213,113.180,113.183,113.209,113.220,113.187,113.190,651
08/12/2017,00:01,113.183,113.190,113.164,113.167,113.190,113.196,113.171,113.174,333
08/12/2017,00:02,113.167,113.182,113.156,113.156,113.174,113.188,113.162,113.163,265
08/12/2017,00:03,113.156,113.165,113.151,113.163,113.163,113.172,113.158,113.170,222
08/12/2017,00:04,113.163,113.163,113.154,113.159,113.170,113.170,113.159,113.166,148
08/12/2017,00:05,113.159,113.163,113.153,113.154,113.166,113.168,113.159,113.162,162
For starters I would be interested in using just the first two (or 3 if date and time are separate) columns for this exercise. So for example:
07/12/2017,21:54,113.098
07/12/2017,21:55,113.096
07/12/2017,21:56,113.087
07/12/2017,21:57,113.075
07/12/2017,21:58,113.087
07/12/2017,21:59,113.079
New rows are being added with more recent date time every second or so.
I can do something like
df = pd.read_csv("C:\\Users\\xxx\\Desktop\\csvexport\\thefile.csv")
print(df[-1:])
To see the last row (tail) from the dataframe
Now, I can't see how to do the following and appreciate your help:
Update the dataframe so that I have the most recent version up to date available to make calculations on when new rows appear (without using sleep timer?)
Be able to plot the data with the newly updating data being reflected in the plot automatically as new data arrives (datetime on x axis, float on y)
The output I see in the command window from the program generating the .csv file is like this, if that matters
asset 08/12/2017 05:16:37 float:113.336 floattwo:113.328 digit:20
asset 08/12/2017 05:16:40 float:113.334 floattwo:113.328 digit:21
asset 08/12/2017 05:16:40 float:113.335 floattwo:113.323 digit:22
asset 08/12/2017 05:16:41 float:113.331 floattwo:113.328 digit:23
asset 08/12/2017 05:16:43 float:113.334 floattwo:113.327 digit:24
asset 08/12/2017 05:16:47 float:113.332 floattwo:113.328 digit:25
So you can see the updates are not exactly one second apart, they can have gaps, and can sometimes occur within the same second too (05:16:40 twice)
Therefore, what I would like to happen is keep the plot at equal time intervals actually (1 minute, or 5 minutes, etc) but keep changing the most recent point according to the float vlaue in the .csv belonging to that minute. When a row with the next minute arrives, only then should the plot move to the right (but constantly fluctuate in value as the float number is changing)... Hope you get the idea. I would like to use pyqtgraph for the plot.
I managed to code this much... but it is not the greatest example, excuse me. Of course the plot is not meant to look like this. Just illustrating what I would like to see. So the green bar should be changing value constantly until the next time step is added to the csv
import pyqtgraph as pg
from pyqtgraph import QtCore, QtGui
import pandas as pd
import datetime
x = pd.read_csv("C:\\Users\\xxx\\Desktop\\csvexport\\thefile.csv")
z = x[-1:]
def getlastrow():
for a in z.iterrows():
d = ((int(((a[1][0]).split("/")[0]))))
m = ((int(((a[1][0]).split("/")[1]))))
y = ((int(((a[1][0]).split("/")[2]))))
hh = ((int(((a[1][1]).split(":")[0]))))
mm = ((int(((a[1][1]).split(":")[1]))))
#ss = ((int(((a[1][1]).split(":")[2]))))
thedate = datetime.date(y, m, d)
thetime = datetime.time(hh, mm)
p = (a[1][2])
return ((thedate,thetime,p))
# print(str(getlastrow()[0]).replace("-",""))
# print(getlastrow()[1])
# print(getlastrow()[2])
class CandlestickItem(pg.GraphicsObject):
def __init__(self):
pg.GraphicsObject.__init__(self)
self.flagHasData = False
def set_data(self, data):
self.data = data
self.flagHasData = True
self.generatePicture()
self.informViewBoundsChanged()
def generatePicture(self):
self.picture = QtGui.QPicture()
p = QtGui.QPainter(self.picture)
p.setPen(pg.mkPen('w'))
w = (self.data[1][0] - self.data[0][0]) / 2.
for (t, open) in self.data:
p.drawLine(QtCore.QPointF(t, open), QtCore.QPointF(t, open))
p.setBrush(pg.mkBrush('r'))
if open > 122.8:
p.setBrush(pg.mkBrush('g'))
p.drawRect(QtCore.QRectF(t-w, open, w*2, open))
p.end()
def paint(self, p, *args):
if self.flagHasData:
p.drawPicture(0, 0, self.picture)
def boundingRect(self):
return QtCore.QRectF(self.picture.boundingRect())
app = QtGui.QApplication([])
data = [
[(int(str(getlastrow()[0]).replace("-",""))), (getlastrow()[2])],
[(int(str(getlastrow()[0]).replace("-","")))+1, (getlastrow()[2])+0.1],
[(int(str(getlastrow()[0]).replace("-","")))+2, (getlastrow()[2])+0.2],
]
item = CandlestickItem()
item.set_data(data)
plt = pg.plot()
plt.addItem(item)
plt.setWindowTitle('pyqtgraph example: customGraphicsItem')
def update():
global item, data
new_bar = (int(str(getlastrow()[0]).replace("-","")))+3, ((getlastrow()[2])+10)
data.append(new_bar)
item.set_data(data)
app.processEvents()
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(100)
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
Hopefully the code below will help with point(1). I realise this is a partial answer. I tested using Linux. The code should be OS agnostic, but I have not tested this.
The code monitors the directory defined in TEST_DIR using the watchdog library. If the file defined in TEST_FILE is changed, then a message is sent from the event handling class called MyHandler to the main function. I put in some ugly time checking as each time a file is altered, multiple events are triggered. So only a single dispatch will be triggered for events occurring within THRESHOLD time. I set this to 0.01 s.
Add code to the dispatcher_receiver function to read in the updated file.
import ntpath
# pip3 install pydispatcher --user
from pydispatch import dispatcher
import sys
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
MYHANDLER_SENDER = 'myhandler_sender'
MYHANDLER_SIGNAL = 'myhandler_signal'
TEST_FILE = 'test_data.csv'
TEST_DIR = '/home/bill/data/documents/infolab2/progs/jupyter_notebooks/pyqtgraph/test_data/'
THRESHOLD_TIME = 0.01
class MyHandler(FileSystemEventHandler):
''' handle events from the file system '''
def __init__(self):
self.start_time = time.time()
def on_modified(self, event):
now_time = time.time()
# filter out multiple modified events occuring for a single file operation
if (now_time - self.start_time) < THRESHOLD_TIME:
print('repeated event, not triggering')
return
changed_file = ntpath.basename(event.src_path)
if changed_file == TEST_FILE:
print('changed file: {}'.format(changed_file))
print('event type: {}'.format(event.event_type))
print('do something...')
# print(event)
message = '{} changed'.format(changed_file)
dispatcher.send(message=message, signal=MYHANDLER_SIGNAL, sender=MYHANDLER_SENDER)
self.start_time = now_time
def main():
dispatcher.connect(dispatcher_receive, signal=MYHANDLER_SIGNAL, sender=MYHANDLER_SENDER)
observer = Observer()
observer.schedule(event_handler, path=TEST_DIR, recursive=False)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
def dispatcher_receive(message):
print('received dispatch: {}'.format(message))
# read in the altered file
if __name__ == "__main__":
event_handler = MyHandler()
main()
Related
Stopping a function based on a value
I am running a python script on a raspberry-pi. Essentially, I would like a camera to take a picture every 5 seconds, but only if I have set a boolean to true, which gets toggled on a physical button. initially I set it to true, and then in my while(true) loop, I want to check to see if the variable is set to true, and if so, start taking pictures every 5 seconds. The issue is if I use something like time time.sleep(5), it essentially freezes everything, including the check. Combine that with the fact that I am using debouncing for the button, it then becomes impossible for me to actually toggle the script since I would have to press it exactly after the 5s wait time, right for the value check... I've been searching around and I think the likely solution would have to include threading, but I can't wrap my head around it. One kind of workaround I thought of would be to look at the system time and if the seconds is a multiple of 5, then take picture (all within the main loop). This seems a bit sketchy. Script below: ### Imports from goprocam import GoProCamera, constants import board import digitalio from adafruit_debouncer import Debouncer import os import shutil import time ### GoPro settings goproCamera = GoProCamera.GoPro() ### Button settings pin = digitalio.DigitalInOut(board.D12) pin.direction = digitalio.Direction.INPUT pin.pull = digitalio.Pull.UP switch = Debouncer(pin, interval=0.1) save = False #this is the variable while(True): switch.update() if switch.fell: print("Pressed, toggling value") save = not save if save: goproCamera.take_photo() goproCamera.downloadLastMedia() time.sleep(5)
Here's something to try: while(True): switch.update() if switch.fell: print("Pressed, toggling value") save = not save if save: current_time = time.time() if current_time - last_pic_time >= 5: goproCamera.take_photo() goproCamera.downloadLastMedia() last_pic_time = current_time Depending on exactly what sort of behavior you want, you may have to fiddle with when and how often time.time() is called.
Cheers! Maybe something like this? import threading def set_interval(func, sec): def func_wrapper(): set_interval(func, sec) func() t = threading.Timer(sec, func_wrapper) t.start() return t We call the function above inside the main loop. Wrap your while loop content on a function: def take_photo: goproCamera.take_photo() goproCamera.downloadLastMedia() Now we create a flag initially set to False to avoid creating multiple threads. Notice that I did this before the while loop. We just need a starting value here. active = False while(True): switch.update() if switch.fell: print("Pressed, toggling value") save = not save if save: # we need to start taking photos. if not active: # it is not active... so it is the first time it is being called or it has been toggled to save as True again. photo_thread = set_interval(take_photo, 5) # grabbing a handle to the thread - photo_thread - so we can cancel it later when save is set to False. active = True # marking as active to be skipped from the loop until save is False else: try: # photo_thread may not exist yet so I wrapped it inside a try statement here. photo_thread.cancel() # if we have a thread we kill it active = False #setting to False so the next time the button is pressed we can create a new one. Let me know if it works. =)
What I ended up doing: ### Imports from goprocam import GoProCamera, constants import board import digitalio from adafruit_debouncer import Debouncer import os import time import threading ### GoPro settings gopro = GoProCamera.GoPro() ### Button settings pin = digitalio.DigitalInOut(board.D12) pin.direction = digitalio.Direction.INPUT pin.pull = digitalio.Pull.UP switch = Debouncer(pin, interval=0.1) ### Picture save location dir_path = os.path.dirname(os.path.realpath(__file__)) new_path = dir_path+"/pictures/" save = False ### Functions def takePhoto(e): while e.isSet(): gopro.take_photo() gopro.downloadLastMedia() fname = '100GOPRO-' + gopro.getMedia().split("/")[-1] current_file = dir_path+'/'+fname if os.path.isfile(current_file): os.replace(current_file, new_path+fname) #move file, would be cleaner to download the file directly to the right folder, but the API doesn't work the way I thought it did e.wait(5) ### Initial settings e = threading.Event() t1 = threading.Thread(target=takePhoto, args=([e])) print("Starting script") while(True): switch.update() if switch.fell: #toggle value save = not save if save: e.set() #should be taking pictures else: e.clear() #not taking pictures if not t1.is_alive(): #start the thread if it hasn't been yet if e.is_set(): t1.start()
Multiprocessing function not writing to file or printing
I'm working on a Raspberry Pi (3 B+) making a data collection device and I'm trying to spawn a process to record the data coming in and write it to a file. I have a function for the writing that works fine when I call it directly. When I call it using the multiprocess approach however, nothing seems to happen. I can see in task monitors in Linux that the process does in fact get spawned but no file gets written, and when I try to pass a flag to it to shut down it doesn't work, meaning I end up terminating the process and nothing seems to have happened. I've been over this every which way and can't see what I'm doing wrong; does anyone else? In case it's relevant, these are functions inside a parent class, and one of the functions is meant to spawn another as a thread. Code I'm using: from datetime import datetime, timedelta import csv from drivers.IMU_SEN0 import IMU_SEN0 import multiprocessing, os class IMU_data_logger: _output_filename = '' _csv_headers = [] _accelerometer_headers = ['Accelerometer X','Accelerometer Y','Accelerometer Z'] _gyroscope_headers = ['Gyroscope X','Gyroscope Y','Gyroscope Z'] _magnetometer_headers = ['Bearing'] _log_accelerometer = False _log_gyroscope= False _log_magnetometer = False IMU = None _writer=[] _run_underway = False _process=[] _stop_value = 0 def __init__(self,output_filename='/home/pi/blah.csv',log_accelerometer = True,log_gyroscope= True,log_magnetometer = True): """data logging device NOTE! Multiple instances of this class should not use the same IMU devices simultaneously!""" self._output_filename = output_filename self._log_accelerometer = log_accelerometer self._log_gyroscope = log_gyroscope self._log_magnetometer = log_magnetometer def __del__(self): # TODO Update this if self._run_underway: # If there's still a run underway, end it first self.end_recording() def _set_up(self): self.IMU = IMU_SEN0(self._log_accelerometer,self._log_gyroscope,self._log_magnetometer) self._set_up_headers() def _set_up_headers(self): """Set up the headers of the CSV file based on the header substrings at top and the input flags on what will be measured""" self._csv_headers = [] if self._log_accelerometer is not None: self._csv_headers+= self._accelerometer_headers if self._log_gyroscope is not None: self._csv_headers+= self._gyroscope_headers if self._log_magnetometer is not None: self._csv_headers+= self._magnetometer_headers def _record_data(self,frequency,stop_value): self._set_up() #Run setup in thread """Record data function, which takes a recording frequency, in herz, as an input""" previous_read_time=datetime.now()-timedelta(1,0,0) self._run_underway = True # Note that a run is now going Period = 1/frequency # Period, in seconds, of a recording based on the input frequency print("Writing output data to",self._output_filename) with open(self._output_filename,'w',newline='') as outcsv: self._writer = csv.writer(outcsv) self._writer.writerow(self._csv_headers) # Write headers to file while stop_value.value==0: # While a run continues if datetime.now()-previous_read_time>=timedelta(0,1,0): # If we've waited a period, collect the data; otherwise keep looping print("run underway value",self._run_underway) if datetime.now()-previous_read_time>=timedelta(0,Period,0): # If we've waited a period, collect the data; otherwise keep looping previous_read_time = datetime.now() # Update previous readtime next_row = [] if self._log_accelerometer: # Get values in m/s^2 axes = self.IMU.read_accelerometer_values() next_row += [axes['x'],axes['y'],axes['z']] if self._log_gyroscope: # Read gyro values gyro = self.IMU.read_gyroscope_values() next_row += [gyro['x'],gyro['y'],gyro['z']] if self._log_magnetometer: # Read magnetometer value b= self.IMU.read_magnetometer_bearing() next_row += b self._writer.writerow(next_row) # Close the csv when done outcsv.close() def start_recording(self,frequency_in_hz): # Create recording process self._stop_value = multiprocessing.Value('i',0) self._process = multiprocessing.Process(target=self._record_data,args=(frequency_in_hz,self._stop_value)) # Start recording process self._process.start() print(datetime.now().strftime("%H:%M:%S.%f"),"Data logging process spawned") print("Logging Accelerometer:",self._log_accelerometer) print("Logging Gyroscope:",self._log_gyroscope) print("Logging Magnetometer:",self._log_magnetometer) print("ID of data logging process: {}".format(self._process.pid)) def end_recording(self,terminate_wait = 2): """Function to end the recording multithread that's been spawned. Args: terminate_wait: This is the time, in seconds, to wait after attempting to shut down the process before terminating it.""" # Get process id id = self._process.pid # Set stop event for process self._stop_value.value = 1 self._process.join(terminate_wait) # Wait two seconds for the process to terminate if self._process.is_alive(): # If it's still alive after waiting self._process.terminate() print(datetime.now().strftime("%H:%M:%S.%f"),"Process",id,"needed to be terminated.") else: print(datetime.now().strftime("%H:%M:%S.%f"),"Process",id,"successfully ended itself.") ==================================================================== ANSWER: For anyone following up here, it turns out the problem was my use of the VS Code debugger which apparently doesn't work with multiprocessing and was somehow preventing the success of the spawned process. Many thanks to Tomasz Swider below for helping me work through issues and, eventually, find my idiocy. The help was very deeply appreciated!!
I can see few thing wrong in your code: First thing stop_value == 0 will not work as the multiprocess.Value('i', 0) != 0, change that line to while stop_value.value == 0 Second, you never update previous_read_time so it will write the readings as fast as it can, you will run out of disk quick Third, try use time.sleep() the thing you are doing is called busy looping and it is bad, it is wasting CPU cycles needlessly. Four, terminating with self._stop_value = 1 probably will not work there must be other way to set that value maybe self._stop_value.value = 1. Well here is a pice of example code based on the code that you have provided that is working just fine: import csv import multiprocessing import time from datetime import datetime, timedelta from random import randint class IMU(object): #staticmethod def read_accelerometer_values(): return dict(x=randint(0, 100), y=randint(0, 100), z=randint(0, 10)) class Foo(object): def __init__(self, output_filename): self._output_filename = output_filename self._csv_headers = ['xxxx','y','z'] self._log_accelerometer = True self.IMU = IMU() def _record_data(self, frequency, stop_value): #self._set_up() # Run setup functions for the data collection device and store it in the self.IMU variable """Record data function, which takes a recording frequency, in herz, as an input""" previous_read_time = datetime.now() - timedelta(1, 0, 0) self._run_underway = True # Note that a run is now going Period = 1 / frequency # Period, in seconds, of a recording based on the input frequency print("Writing output data to", self._output_filename) with open(self._output_filename, 'w', newline='') as outcsv: self._writer = csv.writer(outcsv) self._writer.writerow(self._csv_headers) # Write headers to file while stop_value.value == 0: # While a run continues if datetime.now() - previous_read_time >= timedelta(0, 1, 0): # If we've waited a period, collect the data; otherwise keep looping print("run underway value", self._run_underway) if datetime.now() - previous_read_time >= timedelta(0, Period, 0): # If we've waited a period, collect the data; otherwise keep looping next_row = [] if self._log_accelerometer: # Get values in m/s^2 axes = self.IMU.read_accelerometer_values() next_row += [axes['x'], axes['y'], axes['z']] previous_read_time = datetime.now() self._writer.writerow(next_row) # Close the csv when done outcsv.close() def start_recording(self, frequency_in_hz): # Create recording process self._stop_value = multiprocessing.Value('i', 0) self._process = multiprocessing.Process(target=self._record_data, args=(frequency_in_hz, self._stop_value)) # Start recording process self._process.start() print(datetime.now().strftime("%H:%M:%S.%f"), "Data logging process spawned") print("ID of data logging process: {}".format(self._process.pid)) def end_recording(self, terminate_wait=2): """Function to end the recording multithread that's been spawned. Args: terminate_wait: This is the time, in seconds, to wait after attempting to shut down the process before terminating it.""" # Get process id id = self._process.pid # Set stop event for process self._stop_value.value = 1 self._process.join(terminate_wait) # Wait two seconds for the process to terminate if self._process.is_alive(): # If it's still alive after waiting self._process.terminate() print(datetime.now().strftime("%H:%M:%S.%f"), "Process", id, "needed to be terminated.") else: print(datetime.now().strftime("%H:%M:%S.%f"), "Process", id, "successfully ended itself.") if __name__ == '__main__': foo = Foo('/tmp/foometer.csv') foo.start_recording(20) time.sleep(5) print('Ending recording') foo.end_recording()
Trying to control a GNUradio flow graph object
Good Day: I am trying to write some python code to control a gnuradio block. To illustrate the issue I encountered, I have created a simple flowgraph consisting of an audio source connected to the sound card. There is a single gnuradio companion WX GUI element (a variable slider) to control the audio frequency. I tried to take the python code created by gnuradio companion and create an object within python, then create two threads. One thread starts the GNUradio object, the second thread queries the user to input a frequency, then queries the object's frequency and prints it to the terminal for confirmation. When the code is run, the audio generator starts, the WX GUI slider is shown, and the terminal prompts the user to input a frequency. When the frequency is input via the terminal query, that number is echoed back as expected but the GNUradio block does not change its frequency. Changing the frequency via the WX GUI slider works as expected. Obviously I'm not linking the variable to the GNUradio block correctly. The code is copied below, any assistance would be appreciated. Thank you. -Ed #!/usr/bin/env python2 # -*- coding: utf-8 -*- ################################################## # GNU Radio Python Flow Graph # Title: Top Block # Generated: Wed Oct 4 06:04:54 2017 ################################################## if __name__ == '__main__': import ctypes import sys if sys.platform.startswith('linux'): try: x11 = ctypes.cdll.LoadLibrary('libX11.so') x11.XInitThreads() except: print "Warning: failed to XInitThreads()" from gnuradio import analog from gnuradio import audio from gnuradio import eng_notation from gnuradio import gr from gnuradio.eng_option import eng_option from gnuradio.filter import firdes from gnuradio.wxgui import forms from grc_gnuradio import wxgui as grc_wxgui from optparse import OptionParser import wx import threading from threading import Thread class top_block(grc_wxgui.top_block_gui): def __init__(self): grc_wxgui.top_block_gui.__init__(self, title="Top Block") _icon_path = "/usr/share/icons/hicolor/32x32/apps/gnuradio-grc.png" self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) ################################################## # Variables ################################################## self.samp_rate = samp_rate = 32000 self.freq = freq = 1000 ################################################## # Blocks ################################################## _freq_sizer = wx.BoxSizer(wx.VERTICAL) self._freq_text_box = forms.text_box( parent=self.GetWin(), sizer=_freq_sizer, value=self.freq, callback=self.set_freq, label="frequency", converter=forms.float_converter(), proportion=0, ) self._freq_slider = forms.slider( parent=self.GetWin(), sizer=_freq_sizer, value=self.freq, callback=self.set_freq, minimum=300, maximum=5000, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Add(_freq_sizer) self.audio_sink_0 = audio.sink(samp_rate, "", True) self.analog_sig_source_x_0 = analog.sig_source_f(samp_rate, analog.GR_COS_WAVE, freq, .25, 0) ################################################## # Connections ################################################## self.connect((self.analog_sig_source_x_0, 0), (self.audio_sink_0, 0)) def get_samp_rate(self): return self.samp_rate def set_samp_rate(self, samp_rate): self.samp_rate = samp_rate self.analog_sig_source_x_0.set_sampling_freq(self.samp_rate) def get_freq(self): return self.freq def set_freq(self, freq): self.freq = freq self.analog_sig_source_x_0.set_frequency(self.freq) self._freq_slider.set_value(self.freq) self._freq_text_box.set_value(self.freq) toneGen = top_block() def runToneGen(): toneGen.Start(True) toneGen.Wait() def userInput(): while True: freq = raw_input("Enter frequency: ") toneGen.freq = freq print "tone generator freq set to: ",toneGen.freq," Hz" #initiate thread to query user for freq uiThread = Thread(target=userInput, args=()) uiThread.start() #initiate thread to run gnuradio block gnuThread = Thread(target=runToneGen, args=()) gnuThread.start()
I believe I have found the answer to my own question. In the code sample above, in the 'userInput()' definition, I was trying to change the frequency of the tone generator block by directly setting the variable 'toneGen.freq'. This did not work. In order to correctly set the frequency the method 'set_freq' in the toneGen object must be used with this syntax: toneGen.set_freq(int(freq)) this function call will correctly set the audio frequency of the generator and update the UI slider.
django time checker database
I am trying to create a thread function that allow me to check a database field in order to see if the time.now() is bigger than the one recorded in the database(postgresql); the problem is that the view.py where I am calling this, is blocked by this thread, this is my actual code: PD: expire_pet is a text field, then I cast it to datetime. import socket import struct from time import * from datetime import datetime from models import Zone from multiprocessing import pool import threading class ControlHora(threading.Thread): def __init__(self,zone_id): threading.Thread.__init__(self) self.zone_id = zone_id def run(self): while(True): zone_pet = Zone.objects.filter(id = self.zone_id) for i in zone_pet: if i.pet_state == True: hour = datetime.datetime.strptime(i.expire_pet, '%I:%M') if hour <= datetime.datetime.now(): Zone.objects.filter(id = self.zone_id).update(vitrasa_pet = False) Zone.objects.filter(id = self.zone_id).update(esycsa_pet = False) Zone.objects.filter(id = self.zone_id).update(pet_state = False) Zone.objects.filter(id = self.zone_id).update(expire_pet='') sleep(5)
It works, the problem was that I have been calling the run in the wrong place, thanks
Tkinter window not playing well with threads
I've got a program that will eventually receive data from an external source over serial, but I'm trying to develop the display-side first. I've got this "main" module that has the simulated data send and receive. It updates a global that is used by a Matplotlib stripchart. All of this works. #------------------------------------------------------------------------------- # Name: BBQData # Purpose: Gets the data from the Arduino, and runs the threads. #------------------------------------------------------------------------------- import time import math import random from threading import Thread import my_globals as bbq import sys import BBQStripChart as sc import serial import BBQControl as control ser = serial.serial_for_url('loop://', timeout=10) def simData(): newTime = time.time() if not hasattr(simData, "lastUpdate"): simData.lastUpdate = newTime # it doesn't exist yet, so initialize it simData.firstTime = newTime # it doesn't exist yet, so initialize it if newTime > simData.lastUpdate: simData.lastUpdate = newTime return (140 + 0.05*(simData.lastUpdate - simData.firstTime), \ 145 + 0.022*(simData.lastUpdate - simData.firstTime), \ 210 + random.randrange(-10, 10)) else: return None def serialDataPump(): testCtr = 0; while not bbq.closing and testCtr<100: newData = simData() if newData != None: reportStr = "D " + "".join(['{:3.0f} ' for x in newData]) + '\n' reportStr = reportStr.format(*newData) ser.write(bytes(reportStr, 'ascii')) testCtr+=1 time.sleep(1) bbq.closing = True def serialDataRcv(): while not bbq.closing: line = ser.readline() rcvdTime = time.time() temps = str(line, 'ascii').split(" ") temps = temps[1:-1] for j, x in enumerate(temps): bbq.temps[j].append(float(x)) bbq.plotTimes.append(rcvdTime) def main(): sendThread = Thread(target = serialDataPump) receiveThread = Thread(target = serialDataRcv) sendThread.start() receiveThread.start() # sc.runUI() control.runControl() #blocks until user closes window bbq.closing = True time.sleep(2) exit() if __name__ == '__main__': main() ## testSerMain() However, I'd like to add a SEPARATE tkinter window that just has the most recent data on it, a close button, etc. I can get that window to come up, and show data initially, but none of the other threads run. (and nothing works when I try to run the window and the plot at the same time.) #------------------------------------------------------------------------------- # Name: BBQ Display/Control # Purpose: displays current temp data, and control options #------------------------------------------------------------------------------- import tkinter as tk import tkinter.font import my_globals as bbq import threading fontSize = 78 class BBQControl(tk.Tk): def __init__(self,parent): tk.Tk.__init__(self,parent) self.parent = parent self.labelFont = tkinter.font.Font(family='Helvetica', size=int(fontSize*0.8)) self.dataFont = tkinter.font.Font(family='Helvetica', size=fontSize, weight = 'bold') self.makeWindow() def makeWindow(self): self.grid() btnClose = tk.Button(self,text=u"Close") btnClose.grid(column=1,row=5) lblFood = tk.Label(self,anchor=tk.CENTER, text="Food Temps", \ font = self.labelFont) lblFood.grid(column=0,row=0) lblPit = tk.Label(self,anchor=tk.CENTER, text="Pit Temps", \ font = self.labelFont) lblPit.grid(column=1,row=0) self.food1Temp = tk.StringVar() lblFoodTemp1 = tk.Label(self,anchor=tk.E, \ textvariable=self.food1Temp, font = self.dataFont) lblFoodTemp1.grid(column=0,row=1) #spawn thread to update temps updateThread = threading.Thread(target = self.updateLoop) updateThread.start() def updateLoop(self): self.food1Temp.set(str(bbq.temps[1][-1])) def runControl(): app = BBQControl(None) app.title('BBQ Display') app.after(0, app.updateLoop) app.mainloop() bbq.closing = True if __name__ == '__main__': runControl()
Your title sums up the problem nicely: Tkinter doesn't play well with threads. That's not a question, that's the answer. You can only access tkinter widgets from the same thread that created the widgets. If you want to use threads, you'll need your non-gui threads to put data on a queue and have the gui thread poll the queue periodically.
One way of getting tkinter to play well with threads is to modify the library so all method calls run on a single thread. Two other questions deal with this same problem: Updating a TKinter GUI from a multiprocessing calculation and Python GUI is not responding while thread is executing. In turn, the given answers point to several modules that help to solve the problem you are facing. Whenever I work with tkinter, I always use the safetkinter module in case threads appear to be helpful in the program.