I am trying to implement a navigation toolbar that works with my graph that is embedded into Tkinter. When displayed, it looks broken, with the icons pushed into the bottom right corner and the buttons do not work correctly and sometimes get stuck. I am on MacOS BigSur.
Code to replicate:
import tkinter as tk
from PIL import ImageTk, Image
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
root = tk.Tk()
figure = Figure(figsize = (7.5, 5.19), dpi = 100)
line = figure.add_subplot(111)
canvas = FigureCanvasTkAgg(figure, master = root)
canvasToolBar = NavigationToolbar2Tk(canvas, window = root)
canvas.get_tk_widget().pack()
canvasToolBar.pack()
line.plot([0,1,2,3,4,5], [0,1,2,3,4,5], marker='o')
canvas.draw()
root.mainloop()
I have noticed previously that some elements of Tkinter work incorrectly on a Mac system, so is this another problem Mac users will face or is the implementation of the toolbar incorrect?
I tried to connect pyvista and tkinter and i am getting the above error. Here is the code for this. I have checked vtk is installed on my anaconda.
import tkinter
import pyvista
from vtk.tk.vtkTkRenderWindowInteractor import vtkTkRenderWindowInteractor
# Setup for root window
root = tkinter.Tk()
root.title("pyvista tk Demo")
frame = tkinter.Frame(root)
frame.pack(fill=tkinter.BOTH, expand=1, side=tkinter.TOP)
# create an instance of a pyvista.Plotter to be used for tk
mesh = pyvista.Sphere()
pl = pyvista.Plotter()
pl.add_mesh(mesh)
# Setup for rendering window interactor
renwininteract = vtkTkRenderWindowInteractor(root, rw=pl.ren_win,width=400, height=400)
renwininteract.Initialize()
renwininteract.pack(side='top', fill='both', expand=1)
renwininteract.Start()
# Begin execution by updating the renderer and starting the tkinter
# loop
pl.render()
root.mainloop()
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'm trying to make a UI as an input, before closing this and then doing some plotting using pyplot. However, when I try to run a script using tkinter and importing matplotlib.pyplot, the program terminates at root = Tk() in the script, and will not run any commands after this line (including the mainloop).
Is there any way to use both tkinter and matplotlib.pyplot in the same script?
The UI I am running is:
from tkinter import *
class InputSelect(Frame):
def __init__(self, master):
self.master = master
Frame.__init__(self, master, width=200, height=200)
self.fillCanvas()
def fillCanvas(self):
self.canvas = Canvas(self, width=200, height=200)
self.canvas.pack()
self.aButton = Button(self, text="a", height=1, width=15, command=self.buttontest)
self.aButtonWindow = self.canvas.create_window(100, 80, window=self.aButton)
self.pack()
def buttontest(self):
print("buttontest")
def generateInputSelect():
print("test")
root = Tk()
print("test2")
app = InputSelect(root)
app.mainloop()
if __name__ == "__main__":
generateInputSelect()
This runs fine on its own, but if I use it in a separate script:
import ui
import matplotlib.pyplot
ui.generateInputSelect()
The console prints "test" and then closes, it doesn't reach "test2". This separate script runs as expected if I remove import matplotlib.pyplot.
After some discussion and further research, running the program through terminal led me to the error terminating with uncaught exception of type NSException.
I was able to fix this error by setting the TkAgg backend manually using
import matplotlib
matplotlib.use("TkAgg")
from matplotlib import pyplot as plt
as suggested by DonCristobal at Matplotlib Crashing tkinter Application
This issue appears to be related to MacOS, which is why the error wasn't able to be reproduced by some users.
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()