Related
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()
I am trying to plot a figure that has many lines where each line represents a specifc temperature!
An example of what I want is here:
However, I bulit the following code:
x=pd.DataFrame(df1, columns =[0])
J = set(x.iloc[:,0])
print ('Length Temperature',len(J))
O = len(J)
M = len(df1.index)
print('Indexxxxx: ',df1.iloc[0:12+0,5])
for i in range(0,M,O):
figure3 = plt.Figure(figsize=(8, 6), dpi=80)
ax1 = figure3.add_subplot(111)
ax1.scatter(df1.iloc[i+1:M+i,5],df1.iloc[i+1:M+i,6], label = "Temperature " + str((df1.iloc[i, 0])))
scatter1 = FigureCanvasTkAgg(figure3, GraphWindow)
scatter1.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
ax1.set_xlabel('Reduced Frequency [Hz]')
ax1.set_ylabel('Complex Shear Modulus G*')
ax1.set_yscale('log')
ax1.set_xscale('log')
ax1.set_title('MasterCurve ')
ax1.set_facecolor('whitesmoke')
figure3.patch.set_facecolor('whitesmoke')
ax1.spines['bottom'].set_color('black')
ax1.spines['top'].set_color('black')
ax1.spines['left'].set_color('black')
ax1.spines['right'].set_color('black')
toobar = NavigationToolbar2Tk(scatter1, GraphWindow)
ax1.legend(['(Temperature)' + str((df1.iloc[i, 0]))])
hold(True)
Everything is fine in this code but I am obtaining the lines in blue and the legend is the same for all of them.. This is what I obtained:
My question is, how can I change the color of each line and add new legend in each iteration in the above for loop.
Thanks in advance!
You want a single Figure, a single Axes and a single Canvas, and plot the different curves inside of them. In other words, you do too much inside the cycle…
import tkinter
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
import numpy as np
# frequencies and a poor man's dataframe
freq = np.logspace(-1.5, 1.5, 12)
G = {'ABCDE'[n-1]:np.logspace(n-4, n-2-n/4, 12) for n in range(1, 6)}
root = tkinter.Tk()
root.wm_title("Embedding in Tk")
fig = Figure(figsize=(6, 4), dpi=100, layout='constrained')
ax = fig.add_subplot()
# this is our loop on the different curves
lines = [ax.plot(freq, G[letter], '-o', label=letter)[0] for letter in G]
# titles, log axes, legend
ax.set_xlabel('Reduced Frequency [Hz]')
ax.set_ylabel('Complex Shear Modulus G*')
ax.set_yscale('log')
ax.set_xscale('log')
ax.set_title('MasterCurve ')
ax.set_facecolor('whitesmoke')
ax.legend()
connect the figure to a tkinter Canvas --- once
canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea.
canvas.draw()
# boilerplate
toolbar = NavigationToolbar2Tk(canvas, root, pack_toolbar=False)
toolbar.update()
canvas.mpl_connect("key_press_event", key_press_handler)
button_quit = tkinter.Button(master=root, text="Quit", command=root.destroy)
button_quit.pack(side=tkinter.BOTTOM)
toolbar.pack(side=tkinter.BOTTOM, fill=tkinter.X)
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=True)
# let's show our curves
tkinter.mainloop()
I am new to programming. This program I have here is to read and plot 'n' files of xvg format to compare the graphs.
I want the program to be able to let me add as many xvg files as I want and plot them all in a single graph(each of different color) so that they can be compared.
I wrote the program and was able to plot the graph for only a single file at a time. Each file is opening up in a new window.
from tkinter import *
import matplotlib.pyplot as plt
import numpy as np
from tkinter import filedialog
plt.rcParams.update({'font.size':15})
root = Tk()
root.title("Graph plotter")
root.geometry("900x800")
def processxvg(filename):
x, y = [], []
with open(filename) as f:
for line in f:
if line[0] != "#" and line[0] != "#":
cols = line.split()
if len(cols) == 2:
x.append(float(cols[0]))
y.append(float(cols[1]))
return x,y
def random_color():
levels = range(32,256,32)
return tuple(random.choice(levels) for _ in range(3))
def browsefunc():
global filename
filename = filedialog.askopenfilename()
pathlabel.config(text=filename)
def addbox():
browsebutton = Button(root, text="Browse", command=browsefunc)
browsebutton.pack()
my_label = Label(root, text= "Browse the xvg file in the dir", font =("Ariel", "20") )
my_label.pack()
pathlabel = Label(root)
pathlabel.pack()
def clicked1():
(x, y) = processxvg(filename)
f = plt.figure()
f.set_figwidth(20)
f.set_figheight(10)
plt.plot(x,y)
plt.xlabel("Time (ns)", fontsize="35", labelpad=25)
plt.ylabel("RMSD (nm)", fontsize="35", labelpad=25)
plt.legend()
plt.show()
add_files_button = Button(root, text ="Add files", command = addbox)
add_files_button.pack()
plotbutton = Button(root, text ="Plot", command = clicked1 )
plotbutton.pack(pady = 10)
root.mainloop()
You are currently creating a new figure object every time the clicked1 function is called. The simplest solution would be to bring the figure creation outside of the function definition. I would also explicitly use the axis object to ensure everything plots on the same axis:
f, ax = plt.subplots(figsize=(20, 10))
ax.set_xlabel("Time (ns)", fontsize="35", labelpad=25)
ax.set_ylabel("RMSD (nm)", fontsize="35", labelpad=25)
plt.ion() # Enable interactive plotting
def clicked1():
(x, y) = processxvg(filename)
ax.plot(x,y)
ax.legend()
plt.show()
Ideally you would not use global variables, but instead pass ax as an argument to clicked1.
I have been working on a gui to display a graph of values by two indices. That works fine as a standalone py file. I have made a gui that will successfully plot and clear a plot on a frame on canvas. I cannot figure out how to merge the two. The issue is in my plotData function (I have added and commented out a version of it that works very well with a simpler graph). I know my problem is in these lines below because I am using both plt and plot1, but now I don't know anymore what either of them do.
#fig1=plt.figure(1)
if len(blob0)>0:
plt.plot(blob0_et, blob0_acc, "s", color="blue", markersize=10, label = '0')
if len(blob1)>0:
plt.plot(blob1_et, blob1_acc, "s", color="red", markersize=10, label = '1')
if len(blob2)>0:
plt.plot(blob2_et, blob2_acc, "s", color="green", markersize=10, label = '2')
plot1 = fig.add_subplot(111) # adding the subplot
plot1.plot(y) # plotting the graph
plot1.set_title ("Circle Calibration, Accumulator vs Edge Threshold", fontsize=12)
plot1.set_ylabel("Accumulator", fontsize=14)
plot1.set_xlabel("Edge Threshold", fontsize=14)
Full Code
import tkinter.filedialog
import os
import tkinter as tk
from tkinter import ttk
import matplotlib
matplotlib.use("TkAgg") # this is the backend of matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import easygui
from matplotlib.legend_handler import HandlerLine2D
import simple_data
import ETvACCUM3
root = tk.Tk()
root.title("Tab Widget")
root.geometry("500x350")
tabControl = ttk.Notebook(root)
tab1 = ttk.Frame(tabControl)
tab2 = ttk.Frame(tabControl)
tab3 = ttk.Frame(tabControl)
tab4 = ttk.Frame(tabControl)
tabControl.add(tab1, text ='Tab 1')
tabControl.add(tab2, text ='Tab 2')
tabControl.add(tab3, text ='Tab 3')
tabControl.add(tab4, text ='Tab 4')
tk.Grid.rowconfigure(root, 0, weight=1)
tk.Grid.columnconfigure(root, 0, weight=1)
tabControl.grid(column=0, row=0, sticky=tk.E+tk.W+tk.N+tk.S)
#=====================================================================
# TAB 2
#=====================================================================
#=====================================================================
# my_frame_1 and its contents
#=====================================================================
# creating a frame (my_frame_1)
my_frame_1 = tk.Frame(tab2, bd=2, relief=tk.GROOVE)
my_frame_1.pack(side=tk.LEFT, anchor=tk.N, fill=tk.BOTH, expand=True)
#---------------------------------------------------------------------
# the figure that will contain the plot
fig = Figure(figsize = (5, 5), dpi = 100)
#=====================================================================
# frame2 and widgets it contains.
#=====================================================================
def plotData():
fig.clear()
file = easygui.fileopenbox(msg=None, title=None, default="/Users/.../Desktop/tk_gui_grid/", filetypes = None, multiple = False)
print('\n', "This is the selected file:", file, '\n')
# load data as a pandas dataframe
df = pd.read_csv(file, sep='\t', lineterminator='\n')
# make a smaller array by using the loc
df = df.loc[:,['Accum', 'EdgeThr','NumberOfBlobs']]
blob0 = []
blob1 = []
blob2 = []
blob0 = df[df['NumberOfBlobs'] == 0][['Accum', 'EdgeThr']]
blob1 = df[df['NumberOfBlobs'] == 1][['Accum', 'EdgeThr']]
blob2 = df[df['NumberOfBlobs'] == 2][['Accum', 'EdgeThr']]
blob0 = blob0.values.tolist()
blob1 = blob1.values.tolist()
blob2 = blob2.values.tolist()
print('blob2: ',blob2, '\n'*3)
fontTitle = {'family': 'arial',
'color': 'darkred',
'weight': 'normal',
'size': 16,
}
fontAxisLabels = {'family': 'helvetica',
'color': 'darkblue',
'weight': 'normal',
'size': 16,
}
if len(blob0)>0:
blob0_acc, blob0_et = map(list, zip(*blob0))
if len(blob1)>0:
blob1_acc, blob1_et = map(list, zip(*blob1))
if len(blob2)>0:
blob2_acc, blob2_et = map(list, zip(*blob2))
#fig1=plt.figure(1)
if len(blob0)>0:
plt.plot(blob0_et, blob0_acc, "s", color="blue", markersize=10, label = '0')
if len(blob1)>0:
plt.plot(blob1_et, blob1_acc, "s", color="red", markersize=10, label = '1')
if len(blob2)>0:
plt.plot(blob2_et, blob2_acc, "s", color="green", markersize=10, label = '2')
plot1 = fig.add_subplot(111) # adding the subplot
plot1.plot(y) # plotting the graph
plot1.set_title ("Circle Calibration, Acc vs ET", fontsize=12)
plot1.set_ylabel("Acc", fontsize=14)
plot1.set_xlabel("ET", fontsize=14)
toolbar = NavigationToolbar2Tk(my_canvas, my_frame_1)
toolbar.update()
my_canvas.get_tk_widget().pack(side = tkinter.TOP, fill = tkinter.BOTH, expand = 1)
my_canvas.draw_idle()
plt.axis([0,250,0,50])
plt.xlabel('Edge Threshold', fontdict = fontAxisLabels, fontsize = 12)
plt.ylabel('Accumulator', fontdict = fontAxisLabels, fontsize = 12)
plt.title('Accum vs Edge threshold', fontname='arial', color=('black'),fontdict = fontTitle,fontsize = 10)
plt.legend(loc = "upper right")
def getDataFile():
file = easygui.fileopenbox(msg=None, title=None, default="/Users/.../Desktop/tk_gui_grid/", filetypes = None, multiple = False)
print('\n', "This is the selected file:", file, '\n')
# load data as a pandas dataframe
df = pd.read_csv(file, sep='\t', lineterminator='\n')
print(df)
return df
"""
# this function successfully placed a simple plot onto the canvas when the button was pressed
def plotData():
fig.clear()
y = simple_data.some_yVals() #imported from a function in another module
plot1 = fig.add_subplot(111) # adding the subplot
plot1.plot(y) # plotting the graph
plot1.set_title ("Circle Calibration, Accumulator vs Edge Threshold", fontsize=12)
plot1.set_ylabel("Accumulator", fontsize=14)
plot1.set_xlabel("Edge Threshold", fontsize=14)
toolbar = NavigationToolbar2Tk(my_canvas, my_frame_1)
toolbar.update()
my_canvas.get_tk_widget().pack(side = tkinter.TOP, fill = tkinter.BOTH, expand = 1)
my_canvas.draw_idle()
"""
def clearPlot():
fig.clear()
my_canvas.draw_idle()
my_canvas = FigureCanvasTkAgg(fig, master = my_frame_1) # creating the Tkinter canvas containing the Matplotlib figure
my_canvas.get_tk_widget().pack() # placing the canvas on the Tkinter window
my_canvas.draw()
#create another frame(my_frame_2)
my_frame_2 = tk.Frame(tab2, bd=2, relief=tk.GROOVE)
my_frame_2.pack(side=tk.RIGHT)
#---------------------------------------------------------------------
button1 = tk.Button(my_frame_2, text = "Browse \nfiles", command = getDataFile, relief = tk.GROOVE, bg = "red", padx =20,pady =20 )
button1.pack(side="top", fill="x")
button2 = tk.Button(my_frame_2, text = "Plot \nData", command = plotData, relief = tk.GROOVE, bg = "red", padx =20,pady =20 )
button2.pack(side="top", fill="x" )
button3 = tk.Button(my_frame_2, text = "Clear \nPlot", command = clearPlot, relief = tk.GROOVE, bg = "red", padx =20,pady =20 )
button3.pack(side="top", fill="x")
button4 = tk.Button(my_frame_2, text = "Doggy", relief = tk.GROOVE, bg = "red", padx =20,pady =20 )
button4.pack(side="top", fill="x")
root.mainloop()
print('\n'*4)
*** Desired output ***
Any knowledge that helps me understand how to get the plots onto the canvas and to understand the difference between plt.plot and plot1.plot
You should read about the difference between the matplotlib object-oriented interface and the pyplot interface.
In your case you have mixed the two:
pyplot interface
when you call plt.plot, matplotlib will create an Axes instance if one does not already exist, or if one does, it will plot on the current Axes.
Object-oriented interface:
You create an Axes instance called plot1 using plot1 = plt.subplots(111), and then call the function plot from that instance: plot1.plot(). Anything you call from an Axes instance will be displayed on that Axes.
This issue likely comes because an Axes instance is created from your first plt.plot, but then you call plt.subplots and possibly create a different Axes instance which is used for everything afterwards.
In my opinion, it is usually better to use the object-oriented approach, since that way you always know where things you plot are going to end up. Its almost always a bad idea to mix the two approaches, as things end up getting confused somewhere.
Note that in the documentation and in many examples you will see around the web, the Axes instance is often called ax or ax1, rather than plot1 which you have here. Both will work just fine, but that might help to keep in mind when looking at examples elsewhere.
Its hard to tell exactly, and you don't say quite what your desired outcome is, but I think you probably want something like this. Create the plot1 Axes instance before you plot blob0, blob1 or blob2, then call plot1.plot for those three plotting functions too. That should ensure it all turns up on the same Axes.
plot1 = fig.add_subplot(111) # adding the subplot
if len(blob0)>0:
plot1.plot(blob0_et, blob0_acc, "s", color="blue", markersize=10, label = '0')
if len(blob1)>0:
plot1.plot(blob1_et, blob1_acc, "s", color="red", markersize=10, label = '1')
if len(blob2)>0:
plot1.plot(blob2_et, blob2_acc, "s", color="green", markersize=10, label = '2')
plot1.plot(y) # plotting the graph
plot1.set_title ("Circle Calibration, Acc vs ET", fontsize=12)
plot1.set_ylabel("Acc", fontsize=14)
plot1.set_xlabel("ET", fontsize=14)
Its probably not required, but if you are changing to the object-oriented interface, you might as well go the whole way and change it everywhere. For example, these lines:
plt.axis([0,250,0,50])
plt.xlabel('Edge Threshold', fontdict = fontAxisLabels, fontsize = 12)
plt.ylabel('Accumulator', fontdict = fontAxisLabels, fontsize = 12)
plt.title('Accum vs Edge threshold', fontname='arial', color=('black'),fontdict = fontTitle,fontsize = 10)
plt.legend(loc = "upper right")
should become:
plot1.axis([0,250,0,50])
plot1.set_xlabel('Edge Threshold', fontdict = fontAxisLabels, fontsize = 12)
plot1.set_ylabel('Accumulator', fontdict = fontAxisLabels, fontsize = 12)
plot1.set_title('Accum vs Edge threshold', fontname='arial', color=('black'),fontdict = fontTitle,fontsize = 10)
plot1.legend(loc = "upper right")
I'm new to coding and i'm trying to create a Tkinter window using Matplotlib classes that allows me to manipulate a line graph depicting the share prices for 4 companies over two years in a separate window, this is the code I've written:
from tkinter import *
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
import pandas as pd
root = Tk ()
data = pd.read_csv('C:\\Users\\Admin\\Desktop\\shares.csv')
df = data.iloc[:,:5]
print(df)
print()
fig = Figure( figsize = (20,5) , dpi = 100 )
ax1 = fig.add_subplot()
df.plot('Date',figsize = (20,5), ax = ax1)
canvas = FigureCanvasTkAgg ( fig , root )
canvas.draw()
canvas.get_tk_widget().pack(side = TOP, fill = BOTH, expand = 1)
df.plot(kind = 'bar' , ax = ax1)
toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
canvas.get_tk_widget().pack(side = TOP, fill = BOTH, expand = 1)
def on_key(event):
print('You Pressed {}'.format(event.key))
key_press_handler(event, canvas, toolbar)
canvas.mpl_connect('key_press_event' , on_key)
def Quit():
root.quit()
root.destroy()
return
button = Button(root, text = 'QUIT' , command = Quit )
button.pack ()
root.mainloop()
This is the output I have obtained:
Whereas this is the desired graph:
I would appreciate any input as to how I can fix this, and make my code better.
Thanks :)
Try this it may work
df.plot(kind = 'line' , ax = ax1)
You are ploting bar and your expected output is a line graph