I wanna update a matplotlib plot in a tkinter GUI. I tried to do so in the following code example.
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import tkinter as tk
import tkinter.ttk as ttk
import sys
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self,master)
self.createWidgets()
def createWidgets(self):
fig=plt.figure(figsize=(8,8))
ax=fig.add_axes([0.1,0.1,0.8,0.8],polar=True)
canvas=FigureCanvasTkAgg(fig,master=root)
canvas.get_tk_widget().grid(row=0,column=1)
canvas.show()
self.plotbutton=tk.Button(master=root, text="plot", command=self.plot)
self.plotbutton.grid(row=0,column=0)
def plot(self):
for line in sys.stdout: #infinite loop, reads data of a subprocess
theta=line[1]
r=line[2]
ax.plot(theta,r,linestyle="None",maker='o')
plt.show(block=False)
plt.pause(0.001)
plt.cla()
#here set axes
root=tk.Tk()
app=Application(master=root)
app.mainloop()
At the moment the problem is, that the ax object is not known in the plot function. If I try plot(self,canvas,ax) the GUI does not open. Only a figure that plots the data.
I wanna plot the data in the figure that is seen in the GUI. At least with a refresh rate round about 3-5Hz.
Cause I am an absolute beginner this code solution is probably not the best way to do so. So I would be happy, if someone could show me a smarter solution.
Thanks!
Ok I could solve it myself. Here the solution:
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import tkinter as tk
import tkinter.ttk as ttk
import sys
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self,master)
self.createWidgets()
def createWidgets(self):
fig=plt.figure(figsize=(8,8))
ax=fig.add_axes([0.1,0.1,0.8,0.8],polar=True)
canvas=FigureCanvasTkAgg(fig,master=root)
canvas.get_tk_widget().grid(row=0,column=1)
canvas.show()
self.plotbutton=tk.Button(master=root, text="plot", command=lambda: self.plot(canvas,ax))
self.plotbutton.grid(row=0,column=0)
def plot(self,canvas,ax):
for line in sys.stdout: #infinite loop, reads data of a subprocess
theta=line[1]
r=line[2]
ax.plot(theta,r,linestyle="None",maker='o')
canvas.draw()
ax.clear()
#here set axes
root=tk.Tk()
app=Application(master=root)
app.mainloop()
Thanks Abishek for taking the time to post the answer to your own problem.
I've just modified your answer a bit so that it runs as a standalone module without needing input from sys.stdout. Also changed the tkinter imports for python 2.7
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import Tkinter as tk # python 2.7
import ttk # python 2.7
import sys
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self,master)
self.createWidgets()
def createWidgets(self):
fig=plt.figure(figsize=(8,8))
ax=fig.add_axes([0.1,0.1,0.8,0.8],polar=True)
canvas=FigureCanvasTkAgg(fig,master=root)
canvas.get_tk_widget().grid(row=0,column=1)
canvas.show()
self.plotbutton=tk.Button(master=root, text="plot", command=lambda: self.plot(canvas,ax))
self.plotbutton.grid(row=0,column=0)
def plot(self,canvas,ax):
c = ['r','b','g'] # plot marker colors
ax.clear() # clear axes from previous plot
for i in range(3):
theta = np.random.uniform(0,360,10)
r = np.random.uniform(0,1,10)
ax.plot(theta,r,linestyle="None",marker='o', color=c[i])
canvas.draw()
root=tk.Tk()
app=Application(master=root)
app.mainloop()
I don't know if it will be useful to anybody, but I updated the code to run with matplotlib 3.1.x+ and python 3.
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
import numpy as np
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self,master)
self.createWidgets()
def createWidgets(self):
fig=plt.figure(figsize=(8,8))
ax=fig.add_axes([0.1,0.1,0.8,0.8],polar=True)
canvas=FigureCanvasTkAgg(fig,master=root)
canvas.get_tk_widget().grid(row=0,column=1)
canvas.draw()
self.plotbutton=tk.Button(master=root, text="plot", command=lambda: self.plot(canvas,ax))
self.plotbutton.grid(row=0,column=0)
def plot(self,canvas,ax):
c = ['r','b','g'] # plot marker colors
ax.clear() # clear axes from previous plot
for i in range(3):
theta = np.random.uniform(0,360,10)
r = np.random.uniform(0,1,10)
ax.plot(theta,r,linestyle="None",marker='o', color=c[i])
canvas.draw()
root=tk.Tk()
app=Application(master=root)
app.mainloop()
Related
I'm getting the following error message when I try to create a bars graph, using python and tkinter:
AttributeError: 'FigureCanvasTkAgg' object has no attribute 'show'
Here's my code
import matplotlib,numpy,sys
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from tkinter import *
root=Tk()
f=Figure(figsize=(5,4),dpi=100)
ax=f.add_subplot(111)
data=(20,35,30,35,27)
ind=numpy.arange(5) # the x locations for the groups
width=.5
rects1=ax.bar(ind,data,width)
canvas=FigureCanvasTkAgg(f,master=root)
canvas.show()
canvas.get_tk_widget().pack(side=TOP,fill=BOTH,expand=1)
root.mainloop()
Simply delete the line that's causing the problem:
import matplotlib,numpy,sys
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from tkinter import Tk,TOP,BOTH
root=Tk()
f=Figure(figsize=(5,4),dpi=100)
ax=f.add_subplot(111)
data=(20,35,30,35,27)
ind=numpy.arange(5) # the x locations for the groups
width=.5
rects1=ax.bar(ind,data,width)
canvas=FigureCanvasTkAgg(f,master=root)
canvas.get_tk_widget().pack(side=TOP,fill=BOTH,expand=1)
root.mainloop()
Or replace .show() with .draw() as #acw1668 mentioned in comment.
I know it is a common problem and I have read all the links related to this error. I realized I have to put matplotlib.use() before pyplot and etc. imports but still it does not work. I am working with jupyter notebook and python 2.7.These are all I have imported:
import Tkinter as tk
from Tkinter import*
import tkMessageBox
import tkFileDialog
import pandas as pd
import matplotlib
matplotlib.use('tkagg')
from PIL import Image, ImageTk
from tkinter import ttk
import pylab as plt
from matplotlib.ticker import (MultipleLocator, FormatStrFormatter,
AutoMinorLocator)
from matplotlib.figure import Figure
import seaborn as sns
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from collections import Counter
try:
from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk as PlotNav
except:
from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg as PlotNav
and this the function that used tkagg in it to provide a navigation toolbar:
def monthly_sells(array,hh):
monse = tk.Toplevel(hh)
array['monthj'] = pd.to_numeric(array['monthj'])
x=array['monthj'].values.tolist()
x.sort(key=int)
f = Figure(figsize=(4,3), dpi=80)
f.add_axes([0.2, 0.15,0.78,0.7])
canvas = FigureCanvasTkAgg(f, master=monse)
canvas.get_tk_widget().grid(row=1, column=3, rowspan=6)
tkagg.PlotNav(canvas, monse)
p = f.gca()
bins=[1,2,3,4,5,6,7,8,9,10,11,12,13]
p.hist(x,bins,rwidth=0.8, label='sales amount in different months of year' )
p.set_xlabel('month', fontsize = 10)
p.set_ylabel('number of orders', fontsize = 10)
This error occurs when I click on the button that uses monthly_sells function as command.
global name 'tkagg' is not defined
Can anyone help me here? The textvariable is not being updated when I add the line:
f = plt.figure(0, figsize=(20,9))
If I comment the line above, then I see the textvariable being updated in the Label, but as soon as I uncomment the line, the Label is not updated anymore.
Can anyone help me, please?
from tkinter import *
from tkinter import ttk
from tkinter import StringVar
import matplotlib
import matplotlib.artist as artists
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.animation as animation
f = plt.figure(0, figsize=(20,9))
class make_window():
def __init__(self, *args, **kwargs):
self.win = Tk()
self.win.title("Test")
self.win.state("zoomed")
Frame = ttk.Frame(self.win)
Frame.pack()
labelVar = StringVar()
labelVar.set("Hi")
self.LabelUpdate = Label(Frame, textvariable = labelVar)
self.LabelUpdate.pack()
#################################################################################
window = make_window()
window.win.mainloop()
I have noticed a few issues that you need to address.
First the way you are importing (from tkinter import *) is going to cause problems and in fact here in this case you make the kind of mistake this import is known for. Later in your code you do Frame = ttk.Frame() and this one line is actually overriding the Frame import from from tkinter import *. Do not name your variables the same as built in methods. In order to prevent this kind of mistake you can simply do import tkinter as tk and always use the tk. prefix to prevent any overriding in your code.
Next I noticed your Label does pack to the screen but does not show any text oddly enough but when I comment out f = plt.figure(0, figsize=(20,9)) the Label starts to work as expected.
So there is some odd interaction here with matplotlib and this label.
When I change f = plt.figure(0, figsize=(20,9)) to f = Figure(figsize=(20,9)) the label also works again.
If Figure() is not exactly what you are trying to use here then please add some more context to your code as to what you are expecting to do with f.
Update:
I did also notice that when you move f to be after self.win = tk.Tk() the code works fine. My only guess as to why this is happening is possible how matplotlib does have its own tkinter code in it. So if you try to create a plt.figure before you create your tkinter instance then I imaging in the back end a tkinter instance is being ran by matplotlib and this is what is causing the odd behavior. I could be wrong but I cannot think of anything else. Maybe the Tkinter master Bryan Oakley can enlighten us as to what is causing this :D
import tkinter as tk
import tkinter.ttk as ttk
import matplotlib
import matplotlib.artist as artists
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.animation as animation
class make_window():
def __init__(self, *args, **kwargs):
self.win = tk.Tk()
# moving the line to here should at least allow your code to work.
f = plt.figure(0, figsize=(20,9))
self.win.title("Test")
self.win.state("zoomed")
frame = ttk.Frame(self.win)
frame.pack()
labelVar = tk.StringVar()
labelVar.set("Hi")
self.LabelUpdate = tk.Label(frame, textvariable = labelVar)
self.LabelUpdate.pack()
window = make_window()
window.win.mainloop()
I've got a program that on launch will open a matplotlib graph of bitcoin prices and will update every time the price changes. Then when I close it a fullscreen tkinter application opens with a background image and a updating clock in the corner. What I would like to happen is when I launch the program a fullscreen tkinter application opens with a background image, an updating clock in the corner, and the matplotlib graph in the middle. Basically I want to embed the matplotlib graph into my application. I've tried putting the graph code into my class and calling it but that just gives the results mentioned earlier. I tried replacing the plt.show() with this:
canvas = FigureCanvasTkAgg(fig, self)
canvas.show()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
but then the program wasn't launching at all. No matter where I put it in the script it would give errors like ''self' is not defined' when I directly replaced plt.show()with it and ''canvas' is not defined' when I tried to integrate it into the class. Here is the full code below:
from tkinter import *
from PIL import ImageTk, Image
import os
import time
import requests
import tkinter as tk
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
from tkinter import ttk
import urllib
import json
import pandas as pd
import numpy as np
from PyQt5 import QtWidgets
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
class App(tk.Tk):
def __init__(self):
# call the __init__ method of Tk class to create the main window
tk.Tk.__init__(self)
# background image
img = ImageTk.PhotoImage(Image.open("0.png"))
panel = Label(self, image=img)
panel.pack()
# clock
self.label = tk.Label(self, text="", font=('comic',50,'bold'),
bg='#464545', fg='#1681BE')
self.label.place(height=204, width=484, x=1384, y=824)
self.update_clock()
# window geometry
w, h = self.winfo_screenwidth(), self.winfo_screenheight()
self.geometry("%dx%d+0+0" % (w, h))
self.overrideredirect(True)
self.mainloop()
def update_clock(self):
now = time.strftime('%H:%M:%S')
self.label.configure(text=now)
self.after(1000, self.update_clock)
def animate(self):
dataLink = 'https://btc-e.com/api/3/trades/btc_usd?limit=2000d'
data = urllib.request.urlopen(dataLink)
data = data.read().decode("utf-8")
data = json.loads(data)
data = data["btc_usd"]
data = pd.DataFrame(data)
buys = data[(data['type']=="ask")]
buys["datestamp"] = np.array(buys["timestamp"]).astype("datetime64[s]")
buyDates = (buys["datestamp"]).tolist()
plot(buyDates, buys["price"])
xlabel('time')
ylabel('Temperature')
title('Temperature Chart')
grid(True)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
app = App()
There are a few dependable's like '0.png' which is the background image and of course the many modules but any feedback would be much appreciated.
plt.show() will open a new dedicated GUI window to host the figure(s) currently present in the matplotlib state machine. If you don't want that, don't use plt.show().
To be on the save side, best don't use pyplot at all as even such innocent commands as plt.xticks() can cause a new window to open.
Also, get rid of from pylab import *. Pylab is a construct which includes several packages like numpy and pyplot in a single namespace. This may be desired for interactive notebook-like applications, but is usually a bad idea for scripts, as it makes debugging very cumbersome. Instead, try to fully stick to matplotlib API commands.
This linked question actually is an example of how to use FigureCanvasTkAgg and NavigationToolbar2Tk to embed a figure into tk.
A better start might be to look at the matplotlib example page where a minimal example for tk embedding is presented.
I am trying to embed a pyplot in a tkinter canvas. I am using python 2.7 on Windows.
I looked around for some example code and I found the following in the matplotlib documentation.
import matplotlib
matplotlib.use('TkAgg')
from numpy import arange, sin, pi
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
# implement the default mpl key bindings
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
import sys
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
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 tk.DrawingArea
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)
def on_key_event(event):
print('you pressed %s'%event.key)
key_press_handler(event, canvas, toolbar)
canvas.mpl_connect('key_press_event', on_key_event)
def _quit():
root.quit() # stops mainloop
root.destroy() # this is necessary on Windows to prevent
# Fatal Python Error: PyEval_RestoreThread: NULL tstate
button = Tk.Button(master=root, text='Quit', command=_quit)
button.pack(side=Tk.BOTTOM)
Tk.mainloop()
# If you put root.destroy() here, it will cause an error if
# the window is closed with the window manager.
However running this code gives me the following error:
from matplotlib.backends import _tkagg
ImportError: cannot import name _tkagg
Does anyone know what the go is here? I've only found people asking about this on python 3 or on another OS.