Matplotlib rcParams ignored when plotting - python

I am working on an interactive plotting script using matplotlib version 3.2.2 and tkinter.
When the script is run, the first window looks like this:
Furthermore, the rcParams are updated and plotted once the Plot figure button is clicked:
If I now hit the button Change plot settings and change for instance the markersize parameter -> Plot figure, the plot is updated like so:
if I, however, try changing the label sizes to 20 px and then validate that the rcParams['axes.labelsize'] is changed, they are. But the size of the x and y labels are never updated in the actual plot.
The plot title (text input field all the way to the top in the plot window) fontsize can be changed after it has been plotted.
The minimal code:
"""
This is a script for interactively plotting a scatterplot and changing the plot params.
"""
import numpy as np
import matplotlib as mpl
import matplotlib.style
import random
mpl.use('TkAgg')
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from tkinter import *
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from pandas.api.types import is_numeric_dtype
def center_tk_window(window, height, width):
# Helper method for centering windows
screen_width = window.winfo_screenwidth()
screen_height = window.winfo_screenheight()
x_coordinate = int((screen_width / 2) - (width / 2))
y_coordinate = int((screen_height / 2) - (height / 2))
window.geometry("{}x{}+{}+{}".format(width, height, x_coordinate, y_coordinate))
def plot_data(data, chosen_columns, ax=None, initial_box=None, fig=None):
if fig is None:
fig = Figure()
if ax is None:
# Create a new subplot
ax = fig.add_subplot(111)
# Selected x-coordinates
x_data = data[chosen_columns['x_col']]
# Selected y-coordinates
if 'y_col' in chosen_columns:
y_data = data[chosen_columns['y_col']]
filled_markers = ('o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h', 'H', 'D', 'd', 'P', 'X')
# Category column
if 'category_col' in chosen_columns:
category_data = data[chosen_columns['category_col']]
# Plotting it all
sns.scatterplot(ax=ax, x=x_data, y=y_data, hue=category_data, style=category_data,
markers=filled_markers
)
# Shrink current axis's height by 20% on the bottom
if initial_box is None:
initial_box = ax.get_position()
ax.set_position([initial_box.x0, initial_box.y0 + initial_box.height * 0.2,
initial_box.width, initial_box.height * 0.80])
# Put a legend below current axis
ax.legend(bbox_to_anchor=(0.5, -0.15), ncol=6)
else: # Normal scatterplot without any categorical values
sns.scatterplot(ax=ax, x=x_data, y=y_data)
ax.set_ylabel(chosen_columns['y_col'])
ax.set_xlabel(chosen_columns['x_col'])
return fig, ax, initial_box
class GenericPlot:
def __init__(self, data):
# Parameters window selection
self.canvas = None
self.fig = None
self.ax = None
self.chosen_columns = None
self.initial_box = None
self.updated_rc_params = None
self.set_plot_params(data)
# Plot window
self.save_plot_bool = False
self.plot_window = Tk()
self.interactive_plot(data)
self.plot_window.mainloop()
def set_plot_params(self, data):
def plot_with_settings():
def format_input(input):
if input == '':
# Use the default value
return mpl.rcParams[input]
if ',' in input:
return float(input.replace(',', '.'))
else:
return float(input)
# Figure size
figure_params = {}
if figsize_width.get() != '' and figsize_height.get() != '':
figure_params['figsize'] = (format_input(figsize_width.get()), format_input(figsize_height.get()))
# label sizes
axes_params = {}
if label_size.get() != '':
axes_params['labelsize'] = format_input(label_size.get())
if title_size.get() != '':
axes_params['titlesize'] = format_input(title_size.get())
legend_params = {}
if legend_title_fontsize.get() != '': legend_params['title_fontsize'] = format_input(
legend_title_fontsize.get())
legend_additional = {'loc': 'upper center',
'fancybox': False,
'shadow': False
}
legend_params.update(legend_additional)
if marker_size.get() != '': lines_params = {'markersize': format_input(marker_size.get())}
legend_params['markerscale'] = format_input(legend_markerscale.get())
mpl.rc('figure', **figure_params)
mpl.rc('axes', **axes_params)
mpl.rc('lines', **lines_params)
mpl.rc('legend', **legend_params)
self.updated_rc_params = mpl.rcParams
# Update canvas if the params were changed after it was drawn:
if self.ax is not None:
self.ax.clear()
mpl.rcParams.update(self.updated_rc_params)
self.fig, self.ax, _ = plot_data(data, self.chosen_columns, self.ax,
self.initial_box, self.fig)
self.canvas.draw()
custom_params_window.destroy() # Close the tk window
# Create a new window
custom_params_window = Tk()
center_tk_window(custom_params_window, 300, 400) # window, height, width
custom_params_window.title('Set plot parameters')
# Set up GUI
custom_params_window.columnconfigure(0, weight=1)
custom_params_window.columnconfigure(1, weight=1)
n_rows = 8
for r in range(n_rows):
custom_params_window.rowconfigure(r, weight=1)
row_num = 0
# Figsize
Label(custom_params_window, text="Figure width (px)").grid(row=row_num, column=0, sticky="e")
figsize_width = Entry(custom_params_window)
placeholder_width = self.updated_rc_params['figure.figsize'][
0] if self.updated_rc_params is not None else 7.0
figsize_width.insert(0, placeholder_width)
figsize_width.grid(row=row_num, column=1)
row_num += 1
Label(custom_params_window, text="Figure height (px)").grid(row=row_num, column=0, sticky="e")
figsize_height = Entry(custom_params_window)
placeholder_height = self.updated_rc_params['figure.figsize'][
1] if self.updated_rc_params is not None else 6.0
figsize_height.insert(0, placeholder_height)
figsize_height.grid(row=row_num, column=1)
# User input label size
row_num += 1
Label(custom_params_window, text="Label sizes (px)").grid(row=row_num, column=0, sticky="e")
label_size = Entry(custom_params_window)
placeholder_label_size = self.updated_rc_params[
'axes.labelsize'] if self.updated_rc_params is not None else 10.0
label_size.insert(0, placeholder_label_size)
label_size.grid(row=row_num, column=1)
# User input title size
row_num += 1
Label(custom_params_window, text="Title font size (px)").grid(row=row_num, column=0, sticky="e")
title_size = Entry(custom_params_window)
placeholder_axes_titlesize = self.updated_rc_params[
'axes.titlesize'] if self.updated_rc_params is not None else 14.0
title_size.insert(0, placeholder_axes_titlesize)
title_size.grid(row=row_num, column=1)
print(" self.updated_rc_params STATUS:", self.updated_rc_params)
# Marker_size
row_num += 1
Label(custom_params_window, text="Marker size (px)").grid(row=row_num, column=0, sticky="e")
marker_size = Entry(custom_params_window)
placeholder_legend_markersize = self.updated_rc_params[
'lines.markersize'] if self.updated_rc_params is not None else 6.0
marker_size.insert(0, placeholder_legend_markersize)
marker_size.grid(row=row_num, column=1)
# Legend markerscale
row_num += 1
Label(custom_params_window, text="Legend markerscale\n(Relative size to marker size) ").grid(
row=row_num, column=0,
sticky="e")
legend_markerscale = Entry(custom_params_window)
placeholder_legend_markerscale = self.updated_rc_params[
'legend.markerscale'] if self.updated_rc_params is not None else 1.0
legend_markerscale.insert(0, placeholder_legend_markerscale)
legend_markerscale.grid(row=row_num, column=1)
# Legend title size
row_num += 1
Label(custom_params_window, text="Legend title font size").grid(row=row_num, column=0, sticky="e")
legend_title_fontsize = Entry(custom_params_window)
placeholder_legend_title_size = self.updated_rc_params[
'legend.title_fontsize'] if self.updated_rc_params is not None else 1.0
legend_title_fontsize.insert(0, placeholder_legend_title_size)
legend_title_fontsize.grid(row=row_num, column=1)
row_num += 1
Button(custom_params_window, text="Plot figure", command=lambda: plot_with_settings(), height=2,
width=8).grid(row=row_num, column=0)
custom_params_window.mainloop()
def interactive_plot(self, data):
"""
Input :
window : tkinter window
data : DataFrame object
"""
def close_plot_window():
self.plot_window.destroy()
def set_save_plot_bool():
self.save_plot_bool = True
self.plot_window.destroy()
center_tk_window(self.plot_window, 750, 600)
# Drop-down variables (3 drop-downs)
dropdown_choice_x = StringVar(self.plot_window) # Variable holding the dropdown selection for the x column
dropdown_choice_y = StringVar(self.plot_window) # Variable holding the dropdown selection for the y column
dropdown_choice_category = StringVar(
self.plot_window) # Variable holding the dropdown selection for the category column
# Create set of column names in the dataset
choices = data.columns.values
# Find numeric and string columns
string_columns = []
numeric_columns = []
[numeric_columns.append(col) if is_numeric_dtype(data[col]) else string_columns.append(col) for col in
data.columns]
if len(numeric_columns) < 1:
raise Exception("Unable to plot, there are too few numerical columns.")
if len(numeric_columns) == 1:
raise Exception(
"Unable to create scatter plot- need more than two numerical columns in the imported dataset.")
# GUI setup
self.plot_window.columnconfigure(0, weight=1)
self.plot_window.columnconfigure(1, weight=1)
n_rows = 6
for r in range(n_rows):
self.plot_window.rowconfigure(r, weight=1)
def update_ax_title(title):
self.ax.set_title(title.get())
self.canvas.draw()
title = StringVar()
title.trace("w", lambda name, index, mode, title=title: update_ax_title(title))
# Set title
Label(self.plot_window, text="Set plot title:").grid(row=0, column=0, sticky="e")
e = Entry(self.plot_window, textvariable=title, width=23)
e.grid(row=0, column=1, sticky="w")
# Drop-down 1: x-value selection
if len(numeric_columns) >= 1:
x_values_column = numeric_columns[0] # Select the first numeric column as the default x values to plot
dropdown_choice_x.set(x_values_column) # Set the default option in the dropdown with the first column
Label(self.plot_window, text="Select x column:").grid(row=1, column=0, sticky="e")
choices_numeric = numeric_columns # Only show numeric columns in the drop-down for x and y
dropdown_menu_x = OptionMenu(self.plot_window, dropdown_choice_x, *choices_numeric)
dropdown_menu_x.grid(row=1, column=1, sticky="w")
dropdown_menu_x.config(width=16)
self.chosen_columns = {'x_col': x_values_column}
# Drop-down 2: y-value selection
if len(numeric_columns) >= 2:
y_values_column = numeric_columns[1] # Select the second alternative in the dropdown list for the y values
dropdown_choice_y.set(y_values_column) # Set the default option in the dropdown with the first column
l2 = Label(self.plot_window, text="Select y column:")
l2.grid(row=2, column=0, sticky='e')
dropdown_menu_y = OptionMenu(self.plot_window, dropdown_choice_y, *choices_numeric)
dropdown_menu_y.config(width=16)
dropdown_menu_y.grid(row=2, column=1, sticky='w')
self.chosen_columns = {'x_col': x_values_column,
'y_col': y_values_column}
if len(data.columns) > 2: # There exist a third columns as well -> include drop-down for category selection
# Drop-down 3: Category selections
category_column = string_columns[0] if (len(string_columns) > 0) else numeric_columns[2]
dropdown_choice_category.set(
category_column) # Set the default option in the dropdown with the first column
l3 = Label(self.plot_window, text="Select category column:")
l3.grid(row=3, column=0, sticky='e')
dropdown_menu_category = OptionMenu(self.plot_window, dropdown_choice_category, *choices, 'Set title above')
dropdown_menu_category.config(width=16)
dropdown_menu_category.grid(row=3, column=1, sticky='w')
self.chosen_columns = {'x_col': x_values_column,
'y_col': y_values_column,
'category_col': category_column}
# Plot the initially selected columns
self.fig, self.ax, self.initial_box = plot_data(data, self.chosen_columns)
self.canvas = FigureCanvasTkAgg(self.fig, master=self.plot_window)
self.canvas.get_tk_widget().grid(row=4, columnspan=2, rowspan=True)
self.canvas.draw()
def change_dropdown_x(*args):
# This function is triggered once a dropdown selection is made
selected_x_col = dropdown_choice_x.get()
self.chosen_columns['x_col'] = selected_x_col
# Create a new plot now
self.ax.clear() # Clearing the previous plot
self.fig, self.ax, _ = plot_data(data, self.chosen_columns, self.ax, self.initial_box,
self.fig)
self.canvas.draw()
# chosen columns might not be updated...
def change_dropdown_y(*args):
# This function is triggered once a dropdown selection is made
selected_y_col = dropdown_choice_y.get()
self.chosen_columns['y_col'] = selected_y_col
# Create a new plot now
self.ax.clear() # Clearing the previous plot
self.fig, self.ax, _ = plot_data(data, self.chosen_columns, self.ax, self.initial_box,
self.fig)
self.canvas.draw()
def change_dropdown_category(*args):
# This function is triggered once a dropdown selection is made
selected_category = dropdown_choice_category.get()
self.chosen_columns['category_col'] = selected_category
# Create a new plot now
self.ax.clear() # Clearing the previous plot
self.fig, self.ax, _ = plot_data(data, self.chosen_columns, self.ax, self.initial_box,
self.fig)
self.canvas.draw()
# Link functions to change dropdown
dropdown_choice_x.trace('w',
lambda *args: change_dropdown_x(
*args))
dropdown_choice_y.trace('w',
lambda *args: change_dropdown_y(
*args))
dropdown_choice_category.trace('w', lambda *args: change_dropdown_category(
*args))
def change_settings():
self.plot_params_type = 'customize'
self.set_plot_params(data)
# self.plot_window.destroy()
# Save and close buttons
Button(self.plot_window, text="<- Change plot settings", command=change_settings, height=2, width=20).grid(
row=5, columnspan=2)
Button(self.plot_window, text="CLOSE", command=close_plot_window, height=2, width=8).grid(row=6, column=0)
Button(self.plot_window, text="SAVE PLOT ->", command=set_save_plot_bool, height=2, width=8).grid(row=6,
column=1)
# Create dummy data to plot
df = pd.DataFrame(np.random.randint(0, 100, size=(100, 4)), columns=list('ABCD'))
# Add a category column to the DataFrame
labels = ['q', 'r', 's', 't']
df['Category'] = [labels[random.randint(0, len(labels) - 1)] for i in range(100)]
GenericPlot(df)
I have tried to change the x and y label sizes in the function called update_ax_title (just to debug:
And run the function
def update_ax_title(title):
self.ax.set_title(title.get()) # Correct size (5.0)
self.ax.set_xlabel('gdsgsdgsdgsdgdsg') # Incorrect size...
print(mpl.rcParams['axes.labelsize']) # prints 5.0
print(mpl.rcParams['axes.titlesize']) # prints 5.0
self.canvas.draw()
Only the title size is updated, even though the rcParams are updated globally. The x and y label sizes change after running specifically self.ax.set_xlabel('gdsgsdgsdgsdgdsg',fontsize=5)
How can this issue be solved? Thanks!

Although I don't fully understand why it doesn't work, you can solve it by applying the labelsize manually to each axis using set_size:
...
# Update canvas if the params were changed after it was drawn:
if self.ax is not None:
self.ax.clear()
mpl.rcParams.update(self.updated_rc_params)
# NEW!
self.ax.xaxis.label.set_size(self.updated_rc_params['axes.labelsize'])
self.ax.yaxis.label.set_size(self.updated_rc_params['axes.labelsize'])
# End of New
self.fig, self.ax, _ = plot_data(data, self.chosen_columns, self.ax,
self.initial_box, self.fig)
self.canvas.draw()
custom_params_window.destroy() # Close the tk window
...

Related

Changing axis legends and update labels in a tkinter treeview

In this GUI I am plotting 3 graphs with legends. There are 2 toplevel windows which can be opened by buttons. From one toplevel I am changing plot legends. In the MWE, if you hit "save legs" the plot legends are updating all with 1. That treeview is editable, so the idea was that the user can edit the legends.
In the second toplevel I want to "get" the current plot legends and fill the 1st column with those legends. The idea is, if a user changed the legends by using the 1st toplevel, the second can be updated with the new legends by getting the current plot legends.
My problem is even if the plot legends are changed, the second toplevel is populated by the old plot legends. How can it be? I am getting the legends by ax.get_legend_handles_labels(). Also the legends are not correctly populating inside the column. I want Leg1, Leg2, Leg 3 separately in each row. Someone can suggest a way to fix this?
Here's the MWE,
import matplotlib.pyplot as plt
import numpy as np
from tkinter import ttk
import tkinter as tk
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
import pandas as pd
plt.close("all")
root = tk.Tk()
root.geometry('800x600')
pi2 = [452,989,1488]
pout2 = [316,698,1057]
pout3 = [311,693,1048]
pout4 = [321,653,1068]
fig, ax = plt.subplots()
canvas = FigureCanvasTkAgg(fig,root)
ax.plot(pi2,pout2,'o-',label='Leg1')
ax.plot(pi2,pout3,'o-',label='Leg2')
ax.plot(pi2,pout4,'o-',label='Leg3')
ax.legend(loc = 'best', fontsize = 7)
canvas.draw()
canvas.get_tk_widget().pack()
def add_leg():
global leg
top_add = tk.Toplevel(root)
top_add.attributes('-topmost', 'true')
top_add.geometry("200x260")
def edit(event):
if tree_top.identify_region(event.x, event.y) == 'cell':
def ok(event):
tree_top.set(item, column, tree_entry.get())
tree_entry.destroy()
column = tree_top.identify_column(event.x) # identify column
item = tree_top.identify_row(event.y) # identify item
x, y, width, height = tree_top.bbox(item, column)
value = tree_top.set(item, column)
elif tree_top.identify_region(event.x, event.y) == 'nothing':
column = tree_top.identify_column(event.x) # identify column
x, y, width, height = tree_top.bbox(tree_top.get_children('')[-1], column)
if event.y > y:
def ok(event):
item = tree_top.insert("", "end", values=("", ""))
tree_top.set(item, column, tree_entry.get())
tree_entry.destroy()
y += height
value = ""
else:
return
else:
return
tree_entry = ttk.Entry(tree_top) # create edition entry
tree_entry.place(x=x, y=y, width=width, height=height, anchor='nw') # display entry on top of cell
tree_entry.insert(0, value) # put former value in entry
tree_entry.bind('<FocusOut>', lambda e: tree_entry.destroy())
tree_entry.bind('<Return>', ok) # validate with Enter
tree_entry.focus_set()
tree_top = ttk.Treeview(top_add, show='headings', columns=("1", "2"))
tree_top['show'] = 'headings'
tree_top.column("#0", width=0)
tree_top.column("1", anchor=tk.CENTER, width=80)
tree_top.heading("1", text="File Name")
tree_top.column("2", anchor=tk.CENTER, width=80)
tree_top.heading("2", text="New Legend")
for i in range(3):
item = tree_top.insert("", "end",values=("",1))
tree_top.item(item, tags=item)
tree_top.bind('<1>', edit)
tree_top.place(x=7,y=7)
def save_leg():
global df_leg, new_legs,leglst
leglst = []
for i in tree_top.get_children():
row=tree_top.item(i)['values']
leglst.append(row)
leglst = list(map(list,leglst))
df_leg = pd.DataFrame(leglst,columns = ['File Name', 'New Legend'])
new_legs = df_leg['New Legend']
ax.legend(new_legs,loc = 'upper left', bbox_to_anchor=(0.01,0.7), framealpha=0.5, fontsize = 8)
canvas.draw()
saveleg_btn = tk.Button(top_add,text="Save Legs",command=lambda:save_leg())
saveleg_btn.place(x=0,y=100)
def top():
global data,lt,new_legs
topi = tk.Toplevel(root)
topi.geometry("200x250")
tree = ttk.Treeview(topi)
tree['columns']=('File Name','Pump %')
tree.column('#0', width=0, stretch=tk.NO)
tree.column('File Name', anchor=tk.CENTER, width=100)
tree.heading('#0', text='', anchor=tk.CENTER)
tree.heading('File Name', text='File Name', anchor=tk.CENTER)
tree.place(x=0,y=0)
lt = ax.get_legend_handles_labels()
tree.insert('', tk.END, values=(lt[-1],""))
addleg_btn = tk.Button(root,text="Add Legends",command=lambda:add_leg())
addleg_btn.pack()
top_btn = tk.Button(root,text="Ratios",background="#707087",command=lambda:top())
top_btn.pack()
root.mainloop()

How to plot an automatic graph using mouse without clicking MATPLOTLIB

I'm looking to plot Data automatically using mouse without clicking From a DZT file.
i Created a program in order to plot data as you can see in this graph:
As you can see in this picture, x axes going from 0 to 3525 ans every x is a signal so i have more than 3500 signals making this graph.
for exemple if i want to see the signal of x=999, it looks like this.
what i want really to do is every time when i pass my mouse without clicking on the graph it should plot it's signal automatically .
i try to use a lot of methods but rally i dont know how to do it.
i'll be grateful for your help
this is my file:
https://www.grosfichiers.com/mvabGETFfna
This is my program:
from tkinter import *
from tkinter import messagebox, filedialog
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import os
from readgssi import readgssi
root = Tk()
root.title("IHM")
root.geometry("1000x800")
Resources_frame = LabelFrame(root, bd=2, relief=GROOVE, text="Conversion Area")
Resources_frame.place(x=5, y=5, width=250, height=80)
lbl_ra = Label(Resources_frame, text="Select your File ")
lbl_ra.grid(row=0, column=0, sticky=W)
lbl_ra = Label(Resources_frame, text="Convert Selected file ")
lbl_ra.grid(row=1, column=0, sticky=W)
NumberOfSimples = ''
DataAScan = ''
Data = ''
xData = ''
xAxes = ''
def ReadDZT():
file_path = filedialog.askopenfilename()
file_name, file_extension = os.path.splitext(file_path)
print(file_extension)
if file_extension == '.DZT':
messagebox.showinfo("INFO", "Your DZT File Has Been Selected Successfully")
hdr, arrs, gps = readgssi.readgssi(infile=file_path, verbose=True)
TimeRange = hdr['rhf_range']
Samples = hdr['rh_nsamp']
X_Axes = np.array(range(0, Samples))
xAxes = X_Axes[2::1]
df = pd.DataFrame(arrs[0])
Data = df.iloc[2::1, 0::1]
fig2 = plt.figure()
plt.plot(xAxes, Data[999])
plt.show()
fig = plt.figure()
plt.imshow(Data, aspect='auto', cmap='bone')
plt.show()
elif file_extension == '.csv':
messagebox.showinfo("INFO", "Your CSV File Has Been Selected Successfully")
df = pd.read_csv(file_path)
NumberOfSimples = df.iloc[2::1, 0]
Data = df.iloc[::1, ::1]
DataAScan = df.iloc[2::1, 999]
fig1 = plt.figure()
plt.plot(NumberOfSimples, DataAScan)
plt.show()
fig2 = plt.figure()
plt.imshow(Data, aspect='auto', cmap='bone')
plt.show()
else:
messagebox.showwarning("WARNING", "You Have Been Selected a Different Format")
# =============Resources Buttons================#
btn_rs = Button(Resources_frame, relief=GROOVE, padx=8, pady=1, text="Browse", command=ReadDZT).grid(row=0,
column=1)
root.mainloop()
As indicated in the previous question Ploting a graph automatically using mouse coordinate (and I think this question should have been an edit of it), the mouse motion can be monitored with plt.connect('motion_notify_event', mouse_move). The slice of Data to be plotted in fig2 simply corresponds to the x-coordinate of the mouse, that is Data[int(event.xdata)] in mouse_move(event).
Therefore, in mouse_move(event), you:
Clear the axis: ax2.clear() (where ax2 is the AxesSubplot object of fig2)
Plot the slice ax2.plot(self.xAxes, self.Data[int(event.xdata)])
Update the figure fig2.canvas.draw_idle()
However, I had issues with using simultaneously the matplotlib figures and the tkinter GUI because of the separate event loops. Therefore I embedded the figures in the tkinter GUI directly (see e.g. https://matplotlib.org/3.4.0/gallery/user_interfaces/embedding_in_tk_sgskip.html).
I also put the graph related code in a class to keep the loaded data in attributes to avoid using global variables.
import tkinter as tk
from tkinter import messagebox, filedialog
import pandas as pd
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import numpy as np
import os
from readgssi import readgssi
class Graphs(tk.Toplevel):
def __init__(self, master=None, **kw):
tk.Toplevel.__init__(self, master, **kw)
self.rowconfigure(0, weight=1)
self.rowconfigure(2, weight=1)
# data
self.Data = []
self.xData = []
self.xAxes = []
self.line = None
# figure1 : 2D data map
self.fig1 = Figure()
self.ax1 = self.fig1.add_subplot(111)
self.canvas1 = FigureCanvasTkAgg(self.fig1, self)
self.canvas1.draw()
self.canvas1.get_tk_widget().grid(sticky='ewsn')
self.toolbar1 = NavigationToolbar2Tk(self.canvas1, self, pack_toolbar=False)
self.toolbar1.grid(sticky="ew")
# figure 2: slice plot
self.fig2 = Figure()
self.ax2 = self.fig2.add_subplot(111)
self.canvas2 = FigureCanvasTkAgg(self.fig2, self)
self.canvas2.draw()
self.canvas2.get_tk_widget().grid(sticky='ewsn')
self.toolbar2 = NavigationToolbar2Tk(self.canvas2, self, pack_toolbar=False)
self.toolbar2.grid(sticky="ew")
# bind plotting to mouse motion
self.canvas1.mpl_connect('motion_notify_event', self.mouse_move)
def mouse_move(self, event):
x = event.xdata
if len(self.Data) and x is not None: # there is something to plot
self.ax2.clear()
x = int(x)
self.ax2.plot(self.xAxes, self.Data[x])
self.line.set_data([x, x], [len(self.Data), 0])
self.canvas1.draw_idle()
self.canvas2.draw_idle()
def readDZT(self):
file_path = filedialog.askopenfilename()
file_name, file_extension = os.path.splitext(file_path)
if file_extension == '.DZT':
messagebox.showinfo("INFO", "Your DZT File Has Been Selected Successfully")
hdr, arrs, gps = readgssi.readgssi(infile=file_path, verbose=True)
Samples = hdr['rh_nsamp']
X_Axes = np.array(range(0, Samples))
self.xAxes = X_Axes[2::1]
df = pd.DataFrame(arrs[0])
self.Data = df.iloc[2::1, 0::1]
# clear plots
self.ax1.clear()
self.ax2.clear()
# plot slice
self.ax2.plot(self.xAxes, self.Data[999])
self.canvas2.draw_idle()
# plot 2D map
self.ax1.imshow(self.Data, aspect='auto', cmap='bone')
self.line = self.ax1.plot([999, 999], [len(self.Data), 0], 'r')[0]
self.ax1.set_ylim(len(self.Data), 0)
self.canvas1.draw_idle()
else:
messagebox.showwarning("WARNING", "You Have Been Selected a Different Format")
root = tk.Tk()
root.title("IHM")
root.geometry("1000x800")
Resources_frame = tk.LabelFrame(root, bd=2, relief=tk.GROOVE, text="Conversion Area")
Resources_frame.place(x=5, y=5, width=250, height=80)
tk.Label(Resources_frame, text="Select your File ").grid(row=0, column=0, sticky=tk.W)
tk.Label(Resources_frame, text="Convert Selected file ").grid(row=1, column=0, sticky=tk.W)
graphs = Graphs(root)
btn_rs = tk.Button(Resources_frame, relief=tk.GROOVE, padx=8, pady=1, text="Browse",
command=graphs.readDZT).grid(row=0, column=1)
root.mainloop()

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.

Categories

Resources