Graphs in Canvas - python

I am trying to show up the line graphs (Strings and Numbers )as shown in the array) in a Canvas. I got this code from different questions in this forums, trying to modify for my requirements.Can some one please guide me.
import matplotlib.pyplot as plt
import pandas as pd
df = pd.DataFrame({"Week": ['Week1','Week2','Week3','Week4','Week5'],
"App1" : [2.6,3.4,3.25,2.8,1.75],
"App2" : [2.5,2.9,3.0,3.3,3.4],
"App3" : [1.6,2.4,1.25,5.8,6.75]})
df.plot(x="Week", y=["App1", "App2", "App3"])
plt.show()

Here is the one, in case if someone else is looking.
# --- matplotlib ---
import matplotlib
matplotlib.use('TkAgg') # choose backend
from tkinter import messagebox
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,
NavigationToolbar2TkAgg
from matplotlib.pyplot import Figure
from matplotlib import pyplot as plt
# --- other ---
import tkinter as tk
import pandas as pd
# --- GUI ---
root = tk.Tk()
# top frame for canvas and toolbar - which need `pack()` layout manager
top = tk.Frame(root)
top.pack()
# bottom frame for other widgets - which may use other layout manager
bottom = tk.Frame(root)
bottom.pack()
# create figure
fig = matplotlib.pyplot.Figure()
# create matplotlib canvas using `fig` and assign to widget `top`
canvas = FigureCanvasTkAgg(fig, top)
# get canvas as tkinter widget and put in widget `top`
canvas.get_tk_widget().pack()
canvas._tkcanvas.pack()
# --- plot ---
data = {"Week": ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],
"App1" : [2.6,3.4,3.25,2.8,1.75,5,2],
"App2" : [2.5,2.9,3.0,3.3,3.4,5,3],
"App3" : [1.6,15,1.25,5.8,6.75,6,4]
}
new_df = pd.DataFrame(data)
ax = fig.add_subplot(111)
fig.suptitle('Graph Title', fontsize=12)
new_df.plot(x="Week", y=["App1", "App2", "App3"],ax=ax)
def on_closing():
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.lift()
root.attributes('-topmost',True)
root.mainloop()

Related

Deleting matplotlib plot /tkinter canvas from window

I was wondering how you can completely delete a plot from a tkinter window.
Assuming i would have a tkinter project like the following:
import tkinter as tk
from pandas import DataFrame
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
data1 = {'Country': ['US','CA','GER','UK','FR'],
'GDP_Per_Capita': [45000,42000,52000,49000,47000]
}
df1 = DataFrame(data1,columns=['Country','GDP_Per_Capita'])
root= tk.Tk()
figure1 = plt.Figure(figsize=(6,5), dpi=100)
ax1 = figure1.add_subplot(111)
bar1 = FigureCanvasTkAgg(figure1, root)
bar1.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
df1 = df1[['Country','GDP_Per_Capita']].groupby('Country').sum()
df1.plot(kind='bar', legend=True, ax=ax1)
ax1.set_title('Country Vs. GDP Per Capita')
def close_plot():
plt.close(figure1)
button_delete = Button(root, text='Delete', command = lambda: close_plot()).place(height=30, width = 100, rely=0.02, relx = 0.4)
root.mainloop()
I veen trying to use matplotlib.pyplot.close within a button but it doens't seem to work.
Anyone have a clue how to get rid of this plot.
Thank you very much!
Your figure is embedded in a tkinter widget. You need to keep a reference to that widget, and use its methods to add/remove it from the tkinter window:
Here we use pack_forget(): the widget is removed from the window, but still exists, and could be reused later.
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
def remove_plot():
w.pack_forget() # here you remove the widget from the tk window
# w.destroy()
if __name__ == '__main__':
# data
x, y = [1, 2, 3, 4], [1, 4, 9, 16]
# matplotlib stuff
figure1 = plt.Figure(figsize=(6, 5), dpi=100)
ax1 = figure1.add_subplot(111)
ax1.plot(x, y)
ax1.set_title('Country Vs. GDP Per Capita')
# tkinter stuff
root = tk.Tk()
bar1 = FigureCanvasTkAgg(figure1, root)
w = bar1.get_tk_widget()
w.pack(side=tk.LEFT, fill=tk.BOTH) # here you insert the widget in the tk window
button_delete = tk.Button(root, text='Remove Plot', command=remove_plot)
button_delete.place(height=30, width=100, rely=0.02, relx=0.4) # place is an odd choice of geometry manager, you will have to adjust it every time the title changes
root.mainloop()
Alternatively, if you don't need that figure any longer, you could use w.destroy (commented line) to destroy the widget.

Tkinter with matplotlib - Heatmap colorbar not clearing with rest of axes

I'm building a GUI using tkinter and matplotlib (and seaborn) to show a heatmap from a user chosen csv. I want the heatmap to update each time it's loaded, with the appropriate colorbar. I clear my axes each time I load new data in, but the colorbar never goes away, and the new heatmap squishes off to the side. I want the old colorbar to be cleared as well so the new heatmap can fill the space properly.
I made a MWE to show off my problem:
import numpy as np
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import seaborn as sns
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.state('zoomed')
self.winfo_toplevel().title('App')
frame = tk.Frame(self)
frame.pack()
button_reload = tk.Button(frame, text='Reload data', command=self.reload_data)
button_reload.pack()
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
self.fig.tight_layout() # Small margins
self.ax.axis('off') # Disable axis lines
self.canvas_heatmap = FigureCanvasTkAgg(self.fig, master=frame)
self.canvas_heatmap.get_tk_widget().pack(expand=True, fill='both')
def reload_data(self):
# dummy data for example
data = np.random.rand(3,3)
# Clear old heatmap from axes
self.ax.clear()
# Set up new heatmap
self.ax = sns.heatmap(data, ax=self.ax, linewidth=0.1)
self.canvas_heatmap.draw()
self.canvas_heatmap.get_tk_widget().pack(expand=True, fill='both') # necessary?
def quit_GUI():
root.quit()
root.destroy()
if __name__ == '__main__':
root = App()
root.protocol('WM_DELETE_WINDOW', quit_GUI) # Kill process on clicking 'X'
root.mainloop()
Here are some photos where you can see the colorbars sticking around when I don't want them to.
GOOD SO FAR:
BAD:
WORSE:
I could keep going like this until my heatmap is a sliver.
You need to clear the figure and remake the ax.
import numpy as np
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import seaborn as sns
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.state('zoomed')
self.winfo_toplevel().title('App')
frame = tk.Frame(self)
frame.pack()
button_reload = tk.Button(frame, text='Reload data', command=self.reload_data)
button_reload.pack()
self.fig = Figure()
self.canvas_heatmap = FigureCanvasTkAgg(self.fig, master=frame)
self.canvas_heatmap.get_tk_widget().pack(expand=True, fill='both')
def reload_data(self):
data = np.random.rand(3,3)
self.fig.clear()
ax = self.fig.add_subplot(111)
ax.axis('off') # Disable axis lines
line = sns.heatmap(data, ax=ax, linewidth=0.1)
self.fig.tight_layout() # Should go after the drawing
self.canvas_heatmap.draw()
# ~ self.canvas_heatmap.get_tk_widget().pack(expand=True, fill='both') # not necessary
if __name__ == '__main__':
root = App()
# ~ root.protocol('WM_DELETE_WINDOW', quit_GUI) # not needed
root.mainloop()

How can I update a heatmap on a GUI when I change the input values

I am trying to display a GUI with a heatmap and scales/sliders with the scales/sliders changing the values in the heatmap.
I can display the heatmap and sliders and can read from the sliders but I cannot get the heat map to update after I have moved the sliders.
I have tried putting the code (I think) updates the heatmap in a function which is called whenever the scale/slider is moved but I am clearly missing something.
import tkinter
from tkinter import ttk
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
def myFunc(value):
print (mySlider.get())
array[1][2]=mySlider.get()
#I think I need to put something here to update the heatmap when the
#scale/slider is changed but do not know what
figure, ax = plt.subplots()
ax.imshow(array)
canvas.get_tk_widget().pack()
root = tkinter.Tk()
root.title("Something")
array = ([[1,2,3,4],
[3,9,1,5],
[8,4,1,7],
[2,4,9,1]])
figure, ax = plt.subplots()
ax.imshow(array)
canvas = plt.Figure()
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().pack()
mySlider = tkinter.Scale(root, from_=0, to=15, orient=HORIZONTAL, command=myFunc)
mySlider.pack()
Like this:
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
def myFunc(value):
print (mySlider.get())
array[1][2]=mySlider.get()
im.set_array(array)
canvas.draw()
root = tk.Tk()
root.title("Something")
array = ([[1,2,3,4],
[3,9,1,5],
[8,4,1,7],
[2,4,9,1]])
figure, ax = plt.subplots()
im = ax.imshow(array)
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().pack()
mySlider = tk.Scale(root, from_=0, to=15, orient=tk.HORIZONTAL, command=myFunc)
mySlider.pack()
root.mainloop()
However tkinter is not needed here. matplotlib has a slider built in (I assume you know since you imported it) which is a lot easier to implement:
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
def myFunc(value):
array[1][2]=value
im.set_array(array)
array = ([[1,2,3,4],
[3,9,1,5],
[8,4,1,7],
[2,4,9,1]])
figure, ax = plt.subplots()
im = ax.imshow(array)
ax_slider = plt.axes([0.1, 0.1, 0.8, 0.03]) # [left, bottom, width, height]
slide = Slider(ax_slider, '', 0, 15, valinit=0)
slide.on_changed(myFunc)
plt.show()

Matplot/Pandas X-axis is not showing for all the values

I ran this program. For some reason I am not getting all the X-axis values. Tried to change the data still no printing.
import matplotlib.pyplot as plt
import pandas as pd
import tkinter as tk
import pandas as pd
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from pandas import Timestamp
import sys
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
root = Tk.Tk()
top = Tk.Frame(root)
top.pack()
bottom = Tk.Frame(root)
bottom.pack()
fig = plt.Figure()
canvas = FigureCanvasTkAgg(fig, top)
canvas.get_tk_widget().pack()
canvas._tkcanvas.pack()
df = pd.read_csv("../data.csv")
ax = fig.add_subplot(111)
fig.suptitle('App Server CPU%', fontsize=12)
df['date'] = pd.to_datetime(df['date'], format='%Y/%m/%d %H:%M:%S')
df = df.set_index('date')
grouped = df.groupby(['server'])
for key, group in grouped:
group['utilization'].plot(label=key, ax=ax)
plt.legend(loc='best')
def on_closing():
#if messagebox.askokcancel("Quit", "Do you want to quit?"):
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.lift()
root.attributes('-topmost',True)
root.mainloop()
date,server,utilization
2004-08-19,App1,105.01
2004-08-20,App1,106.06
2004-08-23,App1,116.3
2004-08-24,App1,116.8
2004-08-25,App1,110
2004-08-26,App1,110.2
2004-08-27,App1,113.51
2004-08-19,App2,126.01
2004-08-20,App2,127.27
2004-08-23,App2,139.56
2004-08-24,App2,140.16
2004-08-25,App2,132
2004-08-26,App2,132.24
2004-08-27,App2,136.21
2004-08-19,App3,157.52
2004-08-20,App3,159.09
2004-08-23,App3,174.45
2004-08-24,App3,175.2
2004-08-25,App3,165
2004-08-26,App3,165.3
2004-08-27,App3,170.26

Close a tkinter progressbar window while matplotlib window is open

I am writing a program that does some work and uses matplotlib to plot some data. This can take some time so I set up a progressbar using tkinter. Threading with tkinter was not that easy. I am running the progressbar in the main thread and my working stuff in a substhread. However I can not close the progressbar window after my work is done, because apparently matplotlib does something in the tk root window. I don't know what. I added a minimal example of what I am trying to do. Notice that removing the line "plotsomething()" makes it do what I want: close the progressbar after work is done.
Can you please help me figuring out how I can close the progressbar window without closing the matplotlib windows?
# coding = utf-8
import numpy as np
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import ttk
import threading, queue
import time
def MAIN():
PB = q.get()
for i in np.arange(10):
time.sleep(0.2)
print(i)
PB.step(10)
PB.update()
print("Done")
def plotsomething():
x = np.linspace(0,10,100)
y = np.sin(x)
plt.plot(x,y)
root = tk.Tk()
root.title("Progress")
PB = ttk.Progressbar(root, orient = "horizontal",length=300, mode = 'determinate')
PB.pack()
q = queue.Queue()
q.put(PB)
plotsomething()
T = threading.Thread(target=MAIN(), name="MAIN")
T.start()
T.join()
plt.show()
EDIT - SOLUTION: I am solving the problem now by drawing every window seperatly by using the matplotlib tk backend. Apparently PyPlot is interfering with the tkinter root windows. See tcaswell's comment for more details and hints. Thank you very much!
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
from numpy import arange, sin, pi
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import tkinter as tk
from tkinter import ttk
import queue, threading, time
def center_window(window_parent, w=300, h=20):
# get screen width and height
ws = window_parent.winfo_screenwidth()
hs = window_parent.winfo_screenheight()
# calculate position x, y
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
window_parent.geometry('%dx%d+%d+%d' % (w, h, x, y))
def MAIN():
PB = q.get()
for i in np.arange(10):
time.sleep(0.2)
print(i)
PB.step(10)
PB.update()
print("Done")
root = tk.Tk()
root.wm_title("Embedding in TK")
f = Figure(figsize=(5,4), dpi=100)
a = f.add_subplot(111)
t = arange(0.0,3.0,0.01)
s = sin(2*pi*t)
a.plot(t,s)
a.set_title('Tk embedding')
a.set_xlabel('X axis label')
a.set_ylabel('Y label')
#a tk.DrawingArea
root2 = tk.Tk()
PB = ttk.Progressbar(root2, orient = "horizontal",length=300, mode = 'determinate')
PB.pack()
canvas = FigureCanvasTkAgg(f, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
toolbar = NavigationToolbar2TkAgg( canvas, root )
toolbar.update()
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
root2.iconify()
root2.update()
root2.deiconify()
center_window(root2)
q = queue.Queue()
q.put(PB)
T = threading.Thread(target=MAIN(), name="MAIN")
T.start()
T.join()
root2.quit()
root2.destroy()
tk.mainloop()
You are getting conflicting gui-main loops between the TK you are starting and the TK that plt is start. If you want to use matplotlib with your own gui, you must embed it your self and you can not import pyplot. All the behind the scenes magic that makes the pyplot interface wonderful is what is messing you up here.
For a tutorial see here, for how mpl does the embedding see here.
Also see:
Unable to save matplotlib.figure Figure, canvas is None
Python Matplotlib runtime error upon closing the console
Matplotlib bar chart in a wx Frame instead of a new window

Categories