Related
I am doing an EEG in python using the matplotlib library,
I generate random informations and I display them in a Tkinter window,
I want to update the animation 10 times per seconds,
So 10 updates should last 1 second, right ?
But instead the animation lasts between 1.1 and 1.3 seconds ...
I guess this is an optimization issue ?
I will be very grateful if you could help me !
How my EEG looks like :
my matplotlib's EEG at 17 seconds
my matplotlib's EEG at 29 seconds
Here's my code (main.py):
import tkinter as tk
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from Sources.Model.grapheEEG5 import grapheEEG5
from matplotlib.animation import FuncAnimation
HAUTEUR = 768
LARGEUR = 1366
root = tk.Tk()
root.title("Interface logicielle d'acquisition")
fenetre = tk.Canvas(root, height=HAUTEUR, width=LARGEUR)
fenetre.pack()
fig = plt.Figure(figsize = (10, 6), dpi = 100)
cadreMilieu = tk.Frame(fenetre, bg='white')
cadreMilieu.place(relx=0.1, rely=0.1, relwidth=0.8, relheight=0.8)
canvas = FigureCanvasTkAgg(fig, master = cadreMilieu).get_tk_widget().pack()
grapheDeroulant = grapheEEG5(8, fig, None, "EEG", "temps (en secondes)", "capteurs")
aniPoints = FuncAnimation(fig, grapheDeroulant.animation, cache_frame_data = False,
save_count = 0, frames=None, blit=False, interval=100, repeat=False)
root.mainloop()
And my class (grapheEEG5.py):
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import random as random
import time
class grapheEEG5:
#variables
axe = None
figure = None
toolbar = None
tailleDonnees = None
nbEEG = None
compteur = 0
#parametres
echelle = 1.0/10
periodeTrame = 0.1
boucle = 0
dureeSecondesFigure = 20
#courbes
ligneCurseur = None
ordonnees = None
ordonneesFantome = None
#coords curseur
cursorY = None
#coords vrais points
xVraiPoints = []
yVraiPoints = []
def __init__(self, nbEEG, figure, toolbar, titre, nomx, nomy, grid = None):
self.nbEEG = nbEEG
self.cursorY = [0, -self.nbEEG-1]
self.figure = figure
self.tailleDonnees = int(self.dureeSecondesFigure / self.periodeTrame)
print(self.tailleDonnees, "donnée(s)")
self.axe = self.figure.add_subplot(111)
self.axe.grid(ls="--", lw=0.5)
self.axe.set_title(titre)
self.axe.set_xlabel(nomx)
self.axe.set_ylabel(nomy)
self.axe.set_xlim(0, self.dureeSecondesFigure)
self.axe.xaxis.set_major_locator(plt.MaxNLocator(self.dureeSecondesFigure))
self.axe.set_ylim(-self.nbEEG-1,0)
self.axe.get_yaxis().set_visible(False)
for i in range(self.nbEEG):
self.xVraiPoints.append([None]*self.tailleDonnees)
self.yVraiPoints.append([None]*self.tailleDonnees)
self.ordonnees = []
self.ordonneesFantome = []
for i in range(self.nbEEG):
self.ordonnees.append(self.axe.plot([], [], lw=0.75, color="black", label="nouvelles valeurs")[0])
self.ordonneesFantome.append(self.axe.plot([], [], lw=0.75, color="grey", label="anciennes valeurs")[0])
self.ligneCurseur, = self.axe.plot([], [], color="red", label="curseur")
if toolbar != None:
self.toolbar = toolbar
self.toolbar.update()
for i in range(nbEEG):
self.axe.text(-30*self.periodeTrame, -i-1, "capteur " + str(i+1))
self.figure.canvas.draw()
def animation(self, i):
temps = self.compteur*self.periodeTrame
for _ in range(self.nbEEG):
self.xVraiPoints[_][self.compteur] = temps
indexEeg = -_-1
y = indexEeg + (random.randint(-3,3) * self.echelle)
self.yVraiPoints[_][self.compteur] = y
self.ordonnees[_].set_data(self.xVraiPoints[_], self.yVraiPoints[_])
cursorX = [temps, temps]
self.ligneCurseur.set_data(cursorX, self.cursorY)
self.compteur += 1
if self.compteur >= self.tailleDonnees:
self.compteur = 0
return self.ordonnees
I printed the time when i call my animations referenced in funcAnimation and it seems that it is never the exact time, for example if i put interval = 0.5
then my script will call it after 0.46 sec or 0.56, and this is this approximation that was creating delay. Now i am using a manual animation and i am calling it with a method that does not drift and it works perfectly.
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
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:
I am writing a python application using tkinter that plots weather data on a map. I am currently testing out my button that is supposed to plot the data on the map I created using basemap. However, when I run my program, my button does nothing. I defined my function that plots the data and called the function in the command attribute of my button. Anyone know what the problem could be?
import tkinter as tk
from tkinter import ttk
import tkinter.font as tkFont
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
import requests
lon = [-76.5152, -76.7311, -76.3055, -76.7277, -75.4714, -75.1652, -77.0011, -75.6624, -78.3947, -77.8600, -78.7583, -79.9959, -80.0851]
lat = [40.3295, 40.1998, 40.0379, 39.9626, 40.6023, 39.9526, 41.2412, 41.4090, 40.5187, 40.7934, 41.1210, 40.4406, 42.1292]
def current_weather():
key = '4a7a419e4e16e2629a4cedc37cbf7e50'
url = 'https://api.openweathermap.org/data/2.5/weather'
global observation
observation = []
for i in range(0,len(lon)):
params = {'lat': lat[i], 'lon': lon[i], 'appid': key, 'units': 'imperial'}
response = requests.get(url, params = params)
weather = response.json()
observation.append(round(weather['main']['temp']))
current_weather()
root = tk.Tk()
#place map on canvas
fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_subplot(111)
map = Basemap(llcrnrlon=-80.5, llcrnrlat=39.8, urcrnrlon=-74.7, urcrnrlat=42.3,resolution='i',
lat_0 = 40., lon_0 = -80, ax = ax)
map.drawmapboundary(fill_color='#A6CAE0')
map.drawcounties(zorder = 20)
map.drawstates()
map.fillcontinents(color='#e6b800',lake_color='#A6CAE0')
def plot_data():
for i in range(0, len(lon)):
x,y = map(lon[i], lat[i])
ax.text(x, y, observation[i], fontsize = 7)
#create widgets
canvas = FigureCanvasTkAgg(fig, master = root)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
radiovalue = tk.IntVar()
temperature = tk.Radiobutton(root, text = "Plot current temperatures", variable = radiovalue, value = 1)
dewpoint = tk.Radiobutton(root, text = "Plot current dewpoints", variable = radiovalue, value = 2)
wind = tk.Radiobutton(root, text = "Plot current winds", variable = radiovalue, value = 3)
button = tk.Button(root, text = "Plot", command = plot_data)
#organize widgets
temperature.place(relx = 0.025, rely = 0.8)
dewpoint.place(relx = 0.385, rely = 0.8)
wind.place(relx = 0.675, rely = 0.8)
button.place(relx = 0.35, rely = 0.1)
root.mainloop()
-----EDIT # 1 -----
In the code box is my attempt at making this work, but I am unable to get my labels and text boxes to show in window... perhaps my Qgridlayout is wrong??? any help or direction would be great! thanks!
----END EDIT #1 ----
What I would like is to display the channels read from channel 1 through 8 below the matplotlib graph. The graph was easy (presuming what I did works) to embed and it refreshes every 5 seconds displaying the last 5 minutes of data. So, what I would like to have two rows to display all eight channels... something like below:
Channel 1: (RAW VALUE) Channel2: (RAW VALUE) .....
Channel 5: (RAW VALUE) Channel6: (RAW VALUE) .....
I am unsure how to have PyQt 'refresh' or 'fetch' the new values every 5 seconds.
Below is my code for what it does now,
#matplotlib and read/write aquisition
import Queue
import datetime as DT
import collections
import matplotlib.pyplot as plt
import numpy as np
import multiprocessing as mp
import time
import datetime
import os
import matplotlib.dates as mdates
import matplotlib.animation as animation
#ADC
from ABE_DeltaSigmaPi import DeltaSigma
from ABE_helpers import ABEHelpers
#PyQt
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
#ADC INFO
import sys
i2c_helper = ABEHelpers()
bus = i2c_helper.get_smbus()
adc = DeltaSigma(bus, 0x68, 0x69, 18)
#Rename file to date
base_dir = '/home/pi/Desktop/DATA'
ts = time.time()
filename_time = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d')
filename_base = os.path.join(base_dir, filename_time)
filename = '%s.txt' % filename_base
# you will want to change read_delay to 5000
read_delay = int(5000) # in milliseconds
write_delay = read_delay/1000.0 # in seconds
window_size = 60
nlines = 8
datenums = collections.deque(maxlen=window_size)
ys = [collections.deque(maxlen=window_size) for i in range(nlines)]
#PyQt window to display readings
class Window(QtGui.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.figure = plt.show()
self.canvas = FigureCanvas(self.figure)
#Labels
self.channel1 = QtGui.QLabel('Channel 1:')
self.channel2 = QtGui.QLabel('Channel 2:')
self.channel3 = QtGui.QLabel('Channel 3:')
self.channel4 = QtGui.QLabel('Channel 4:')
self.channel5 = QtGui.QLabel('Channel 5:')
self.channel6 = QtGui.QLabel('Channel 6:')
self.channel7 = QtGui.QLabel('Channel 7:')
self.channel8 = QtGui.QLabel('Channel 8:')
#textboxes
self.textbox1 = QtGui.QLineEdit()
self.textbox2 = QtGui.QlineEdit()
self.textbox3 = QtGui.QlineEdit()
self.textbox4 = QtGui.QlineEdit()
self.textbox5 = QtGui.QlineEdit()
self.textbox6 = QtGui.QlineEdit()
self.textbox7 = QtGui.QlineEdit()
self.textbox8 = QtGui.QlineEdit()
#timer to refresh textboxes
def refreshtext(self):
self.textbox1.setText(enumerate(row[1]))
self.textbox2.setText(enumerate(row[2]))
self.textbox3.setText(enumerate(row[3]))
self.textbox4.setText(enumerate(row[4]))
self.textbox5.setText(enumerate(row[5]))
self.textbox6.setText(enumerate(row[6]))
self.textbox7.setText(enumerate(row[7]))
self.textbox8.setText(enumerate(row[8]))
#Layout
layout = QtGui.QGridLayout()
layout.setAlignment(QtCore.Qt.AlignCenter)
layout.addWidget(self.canvas,0,0,1,4)
layout.addWidget(self.channel1,1,0,1,1)
layout.addWidget(self.channel2,1,1,1,1)
layout.addWidget(self.channel3,1,2,1,1)
layout.addWidget(self.channel4,1,3,1,1)
layout.addWidget(self.textbox1,2,0,1,1)
layout.addWidget(self.textbox2,2,1,1,1)
layout.addWidget(self.textbox3,2,2,1,1)
layout.addWidget(self.textbox4,2,3,1,1)
layout.addWidget(self.channel5,3,0,1,1)
layout.addWidget(self.channel6,3,1,1,1)
layout.addWidget(self.channel7,3,2,1,1)
layout.addWidget(self.channel8,3,3,1,1)
layout.addWidget(self.textbox5,4,0,1,1)
layout.addWidget(self.textbox6,4,1,1,1)
layout.addWidget(self.textbox7,4,2,1,1)
layout.addWidget(self.textbox8,4,3,1,1)
self.setLayout(layout)
def animate(i, queue):
try:
row = queue.get_nowait()
except Queue.Empty:
return
datenums.append(mdates.date2num(row[0]))
for i, y in enumerate(row[1:]):
ys[i].append(y)
for i, y in enumerate(ys):
lines[i].set_data(datenums, y)
ymin = min(min(y) for y in ys)
ymax = max(max(y) for y in ys)
xmin = min(datenums)
xmax = max(datenums)
if xmin < xmax:
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
fig.canvas.draw()
def write_data(filename, queue):
while True:
delay1 = DT.datetime.now()
row = []
for i in range(nlines):
# read from adc channels and print to screen
channel = adc.read_voltage(i)
row.append(channel)
queue.put([delay1]+row)
#print voltage variables to local file
with open(filename, 'a') as DAQrecording:
time1 = delay1.strftime('%Y-%m-%d')
time2 = delay1.strftime('%H:%M:%S')
row = [time1, time2] + row
row = map(str, row)
DAQrecording.write('{}\n'.format(', '.join(row)))
#Delay until next 5 second interval
delay2 = DT.datetime.now()
difference = (delay2 - delay1).total_seconds()
time.sleep(write_delay - difference)
def main():
global fig, ax, lines
queue = mp.Queue()
proc = mp.Process(target=write_data, args=(filename, queue))
# terminate proc when main process ends
proc.daemon = True
# spawn the writer in a separate process
proc.start()
fig, ax = plt.subplots()
xfmt = mdates.DateFormatter('%H:%M:%S')
ax.xaxis.set_major_formatter(xfmt)
# make matplotlib treat x-axis as times
ax.xaxis_date()
fig.autofmt_xdate(rotation=25)
lines = []
for i in range(nlines):
line, = ax.plot([], [])
lines.append(line)
ani = animation.FuncAnimation(fig, animate, interval=read_delay, fargs=(queue,))
app = QtGui.QApplication(sys.argv)
win = Window()
win.setWindowTitle('Real Time Data Aquisition')
win.show()
timer = QtCore.QTimer()
timer.timeout.connect(self.refreshtext)
timer.start(5000)
sys.exit(app.exec_())
if __name__ == '__main__':
main()