I am trying to create a interactive 3d plot that can be rotated, I was able to do this using the following, however I am unable to reset the orientation when I click on the home button. I tried all the answers posted before me none seem to work.
matplolib version - 3.2.2
import tkinter as tk
from tkinter import ttk
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
np.random.seed(1968001)
def randrange(n, vmin, vmax):
'''
Helper function to make an array of random numbers having shape (n, )
with each number distributed Uniform(vmin, vmax).
'''
return (vmax - vmin) * np.random.rand(n) + vmin
fig = Figure()
# Unable to have rotation otherwise so defined this function
def func():
ax = fig.add_subplot(111, projection='3d')
n = 100
# For each set of style and range settings, plot n random points in the box
# defined by x in [23, 32], y in [0, 100], z in [zlow, zhigh].
for m, zlow, zhigh in [('o', -50, -25), ('^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zlow, zhigh)
ax.scatter(xs, ys, zs, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
top = tk.Tk()
top_frame = ttk.Frame(top)
top_frame.pack()
canv = FigureCanvasTkAgg(fig, master=top_frame)
canv.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
func()
canv.draw()
toolbar = NavigationToolbar2Tk(canv, top_frame)
toolbar.update()
top.mainloop()
By home button this is what I mean
click here
Edit 1: I've changed the plt.figure to figure.Figure as pointed out by Henry Yik, but still, it's not working
Related
I have a tkinter GUI where I'm plotting live data from a sensor using the matplotlib animation. When enough data are collected, a fitting ellipse is calculated. And this ellipse should then be plotted in the same figure as the recorded sensor data.
The original code is a bit lengthy, so here is a piece of working code that shows the problem. Currently the ellipse is drawn in a new window when the animation is stopped by button. Can someone explain me how to add this ellipse to the live plot figure in tkinter GUI?
import tkinter
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib import pyplot as plt, animation
from matplotlib.patches import Ellipse
import numpy as np
plt.rcParams["figure.figsize"] = [7.00, 3.50]
plt.rcParams["figure.autolayout"] = True
root = tkinter.Tk()
root.wm_title("Embedding in Tk")
fig = plt.Figure(dpi=100)
ax = fig.add_subplot(xlim=(0, 2), ylim=(-1, 1))
line, = ax.plot([], [], lw=2)
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
def StopAnimation():
anim.pause()
plotEllipse()
def StartAnimation():
anim.resume()
buttonStop = tkinter.Button(master=root, text="Stop Animation", command=StopAnimation)
buttonStop.pack(side=tkinter.BOTTOM)
buttonStart = tkinter.Button(master=root, text="Start Animation", command=StartAnimation)
buttonStart.pack(side=tkinter.BOTTOM)
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)
def plotEllipse():
ell= Ellipse((1,0), 1, 0.75, angle=45, edgecolor = 'red', facecolor='none', lw = 2)
fig1, ax1 = plt.subplots(1, 1, figsize=(10, 6))
ax1.add_artist(ell)
ax1.set_xlim(0, 2)
ax1.set_ylim(-1, 1)
plt.show()
def init():
line.set_data([], [])
return line,
def animate(i):
x = np.linspace(0, 2, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,frames=200, interval=20, blit=True)
tkinter.mainloop()
i would have expected to plot into the tkinter gui figure with ax.add_artist(ell)
I need the program to display 2 graphs of mathematical functions on the same coordinate line, which the user enters manually into the input window. How can I make this graphs show up in a tkinter window? I heard about using Figure, but then 2 graphs will not be on the same coordinate line
replacements = {
'sin' : 'np.sin',
'cos' : 'np.cos',
'exp': 'np.exp',
'sqrt': 'np.sqrt',
'^': '**',
}
allowed_words = [
'x',
'sin',
'cos',
'sqrt',
'exp',
]
def string2func(string):
for word in re.findall('[a-zA-Z_]+', string):
if word not in allowed_words:
raise ValueError(
'"{}" is forbidden to use in math expression'.format(word)
)
for old, new in replacements.items():
string = string.replace(old, new)
def func(x):
return eval(string)
return func
def create_plot():
a = int(value1.get())
b = int(value2.get())
x = np.linspace(a, b, 1000)
func1 = str(value3.get())
func2 = str(value4.get())
if func1.isnumeric():
func1 = int(value3.get())
func1 = [func1] * 1000
plt.plot(x, func1 , label = 'F1(X)')
else:
func1 = str(value3.get())
strfunc1 = string2func(func1)
plt.plot(x, strfunc1(x), label ='F1(X)')
if func2.isnumeric():
func2 = int(value4.get())
func2 = [func2] * 1000
plt.plot(x, func2, label = 'F2(X)')
else:
func2 = str(value4.get())
strfunc2 = string2func(func2)
plt.plot(x, strfunc2(x), label ='F2(X)')
plt.grid(True)
plt.xlabel('x')
plt.ylabel('y')
plt.title("Simple Plot")
plt.ylim(-50, 50)
plt.show()
You can get axis and use it to draw another line in the same plot - ax.plot().
Matplotlib has special classes to display in different GUIs (tkinter, PyQt, etc.)
Embedding in Tk — Matplotlib 3.6.0 documentation
Example which draws two lines on one plot (in Tkinter) when you press button.
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import numpy as np
# --- functions ---
def draw_plots():
x = np.arange(0, 3.01, .01)
scale = np.random.randint(1, 10)
y1 = scale * np.sin(scale * np.pi * x)
scale = np.random.randint(1, 10)
y2 = scale * np.sin(scale * np.pi * x)
ax.clear() # remove previous plots
#ax.grid(True)
ax.plot(x, y1)
ax.plot(x, y2)
canvas.draw() # refresh window
# --- main ---
root = tk.Tk()
fig = Figure() # Figure(figsize=(5, 4), dpi=100)
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack(fill='both', expand=True) # resize plot when window is resized
ax = fig.add_subplot(111)
#ax.grid(True)
button = tk.Button(root, text='Draw', command=draw_plots)
button.pack()
tk.mainloop()
EDIT:
Example which draws on separated plots
# author: Bartlomiej "furas" Burek (https://blog.furas.pl)
# date: 2022.10.09
# [python - Plots in Tkinter - Stack Overflow](https://stackoverflow.com/questions/73997593/plots-in-tkinter/)
# [Embedding in Tk — Matplotlib 3.6.0 documentation](https://matplotlib.org/stable/gallery/user_interfaces/embedding_in_tk_sgskip.html)
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import numpy as np
# --- functions ---
def draw_plots():
x = np.arange(0, 3.01, .01)
scale = np.random.randint(1, 10)
y1 = scale * np.sin(scale * np.pi * x)
scale = np.random.randint(1, 10)
y2 = scale * np.sin(scale * np.pi * x)
ax1.clear() # remove previous plots
ax2.clear() # remove previous plots
#ax1.grid(True)
#ax2.grid(True)
ax1.plot(x, y1)
ax2.plot(x, y2)
canvas.draw() # refresh window
# --- main ---
root = tk.Tk()
fig = Figure() # Figure(figsize=(5, 4), dpi=100)
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack(fill='both', expand=True) # resize plot when window is resized
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
#ax1.grid(True)
#ax2.grid(True)
button = tk.Button(root, text='Draw', command=draw_plots)
button.pack()
tk.mainloop()
I have one, two, or three plots in a canvas of Tkinter. Let's assume I don't have any Mouse. There only is a keyboard. I want a cursor on these plots that moves at the same horizontal axis on all graphs in the canvas and of course when the Tk window shows up the yellow cursors (mplcursors) on all graphs be at the first point of graphs without any mouse movement and clicks(if you run the following code there are two cursors yellow and green, yellow appears by clicking on the line). The yellow cursors must move together (the same x-axes point) by right/left arrow keys on the keyboard.
from matplotlib.widgets import MultiCursor
import matplotlib.pyplot as plt
import numpy as np
from tkinter import *
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import mplcursors
root = Tk()
fig = Figure(figsize=(8,6))
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
x = np.linspace(-np.pi, np.pi, 256, endpoint=True)
y = np.sin(x)
z = np.cos(x)
ax1.plot(x, y, label="sin function")
ax1.legend(loc="upper right")
ax2.plot(x, z, label="cos function")
canvas = FigureCanvasTkAgg(fig,root)
c1 = mplcursors.cursor(ax1, multiple=False,hover=False,highlight=False,bindings={"toggle_visible": "h", "toggle_enabled": "e","left": "left","right": "right"})
c2 = mplcursors.cursor(ax2, multiple=False,hover=False,highlight=False,bindings={"toggle_visible": "h", "toggle_enabled": "e","left": "left","right": "right"})
multi = MultiCursor(fig.canvas, (ax1, ax2), color='g', lw=0.5, horizOn=True, vertOn=True)
ax2.legend(loc="upper left")
canvas.draw()
#pack canavs
canvas.get_tk_widget().pack(side = BOTTOM, fill=BOTH, expand=True)
#create Navigation Toolbar
toolbar = NavigationToolbar2Tk(canvas, root) #add to canvas
canvas._tkcanvas.pack(side = TOP, fill=BOTH, expand=True)
root.mainloop()
Do you have any suggestions? Can I also make the Multi-Cursor (green lines) to move with mplcursors on plots and stick to them too?
I have tried multi-threading from this link, used mouse controlling from this link and getting pixels of first plot points by this question but I couldn't handle the first display event and it needed the mouse to be clicked on both plots separately to show the cursors.
My plots are more complicated, they are signals and I have multiple pages in my application.
The following code does what I wanted But couldn't solve to keep the green Multi-Cursor on the yellow ones! (Exploring yet)
the following code uses MouseEvent and _process_event which clicks on the plot by default, when calling it you should declare when this process happens?, on which plot, in which points!
from matplotlib.widgets import MultiCursor
import matplotlib.pyplot as plt
import numpy as np
from tkinter import *
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import mplcursors
import subprocess
from matplotlib.backend_bases import KeyEvent, MouseEvent
import copy
def _process_event(name, ax, coords, *args):
ax.viewLim # unstale viewLim.
if name == "__mouse_click__":
# So that the dragging callbacks don't go crazy.
_process_event("button_press_event", ax, coords, *args)
_process_event("button_release_event", ax, coords, *args)
return
display_coords = ax.transData.transform(coords)
if name in ["button_press_event", "button_release_event",
"motion_notify_event", "scroll_event"]:
event = MouseEvent(name, ax.figure.canvas, *display_coords, *args)
elif name in ["key_press_event", "key_release_event"]:
event = KeyEvent(name, ax.figure.canvas, *args, *display_coords)
else:
raise ValueError(f"Unknown event name {name!r}")
ax.figure.canvas.callbacks.process(name, event)
root = Tk()
fig = Figure(figsize=(8,6))
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
x = np.linspace(-np.pi, np.pi, 256, endpoint=True)
y = np.sin(x)
z = np.cos(x)
ax1.plot(x, y, label="sin function")
ax1.legend(loc="upper right")
ax2.plot(x, z, label="cos function")
canvas = FigureCanvasTkAgg(fig,root)
c1 = mplcursors.cursor(ax1, multiple=False,hover=False,highlight=False,bindings={"toggle_visible": "h", "toggle_enabled": "e","left": "left","right": "right"})
c2 = mplcursors.cursor(ax2, multiple=False,hover=False,highlight=False,bindings={"toggle_visible": "h", "toggle_enabled": "e","left": "left","right": "right"})
multi = MultiCursor(fig.canvas, (ax1, ax2), color='g', lw=0.5, horizOn=True, vertOn=True)
ax2.legend(loc="upper left")
_process_event("__mouse_click__", ax1, (x[0], y[0]), 1)
sel, = c1.selections
c1.add_selection(copy.copy(sel))
_process_event("__mouse_click__", ax2, (x[0], z[0]), 1)
sel2, = c2.selections
c2.add_selection(copy.copy(sel2))
canvas.draw()
#pack canavs
canvas.get_tk_widget().pack(side = BOTTOM, fill=BOTH, expand=True)
#create Navigation Toolbar
toolbar = NavigationToolbar2Tk(canvas, root) #add to canvas
canvas._tkcanvas.pack(side = TOP, fill=BOTH, expand=True)
root.mainloop()
On a tkinter canvas I added the matplotlib navigation toolbar and plotted several lines which I want to pick and modify using a function. I also want to be able to zoom into the canvas and select the lines without activating and deactivating the 'zoom to rectangle' everytime I want to excecute the defined function. Is there a way to simultaneously use matplotlib's zoom function and my defined function?
import sys
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
from matplotlib.figure import Figure
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
root = Tk.Tk()
fig = Figure()
ax = fig.add_subplot(111)
canvas = FigureCanvasTkAgg(fig, master=root)
nav = NavigationToolbar2Tk(canvas, root)
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
x = np.arange(10)
ax.plot(x, x, picker=True)
ax.plot(x, 2 * x, picker=True)
ax.plot(x, 3 * x, picker=True)
ax.plot(x, 4 * x, picker=True)
def _onPick(event):
thisline = event.artist
thisline.set_linewidth(5)
fig.canvas.draw()
fig.canvas.callbacks.connect('pick_event', _onPick)
root.mainloop()
You can connect a "button_press_event" and find out yourself if the click occured at a place that is covered by a line. That is slightly more complicated than using the inbuilt picker.
x = np.arange(10)
ax.plot(x, x, picker=6)
ax.plot(x, 2 * x, picker=6)
ax.plot(x, 3 * x, picker=15)
ax.plot(x, 4 * x, picker=1)
def _onPick(event):
update = False
if event.inaxes == ax:
for line in ax.lines:
if line.get_picker():
cont, ind = line.contains(event)
if cont:
line.set_linewidth(5)
update=True
if update:
fig.canvas.draw_idle()
fig.canvas.callbacks.connect('button_press_event', _onPick)
Note that I redefined the picker to state a radius here, which might be useful to make sure one actually hits a line.
I wanted to add some comments outside the plots. The code and figures are d below. The problem is that fig2 essentially the same but with the "text1" and "text2" cut off. Can anybody explain why or provide a better solution of adding texts outside the plot? Thanks a lot!
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
import Tkinter as Tk
root = Tk.Tk()
x = np.arange(0, 10, 1)
y = np.arange(0, 10, 1)
fig1 = plt.figure()
ax = fig1.add_subplot(111)
ax.plot(x,y)
textx = ax.get_xlim()[0]
texty = ax.get_ylim()[0]
ydist = ax.get_yticks()[1] - ax.get_yticks()[0]
ax.text(textx, texty-2*ydist, "text1")
ax.text(textx, texty-3*ydist, "text2")
fig1.show()
fig2 = plt.Figure()
ax2 = fig2.add_subplot(111)
ax2.plot(x,y)
textx = ax2.get_xlim()[0]
texty = ax2.get_ylim()[0]
ydist = ax2.get_yticks()[1] - ax2.get_yticks()[0]
ax2.text(textx, texty-2*ydist, "text1")
ax2.text(textx, texty-3*ydist, "text2")
canvas = FigureCanvasTkAgg(fig2, master=root)
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1.0)
canvas.show()
canvas.draw()
Tk.mainloop()