Delete data from canvas in tkinter - python

how do i delete data from a figure when it is used in tktinter via FigureCanvasTkAgg?
I have tried to clear the figure with cla, clf and to delete the canvas with .delete, .clear.
My code: (If you have any advise how i can improve my code let me now. This is my first project in coding)
import tkinter as tk
from tkinter import *
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
#createplot
fig_CDF = Figure(figsize = (6, 5), facecolor = "white")
axis_CDF = fig_CDF.add_subplot(111)
canvas_CDF = FigureCanvasTkAgg(fig_CDF, master = window_main)
canvas_CDF._tkcanvas.pack(side = tk.TOP, fill = tk.BOTH, expand = 1)
canvas_CDF.get_tk_widget().place(x=400,y=50)
#plotdata
axis_CDF.plot(datax, datay, label = "data",marker=".", linestyle = "")
canvas_CDF.draw()
Thanks for your help!

In this code I added three buttons which shows three methods - all of them need canvas_CDF.draw() to redraw element in window.
first replaces data in plot and it removes points from chart but it still show axes.
second clears axis - so it remove data and set axis to 0..1
third clears figure - it removes even axis.
First version needs
plot = axis_CDF.plot(...)
to have access to data.
I use random only to create some data.
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import random
# --- functions ---
def replace_data():
datax = []
datay = []
#datax = range(10)
#datay = [x/10 for x in random.sample(range(10), 10)]
plot[0].set_data(datax, datay)
canvas_CDF.draw()
def clear_axis():
#axis_CDF.clear()
axis_CDF.cla()
canvas_CDF.draw()
def clear_figure():
fig_CDF.clear()
#fig_CDF.clf()
canvas_CDF.draw()
# --- main ---
window_main = tk.Tk()
datax = range(10)
datay = [x/10 for x in random.sample(range(10), 10)]
# create plot
fig_CDF = Figure(figsize=(6, 5), facecolor="white")
axis_CDF = fig_CDF.add_subplot(111)
canvas_CDF = FigureCanvasTkAgg(fig_CDF, master=window_main)
canvas_CDF._tkcanvas.pack(side='top', fill='both', expand=True)
canvas_CDF.get_tk_widget().pack()
# plot data
plot = axis_CDF.plot(datax, datay, label="data", marker=".", linestyle="")
canvas_CDF.draw()
# buttons
b = tk.Button(window_main, text='Replace data', command=replace_data)
b.pack()
b = tk.Button(window_main, text='Clear axis', command=clear_axis)
b.pack()
b = tk.Button(window_main, text='Clear figure', command=clear_figure)
b.pack()
window_main.mainloop()

Related

Tkinter GUI in for loop

I'm doing a tkinter GUI for my program and I have to display some real time data. I made a simple program (below) to demonstrate my problem on a simple case. I'm actually plotting some data every iteration of my for loop so I can observe data while the program in still calculating. Note that the real program si calculating a bit slower and have more iterations.
Now I would like to add 2 buttons (one to pause the program and one to continue) and a label (diplay variable k so i know where my program is), but I am unable to do it.
I've already lost a lot of time on it so if anyone have a hint or a solution i would love to see it.
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import matplotlib.pyplot as plt
from matplotlib import style
def func_A(a, x):
import numpy
data_x = numpy.arange(0, x)
data_y = a * numpy.sin(data_x/5)
return data_x, data_y
a = 1
root = tk.Tk()
root.title("Graph")
root.geometry("800x400")
fig = plt.figure(figsize=(5, 5), dpi=100)
canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea.
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
plt.grid("both")
style.use("ggplot")
for k in range(0, 100):
data_x, data_y = func_A(a, k)
print("iteration", k)
print("data_x", data_x)
print("data_y", data_y)
if k == 0:
ax1 = plt.subplot(111)
line1, = ax1.plot([0], [0])
else:
line1.set_xdata(data_x)
line1.set_ydata(data_y)
ax1.set_ylim([-1, 1])
ax1.set_xlim([0, 100])
plt.grid("both")
canvas.draw()
canvas.flush_events()
root.mainloop()
To add pause/resume function:
create a frame to hold the progress label and the two buttons: pause and resume
create a tkinter BooleanVar() to store the pause/resume state
move the update plot code inside a function, e.g. update_plot()
use .after() to replace the for loop to call update_plot() periodically
inside update_plot(), check the pause/resume state to determine whether to update the plot or not
Below is a modified example based on your code:
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import matplotlib.pyplot as plt
from matplotlib import style
import matplotlib
matplotlib.use("Agg")
root = tk.Tk()
root.title("Graph")
#root.geometry("800x400")
# progress label, pause and resume buttons
frame = tk.Frame(root)
frame.pack(fill="x", side=tk.TOP)
progress = tk.Label(frame)
progress.pack(side="left")
is_paused = tk.BooleanVar() # variable to hold the pause/resume state
tk.Button(frame, text="Pause", command=lambda: is_paused.set(True)).pack(side="right")
tk.Button(frame, text="Resume", command=lambda: is_paused.set(False)).pack(side="right")
# the plot
fig = plt.figure(figsize=(10, 5), dpi=100)
canvas = FigureCanvasTkAgg(fig, master=root)
toolbar = NavigationToolbar2Tk(canvas, root)
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
plt.grid("both")
style.use("ggplot")
a = 1
ax1 = plt.subplot(111)
line1, = ax1.plot([0], [0])
def func_A(a, x):
import numpy
data_x = numpy.arange(0, x)
data_y = a * numpy.sin(data_x/5)
return data_x, data_y
# function to update ploat
def update_plot(k=0):
if not is_paused.get():
progress["text"] = f"iteration: {k}"
data_x, data_y = func_A(a, k)
#print("iteration", k)
#print("data_x", data_x)
#print("data_y", data_y)
line1.set_xdata(data_x)
line1.set_ydata(data_y)
ax1.set_ylim([-1, 1])
ax1.set_xlim([0, 100])
plt.grid("both")
canvas.draw()
canvas.flush_events()
k += 1
if k <= 100:
# update plot again after 10ms. You can change the delay to whatever you want
root.after(10, update_plot, k)
update_plot() # start updating plot
root.mainloop()

Unable to get desired output in a Tkinter window for a line graph

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

Button Not Working in Tkinter Python Application

I am writing a python application using tkinter that plots weather data on a map. I am currently testing out my button that is supposed to plot the data on the map I created using basemap. However, when I run my program, my button does nothing. I defined my function that plots the data and called the function in the command attribute of my button. Anyone know what the problem could be?
import tkinter as tk
from tkinter import ttk
import tkinter.font as tkFont
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
import requests
lon = [-76.5152, -76.7311, -76.3055, -76.7277, -75.4714, -75.1652, -77.0011, -75.6624, -78.3947, -77.8600, -78.7583, -79.9959, -80.0851]
lat = [40.3295, 40.1998, 40.0379, 39.9626, 40.6023, 39.9526, 41.2412, 41.4090, 40.5187, 40.7934, 41.1210, 40.4406, 42.1292]
def current_weather():
key = '4a7a419e4e16e2629a4cedc37cbf7e50'
url = 'https://api.openweathermap.org/data/2.5/weather'
global observation
observation = []
for i in range(0,len(lon)):
params = {'lat': lat[i], 'lon': lon[i], 'appid': key, 'units': 'imperial'}
response = requests.get(url, params = params)
weather = response.json()
observation.append(round(weather['main']['temp']))
current_weather()
root = tk.Tk()
#place map on canvas
fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_subplot(111)
map = Basemap(llcrnrlon=-80.5, llcrnrlat=39.8, urcrnrlon=-74.7, urcrnrlat=42.3,resolution='i',
lat_0 = 40., lon_0 = -80, ax = ax)
map.drawmapboundary(fill_color='#A6CAE0')
map.drawcounties(zorder = 20)
map.drawstates()
map.fillcontinents(color='#e6b800',lake_color='#A6CAE0')
def plot_data():
for i in range(0, len(lon)):
x,y = map(lon[i], lat[i])
ax.text(x, y, observation[i], fontsize = 7)
#create widgets
canvas = FigureCanvasTkAgg(fig, master = root)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
radiovalue = tk.IntVar()
temperature = tk.Radiobutton(root, text = "Plot current temperatures", variable = radiovalue, value = 1)
dewpoint = tk.Radiobutton(root, text = "Plot current dewpoints", variable = radiovalue, value = 2)
wind = tk.Radiobutton(root, text = "Plot current winds", variable = radiovalue, value = 3)
button = tk.Button(root, text = "Plot", command = plot_data)
#organize widgets
temperature.place(relx = 0.025, rely = 0.8)
dewpoint.place(relx = 0.385, rely = 0.8)
wind.place(relx = 0.675, rely = 0.8)
button.place(relx = 0.35, rely = 0.1)
root.mainloop()

Tkinter updating matplotlib figure from different tk.Button in another frame

I'm building a GUI using Tkinter for the first time and have run into a problem updating the data in a Matplotlib Figure using a button in a different frame. Below is some generalized code to show the error I'm getting.
from Tkinter import *
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
class testApp():
def app(self):
self.root = Tk()
self.create_frame1()
self.create_frame2()
self.root.mainloop()
def create_frame1(self):
frame1 = Frame(self.root)
frame1.grid(row=0)
(x, y) = self.create_data()
f = plt.Figure(figsize = (5,2), dpi=100)
a = f.add_subplot(111)
lines1, = a.plot(x, y)
f.tight_layout()
canvas = FigureCanvasTkAgg(f, frame1)
canvas.get_tk_widget().grid()
def create_frame2(self):
frame2 = Frame(self.root)
frame2.grid(row=1)
reEval = Button(frame2, text = "Reevaluate", command = lambda: self.reRand()).grid(sticky = "W")
def reRand(self):
(x, y) = self.create_data()
ax = self.root.frame1.lines1
ax.set_data(x, y)
ax.set_xlim(x.min(), x.max())
ax.set_ylim(y.min(), y.max())
self.root.frame1.canvas.draw()
def create_data(self):
y = np.random.uniform(1,10,[25])
x = np.arange(0,25,1)
return (x, y)
if __name__ == "__main__":
test = testApp()
test.app()
When I run this code, I get an error:
AttributeError: frame1
I think my problem stems from how I am referencing the frame containing the figure, itself, so I'm fairly certain this problem is arising from my lack of Tkinter experience. Any help would be greatly appreciated.
This is more to do with using Python classes than Tkinter. All you really need to do is change all frame1 in create_frame1 to be self.frame1. Similarly in reRand, self.root.frame1 becomes self.frame1.
As it is the name "frame1" doesn't exist beyond the end of create_frame1, but if you save it as an attribute of self you can access it later.

Interactive plot based on Tkinter and matplotlib

Dear programmming communauty,
I am trying to perform a "interactive plot" based on Tkinter and pylab.plot in order to plot 1D values. The abssissa are a 1D numpy array x and the ordonates values are in a multidimension array Y, eg.
import numpy
x = numpy.arange(0.0,3.0,0.01)
y = numpy.sin(2*numpy.pi*x)
Y = numpy.vstack((y,y/2))
I want to display y or y/2 (the elements of Y matrix) according to x and change between them with 2 buttons left and right (in order to go to more complex cases). Usually I create some functions like the following to plot graphs.
import pylab
def graphic_plot(n):
fig = pylab.figure(figsize=(8,5))
pylab.plot(x,Y[n,:],'x',markersize=2)
pylab.show()
To add two buttons to change the value of nparameter, I have tried this without success :
import Tkinter
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class App:
def __init__(self,master):
# Create a container
frame = Tkinter.Frame(master)
frame.pack()
# Create 2 buttons
self.button_left = Tkinter.Button(frame,text="<",command=self.decrease)
self.button_left.pack(side="left")
self.button_right = Tkinter.Button(frame,text=">",command=self.increase)
self.button_right.pack(side="left")
self.canvas = FigureCanvasTkAgg(fig,master=self)
self.canvas.show()
def decrease(self):
print "Decrease"
def increase(self):
print "Increase"
root = Tkinter.Tk()
app = App(root)
root.mainloop()
Can someone help me to understand how to perform such kind of feature ? Many thanks.
To change the y-values of the line, save the object that's returned when you plot it (line, = ax.plot(...)) and then use line.set_ydata(...). To redraw the plot, use canvas.draw().
As a more complete example based on your 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="< Decrease Slope",
command=self.decrease)
self.button_left.pack(side="left")
self.button_right = Tkinter.Button(frame,text="Increase Slope >",
command=self.increase)
self.button_right.pack(side="left")
fig = Figure()
ax = fig.add_subplot(111)
self.line, = ax.plot(range(10))
self.canvas = FigureCanvasTkAgg(fig,master=master)
self.canvas.show()
self.canvas.get_tk_widget().pack(side='top', fill='both', expand=1)
frame.pack()
def decrease(self):
x, y = self.line.get_data()
self.line.set_ydata(y - 0.2 * x)
self.canvas.draw()
def increase(self):
x, y = self.line.get_data()
self.line.set_ydata(y + 0.2 * x)
self.canvas.draw()
root = Tkinter.Tk()
app = App(root)
root.mainloop()

Categories

Resources