I have the following code for a counter with two buttons, one to increase the count and the other to decrease it. The count is a label containing a number. The graph that appears with the buttons is supposed to visualise the history of the counted number ie. x axis is the index number of results_table and the y axis is the number that appears in the count. The buttons and the count work but the graph doesn't show with the following code. There is obviously something I am missing to get the graph to update. Here's the code:
import tkinter
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
class App:
def __init__(self, master):
# Create a container
frame = tkinter.Frame(master)
# Create 2 buttons
self.button_left = tkinter.Button(frame,text="-", command=self.decrease, bg = 'red', fg = 'white')
self.button_left.pack(side="left")
self.button_right = tkinter.Button(frame,text="+", command=self.increase, bg = 'green', fg = 'white')
self.button_right.pack(side="right")
self.label_value = tk.Label(frame, text = '0')
self.label_value.pack(side = "bottom")
fig = Figure()
ax = fig.add_subplot(111)
self.line, = ax.plot(0)
self.canvas = FigureCanvasTkAgg(fig,master=master)
self.canvas.draw()
self.canvas.get_tk_widget().pack(side='top', fill='both', expand=1)
frame.pack()
result_table = []
def decrease(self):
value = int(self.label_value["text"])
self.label_value["text"] = f"{value - 1}"
result_table.append(self.label_value['text'])
x, y = self.line.get_data('result_table')
self.canvas.draw()
def increase(self):
value = int(self.label_value["text"])
self.label_value["text"] = f"{value + 1}"
result_table.append(self.label_value['text'])
x, y = self.line.get_data('result_table')
self.canvas.draw()
root = tkinter.Tk()
app = App(root)
root.mainloop()
Any help graetly appreciated.
Matt
so I solved your problem, but it is not a straight forward answer/solution.
First of all you designed your GUI quiet good! I just wouldn't use pack() but I prefer grid().
Next, I had to delete your class, because I never used tkinter with a class. Maybe you will be able to put it back in.
So what I did:
As already mentioned by Matiiss, you don't really use your x and y values to plot your figure. Your result_table on the other hand just works fine and stores all values created!
So you have to plot them as y values and your x values are basically dependent on the length of your result_table len(result_table). For this part I used numpy to always generate an array of the appropriate length.
Furthermore, I created an extra container fig_frame inside the root, where I always display the figure.
This code worked for me:
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import numpy as np
def decrease():
value = int(label_value["text"])
label_value["text"] = f"{value - 1}"
result_table.append(label_value['text'])
x = np.arange(len(result_table))
y = result_table
create_fig(x, y)
def increase():
value = int(label_value["text"])
label_value["text"] = f"{value + 1}"
result_table.append(label_value['text'])
x = np.arange(len(result_table))
y = result_table
create_fig(x, y)
def create_fig(x, y):
fig = Figure()
ax = fig.add_subplot(111)
line, = ax.plot(x, y)
canvas = FigureCanvasTkAgg(fig, fig_frame)
canvas.draw()
canvas.get_tk_widget().grid(row=0, column=0)
root = tk.Tk()
# Create a container
frame = tk.Frame(root)
fig_frame = tk.Canvas(root, height=650, width=650, borderwidth=1, relief='ridge')
fig_frame.pack()
# Create 2 buttons
button_left = tk.Button(frame, text="-", command=decrease, bg='red', fg='white')
button_left.pack(side="left")
button_right = tk.Button(frame, text="+", command=increase, bg='green', fg='white')
button_right.pack(side="right")
label_value = tk.Label(frame, text='0')
label_value.pack(side="bottom")
fig = Figure()
ax = fig.add_subplot(111)
line, = ax.plot(0)
canvas = FigureCanvasTkAgg(fig, fig_frame)
canvas.draw()
canvas.get_tk_widget().grid(row=0, column=0)
frame.pack()
result_table = []
root.mainloop()
Keep going like this! You really already did a great work and even if my code looks different to your code, most parts are just rearranged!
Related
I have a simple code to plot a figure. I want to manually change the range for the colorbar.
So, I added two Entries and defined a second function change(). I want to make this change for the colorbar to happen instantly without having the second button.
from tkinter import *
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
root = Tk()
root.geometry("500x500")
Max, Min = IntVar(), IntVar()
label1 = Label(root, text="Max")
label1.place(x=10, y=35)
label2 = Label(root, text="Min")
label2.place(x=10, y=60)
entry1 = Entry(root, textvariable=Max, width=5)
entry1.place(x=50, y=35)
entry2 = Entry(root, textvariable=Min, width=5)
entry2.place(x=50, y=60)
def plot():
global x, y
x, y = np.mgrid[slice(0, 100), slice(0, 100)]
z = (x*y)
figure = Figure(figsize=(4, 4))
ax = figure.add_subplot(111)
c = ax.pcolormesh(x, y, z, cmap='YlGn')
ax.figure.colorbar(c)
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().place(x=0, y=80)
def change():
z = (x*y)
figure = Figure(figsize=(4, 4))
ax = figure.add_subplot(111)
c = ax.pcolormesh(x, y, z, cmap='YlGn', vmin=entry1.get(), vmax=entry2.get())
ax.figure.colorbar(c)
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().place(x=0, y=80)
button1 = Button(root, text="Plot", command=plot)
button1.place(x=30, y=0)
button2 = Button(root, text="change", command=change)
button2.place(x=80, y=0)
root.mainloop()
I found this post Constantly Update Label Widgets From Entry Widgets TKinter, and I tried to use method 2, and I changed the code in this part:
...
def auto():
c.config(vmin=entry1.get(), vmax=entry2.get())
entry1 = Entry(root, textvariable=Max, width=5)
entry1.place(x=50, y=35)
entry2 = Entry(root, textvariable=Min, width=5)
entry2.place(x=50, y=60)
auto()
...
But as c is a local variable, the code doesn't work. can anybody help me instantly update the colorbar range?
So what You want in the end is to change colormap vmin and vmax, when user change Min and Max input. You don't need to constantly update colormap, but just on change of those inputs.
You can do that by tracing input change with update callback.
Here is modified code which does colormap update when Min and Max input is changed:
from tkinter import *
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
root = Tk()
root.geometry("500x500")
Max, Min = IntVar(), IntVar()
label1 = Label(root, text="Min")
label1.place(x=10, y=35)
label2 = Label(root, text="Max")
label2.place(x=10, y=60)
vmin_entry = Entry(root, textvariable=Min, width=5)
vmin_entry.place(x=50, y=35)
vmax_entry = Entry(root, textvariable=Max, width=5)
vmax_entry.place(x=50, y=60)
# Define global variables
c, canvas = None, None
def plot():
global x, y, c, canvas
x, y = np.mgrid[slice(0, 100), slice(0, 100)]
z = (x * y)
figure = Figure(figsize=(4, 4))
ax = figure.add_subplot(111)
c = ax.pcolormesh(x, y, z, cmap='YlGn')
ax.figure.colorbar(c)
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().place(x=0, y=80)
canvas.draw()
def update_colormap(*args, **kwargs):
global c, canvas
if c is not None:
try:
# Get vmin and vmax
vmin, vmax = int(vmin_entry.get()), int(vmax_entry.get())
except ValueError:
# Could not convert values to int, non integer value
return
if vmin > vmax:
return
# Set new limits
c.set_clim(vmin, vmax)
# Update plot
canvas.flush_events()
canvas.draw()
# Trace change of Min and Max and call update_colormap as a callabck
Min.trace("w", update_colormap)
Max.trace("w", update_colormap)
button1 = Button(root, text="Plot", command=plot)
button1.place(x=30, y=0)
root.mainloop()
You can bind the "<Key>" event of the Entry widget to the change function as the callback, this will call the change function whenever, anything is typed within the entry widget.
entry1.bind('<Key>', lambda x : change())
Doing this for both entries would be adding these lines to your code -:
entry1.bind('<Key>', lambda x : change())
entry2.bind('<Key>', lambda x : change())
The full code will become -:
from tkinter import *
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
root = Tk()
root.geometry("500x500")
Max, Min = IntVar(), IntVar()
label1 = Label(root, text="Max")
label1.place(x=10, y=35)
label2 = Label(root, text="Min")
label2.place(x=10, y=60)
entry1 = Entry(root, textvariable=Max, width=5)
entry1.place(x=50, y=35)
entry2 = Entry(root, textvariable=Min, width=5)
entry2.place(x=50, y=60)
def plot():
global x, y
x, y = np.mgrid[slice(0, 100), slice(0, 100)]
z = (x*y)
figure = Figure(figsize=(4, 4))
ax = figure.add_subplot(111)
c = ax.pcolormesh(x, y, z, cmap='YlGn')
ax.figure.colorbar(c)
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().place(x=0, y=80)
def change():
z = (x*y)
figure = Figure(figsize=(4, 4))
ax = figure.add_subplot(111)
c = ax.pcolormesh(x, y, z, cmap='YlGn', vmin=entry1.get(), vmax=entry2.get())
ax.figure.colorbar(c)
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().place(x=0, y=80)
button1 = Button(root, text="Plot", command=plot)
button1.place(x=30, y=0)
entry1.bind('<Key>', lambda x : change()) # binding the first entry's keypress event to the change function.
entry2.bind('<Key>', lambda x : change()) # binding the second entry's keypress event to the change function.
root.mainloop()
NOTE:
The "<Key>" event's callback is triggered whenever a key is pressed inside the widget, for more info take a look at events and bindings.
I have a problem regarding showing pyplots in tkinkter. I have a function plot_function that returns 4 matplotlib.pyplot-plots. I want to be able to show these plots in a tkinter GUI, and show one plot in the beginning and display the other three with two "next" and "previous" buttons. The plot_funtion is only run once.
At the moment I'm trying to do the following (simplified):
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class GUI:
def plot_function(self):
...
# Generating plots #
...
self.plots = [plt1, plt2, plt3, plt4]
self.init_plots()
def init_plots(self): # Showing the inital plot, plt1
self.plot_ctr = 0
fig = plt.Figure(figsize=(5,4), dpi = 80)
plot1 = fig.add_subplot(111)
plot1.show(self.plots[0])
first_fig = FigureCanvasTkAgg(fig,self.frame1)
first_fig.draw()
first_fig.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
self.master.update_idletasks()
def next_plot(self):
if self.plot_ctr < 3:
self.plot_ctr += 1
else:
self.plot_ctr = 0
self.first_fig.config(image = self.plots[self.plots_ctr])
self.master.update_idletasks()
def prev_plot(self):
if self.plot_ctr > 0:
self.plot_ctr -= 1
else:
self.plot_ctr = 3
self.first_fig.config(image = self.plots[self.plots_ctr])
self.master.update_idletasks()
def __init__(self, master):
self.master = master
self.frame1 = tk.Frame(self.master, relief = tk.RAISED, highlightbackground = "black", highlightthickness = 1)
self.frame1.pack(side = tk.TOP, fill=tk.BOTH, expand=True)
self.next_btn = tk.Button(self.frame1, text = "Next plot", width = 10, command = self.next_plot)
self.prev_btn = tk.Button(self.frame1, text = "Previous plot", width = 10, command = self.prev_plot)
self.prev_btn.pack(side = tk.LEFT, padx = 10, pady = 10)
self.next_btn.pack(side = tk.LEFT, padx = 10, pady = 10)
root = tk.Tk()
my_gui = GUI(root)
root.mainloop()
I feel very unsure on how to properly use TkAgg and would be very grateful on any tips on how I would get this to work.
Here is an example showing how you could organize your code to rotate a sequence of matplotlib figures embedded in a tkinter window
import matplotlib
import tkinter as tk
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
class Data:
def __init__(self):
self.x = list(range(10))
self.y0 = self.x[:]
self.y1 = [5 for _ in range(10)]
self.y2 = [-y for y in self.y0]
self.d = [self.y0, self.y1, self.y2]
def get_next_dataset(self):
y = self.d.pop(0)
self.d.append(y)
return self.x, y
class MPLGraph(Figure):
def __init__(self, data):
Figure.__init__(self, figsize=(5, 5), dpi=100)
self.data = data
def obtain_next_figure(self):
self.plot = self.add_subplot(111)
self.plot.plot(*self.data.get_next_dataset())
return self
class GraphPage(tk.Frame):
def __init__(self, parent, graphs):
tk.Frame.__init__(self, parent)
self.title_label = tk.Label(self, text="Graph Page Example")
self.title_label.pack()
self.pack()
self.next_graph_btn = tk.Button(self, text='next graph', command=self.obtain_next_figure)
self.next_graph_btn.pack()
self.graphs = graphs
self.obtain_next_figure()
def obtain_next_figure(self):
try: # protects against AttributeError the first time when self.figure_widget not yet exists yet
self.figure_widget.destroy()
except AttributeError:
pass
self.add_mpl_figure(self.graphs.obtain_next_figure())
def add_mpl_figure(self, fig):
self.mpl_canvas = FigureCanvasTkAgg(fig, self)
self.figure_widget = self.mpl_canvas.get_tk_widget()
self.figure_widget.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
if __name__ == '__main__':
root = tk.Tk()
graph_page = GraphPage(root, MPLGraph(Data()))
root.mainloop()
I tried to plot the graph in pop up window. It pops up. But there is an error.
import tkinter as tk
window = tk.Tk()
window.configure(background='white')
label_1 = tk.Label(window, text="Conpyright 123456789123456798", anchor=tk.S)
label_1.pack()
ws = window.winfo_screenwidth()
hs = window.winfo_screenheight()
w = 980 # width for the Tk root
h = 600 # height for the Tk root
x = (ws / 2) - (w / 2)
y = (hs / 2) - (h / 2)
window.geometry('%dx%d+%d+%d' % (w, h, x, y))
canvas = tk.Canvas(window, bg="white", width=980, height=580, highlightthickness=0)
canvas.pack()
canvas_scroll = tk.Scrollbar(canvas, command=canvas.yview)
canvas_scroll.place(relx=1, rely=0, relheight=1, anchor=tk.NE)
canvas.configure(yscrollcommand=canvas_scroll.set, scrollregion=())
minw_var = tk.DoubleVar()
entry_minw_number = tk.Entry(canvas, textvariable=minw_var)
canvas.create_window(220,215, window=entry_minw_number)
maxw_var = tk.DoubleVar()
entry_maxw_number = tk.Entry(canvas, textvariable=maxw_var)
canvas.create_window(355,215, window=entry_maxw_number)
minl_var = tk.DoubleVar()
entry_minl_number = tk.Entry(canvas, textvariable=minl_var)
canvas.create_window(220,240, window=entry_minl_number)
maxl_var = tk.DoubleVar()
entry_maxl_number = tk.Entry(canvas, textvariable=maxl_var)
canvas.create_window(355,240, window=entry_maxl_number)
rect_var = tk.IntVar()
entry_rect_number = tk.Entry(canvas, textvariable=rect_var)
canvas.create_window(290,270, window=entry_rect_number)
And this is the part for matplotlib
-------------------------------------------------------------------------
def plot_sheet(self):
fig,ax = plt.subplots(1)
ax.set_xlim([0, self.W])
ax.set_ylim([0, self.L])
recs = []
for i in range(len(self.rect_list)):
if self.rect_rotate[i]:
ax.add_patch(patches.Rectangle((self.rect_pos[i][0], self.rect_pos[i][1]), self.rect_list[i].l, self.rect_list[i].w,linewidth=3,edgecolor='r'))
else:
ax.add_patch(patches.Rectangle((self.rect_pos[i][0], self.rect_pos[i][1]), self.rect_list[i].w, self.rect_list[i].l,linewidth=3,edgecolor='r'))
#plt.show()
return fig
def plot_sheets(self):
for i in range(len(self.sheets)):
self.sheets[i].plot_sheet()
def cal_culate1():
fig = packing_options[best_index].plot_sheets()
dataPlot = FigureCanvasTkAgg(fig, master = window)
dataPlot.show()
dataPlot.get_tk_widget().pack(side='top', fill='both', expand=1)
window.mainloop()
I wrote dataPlot = FigureCanvasTkAgg(fig, master = window). There is an error in master = window.
File "", line 687, in cal_culate1
dataPlot = FigureCanvasTkAgg(fig, master = window)
File "C:\Users\sel\Anaconda3\lib\site-packages\matplotlib\backends_backend_tk.py", line 204, in init
super(FigureCanvasTk, self).init(figure)
File "C:\Users\sel\Anaconda3\lib\site-packages\matplotlib\backend_bases.py", line 1618, in init
figure.set_canvas(self)
AttributeError: 'NoneType' object has no attribute 'set_canvas'
What should be written there?
You didn't show how you create your class for plotting, so i can only go by assumption here. First create a empty list:
import tkinter as tk
window = tk.Tk()
window.configure(background='white')
figure_holder = []
Then append to the list when you create your figure:
def plot_sheets(self):
for i in range(len(self.sheets)):
a = self.sheets[i].plot_sheet()
figure_holder.append(a)
Retrieve the figure object from the list when you plot it:
def cal_culate1():
fig = figure_holder[0]
dataPlot = FigureCanvasTkAgg(fig, master = window)
#dataPlot.show()
dataPlot.get_tk_widget().pack(side='top', fill='both', expand=1)
I made minimal working example which shows how do this.
it will need changes for your code but I don't know what you have in code and you didn't create minimal working example.
It creates three figures in generate_all_figures (in your code it will be plot_sheets with s) using plot_sheet (without s) and keep on list.
window display first figure from this list.
Buttons remove canvas with figure and create new canvas with next/previous figure from list.
I use grid() instead of pack() because this way I can easily put new canvas in the same place.
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class MyClass():
def __init__(self):
self.sheets = [[1,2,3], [3,1,2], [1,5,1]]
self.W = 2
self.L = 5
self.all_figures = []
def plot_sheet(self, data):
"""plot single figure"""
fig, ax = plt.subplots(1)
ax.set_xlim([0, self.W])
ax.set_ylim([0, self.L])
ax.plot(data)
return fig
def generate_all_figures(self):
"""create all figures and keep them on list"""
for data in self.sheets:
fig = self.plot_sheet(data)
self.all_figures.append(fig)
def show_figure(number):
global dataPlot
# remove old canvas
if dataPlot is not None: # at start there is no canvas to destroy
dataPlot.get_tk_widget().destroy()
# get figure from list
one_figure = my_class.all_figures[number]
# display canvas with figuere
dataPlot = FigureCanvasTkAgg(one_figure, master=window)
dataPlot.draw()
dataPlot.get_tk_widget().grid(row=0, column=0)
def on_prev():
global selected_figure
# get number of previous figure
selected_figure -= 1
if selected_figure < 0:
selected_figure = len(my_class.all_figures)-1
show_figure(selected_figure)
def on_next():
global selected_figure
# get number of next figure
selected_figure += 1
if selected_figure > len(my_class.all_figures)-1:
selected_figure = 0
show_figure(selected_figure)
# --- main ---
my_class = MyClass()
my_class.generate_all_figures()
window = tk.Tk()
window.rowconfigure(0, minsize=500) # minimal height
window.columnconfigure(0, minsize=700) # minimal width
# display first figure
selected_figure = 0
dataPlot = None # default value for `show_figure`
show_figure(selected_figure)
# add buttons to change figures
frame = tk.Frame(window)
frame.grid(row=1, column=0)
b1 = tk.Button(frame, text="<<", command=on_prev)
b1.grid(row=0, column=0)
b2 = tk.Button(frame, text=">>", command=on_next)
b2.grid(row=0, column=1)
window.mainloop()
Probably it could be done without replacing canvas but by replacing data in plot (fig.data ???, ax.data ??? I don't remember)
Does anybody know how to modify the following code so that the graph is updated every time I change a goniometric function? The plotting is done in function plot(). There are some related threads on stackoverflow, but I was not able to apply them on my example code...
Many thx,
Macky
import numpy as np
import Tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class App:
def __init__(self, window):
self.window = window
# set up pi
pi = 3.141592654
# create the frame and pack to the object
frame = tk.Frame(window)
frame.pack()
def callback(func, *pargs):
print(func.get())
func.set(func.get())
self.plot()
self.window.update()
self.func = tk.StringVar()
self.func.set("sin") # default value
self.func.trace('w', lambda *pargs: callback(self.func, *pargs))
self.button = tk.OptionMenu(window, self.func, "sin", "cos", "tan")
self.button.pack(side = tk.TOP)
self.min_value = tk.Entry(window, justify = tk.RIGHT)
self.min_value.pack()
self.min_value.delete(0, tk.END)
self.min_value.insert(0, -pi)
self.max_value = tk.Entry(window, justify = tk.RIGHT)
self.max_value.pack()
self.max_value.delete(0, tk.END)
self.max_value.insert(0, pi)
self.button = tk.Button(frame, text = "QUIT", foreground = "red", command = frame.quit)
self.button.pack(side = tk.BOTTOM)
self.draw = tk.Button(frame, text = "DRAW", command = self.plot())
self.draw.pack(side = tk.LEFT)
def plot(self):
# generate numbers for the plot
x = np.array(np.arange(np.float64(self.min_value.get()), np.float64(self.max_value.get()), 0.001))
if self.func.get() == 'sin':
print('plotting sin()')
y = np.sin(x)
elif self.func.get() == 'cos':
print('plotting cos()')
y = np.cos(x)
else:
print('plotting tan()')
y = np.tan(x)
# create the plot
fig = Figure(figsize = (6, 6))
a = fig.add_subplot(1,1,1)
a.plot(x, y, color = 'blue')
a.set_title ("Goniometric Functions", fontsize = 12)
a.set_ylabel(self.func.get() + '(x)', fontsize = 8)
a.set_xlabel('x', fontsize = 8)
# canvas
canvas = FigureCanvasTkAgg(fig, master = self.window)
self.widget = canvas.get_tk_widget().pack()
print('here I should update canvas')
canvas.draw()
root = tk.Tk()
app = App(root)
root.mainloop()
root.destroy()
You should create empty plot on canvas and later only replace data in plot using a.set_xdata(), a.set_ydata()
import numpy as np
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class App:
def __init__(self, window):
self.window = window
# set up pi
pi = 3.141592654
# create the frame and pack to the object
frame = tk.Frame(window)
frame.pack()
def callback(func, *pargs):
print(func.get())
#func.set(func.get()) # <-- it has no sense
self.update_plot()
self.window.update()
self.func = tk.StringVar()
self.func.set("sin") # default value
self.func.trace('w', lambda *pargs: callback(self.func, *pargs))
self.button = tk.OptionMenu(window, self.func, "sin", "cos", "tan")
self.button.pack(side = tk.TOP)
self.min_value = tk.Entry(window, justify = tk.RIGHT)
self.min_value.pack()
self.min_value.delete(0, tk.END)
self.min_value.insert(0, -pi)
self.max_value = tk.Entry(window, justify=tk.RIGHT)
self.max_value.pack()
self.max_value.delete(0, tk.END)
self.max_value.insert(0, pi)
self.button = tk.Button(frame, text="QUIT", foreground="red", command=root.destroy) #<--
self.button.pack(side = tk.BOTTOM)
self.draw = tk.Button(frame, text="DRAW", command=self.update_plot)
self.draw.pack(side = tk.LEFT)
# create empty plot
self.create_plot()
# update plot with current function at start
self.update_plot()
def create_plot(self):
# create the plot
self.fig = Figure(figsize = (6, 6))
self.a = self.fig.add_subplot(1,1,1)
self.p, _ = self.a.plot([], [], color = 'blue')
self.a.set_title ("Goniometric Functions", fontsize = 12)
# canvas
self.canvas = FigureCanvasTkAgg(self.fig, master = self.window)
self.widget = self.canvas.get_tk_widget().pack()
#self.canvas.draw()
def update_plot(self):
print('update')
# generate numbers for the plot
x = np.array(np.arange(np.float64(self.min_value.get()), np.float64(self.max_value.get()), 0.001))
if self.func.get() == 'sin':
print('plotting sin()')
y = np.sin(x)
elif self.func.get() == 'cos':
print('plotting cos()')
y = np.cos(x)
else:
print('plotting tan()')
y = np.tan(x)
# replace labels
self.a.set_ylabel(self.func.get() + '(x)', fontsize = 8)
self.a.set_xlabel('x', fontsize = 8)
# replace data
self.p.set_xdata(x)
self.p.set_ydata(y)
# rescale
self.a.relim()
self.a.autoscale_view()
# update screen
self.canvas.draw()
root = tk.Tk()
app = App(root)
root.mainloop()
#root.destroy() You don't need it
#furas - Thx a lot for the post. Figured out based on your comment 10 minutes after your post. Although being second to you, I am sending my version of the code as well ;). I think, we can close this thread as solved...
Macky
import numpy as np
import Tkinter as tk
import matplotlib.figure as fg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class App:
def __init__(self, window):
self.window = window
# set up pi
pi = 3.141592654
# create empty canvas
x = []
y = []
fig = fg.Figure(figsize = (4,4))
fig_subplot = fig.add_subplot(111)
self.line, = fig_subplot.plot(x, y)
self.canvas = FigureCanvasTkAgg(fig, master = self.window)
self.canvas.show()
self.canvas.get_tk_widget().pack(side = tk.TOP, fill = tk.BOTH, expand = 1)
# create the frame and pack to the object
frame = tk.Frame(window)
frame.pack()
def callback(func, *pargs):
print(func.get())
func.set(func.get())
self.plot()
self.func = tk.StringVar()
self.func.set("sin") # default value
self.func.trace('w', lambda *pargs: callback(self.func, *pargs))
self.button = tk.OptionMenu(window, self.func, "sin", "cos", "tan")
self.button.pack(side = tk.TOP)
self.min_value = tk.Entry(window, justify = tk.RIGHT)
self.min_value.pack()
self.min_value.delete(0, tk.END)
self.min_value.insert(0, -pi)
self.max_value = tk.Entry(window, justify = tk.RIGHT)
self.max_value.pack()
self.max_value.delete(0, tk.END)
self.max_value.insert(0, pi)
self.button = tk.Button(frame, text = "QUIT", foreground = "red", command = frame.quit)
self.button.pack(side = tk.BOTTOM)
def plot(self):
# generate numbers for the plot
x = np.array(np.arange(np.float64(self.min_value.get()), np.float64(self.max_value.get()), 0.001))
if self.func.get() == 'sin':
print('plotting sin()')
y = np.sin(x)
elif self.func.get() == 'cos':
print('plotting cos()')
y = np.cos(x)
else:
print('plotting tan()')
y = np.tan(x)
# update canvas
self.line.set_data(x, y)
ax = self.canvas.figure.axes[0]
ax.set_xlim(x.min(), x.max())
ax.set_ylim(y.min(), y.max())
self.canvas.draw()
root = tk.Tk()
app = App(root)
root.mainloop()
root.destroy()
I've been trying to make a GUI in Python that will take 3 numbers as user input and graph them in a 3D scatter plot. i've been successful in creating the GUI through tkinter, but I'm stuck when it comes to actual graphing. I wanted to define an empty list for x, y, and z variables, then append them with whatever the user inputs. I would then take those variables and have matplotlib graph them. I was unable to do it as originally planned, so I reverted to defining two functions, each tied to their own buttons. The "store" button would record the user input into the respective empyt lists created beforehand and clear the entry box. Then, the user could hit the graph button and the lists would be graphed in a 3D plot. Here is the code:
from tkinter import *
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
#store function
x_List = []
y_List = []
z_List = []
def store():
x_List.append(x_Var)
y_List.append(y_Var)
z_List.append(z_Var)
entry_1.delete(0, END)
entry_2.delete(0, END)
entry_3.delete(0, END)
def graph():
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X = x_List
Y = y_List
Z = z_List
ax.scatter(X, Y, Z, c='b', marker='o')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
home = Tk()
home.title("Graph")
homeLabel = Label(home, text="Graph Program").grid(row=0, column=1)
home.minsize(600,600)
home.maxsize(600,600)
#entry boxes and labels
intel = StringVar()
person = StringVar()
attractive = StringVar()
label_1 = Label(home, text="X:").grid(row=2, column=0)
label_2 = Label(home, text="Y:").grid(row=3, column=0)
label_3 = Label(home, text="Z:").grid(row=4, column=0)
entry_1 = Entry(home, textvariable=x_Var)
entry_1.grid(row=2, column=1)
entry_2 = Entry(home, textvariable=y_Var)
entry_2.grid(row=3, column=1)
entry_3 = Entry(home, textvariable=z_Var)
entry_3.grid(row=4, column=1)
#buttons
storeButton = Button(home, text="Store", command=store).grid(row=5, column=0)
graphButton = Button(home, text="Graph", command=graph).grid(row=5, column=1)
home.mainloop()