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()
Related
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()
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 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
...
I am new to tkinter and I need to be able to draw more than one line plot on the same chart each with its own legend. I can do this in matplotlib using show () but then the graph becomes separate from the GUI window. My code takes files from a file list I have created and then plots them on the screen but keeps overwriting each graph - Is there a way of doing this so all the line charts appear on the same chart in tkinter?
for item in myfile_list:
x, y = np.loadtxt(item + '_' + 'Test.csv', skiprows=1, usecols=[x_axis_column, y_axis_column],
unpack=True, delimiter=',')
# graph size in inches
fig = Figure(figsize=(5, 5))
a = fig.add_subplot(111)
a.plot(x, y, color='blue')
a.set_title("Title", fontsize=16)
a.set_ylabel("Y", fontsize=14)
a.set_xlabel("X", fontsize=14)
canvas = FigureCanvasTkAgg(fig, master=chart_frame)
# place graph in first row and column of chart_frame
canvas.get_tk_widget().grid(row=0, column=0)
canvas.draw()
toolbar_frame = Frame(plot_frame)
toolbar_frame.grid(row=1, column=0)
toolbar = NavigationToolbar2Tk(canvas, toolbar_frame)
toolbar.update()
output
# use tkchart module....
# https://pypi.org/project/tkchart/
# https://github.com/Thisal-D/tkchart
import tkchart
import tkinter
root = tkinter.Tk()
#create chart
chart_1 = tkchart.LineChart(master=root,width=1000 ,height=600
,chart_line_len=80 ,sections=True
,sections_count=10 ,values_labels=True
,values_labels_count=5 ,
max_value = 100)
chart_1.pack()
#create lines for chart
line_1 = tkchart.Line(master=chart_1 ,color="#00ff00" ,height=4)
line_2 = tkchart.Line(master=chart_1 ,height=4 ,color ="#ffff00")
value = [x for x in range(0,100)]
import random
def display():
chart_1.display(line=line_1 ,values=random.choices(value))
chart_1.display(line=line_2 ,values=random.choices(value))
root.after(500,display)
display()
root.mainloop()
hi I'm a student and I'm working with ktinker for the first time. I am currently trying to create a program that follows this order:
open a map in matplotlib
a popup window is called for users to enter in information
when the user clicks on the map grid the coordinates and user
info are stored in a dictionary
matplotlib generates a plot point on the location
The trouble is, when I call the popup window I have written in tkinter, calling the mainloop() function breaks the matplotlib window and it wont generate the plot points until the map window is closed.
getting rid of the mainloop fixes it but then means i cant get the data from the popup.
Anyone know how I can stop one from interfering with the other? i have to keep them in separate classes as Im meant to demonstrate modularity in my code.
here is the main code
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
import popup1 as ap
__author__ = "k1221169"
__date__ = "$15-Nov-2015 11:29:21$"
markerList = []
xplots = []
yplots = []
desc = ''
title = ''
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(np.random.rand(10))
img = mpimg.imread('overland map 1.png')
imgplot = plt.imshow(img)
plt.ylim(ymax=1)
plt.xlim(xmin=1)
def mrkApnd(a,b):
a.update(b)
print a
markerList.append(a)
def onclick(event):
if spacecheck(event.xdata, event.ydata):
#print markerList[0]['x']
idx = np.abs(xplots - event.xdata).argmin()
print xplots[idx]
for i in markerList:
if xplots[idx] == i['x'] :
print i
#c = i
ap.useroutput(i)
else:
input1 = ap.userinput()
input2 = {'x':event.xdata, 'y':event.ydata}
input1['title'] = title
input1['desc'] = desc
mrkApnd(input1,input2)
drawMarks()
print input1
return markerList
cid = fig.canvas.mpl_connect('button_press_event', onclick)
def drawMarks():
plt.ion()
for i in markerList:
xplots.append(i['x'])
yplots.append(i['y'])
plt.plot(i['x'], i['y'], i['type'])
def spacecheck(x,y):
a = bool
if np.isclose(xplots, x, atol=50.0).any() and np.isclose(yplots, y, atol=50.00).any():
a=True
print 'yes'
return a
plt.draw()
plt.show()
and here is the popup code called from another file
from Tkinter import *
class popup1():
def __init__(self):
pass
def userinput():
pop = Toplevel()
pop.title("marker")
pop.geometry("300x500+200+200")
#string for title
frame = Frame(pop)
entry = Entry(frame)
entry.pack(side = TOP)
frame.pack( padx =20, pady =20)
#radius button for visibility
frame2 = Frame(pop)
selection = StringVar()
radio_1 = Radiobutton(frame2, text = 'Character', variable = selection, value = 'ob')
radio_2 = Radiobutton(frame2, text = 'Item', variable = selection, value = 'or')
radio_3 = Radiobutton(frame2, text='Event', variable = selection, value = 'oy')
radio_1.select()
radio_1.pack(side = LEFT)
radio_2.pack(side = LEFT)
radio_3.pack(side = LEFT)
frame2.pack(padx =30, pady =30)
#radius button for marker type
frame3 = Frame(pop)
visible = bool
check_1 = Checkbutton(frame3, text = 'GM Only', variable = visible, onvalue= True, offvalue= False)
check_1.pack(side = LEFT)
frame3.pack(padx =30, pady =30)
#string for user input
frame4 = Frame(pop)
entry4 = Entry(frame4)
entry4.pack(side = LEFT)
frame4.pack( padx =20, pady =20)
def infoPass():
#info1 = {'title': entry.get(), 'type': selection.get(), 'vis': visible, 'Desc': entry4.get()}
#info.update(info1)
#print info
pop.destroy()
#buttons
label = Label(pop, text="", height=0, width=100)
label.pack()
b = Button(pop, text="Cancel", width=20, command= pop.destroy )
b.pack(side='bottom',padx=5,pady=5)
b2 = Button(pop, text="Save", width=20, command= infoPass )
b2.pack(side='bottom',padx=5,pady=5)
info = {'title': entry.get(), 'type': selection.get(), 'vis': visible, 'desc': entry4.get()}
pop.mainloop()
return info
If i underdstood your question right, then try to add your own loop.
Remove pop.mainloop()
Make the code be a class for a cleaner code
class userinput:
def __init__(self):
#open your window here and style it etc. and:
self.data_sent = False
def infopass(self):
self.data_sent = True
#your infopass code here and
Create your own loop at the end of init:
def __init__(self):
#...
while self.data_sent == False:
root.update()
while data_sent == False:
pop.update()
Call your popup by
mypopup = userinput()
Good luck ;)