How do I show two figures(plots) in a single window? - python

fig and fig1 give two different plots. Two different windows pop up. I want to show these two plots in a single window, may be separated by a line in the middle. How can I do that?
packing_options = get_packing_options(min_w, max_w, min_l, max_l, rect_list, step_size = 1, \
result_list = []
for opt in packing_options:
step_w = float(get_max(opt.sheets))
tmp_sheets = trim_sheets(opt.sheets, max(min_w,step_w), min_l)
tmp_l = float(get_max(tmp_sheets,False))
tmp_result_list = []
tmp_w = step_w
while area/tmp_w >= tmp_l:
tmp_sheet = Sheet(tmp_w, area/tmp_w)
tmp_result_list.append(find_best_packing_GA(tmp_sheet, sheets_to_rect_list(tmp_sheets), \
Pm = 0, rotate_prob = 0, flex = True, only_vertical = True))
tmp_w = tmp_w + step_w
result_list.append(best_individual(tmp_result_list)[0])
best, avg_fit, best_index = best_individual(result_list)
fig = packing_options[best_index].plot_sheets()
dataPlot = FigureCanvasTkAgg(fig, master = window)
dataPlot.draw()
dataPlot.get_tk_widget().pack(side='top', fill='both', expand=1)
fig1 = best.plot_sheets()
dataPlot1 = FigureCanvasTkAgg(fig1, master = window)
dataPlot1.draw()
dataPlot1.get_tk_widget().pack(side='top', fill='both', expand=1)
This is the picture of the output: https://imgur.com/NZaZkwa

Related

Matplotlib, how to get a plot inside a plot?

I want to create a subplot inside the first subplot named "logo".
This time I generated my subplots with mosaic (ax_dict = fig.subplot_mosaic(layout))
How to generate a subplot inside the first subplot?
import numpy as np
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import ttk
from tkinter import *
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
root = tk.Tk()
root.title("Transactions")
root.geometry("1920x1080")
tabControl = ttk.Notebook(root)
tab1 = ttk.Frame(tabControl)
tab2 = ttk.Frame(tabControl)
tab3 = ttk.Frame(tabControl)
tabControl.add(tab1, text ='Home')
tabControl.add(tab2, text ='Scatter Plot')
tabControl.add(tab3, text ='Histogram')
#LABEL-FRAME
ontop = tk.Frame(root, background="#ccc5b9",height=33)
ontop.pack(fill=BOTH)
#BUTTON-FRAME
top = tk.Frame(root, background="#e9ecef")
top.pack(fill=X)
tabControl.pack(expand = FALSE, fill ="both")
#TABLE-FRAME
bottom = tk.Frame(root, background="#adb5bd")
bottom.pack(in_=tab1,fill=X)
s = ttk.Style()
s.theme_use('default')
s.configure('TNotebook.Tab', background="green3")
s.map("TNotebook", background= [("selected", "green3")])
layout = [["logo", "logo","logo"],["logo", "logo","logo"],["bar", "bar","bar"],["bar", "bar","bar"],["alpha","beta","edge"]]
fig = plt.figure(figsize = (5,9))
ax_dict = fig.subplot_mosaic(layout)
ax_dict["logo"].annotate(
xy = (3.3, 95),
text = "0-1000 €",
size = 10
)
ax_dict["bar"].annotate(
xy = (1.5,.5),
text = "bar",
ha = "left",
va = "center",
size = 10
)
ax_dict["edge"].annotate(
xy = (.5,.5),
text = "edge",
ha = "left",
va = "center",
size = 10
)
ax_dict["alpha"].annotate(
xy = (.5,.5),
text = "alpha",
ha = "center",
va = "center",
size = 10
)
ax_dict["beta"].annotate(
xy = (.5,.5),
text = "beta",
ha = "center",
va = "center",
size = 10
)
ax_dict["logo"].set_xlim([-10,1010])
ax_dict["bar"].set_xlim([900,100100])
ax_dict["alpha"].set_xlim([99000,1001000])#100T-1M
ax_dict["beta"].set_xlim([990000,10010000])#1M-10M
ax_dict["edge"].set_xlim([9900000,100100000])#10M-100M
ax_dict["logo"].set_xticks([-10,50,100,150,200,250,300,350,400,450,500,550,600,660,700,750,800,850,900,950,1010])
ax_dict["bar"].set_xticks([1000,5000, 10000,15000,20000,25000,30000,35000,40000,45000,50000,55000,60000,65000,70000,75000,80000,85000,90000,95000,100000])
ax_dict["alpha"].set_xticks([100000,200000,300000,400000,500000,600000,700000,800000,900000,1000000])
ax_dict["beta"].set_xticks([1000000,2000000,3000000,4000000,5000000,6000000,7000000,8000000,9000000,10000000])
ax_dict["edge"].set_xticks([10000000,20000000,30000000,40000000,50000000,60000000,70000000,80000000,90000000,100000000])
ax_dict["logo"].set_xticklabels(["0-10","50","100","150", "200","250","300","350","400","450","500","550","600","650","700","750","800","850","900","950","1000"],fontsize=6)
ax_dict["bar"].set_xticklabels(["1k","5k", "10k","15k","20k","25k","30k","35k","40k","45k","50k","55k","60k","65k","70k","75k","80k","85k","90k","95k","100k"],fontsize=8)
ax_dict["alpha"].set_xticklabels(["100k","200k","300k","400k","500k","600k","700k","800k","900k", "1M"],fontsize=8)
ax_dict["beta"].set_xticklabels(["1M","2M","3M","4M","5M","6M","7M","8M","9M","10M"],fontsize=8)
ax_dict["edge"].set_xticklabels(["10M","20M","30M","40M","50M","60M","70M","80M","90M","100M"],fontsize=8)
ax_dict["logo"].grid(visible = True, ls = ":", color = "gray")
ax_dict["edge"].grid(visible = True, ls = ":", color = "red")
ax_dict["bar"].grid(visible = True, ls = ":", color = "gray")
ax_dict["alpha"].grid(visible = True, ls = ":", color = "blue")
ax_dict["beta"].grid(visible = True, ls = ":", color = "green")
ax_dict["logo"].set_yticks([0,10,20,30,40,50,60,70,80,90,100])
ax_dict["bar"].set_yticks([0,10,20,30,40,50,60,70,80,90,100])
ax_dict["alpha"].set_yticks([0,1,2,3,4,5,6,7,8,9,10])
ax_dict["beta"].set_yticks([0,1,2,3,4,5,6,7,8,9,10])
ax_dict["edge"].set_yticks([0,1,2,3,4,5,6,7,8,9,10])
figure_canvas = FigureCanvasTkAgg(fig, tab2)
figure_canvas.draw()
figure_canvas.get_tk_widget().pack(expand=TRUE, fill=BOTH)
fig.set_tight_layout(True)
root.mainloop()
What did you try and what were you expecting?
I searched the web, but found nothing for this special case.
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
axins =inset_axes(ax_dict["logo"], width="70%", height=1.8, loc=1)
axins is then the axes inside the plot "logo", which was defined through
ax_dict = fig.subplot_mosaic(layout)
ax_dict["logo"].annotate(
xy = (3.3, 95),
text = "0-1000 €",
size = 10
)

Embedding matplotlib in tkinter GUI

Trying to embed matplotlib in tkinter GUI, however I get the error:
TypeError: init() got multiple values for argument 'master'
Could you tell me please how to handle it?
Here's the code:
interface - tkinter GUI
visualizer - selv updating graph that uses function live_plotter from pylive_mod file
interface:
# -*- coding: utf-8 -*-
"""
Created on Tue Oct 6 10:24:35 2020
#author: Dar0
"""
from tkinter import * #import tkinter module
from visualizer import main #import module 'visualizer' that shows the graph in real time
class Application(Frame):
''' Interface for visualizing graphs, indicators and text-box. '''
def __init__(self, master):
super(Application, self).__init__(master)
self.grid()
self.create_widgets()
def create_widgets(self):
# Label of the 1st graph
Label(self,
text='Hook Load / Elevator Height / Depth vs Time'
).grid(row = 0, column = 0, sticky = W)
# Graph 1 - Hook Load / Elevator Height / Depth vs Time
# button that displays the plot
plot_button = Button(self,
master = root,
command = main,
height = 2,
width = 10,
text = "Plot").grid(row = 1, column = 0, sticky = W)
# place the button
# in main window
# Label of the 2nd graph
Label(self,
text = 'Hook Load / Elevator Height vs Time'
).grid(row = 2, column = 0, sticky = W)
# Graph 2 - Hook Load / Elevator Height vs Time
#Label of the 3rd graph
Label(self,
text = 'Hook Load vs Time'
).grid(row = 4, column = 0, sticky = W)
#Graph 3 - Hook Load vs Time
#Label of the 1st indicator
Label(self,
text = '1st performance indicator'
).grid(row = 0, column = 1, sticky = W)
#1st performance indicator
#Label of 2nd performance indicator
Label(self,
text = '2nd performance indicator'
).grid(row = 2, column = 1, sticky = W)
#2nd performance indicator
#Label of 3rd performance indicator
Label(self,
text = '3rd performance indicator'
).grid(row = 4, column = 1, sticky = W)
#Text-box showing comments based on received data
self.text_box = Text(self, width = 50, height = 10, wrap = WORD)
self.text_box.grid(row = 6, column = 0, columnspan = 1)
self.text_box.delete(0.0, END)
self.text_box.insert(0.0, 'My message will be here.')
#Main part
root = Tk()
root.title('WiTSML Visualizer by Dar0')
app = Application(root)
root.mainloop()
visualizer:
#WiTSML visualizer
#Created by Dariusz Krol
#import matplotlib
#matplotlib.use('TkAgg')
#from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
#from matplotlib.figure import Figure
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import random
class Visualizer(object):
""" Includes all the methods needed to show streamed data. """
def __init__(self):
self.file_path = 'C:/Anaconda/_my_files/witsml_reader/modified_witsml.csv' #Defines which file is streamed
self.datetime_mod = []
self.bpos_mod = []
self.woh_mod = []
self.torq_mod = []
self.spp_mod = []
self.depth_mod = []
self.flow_in_mod = []
self.rpm_mod = []
def open_file(self):
self.df = pd.read_csv(self.file_path, low_memory = False, nrows = 300000) #Opens the STREAMED file (already modified so that data convert is not required)
self.df = self.df.drop(0)
self.df = pd.DataFrame(self.df)
return self.df
def convert_dataframe(self):
self.df = self.df.values.T.tolist() #Do transposition of the dataframe and convert to list
#Columns are as following:
# - DATETIME
# - BPOS
# - WOH
# - TORQ
# - SPP
# - DEPTH
# - FLOW_IN
# - RPM
self.datetime_value = self.df[0]
self.bpos_value = self.df[1]
self.woh_value = self.df[2]
self.torq_value = self.df[3]
self.spp_value = self.df[4]
self.depth_value = self.df[5]
self.flow_in_value = self.df[5]
self.rpm_value = self.df[7]
return self.datetime_value, self.bpos_value, self.woh_value, self.torq_value, self.spp_value, self.depth_value, self.flow_in_value, self.rpm_value
#print(self.bpos_value)
def deliver_values(self, no_dp, columns):
''' Method gets no_dp amount of data points from the original file. '''
self.no_dp = no_dp #defines how many data points will be presented in the graph
val_dict = {
'datetime': [self.datetime_value, self.datetime_mod],
'bpos': [self.bpos_value, self.bpos_mod],
'woh': [self.woh_value, self.woh_mod],
'torq': [self.torq_value, self.torq_mod],
'spp': [self.spp_value, self.spp_mod],
'depth': [self.depth_value, self.depth_mod],
'flow_in': [self.flow_in_value, self.flow_in_mod],
'rpm': [self.rpm_value, self.rpm_mod]
}
for item in columns:
if self.no_dp > len(val_dict[item][0]):
dp_range = len(val_dict[item][0])
else:
dp_range = self.no_dp
for i in range(dp_range):
val_dict[item][1].append(val_dict[item][0][i])
return self.datetime_mod, self.bpos_mod, self.woh_mod, self.torq_mod, self.spp_mod, self.depth_mod, self.flow_in_mod, self.rpm_mod
def show_graph2(self):
from pylive_mod import live_plotter
self.open_file()
self.convert_dataframe()
self.deliver_values(no_dp = 100000, columns = ['datetime', 'depth', 'bpos', 'woh'])
fst_p = 0
size = 300 # density of points in the graph (100 by default)
x_vec = self.datetime_mod[fst_p:size]
y_vec = self.depth_mod[fst_p:size]
y2_vec = self.bpos_mod[fst_p:size]
y3_vec = self.woh_mod[fst_p:size]
line1 = []
line2 = []
line3 = []
for i in range(self.no_dp):
#print(self.datetime_mod[i:6+i])
#print('Ostatni element y_vec: ', y_vec[-1])
#print(x_vec)
x_vec[-1] = self.datetime_mod[size+i]
y_vec[-1] = self.depth_mod[size+i]
y2_vec[-1] = self.bpos_mod[size+i]
y3_vec[-1] = self.woh_mod[size+i]
line1, line2, line3 = live_plotter(x_vec, y_vec, y2_vec, y3_vec, line1, line2, line3)
x_vec = np.append(x_vec[1:], 0.0)
y_vec = np.append(y_vec[1:], 0.0)
y2_vec = np.append(y2_vec[1:], 0.0)
y3_vec = np.append(y3_vec[1:], 0.0)
def main():
Graph = Visualizer()
Graph.open_file() #Opens the streamed file
Graph.convert_dataframe() #Converts dataframe to readable format
Graph.show_graph2()
#Show us the graph
#main()
pylive_mod (live_plotter):
def live_plotter(x_data, y1_data, y2_data, y3_data, line1, line2, line3, identifier='',pause_time=1):
if line1 == [] and line2 == [] and line3 == []:
# this is the call to matplotlib that allows dynamic plotting
plt.ion()
#fig = Figure(figsize = (5, 4), dpi = 100)
#host = fig.add_subplot()
fig, host = plt.subplots()
fig.set_figheight(7) #adjust figure's height
fig.set_figwidth(14) #adjust figure's width
fig.subplots_adjust(0.15)
#Line1
#line1 = host
ln1 = host
ln2 = host.twinx()
ln3 = host.twinx()
ln2.spines['right'].set_position(('axes', 1.))
ln3.spines['right'].set_position(('axes', 1.12))
make_patch_spines_invisible(ln2)
make_patch_spines_invisible(ln3)
ln2.spines['right'].set_visible(True)
ln3.spines['right'].set_visible(True)
ln1.set_xlabel('Date & Time') #main x axis
ln1.set_ylabel('Depth') #left y axis
ln2.set_ylabel('Elevator Height')
ln3.set_ylabel('Weight on Hook')
#
x_formatter = FixedFormatter([x_data])
x_locator = FixedLocator([x_data[5]])
ln1.xaxis.set_major_formatter(x_formatter)
ln1.xaxis.set_major_locator(x_locator)
#
ln1.locator_params(nbins = 5, axis = 'y')
ln1.tick_params(axis='x', rotation=90) #rotates x ticks 90 degrees down
ln2.axes.set_ylim(0, 30)
ln3.axes.set_ylim(200, 250)
line1, = ln1.plot(x_data, y1_data, color = 'black', linestyle = 'solid', alpha=0.8, label = 'Depth')
line2, = ln2.plot(x_data, y2_data, color = 'blue', linestyle = 'dashed', alpha=0.8, label = 'Elevator Height')
line3, = ln3.plot(x_data, y3_data, color = 'red', linestyle = 'solid', alpha=0.8, label = 'Weight on Hook')
# ----- embedding -----
canvas = FigureCanvasTkAgg(fig, master = root)
canvas.draw()
# placing the canvas on the Tkinter window
canvas.get_tk_widget().pack()
# creating the Matplotlib toolbar
toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
# placing the toolbar on the Tkinter window
canvas.get_tk_widget().pack()
#----- -----
plt.title('WiTSML Visualizer')
fig.tight_layout() #the graphs is not clipped on sides
#Shows legend
lines = [line1, line2, line3]
host.legend(lines, [l.get_label() for l in lines], loc = 'lower left')
#Shows grid
plt.grid(True)
#Shows the whole graph
plt.show()
# after the figure, axis, and line are created, we only need to update the y-data
mod_x_data = convert_x_data(x_data, 10)
line1.axes.set_xticklabels(mod_x_data)
line1.set_ydata(y1_data)
line2.set_ydata(y2_data)
line3.set_ydata(y3_data)
#Debugging
#rint('plt.lim: ', ln2.axes.get_ylim())
# adjust limits if new data goes beyond bounds
# limit for line 1
if np.min(y1_data)<=line1.axes.get_ylim()[0] or np.max(y1_data)>=line1.axes.get_ylim()[1]:
plt.ylim(0, 10)
line1.axes.set_ylim([np.min(y1_data)-np.std(y1_data),np.max(y1_data)+np.std(y1_data)])
# limit for line 2
if np.min(y2_data)<=line2.axes.get_ylim()[0] or np.max(y2_data)>=line2.axes.get_ylim()[1]:
plt.ylim([np.min(y2_data)-np.std(y2_data),np.max(y2_data)+np.std(y2_data)])
#plt.ylim(0, 25)
# limit for line 3
if np.min(y3_data)<=line3.axes.get_ylim()[0] or np.max(y3_data)>=line3.axes.get_ylim()[1]:
plt.ylim([np.min(y3_data)-np.std(y3_data),np.max(y3_data)+np.std(y3_data)])
#plt.ylim(0, 25)
# Adds lines to the legend
#host.legend(lines, [l.get_label() for l in lines])
# this pauses the data so the figure/axis can catch up - the amount of pause can be altered above
plt.pause(pause_time)
# return line so we can update it again in the next iteration
return line1, line2, line3

Multiple matplotlib instances in tkinter GUI

I have build simple tkinter GUI.
Now, I am trying to visualise 3 different graphs (by calling the same function with different variables) and place them in 3 different rows of the GUI.
When I do that I get 2 problems:
Every time I run the script (interface.py) I get 2 windows - both GUI and external graph's window. How to get rid of the second one?
I am not able to visualize all the 3 graphs. The script stops after showing the first one. I believe this is because of that the first graph works in a loop (iterates through plenty of data points). Is there any work around it?
Interface:
# -*- coding: utf-8 -*-
"""
Created on Tue Oct 6 10:24:35 2020
#author: Dar0
"""
from tkinter import * #import tkinter module
from visualizer import main #import module 'visualizer' that shows the graph in real time
class Application(Frame):
''' Interface for visualizing graphs, indicators and text-box. '''
def __init__(self, master):
super(Application, self).__init__(master)
self.grid()
self.create_widgets()
def create_widgets(self):
# Label of the 1st graph
Label(self,
text='Hook Load / Elevator Height / Depth vs Time'
).grid(row = 0, column = 0, sticky = W)
# Graph 1 - Hook Load / Elevator Height / Depth vs Time
# button that displays the plot
#plot_button = Button(self,2
# command = main,
# height = 2,
# width = 10,
# text = "Plot"
# ).grid(row = 1, column = 0, sticky = W)
self.graph_1 = main(root, 1, 0)
# place the button
# in main window
# Label of the 2nd graph
Label(self,
text = 'Hook Load / Elevator Height vs Time'
).grid(row = 3, column = 0, sticky = W)
# Graph 2 - Hook Load / Elevator Height vs Time
self.graph_2 = main(root, 4, 0)
#Label of the 3rd graph
Label(self,
text = 'Hook Load vs Time'
).grid(row = 6, column = 0, sticky = W)
#Graph 3 - Hook Load vs Time
#Label of the 1st indicator
Label(self,
text = '1st performance indicator'
).grid(row = 0, column = 1, sticky = W)
#1st performance indicator
#Label of 2nd performance indicator
Label(self,
text = '2nd performance indicator'
).grid(row = 3, column = 1, sticky = W)
#2nd performance indicator
#Label of 3rd performance indicator
Label(self,
text = '3rd performance indicator'
).grid(row = 6, column = 1, sticky = W)
#Text-box showing comments based on received data
self.text_box = Text(self, width = 50, height = 10, wrap = WORD)
self.text_box.grid(row = 9, column = 0, columnspan = 1)
self.text_box.delete(0.0, END)
self.text_box.insert(0.0, 'My message will be here.')
#Main part
root = Tk()
root.title('WiTSML Visualizer by Dar0')
app = Application(root)
root.mainloop()
Visualizer:
#WiTSML visualizer
#Created by Dariusz Krol
#import matplotlib
#matplotlib.use('TkAgg')
#from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
#from matplotlib.figure import Figure
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import random
class Visualizer(object):
""" Includes all the methods needed to show streamed data. """
def __init__(self):
self.file_path = 'C:/Anaconda/_my_files/witsml_reader/modified_witsml.csv' #Defines which file is streamed
self.datetime_mod = []
self.bpos_mod = []
self.woh_mod = []
self.torq_mod = []
self.spp_mod = []
self.depth_mod = []
self.flow_in_mod = []
self.rpm_mod = []
def open_file(self):
self.df = pd.read_csv(self.file_path, low_memory = False, nrows = 300000) #Opens the STREAMED file (already modified so that data convert is not required)
self.df = self.df.drop(0)
self.df = pd.DataFrame(self.df)
return self.df
def convert_dataframe(self):
self.df = self.df.values.T.tolist() #Do transposition of the dataframe and convert to list
#Columns are as following:
# - DATETIME
# - BPOS
# - WOH
# - TORQ
# - SPP
# - DEPTH
# - FLOW_IN
# - RPM
self.datetime_value = self.df[0]
self.bpos_value = self.df[1]
self.woh_value = self.df[2]
self.torq_value = self.df[3]
self.spp_value = self.df[4]
self.depth_value = self.df[5]
self.flow_in_value = self.df[5]
self.rpm_value = self.df[7]
return self.datetime_value, self.bpos_value, self.woh_value, self.torq_value, self.spp_value, self.depth_value, self.flow_in_value, self.rpm_value
#print(self.bpos_value)
def deliver_values(self, no_dp, columns):
''' Method gets no_dp amount of data points from the original file. '''
self.no_dp = no_dp #defines how many data points will be presented in the graph
val_dict = {
'datetime': [self.datetime_value, self.datetime_mod],
'bpos': [self.bpos_value, self.bpos_mod],
'woh': [self.woh_value, self.woh_mod],
'torq': [self.torq_value, self.torq_mod],
'spp': [self.spp_value, self.spp_mod],
'depth': [self.depth_value, self.depth_mod],
'flow_in': [self.flow_in_value, self.flow_in_mod],
'rpm': [self.rpm_value, self.rpm_mod]
}
for item in columns:
if self.no_dp > len(val_dict[item][0]):
dp_range = len(val_dict[item][0])
else:
dp_range = self.no_dp
for i in range(dp_range):
val_dict[item][1].append(val_dict[item][0][i])
return self.datetime_mod, self.bpos_mod, self.woh_mod, self.torq_mod, self.spp_mod, self.depth_mod, self.flow_in_mod, self.rpm_mod
def show_graph2(self, tr_val, row, column):
from pylive_mod import live_plotter, live_plotter2
self.open_file()
self.convert_dataframe()
self.deliver_values(no_dp = 100000, columns = ['datetime', 'depth', 'bpos', 'woh'])
fst_p = 0
size = 300 # density of points in the graph (100 by default)
x_vec = self.datetime_mod[fst_p:size]
y_vec = self.depth_mod[fst_p:size]
y2_vec = self.bpos_mod[fst_p:size]
y3_vec = self.woh_mod[fst_p:size]
line1 = []
line2 = []
line3 = []
for i in range(self.no_dp):
#print(self.datetime_mod[i:6+i])
#print('Ostatni element y_vec: ', y_vec[-1])
#print(x_vec)
x_vec[-1] = self.datetime_mod[size+i]
y_vec[-1] = self.depth_mod[size+i]
y2_vec[-1] = self.bpos_mod[size+i]
y3_vec[-1] = self.woh_mod[size+i]
line1, line2, line3 = live_plotter2(tr_val, row, column, x_vec, y_vec, y2_vec, y3_vec, line1, line2, line3)
x_vec = np.append(x_vec[1:], 0.0)
y_vec = np.append(y_vec[1:], 0.0)
y2_vec = np.append(y2_vec[1:], 0.0)
y3_vec = np.append(y3_vec[1:], 0.0)
def main(tr_val, row, column):
Graph = Visualizer()
Graph.open_file() #Opens the streamed file
Graph.convert_dataframe() #Converts dataframe to readable format
Graph.show_graph2(tr_val, row, column)
#Show us the graph
#main()
Function that creates the graph:
def live_plotter2(tr_val, row, column, x_data, y1_data, y2_data, y3_data, line1, line2, line3, identifier='',pause_time=1):
if line1 == [] and line2 == [] and line3 == []:
# this is the call to matplotlib that allows dynamic plotting
plt.ion()
fig = plt.figure(figsize = (5, 4), dpi = 100)
fig.subplots_adjust(0.15)
# -------------------- FIRST GRAPH --------------------
host = fig.add_subplot()
ln1 = host
ln2 = host.twinx()
ln3 = host.twinx()
ln2.spines['right'].set_position(('axes', 1.))
ln3.spines['right'].set_position(('axes', 1.12))
make_patch_spines_invisible(ln2)
make_patch_spines_invisible(ln3)
ln2.spines['right'].set_visible(True)
ln3.spines['right'].set_visible(True)
ln1.set_xlabel('Date & Time') #main x axis
ln1.set_ylabel('Depth') #left y axis
ln2.set_ylabel('Elevator Height')
ln3.set_ylabel('Weight on Hook')
#
x_formatter = FixedFormatter([x_data])
x_locator = FixedLocator([x_data[5]])
#ln1.xaxis.set_major_formatter(x_formatter)
ln1.xaxis.set_major_locator(x_locator)
#
ln1.locator_params(nbins = 5, axis = 'y')
ln1.tick_params(axis='x', rotation=90) #rotates x ticks 90 degrees down
ln2.axes.set_ylim(0, 30)
ln3.axes.set_ylim(200, 250)
line1, = ln1.plot(x_data, y1_data, color = 'black', linestyle = 'solid', alpha=0.8, label = 'Depth')
line2, = ln2.plot(x_data, y2_data, color = 'blue', linestyle = 'dashed', alpha=0.8, label = 'Elevator Height')
line3, = ln3.plot(x_data, y3_data, color = 'red', linestyle = 'solid', alpha=0.8, label = 'Weight on Hook')
fig.tight_layout() #the graphs is not clipped on sides
plt.title('WiTSML Visualizer')
plt.grid(True)
#Shows legend
lines = [line1, line2, line3]
host.legend(lines, [l.get_label() for l in lines], loc = 'lower left')
#Shows the whole graph
#plt.show()
#-------------------- Embedding --------------------
canvas = FigureCanvasTkAgg(fig, master=tr_val)
canvas.draw()
canvas.get_tk_widget().grid(row=row, column=column, ipadx=40, ipady=20)
# navigation toolbar
toolbarFrame = tk.Frame(master=tr_val)
toolbarFrame.grid(row=row,column=column)
toolbar = NavigationToolbar2Tk(canvas, toolbarFrame)
# after the figure, axis, and line are created, we only need to update the y-data
mod_x_data = convert_x_data(x_data, 20)
line1.axes.set_xticklabels(mod_x_data)
line1.set_ydata(y1_data)
line2.set_ydata(y2_data)
line3.set_ydata(y3_data)
#Debugging
#rint('plt.lim: ', ln2.axes.get_ylim())
# adjust limits if new data goes beyond bounds
# limit for line 1
if np.min(y1_data)<=line1.axes.get_ylim()[0] or np.max(y1_data)>=line1.axes.get_ylim()[1]:
plt.ylim(0, 10)
line1.axes.set_ylim([np.min(y1_data)-np.std(y1_data),np.max(y1_data)+np.std(y1_data)])
# limit for line 2
if np.min(y2_data)<=line2.axes.get_ylim()[0] or np.max(y2_data)>=line2.axes.get_ylim()[1]:
plt.ylim([np.min(y2_data)-np.std(y2_data),np.max(y2_data)+np.std(y2_data)])
#plt.ylim(0, 25)
# limit for line 3
if np.min(y3_data)<=line3.axes.get_ylim()[0] or np.max(y3_data)>=line3.axes.get_ylim()[1]:
plt.ylim([np.min(y3_data)-np.std(y3_data),np.max(y3_data)+np.std(y3_data)])
#plt.ylim(0, 25)
# Adds lines to the legend
#host.legend(lines, [l.get_label() for l in lines])
# this pauses the data so the figure/axis can catch up - the amount of pause can be altered above
plt.pause(pause_time)
# return line so we can update it again in the next iteration
return line1, line2, line3
The key is to not use pyplot when you want to plot within tkinter as shown in the official example. Use matplotlib.figure.Figure instead (see this for added info).
Below is a minimum sample that plots 3 independent graphs along a Text widget which I see in your code:
import pandas as pd
import numpy as np
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
class Graph(tk.Frame):
def __init__(self, master=None, title="", *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.fig = Figure(figsize=(4, 3))
ax = self.fig.add_subplot(111)
df = pd.DataFrame({"values": np.random.randint(0, 50, 10)}) #dummy data
df.plot(ax=ax)
self.canvas = FigureCanvasTkAgg(self.fig, master=self)
self.canvas.draw()
tk.Label(self, text=f"Graph {title}").grid(row=0)
self.canvas.get_tk_widget().grid(row=1, sticky="nesw")
toolbar_frame = tk.Frame(self)
toolbar_frame.grid(row=2, sticky="ew")
NavigationToolbar2Tk(self.canvas, toolbar_frame)
root = tk.Tk()
for num, i in enumerate(list("ABC")):
Graph(root, title=i, width=200).grid(row=num//2, column=num%2)
text_box = tk.Text(root, width=50, height=10, wrap=tk.WORD)
text_box.grid(row=1, column=1, sticky="nesw")
text_box.delete(0.0, "end")
text_box.insert(0.0, 'My message will be here.')
root.mainloop()
Result:

How to make matplotlib animate in a while loop in tkinter

I'm trying to live plot EEG recording in my tkinter app. However, it does not plot while the data is updating in the while loop, and plots only at the end.
import tkinter as tk
from tkinter import ttk, Button, LabelFrame, N, W, messagebox as box
import time
import datetime as dt
from datetime import date
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
import matplotlib.animation as animation
import matplotlib.pyplot as plt
class NFB_HomePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.nfbprotocol = 0
self.metricstime = 0
self.nfbmetric = 0
self.df = []
self.xList = []
self.yList = []
# Start Neurofeedback
self.StartNFB = Button(self, text="Start NFB", width=25, command=self.onStartNFB)
self.StartNFB.grid(row=7, column=0, padx=5, pady=3, sticky=W)
# LabelFrame for EEG Graph
labelframe3 = LabelFrame(self, text="EEG Plot")
labelframe3.grid(row=4, column=2, padx=5, pady=2, sticky=N, rowspan=5, columnspan=4)
self.fig = plt.Figure(figsize=(10, 5), dpi=100)
self.ax1 = self.fig.add_subplot(111)
self.line, = self.ax1.plot([], [], lw=2)
self.canvas = FigureCanvasTkAgg(self.fig, labelframe3)
self.canvas.draw()
self.canvas.get_tk_widget().grid()
self.canvas._tkcanvas.grid(row=0, column=0, padx=5, pady=3, sticky=W)
def createDF(self):
df_timestamp = []
df_data = np.empty([0, 5])
# metrics
df_alpha = np.empty([0, 1])
df_beta = np.empty([0, 1])
df_theta = np.empty([0, 1])
df_delta = np.empty([0, 1])
df_alpha_theta = np.empty([0, 1])
df_metricstime = np.empty([0, 1])
return df_timestamp, df_data, df_alpha, df_beta, df_theta, df_delta, df_alpha_theta, df_metricstime
def onStartNFB(self):
try:
self.start()
# Initialize dataframes
df_timestamp, df_data, df_alpha, df_beta, df_theta, df_delta, df_alpha_theta, df_metricstime = self.createDF()
# Start recording here!
timestart = time.time()
while time.time() < timestart + int(4):
""" 3.1 ACQUIRE DATA """
self.metricstime = dt.datetime.now().strftime('%H:%M:%S.%f')
""" 3.3 COMPUTE NEUROFEEDBACK METRICS """
alpha_metric, beta_metric, theta_metric, delta_metric, alphatheta_metric = float(np.random.normal(size=1)), float(np.random.normal(size=1)), \
float(np.random.normal(size=1)), float(np.random.normal(size=1)), \
float(np.random.normal(size=1))
self.nfbmetric = alpha_metric
# Save the bands to data frame
df_alpha = np.append(df_alpha, [[alpha_metric]], axis=0)
df_beta = np.append(df_beta, [[beta_metric]], axis=0)
df_theta = np.append(df_theta, [[theta_metric]], axis=0)
df_delta = np.append(df_delta, [[delta_metric]], axis=0)
df_alpha_theta = np.append(df_alpha_theta, [[alphatheta_metric]], axis=0)
df_metricstime = np.append(df_metricstime, [[self.metricstime]], axis=0)
# Update graph
self.df = np.concatenate([df_metricstime, df_alpha, df_theta, df_alpha_theta],
axis=1)
print(self.nfbmetric)
except:
raise RuntimeError("Error with Neurofeedback.")
def start(self):
self.ani = animation.FuncAnimation(self.fig, self.animate, interval=int(500))
self.running = True
self.ani._start()
def animate(self,i):
df = self.df
for eachline in df:
if len(eachline) > 1:
dfX = eachline[0]
dfY = eachline[1]
self.xList.append(dfX)
self.yList.append(dfY)
self.ax1.clear()
self.ax1.plot(self.xList, self.yList)
title = "Neurofeedback Band Power"
self.ax1.set_title(title)
app = ExpGUI()
app.geometry("1280x720")
app.mainloop()
Does somebody know what is wrong with my code? I know how to get it to run outside the while loop, but I have problems getting it to run while the data is updating.

Real time plotting of serial data with python and tkinter

I have been working for some time to find a way to graph incoming data from an arduino with a Python GUI. I was able to accomplish this using the Matplotlib animation function to read in 6 different variables and plot 4 of them 2 on one subplot 2 on another subplot. This was able to be done fast enough that it was graphing real time (20 samples per second).
I now need to modify the system to read in 12 different variables at the same time of which 8 are graphed. 4 on one sub plot 4 on another at the same rate of 20 samples per second. I haven't been able to get this to work and have tried a few different things and done a lot of research but can't seem to figure out how to do it with my limited knowledge of python. Im not very familiar with multiprocessing or multithreading but they seem to be the way that people are able to speed up the graphing process. I know that the matplotlib animated function itself is threaded so I'm not sure how much the threading would help with that or if there's a way to read in one thread and update the graph in another. I'm operating at the highest baudrate that the arduino supports 250000. I also was able to find an example where someone was able to get a very high speed plot in this post but havent been able to modify to work for my use: What is the best real time plotting widget for wxPython?
the data is received from the arduino like this:
integer.integer.integer|integer.integer.integer|integer.integer.integer|integer.integer.integer
where the pipe represents a new actuator (what each variable im sending is coming from)
I'm fairly new to python so sorry if this isnt so pythonic but here are two examples I have:
This is a gui using the animation function:
import Tkinter
import serial
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import matplotlib.animation as animation
from collections import deque
import random
class App:
def __init__(self, master):
self.arduinoData = serial.Serial('com5', 250000)#115200)
frame = Tkinter.Frame(master)
self.running = False
self.ani = None
self.start = Tkinter.LabelFrame(frame, text="Start", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10)
self.start.grid(row=0, column=0, padx=20, pady=20)
self.run = Tkinter.Button(self.start, text="RUN", bd=10, height=5, width=10, command=self.getData)
self.run.grid(row=0, column=0, padx=5, pady=5)
self.stop_frame = Tkinter.LabelFrame(frame, text="STOP", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10 )
self.stop_frame.grid(row=0, column=1, padx=20, pady=20)
self.stop = Tkinter.Button(self.stop_frame, text="STOP", bd=10, height=5, width=10, command=self.stopTest)
self.stop.grid(row=0, column=0, padx=5, pady=5)
self.fig = plt.Figure()
self.ax1 = self.fig.add_subplot(211)
self.line0, = self.ax1.plot([], [], lw=2)
self.line1, = self.ax1.plot([], [], lw=2)
self.line2, = self.ax1.plot([], [], lw=2)
self.line3, = self.ax1.plot([], [], lw=2)
self.ax2 = self.fig.add_subplot(212)
self.line4, = self.ax2.plot([], [], lw=2)
self.line5, = self.ax2.plot([], [], lw=2)
self.line6, = self.ax2.plot([], [], lw=2)
self.line7, = self.ax2.plot([], [], lw=2)
self.canvas = FigureCanvasTkAgg(self.fig,master=master)
self.canvas.show()
self.canvas.get_tk_widget().grid(row=0, column=4, padx=20, pady=20)
frame.grid(row=0, column=0, padx=20, pady=20)
def getData(self):
if self.ani is None:
self.k = 0
self.arduinoData.flushInput()
self.arduinoData.write("<L>")
return self.start()
else:
self.arduinoData.write("<L>")
self.arduinoData.flushInput()
self.ani.event_source.start()
self.running = not self.running
def stopTest(self):
self.arduinoData.write("<H>")
if self.running:
self.ani.event_source.stop()
self.running = not self.running
def resetTest(self):
self.k = 0
self.xdata = []
self.pressure1 = []
self.displacement1 = []
self.cycle1 = []
self.pressure2 = []
self.displacement2 = []
self.cycle2 = []
self.pressure3 = []
self.displacement3 = []
self.cycle3 = []
self.pressure4 = []
self.displacement4 = []
self.cycle4 = []
self.line1.set_data(self.xdata, self.ydata1)
self.line2.set_data(self.xdata, self.ydata2)
self.ax1.set_ylim(0,1)
self.ax1.set_xlim(0,1)
self.ax2.set_ylim(0,1)
self.ax2.set_xlim(0,1)
def start(self):
self.xdata = []
self.pressure1 = []
self.displacement1 = []
self.cycle1 = []
self.pressure2 = []
self.displacement2 = []
self.cycle2 = []
self.pressure3 = []
self.displacement3 = []
self.cycle3 = []
self.pressure4 = []
self.displacement4 = []
self.cycle4 = []
self.k = 0
self.arduinoData.flushInput()
self.ani = animation.FuncAnimation(
self.fig,
self.update_graph,
interval=1,
repeat=True)
self.arduinoData.write("<L>")
self.running = True
self.ani._start()
def update_graph(self, i):
self.xdata.append(self.k)
while (self.arduinoData.inWaiting()==0):
pass
x = self.arduinoData.readline()
strip_data = x.strip()
split_data = x.split("|")
actuator1 = split_data[0].split(".")
actuator2 = split_data[1].split(".")
actuator3 = split_data[2].split(".")
actuator4 = split_data[3].split(".")
self.pressure1.append(int(actuator1[0]))
self.displacement1.append(int(actuator1[1]))
self.cycle1 = int(actuator1[2])
self.pressure2.append(int(actuator2[0]))
self.displacement2.append(int(actuator2[1]))
self.cycle2 = int(actuator2[2])
self.pressure3.append(int(actuator3[0]))
self.displacement3.append(int(actuator3[1]))
self.cycle3 = int(actuator3[2])
self.pressure4.append(int(actuator4[0]))
self.displacement4.append(int(actuator4[1]))
self.cycle4 = int(actuator4[2])
self.line0.set_data(self.xdata, self.pressure1)
self.line1.set_data(self.xdata, self.pressure2)
self.line2.set_data(self.xdata, self.pressure3)
self.line3.set_data(self.xdata, self.pressure4)
self.line4.set_data(self.xdata, self.displacement1)
self.line5.set_data(self.xdata, self.displacement2)
self.line6.set_data(self.xdata, self.displacement3)
self.line7.set_data(self.xdata, self.displacement4)
if self.k < 49:
self.ax1.set_ylim(min(self.pressure1)-1, max(self.pressure3) + 1)
self.ax1.set_xlim(0, self.k+1)
self.ax2.set_ylim(min(self.displacement1)-1, max(self.displacement3) + 1)
self.ax2.set_xlim(0, self.k+1)
elif self.k >= 49:
self.ax1.set_ylim(min(self.pressure1[self.k-49:self.k])-1, max(self.pressure3[self.k-49:self.k]) + 1)
self.ax1.set_xlim(self.xdata[self.k-49], self.xdata[self.k-1])
self.ax2.set_ylim(min(self.displacement1[self.k-49:self.k])-1, max(self.displacement3[self.k-49:self.k]) + 1)
self.ax2.set_xlim(self.xdata[self.k-49], self.xdata[self.k-1])
self.k += 1
root = Tkinter.Tk()
app = App(root)
root.mainloop()
This is a gui that prints to the monitor:
import Tkinter
import serial
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import matplotlib.animation as animation
import time
class App:
def __init__(self, master):
self.arduinoData = serial.Serial('com5', 250000, timeout=0)
frame = Tkinter.Frame(master)
self.go = 0
self.start = Tkinter.LabelFrame(frame, text="Start", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10)
self.start.grid(row=0, column=0, padx=20, pady=20)
self.run = Tkinter.Button(self.start, text="RUN", bd=10, height=5, width=10, command=self.getData)
self.run.grid(row=0, column=0, padx=5, pady=5)
self.stop_frame = Tkinter.LabelFrame(frame, text="STOP", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10 )
self.stop_frame.grid(row=0, column=1, padx=20, pady=20)
self.stop = Tkinter.Button(self.stop_frame, text="STOP", bd=10, height=5, width=10, command=self.stopTest)
self.stop.grid(row=0, column=0, padx=5, pady=5)
self.fig = plt.Figure()
self.ax1 = self.fig.add_subplot(211)
self.line0, = self.ax1.plot([], [], lw=2)
self.line1, = self.ax1.plot([], [], lw=2)
self.line2, = self.ax1.plot([], [], lw=2)
self.line3, = self.ax1.plot([], [], lw=2)
self.ax2 = self.fig.add_subplot(212)
self.line4, = self.ax2.plot([], [], lw=2)
self.line5, = self.ax2.plot([], [], lw=2)
self.line6, = self.ax2.plot([], [], lw=2)
self.line7, = self.ax2.plot([], [], lw=2)
self.canvas = FigureCanvasTkAgg(self.fig,master=master)
self.canvas.show()
self.canvas.get_tk_widget().grid(row=0, column=4, padx=20, pady=20)
frame.grid(row=0, column=0, padx=20, pady=20)
def getData(self):
self.k = 0
self.xdata = []
self.pressure1 = []
self.displacement1 = []
self.cycle1 = []
self.pressure2 = []
self.displacement2 = []
self.cycle2 = []
self.pressure3 = []
self.displacement3 = []
self.cycle3 = []
self.pressure4 = []
self.displacement4 = []
self.cycle4 = []
self.arduinoData.flushInput()
self.go = 1
self.readData()
def readData(self):
if self.go == 1:
self.xdata.append(self.k)
while (self.arduinoData.inWaiting()==0):
pass
x = self.arduinoData.readline()
strip_data = x.strip()
split_data = x.split("|")
actuator1 = split_data[0].split(".")
actuator2 = split_data[1].split(".")
actuator3 = split_data[2].split(".")
actuator4 = split_data[3].split(".")
self.pressure1.append(int(actuator1[0]))
self.displacement1.append(int(actuator1[1]))
self.cycle1 = int(actuator1[2])
self.pressure2.append(int(actuator2[0]))
self.displacement2.append(int(actuator2[1]))
self.cycle2 = int(actuator2[2])
self.pressure3.append(int(actuator3[0]))
self.displacement3.append(int(actuator3[1]))
self.cycle3 = int(actuator3[2])
self.pressure4.append(int(actuator4[0]))
self.displacement4.append(int(actuator4[1]))
self.cycle4 = int(actuator4[2])
self.printData()
root.after(0, self.readData)
def printData(self):
print str(self.pressure1[self.k-1]) + " " +
str(self.displacement1[self.k-1]) + " " + str(self.cycle1) + " " +
str(self.pressure2[self.k-1]) + " " + str(self.displacement2[self.k-
1]) + " " + str(self.cycle2) + " " + str(self.pressure3[self.k-1]) +
" " + str(self.displacement3[self.k-1]) + " " + str(self.cycle3) + "
" + str(self.pressure4[self.k-1]) + " " +
str(self.displacement4[self.k-1]) + " " + str(self.cycle4)
def stopTest(self):
self.arduinoData.write("<H>")
self.go = 0
def resetTest(self):
self.k = 0
self.xdata = []
self.pressure1 = []
self.displacement1 = []
self.cycle1 = []
self.pressure2 = []
self.displacement2 = []
self.cycle2 = []
self.pressure3 = []
self.displacement3 = []
self.cycle3 = []
self.pressure4 = []
self.displacement4 = []
self.cycle4 = []
self.line1.set_data(self.xdata, self.ydata1)
self.line2.set_data(self.xdata, self.ydata2)
self.ax1.set_ylim(0,1)
self.ax1.set_xlim(0,1)
self.ax2.set_ylim(0,1)
self.ax2.set_xlim(0,1)
def start(self):
self.xdata = []
self.pressure1 = []
self.displacement1 = []
self.cycle1 = []
self.pressure2 = []
self.displacement2 = []
self.cycle2 = []
self.pressure3 = []
self.displacement3 = []
self.cycle3 = []
self.pressure4 = []
self.displacement4 = []
self.cycle4 = []
self.k = 0
self.arduinoData.write("<L>")
root = Tkinter.Tk()
app = App(root)
root.mainloop()
and here is an example arduino code:
int analog0 = 0;
int analog1 = 1;
int analog2 = 2;
int sensor0;
int sensor1;
int sensor2;
String pot0;
String pot1;
String Force;
int pot0holder;
int pot1holder;
String Forceholder;
unsigned long i = 0;
String Is;
int val = 0;
boolean Sensordata = false;
int cycles;
const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;
unsigned long CurrentMillis = 0;
unsigned long PrintMillis = 0;
int PrintValMillis = 50;
unsigned long SensorMillis = 0;
int SensorValMillis = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(250000);
}
void loop()
{
CurrentMillis = millis();
recvWithStartEndMarkers();
commands();
sensordata();
}
void sensordata()
{
if (CurrentMillis - SensorMillis >= SensorValMillis)
{
sensor0 = analogRead(analog0);
pot0holder = sensor0;
sensor1 = analogRead(analog1);
pot1holder = sensor1;
i += 1;
String potcolumn = String(pot0holder) + "." + String(pot1holder) + "." + String(i) + "|" + String(int(pot0holder)+30) + "." + String(int(pot1holder)+30) + "." + String(i) + "|" + String(int(pot0holder)+60) + "." + String(int(pot1holder)+60) + "." + String(i) + "|" + String(int(pot0holder)+90) + "." + String(int(pot1holder)+90) + "." + String(i);
Serial.println(potcolumn);
SensorMillis += SensorValMillis;
}
}
void recvWithStartEndMarkers()
{
static boolean recvInProgress = false; //creates variable visible to only one function with boolean
static byte ndx = 0;
char startMarker = '<'; //sets begin condition
char endMarker = '>'; //sets end condition
char rc; //sets variable type to char
while (Serial.available() > 0 && newData == false) {
rc = Serial.read(); //sets rc equal to serial value
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
void commands()
{
if (newData == true)
{
if (receivedChars[0] == 'T')
{
PrintValMillis = atoi(&receivedChars[1]); //atoi -> Converting strings to integer
}
else if (receivedChars[0] == 'S')
{
cycles = atoi(&receivedChars[1]);
i = 0;
}
else if (receivedChars[0] == 'L')
{
val = atoi(&receivedChars[1]);
i = 0;
}
}
newData = false;
}
Thanks in advance for any help or advice any one has.
So your reading process takes most of the time. I would put the reading in a separate task and do the evaluation/splitting of the data in the main (drawing)-process. Unfortunately, I am not a tkinter user, so I have written this without any special gui-framework. But I think you can adapt this to your needs.
That would look like that:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import multiprocessing as mp
import time
# global variables
fig = plt.figure(1)
# first sub-plot
ax1 = fig.add_subplot(211)
line1, = ax1.plot([], [], lw=2)
ax1.grid()
xdata1, ydata1 = [], []
# second sub-plot
ax2 = fig.add_subplot(212)
line2, = ax2.plot([], [], lw=2)
ax2.grid()
xdata2, ydata2 = [], []
# the multiprocessing queue
q = mp.Queue()
# data generator in separate process
# here would be your arduino data reader
def dataGen(output):
for x in range(50):
output.put((x, np.sin(x)))
# update first subplot
def update1(data):
# update the data
t, y = data
xdata1.append(t)
ydata1.append(y)
xmin, xmax = ax1.get_xlim()
ymin, ymax = ax1.get_ylim()
if t >= xmax:
ax1.set_xlim(xmin, 2*xmax)
if y >= ymax:
ax1.set_ylim(ymin, 2*ymax)
if y <= ymin:
ax1.set_ylim(2*ymin, ymax)
line1.set_data(xdata1, ydata1)
return line1,
# update second subplot
def update2(data):
# update the data
t, y = data
xdata2.append(t)
ydata2.append(y)
xmin, xmax = ax2.get_xlim()
ymin, ymax = ax2.get_ylim()
if t >= xmax:
ax2.set_xlim(xmin, 2*xmax)
if y >= ymax:
ax2.set_ylim(ymin, 2*ymax)
if y <= ymin:
ax2.set_ylim(2*ymin, ymax)
line2.set_data(xdata2, ydata2)
return line2,
# called at each drawing frame
def run(data):
# get data from queue, which is filled in separate process, blocks until
# data is available
data = q.get(block=True, timeout=.5)
# put here your variable separation
data1 = (2*data[0], 3*data[1])
data2 = (data[0], data[1])
#provide the data to the plots
a = update1(data1)
b = update2(data2)
fig.canvas.draw()
return a+b
if __name__ == "__main__":
# count of reader processes
n_proc = 1
# setup workers
pool = [mp.Process(target=dataGen, args=(q,)) for x in range(n_proc)]
for p in pool:
p.daemon = True
p.start()
# wait a few sec for the process to become alive
time.sleep(3)
# start your drawing
ani = animation.FuncAnimation(fig, run, frames=60, blit=True, interval=10,
repeat=False)
plt.show()
print('done')
My question is very similar with yours.
I need to get data from a profi-bus network every 80ms, and I want the data to be plotted while sampling.
I used multiprocessing to solve the problem.The Pipe was used for communication between two processes.
When the Plotter gets data from Collector, and it is going to plot something, the Plotter will send a message to the Collector. and then the Collector will stop sending the data and put the data into a list.
when plotter done its job, it tells the collector that 'now you can send data', the collector then send the data and clear the list.
import time
import numpy as np
from matplotlib import pyplot as plt
import multiprocessing
from multiprocessing import Process, Pipe
from random import random
class DataGennerator(object):
"""docstring for DataGennerator"""
def __init__(self, data_pipe):
super(DataGennerator, self).__init__()
print('Data Gennerator Init...')
self.data_buffer = []
self.t = 0
self.start_time = 0
self.data_pipe = data_pipe
self.plot_inprogess = False
self.data_ready = False
def run(self):
self.start_time = time.time()
for i in range(0, 400):
self.loop_cycle()
print('Total Time:', time.time()-self.start_time)
print('Run completion......')
def loop_cycle(self):
self.t = time.time()-self.start_time
new_data = [time.time()-self.start_time, np.sin(self.t), np.cos(2*self.t), np.cos(self.t*4), random()]
self.send_data(new_data)
time.sleep(0.08)
def send_data(self, new_data):
if self.plot_inprogess or not self.data_ready:
self.data_buffer.append(new_data)
self.data_ready = True
# Wait 1ms to read plotter's msg
if self.data_pipe.poll(0.0001):
self.plot_inprogess = self.data_pipe.recv()
else:
self.data_pipe.send(self.data_buffer)
self.data_buffer = []
self.data_ready = False
# Function to collect data by using DataGennerator
def get_data(data_pipe):
dg = DataGennerator(data_pipe)
dg.run()
data_pipe.send('EXIT')
print('>>> Finished')
# use plotter_pipe to communicate with data collector
# and when get data from the collector, updata the figure
def updata_plot(plotter_pipe, plot_inprogess=True):
plot_inprogess = True
fig, ax = plt.subplots(nrows=4, ncols=1, figsize=(6,8), sharex=True)
fig.set_tight_layout(True)
styles = ['rs-', 'gs-', 'bs-', 'ro-', 'go-', 'bo-']*10
lines = []
for index, name in enumerate(['sin(t)', 'cos(t)', 'cos(2t)', 'random']):
line, = ax[index].plot([],[], styles[index],label=name, markersize=4, markerfacecolor='w')
ax[index].set_ylabel(name, color=styles[index][0], fontweight='bold')
lines.append(line)
ax[-1].set_xlabel('Time /s')
fig.align_ylabels(ax)
plt.ion()
plt.show(block=False)
plt.draw()
# Read the 1st data package and convert it to Numpy Array
data_array = np.array(plotter_pipe.recv())
while True:
try:
# Read data_buffer sent by Data Collector
data_buffer = plotter_pipe.recv() #[ [data1], [data2]...]
# If the DataCollector says EXIT, then break the while loop
if data_buffer == 'EXIT': break
# Raise a flag to indicate that Plot is in progress
plotter_pipe.send(True)
# Append data_buffer to Data Array
data_array = np.append(data_array, np.array(data_buffer), axis=0)
for i in range(0, 4):
lines[i].set_xdata(data_array[:,0])
lines[i].set_ydata(data_array[:,i+1])
ax[i].relim()
ax[i].autoscale_view()
fig.canvas.draw()
plt.pause(0.001)
# Tell data collector that Plot has been finished
plotter_pipe.send(False)
except Exception as e:
raise e
finally:
pass
print('>>> Stop receiving data')
data_content = '\n'.join([', '.join(map(str,data_line)) for data_line in data_array])
with open('data.txt', 'w', encoding='UTF-8') as f:
f.write('time, xx, yy, zz, bb\n')
f.writelines(data_content)
plt.show(block=True)
if __name__ == '__main__':
plot_inprogess = True
data_pipe, plotter_pipe = multiprocessing.Pipe(True)
P1 = Process(target=get_data , args=(data_pipe,))
P2 = Process(target=updata_plot, args=(plotter_pipe,))
P1.start()
P2.start()
P1.join()
P2.join()

Categories

Resources