Related
I am trying to display a pyplot that updates every 3 seconds with data from another thread. I need to close the plot and reopen with the updated variables. I do not need an animation, I want to close the figure and open a new one.
This does not work unless I manually close the Figure window.
import numpy as np
from matplotlib import pyplot as plt
import threading as th
from time import sleep
Xplot = []
YPlot = []
def RandomXYGen():
x = np.random.normal(5, .75)
y = np.random.normal(10, 1.5)
return x, y
def LoopData():
while True:
xdata, ydata = RandomXYGen()
Xplot.append(xdata)
YPlot.append(ydata)
print(xdata, ydata)
sleep(0.1)
def Plotter(xToPlot, yToPlot):
plt.close('all')
plt.plot(xToPlot, yToPlot)
plt.show()
t = th.Thread(target=LoopData, daemon=True)
t.start()
while True:
Plotter(Xplot, YPlot)
sleep(3)
Any ideas why it is not closing?
[python 3.10.8 on Windows 10]
After many attempts on figuring it out I was finally able to get help from the Python Discord server. This is what solves the above problem:
plt.show(block=False)
plt.pause(0.01)
instead of:
plt.show()
Leaving this here in case someone runs into the same issue as me.
I am working on a project that requires me to log data over time, while also plotting the data on screen with a live line graph. I have gotten everything but the line graph to work this far and am unsure what I am doing incorrectly. This is the imports that I am currently using.
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import pyplot as plt
from matplotlib import style
from tkinter import *
from PIL import Image
import numpy as np
import serial
from serial import Serial
import sqlite3
import time
from datetime import datetime
from array import *
import cv2
from pathlib import Path
from itertools import count
The data that is meant to be used for the Y axis plotting is stored in an array of data. Each index in this array is to hold the last read value from the sensors, i=0 is sensor 1 and so on.
A=[0,0,0,0,0,0,0,0]
This is the definition of the subplot that I am trying to draw to. I think I am setting this up correctly, however I am not getting the expected result so likely not.
fig1 = plt.Figure(dpi=100, facecolor="#f0f0f0")
a = fig1.add_subplot(111)
a.patch.set_facecolor("#f0f0f0")
a.set_xlabel('time (Sec)')
a.set_ylabel('pressure(kPa)')
a.set_ylim(0,100)
a.set_xlim(0,30)
graph1 = FigureCanvasTkAgg(fig1, master=root)
graph1.get_tk_widget().place(x=10, y=220, width=210, height=280)
graph1.draw();
I am currently just trying to get one of the lines to draw first before handling the, seemingly, minor issue that is overlapping multiple lines. This is the function that I am trying to use in order to draw said line.
def graph_plotdata():
global A
global a
line1 = []
time = []
time.append(next(index))
line1.append(A[0])
a.cla()
a.plot(time, line1)
graph1.draw()
I have tried several iterations of this code in order attempt to solve this problem. The closest I have to getting it to work is in the current state in which something is happening however instead of keeping my min and max limits on the graph it completely reformats my plot and plots an "invisible" line.
Before starting:
After starting:
I am not overwhelmingly experienced when is comes to python libraries so bare with me.
I use a dictionary to store the various lines and line plots and then update the plots using set_data(xdata, ydata). I'm not sure how your datastream works, so mine just updates when I push the update button and generates a random reading. You'll obviously want to change those parts to match your data input.
fig, ax = plt.subplots(1, 1)
plt.subplots_adjust(bottom = 0.20)
num_sensors = 10
latest_reading = [0]*num_sensors
lines = {index: [0] for index in range(num_sensors)}
times = [0]
line_plots = {index: ax.plot(lines[index])[0] for index in range(num_sensors)}
btn_ax = plt.axes([0.475, 0.05, 0.10, 0.05])
def update(event):
latest_reading = np.random.randint(0, 10, num_sensors)
times.append(times[-1] + 1)
for index in range(num_sensors):
lines[index].append(latest_reading[index])
line_plots[index].set_data(times, lines[index])
# Adjust limits
max_time_window = 20
ax.set_xlim(max(0, max(times)-max_time_window), max(times))
ax.set_ylim(0, max(lines))
plt.draw()
btn = mpl.widgets.Button(btn_ax, 'Update')
btn.on_clicked(update)
Thank you for the response.
I figured out the issue, it had nothing to do with my matplotlib/tkinter implementation. I just totally missed that I had a scope inheritance issue. The lists of 'time' and 'line1' are not persistent in the entire scope and therefore being rewritten to empty lists every time the 'graph_plotdata()' function is called.
my solution is as follows:
timet = []
line1 = []
"""----------Graph Updater-----------"""
def graph_plotdata():
global B
global a
global graph1
global timet
global line1
timet.append(next(index))
line1.append(B[0])
a.clear()
a.plot(timet, line1)
a.patch.set_facecolor("#f0f0f0")
a.set_xlabel('time (Sec)')
a.set_ylabel('pressure(kPa)')
a.set_ylim(0,30)
a.set_xlim(0,30)
graph1.draw()
Hopefully this helps people in the future running into a similar issue!
I want to plot data in matplotlib in real time. I want to open a figure once at the start of the programme, then update the figure when new data is acquired. Despite there being a few similar questions out there, none quite answer my specific question.
I want each set of data points new_data1 and new_data2 to be plotted on the same figure at the end of each while loop i.e. one line after the first while loop, two lines on the same figure after the second while loop etc. Currently they are all plotted together, but only right at the end of the programme, which is no use for real time data acquisition.
import matplotlib.pyplot as plt
import numpy
hl, = plt.plot([], [])
def update_line(hl, new_datax, new_datay):
hl.set_xdata(numpy.append(hl.get_xdata(), new_datax))
hl.set_ydata(numpy.append(hl.get_ydata(), new_datay))
plt.xlim(0, 50)
plt.ylim(0,200)
plt.draw()
x = 1
while x < 5:
new_data1 = []
new_data2 = []
for i in range(500):
new_data1.append(i * x)
new_data2.append(i ** 2 * x)
update_line(hl, new_data1, new_data2)
x += 1
else:
print("DONE")
This programme plots all 5 lines, but at the end of the programme. I want each line to be plotted after one another, after the while loop is completed. I have tried putting in plt.pause(0.001) in the function, but it has not worked.
This programme is different from the one that has been put forward - that programme only plots one graph and does not update with time.
If I correctly understood your specifications, you can modify just a bit your MWE as follows:
import matplotlib.pyplot as plt
import numpy
fig = plt.figure(figsize=(11.69,8.27))
ax = fig.gca()
ax.set_xlim(0, 50)
ax.set_ylim(0,200)
hl, = plt.plot([], [])
def update_line(hl, new_datax, new_datay):
# re initialize line object each time if your real xdata is not contiguous else comment next line
hl, = plt.plot([], [])
hl.set_xdata(numpy.append(hl.get_xdata(), new_datax))
hl.set_ydata(numpy.append(hl.get_ydata(), new_datay))
fig.canvas.draw_idle()
fig.canvas.flush_events()
x = 1
while x < 10:
new_data1 = []
new_data2 = []
for i in range(500):
new_data1.append(i * x)
new_data2.append(i ** 2 * x)
update_line(hl, new_data1, new_data2)
# adjust pause duration here
plt.pause(0.5)
x += 1
else:
print("DONE")
which displays :
Not sure, if I am reading the requirements right but below is a blueprint. Please change it to suit your requirements. You may want to change the function Redraw_Function and edit the frames (keyword parameter, which is np.arange(1,5,1) ) in the FuncAnimation call. Also interval=1000 means 1000 milliseconds of delay.
If you are using Jupyter then comment out the second last line (where it says plt.show()) and uncomment the last line. This will defeat your purpose of real time update but I am sorry I had trouble making it work real time in Jupyter. However if you are using python console or official IDLE please run the code as it is. It should work nicely.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
plot, = plt.plot([],[])
def init_function():
ax.set_xlim(0,50)
ax.set_ylim(0,250)
return plot,
def Redraw_Function(UpdatedVal):
new_x = np.arange(500)*UpdatedVal
new_y = np.arange(500)**2*UpdatedVal
plot.set_data(new_x,new_y)
return plot,
Animated_Figure = FuncAnimation(fig,Redraw_Function,init_func=init_function,frames=np.arange(1,5,1),interval=1000)
plt.show()
# Animated_Figure.save('MyAnimated.gif',writer='imagemagick')
When you run the code, you obtain the below result. I tried to keep very little code but I am sorry, if your requirement was totally different.
Best Wishes,
I've started to learn about matplotlib functions because i wanted to visualize data i was receiving via websocket. For that i made a dummy program that mimics the behaviour of my main program but has added the functionality of mathplotlib. what i noticed is the program takes more and more time to finish each loop and eventually 'freezes'. i managed to extend it life by changing the interval in animation.FuncAnimation from 1000 to 10000. But that just the program to plot sometimes up to 9s for 1 new peace of data. I believe the problem lays in a inappropriate way of cleaning old plots. But i don't know where exactly i did the mistake
import time
import datetime
import timeit
import queue
import os
import random
import copy
import matplotlib.pyplot as plt
import matplotlib.animation as animation
q = queue.Queue()
beta=[0,]
b=False
czas=[]
produkty=["primo"]
cena=[[] for _ in range(len(produkty))]
fig=plt.figure()
#ax1=fig.add_subplot(1,1,1)
#ax2=fig.add_subplot(1,1,1)
ax1=plt.subplot(1,1,1)
ax2=plt.subplot(1,1,1)
def animate(i):
ax1.clear()
ax2.clear()
ax1.plot(czas,cena[0])
ax2.plot(czas,beta)
while True:
time.sleep(1)
alpfa=time.time()
#input('press enter')
rand_produkt=random.choice(produkty)
rand_price=random.randint(1,10)
rand_czas=time.ctime()
alfa={'type':'ticker','price':rand_price,'product_id':rand_produkt,'time':rand_czas}
q.put(alfa)
if q.not_empty:
dane=q.get()
typ=dane.get('type',None)
if typ=='ticker':
price=dane.get('price', None)
pair=dane.get('product_id',None)
t=dane.get('time', None)
b=True
if b==True:
b=False
produkt_id=produkty.index(pair)
cena[produkt_id].append(float(price))
czas.append(t)
plt.ion()
ani=animation.FuncAnimation(fig,animate,interval=1000)#, blit=True)repeat=True)
plt.show()
plt.pause(0.001)
#fig.clf()
beta.append(time.time()-alpfa)
print(beta[-1])
The problem with your code is that you call a new animation in you while loop. Hence this will cause slow down down the line. It is better to initiate your plot ones. One trick may be to update the object data directly as such:
from matplotlib.pyplot import subplots, pause, show
from numpy import sin, pi
fig, ax = subplots()
x = [0]
y = [sin(2 * pi * x[-1])]
p1, = ax.plot(x, y)
show(block = False)
while True:
# update data
x.append(x[-1] + .1)
y.append(sin(2 * pi * x[-1]))
p1.set_data(x, y) # update data
ax.relim() # rescale axis
ax.autoscale_view()# update view
pause(1e-3)
This question mostly pertains to Matplotlib and animation. The issue is that when animation is updated i need to clear out the axis each time or I get overlapping images because of color changes. When i was using matplotlib-1.0.1 the code below was working fine, but now that I am using matplotlib-1-3.1 if I continue to use ax.clear() in the code below the images of ax do not show up on the chart. Here is the code:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
from finance_pylab import quotes_historical_yahoo, candlestick,plot_day_summary, candlestick2,volume_overlay
from pylab import *
import f_charting
import f_datamanip # to use idx_of_last_unique
import f_tcnvrt # for dukascopy date
from numpy import*
import f_file_handling
import first_add2file
import f_dukascopy_fetcher
import datetime ## so we can print a time stamp we recognize
import f_candle_stick_maker3_fluent
anim_data_file='candle_test6'
showlast_idxs=40
tframe=5 ##how many minutes candles will be #minutes
dukas_file_name="EURUSD_1m_jan2012"
first_candle=datetime.datetime(2012,1,1,22,10,0) ## start chart here
last_candle=datetime.datetime(2012,1,2,1,55,0) ## start trading after here
candle_name_LDP_diff=datetime.timedelta(0,(tframe-1)*60) ##LDP = last_data_point
dukas_last_data_point=last_candle+candle_name_LDP_diff
Dcsv,D,O,H,L,C,V=f_dukascopy_fetcher.dukas2(dukas_file_name,first_candle,dukas_last_data_point)## will get data up to data_end, could just put in high date to take all in file#,s_date)#,data_start,s_date)
f_candle_stick_maker3_fluent.candle_2_txtfile(anim_data_file,tframe,first_candle,last_candle,Dcsv,D,O,H,L,C,V,0)
filename='candle_test6';file_name=filename+'.txt'
candle_width=.8;colorup='#33CC33'; colordown='#E80000' ;up_col='#B8B8B8';down_col='w'
rect1=[.05,.14,.94,.82]#left, bottom, width, height #rect1=[.1,.1,.8,.7] #seems full:rect1=[.02,.04,.95,.93] , the more you move left , you also have to adjust width. bottom and hight push on each other as well
fig =plt.figure(figsize=(15,7),facecolor='white');axescolor ='#f6f6f6' #'#200000' #'#180000' ##100000'#f6f6f6' # the axies background color # border of chart
ax = fig.add_axes(rect1, axisbg=axescolor) #start with volume axis
ax1v = ax.twinx()
def candle_animate(i):
pullData = open(anim_data_file+'.txt','r').read()
dataArray= pullData.split('\n')
contig_time=[];_open=[];_close=[];_high=[];_low=[];_vol=[]; _timevec=[]
for eachLine in dataArray:
if len(eachLine)>1:
_t,_o,_c,_h,_l,_v,u=eachLine.split(',')##x,y=eachLine.split(',')
_timevec.append(f_tcnvrt.str2time_dukascopy(_t))
_open.append(float(_o))
_close.append(float(_c))
_high.append(float(_h))
_low.append(float(_l))
_vol.append(float(_v))
units_print=u
_open=array(_open);_close=array(_close);_high=array(_high);_low=array(_low);_vol=array(_vol)
ax.clear();ax1v.clear() # this line worked with matplotlib-1.0.1 but matplotlib-1.3.1 keeps ax blank
D=_timevec;O=_open;H=_high;L=_low;C=_close;V=_vol
time_delta=datetime.timedelta(0,tframe*60)
last_data_point=D[-1]
_time,_open,_high,_low,_close,_vol=f_candle_stick_maker3_fluent.cs_maker(tframe,first_candle,last_data_point,D,O,H,L,C,V)
contig_time=range(0,len(_time))
chartstart=len(contig_time)-showlast_idxs
numXlbls=12
myidx,x_label=f_charting.x_labels_last_tick_showall_contig(_time,numXlbls)
data4candleshow=transpose([contig_time,_open,_close,_high,_low,_vol])
data4candle=transpose([contig_time,_open,_close,_high,_low,_vol])[-showlast_idxs:]
candlestick(ax, data4candle, width=candle_width,colorup='#33CC33',colordown='#E80000') #'#00FF00' '#C11B17'
f_charting.bar_vol(contig_time[-showlast_idxs:],_open[-showlast_idxs:],_close[-showlast_idxs:],_vol[-showlast_idxs:],ax1v,candle_width,up_col,down_col)
ani=animation.FuncAnimation(fig,candle_animate, interval=1000)
plt.show()
I took a lot out of the code to make it simpler, but i know there is still a lot to look at. Hopefully some expert knows more about the difference between the 2 matplotlib editions or is familiar with my issue. There must be a a clean way to clear out the chart between updates so i don't get overlapping images.
Note: the line: ax.clear();ax1v.clear() can be found in the code above and has a comment next to it denoting this is the line that worked for its purpose in the older matplotlib, but now unfortunately clears out the graph when using matplotlib-1.3.1
Thank you