Embed 3D plot inside Tkinter Window in Python
In this tutorial, you’ll learn how to embed a 3D plot created with Matplotlib inside a Tkinter window.
You’ll use Python Matplotlib 3D plotting capabilities along with the Tkinter library to build a data visualization tool.
Embed Matplotlib Figure in Tkinter
To embed the Matplotlib figure in the Tkinter window, you can use FigureCanvasTkAgg
:
import tkinter as tk from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy as np root = tk.Tk() root.title("3D Plot in Tkinter") root.geometry("800x600") fig = plt.figure(figsize=(6, 4), dpi=100) ax = fig.add_subplot(111, projection='3d') x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x, y) Z = np.sin(np.sqrt(X**2 + Y**2)) surf = ax.plot_surface(X, Y, Z, cmap='viridis') canvas = FigureCanvasTkAgg(fig, master=root) canvas_widget = canvas.get_tk_widget() canvas_widget.pack(fill=tk.BOTH, expand=True) root.mainloop()
Output:
The canvas is then packed to fill the entire window.
To add a navigation toolbar, you can use NavigationToolbar2Tk
:
import tkinter as tk from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy as np root = tk.Tk() root.title("3D Plot in Tkinter") root.geometry("800x600") fig = plt.figure(figsize=(6, 4), dpi=100) ax = fig.add_subplot(111, projection='3d') x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x, y) Z = np.sin(np.sqrt(X**2 + Y**2)) surf = ax.plot_surface(X, Y, Z, cmap='viridis') canvas = FigureCanvasTkAgg(fig, master=root) canvas_widget = canvas.get_tk_widget() canvas_widget.pack(fill=tk.BOTH, expand=True) toolbar = NavigationToolbar2Tk(canvas, root) toolbar.update() canvas_widget.pack(fill=tk.BOTH, expand=True) root.mainloop()
Output:
This code adds a navigation toolbar below the canvas so you can use standard Matplotlib navigation tools like zoom, pan, and save.
Implement Plot Update
To update the plot dynamically, you can create a function that modifies the plot data and redraws the canvas:
import tkinter as tk from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy as np root = tk.Tk() root.title("3D Plot in Tkinter") root.geometry("800x600") fig = plt.figure(figsize=(6, 4), dpi=100) ax = fig.add_subplot(111, projection='3d') x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x, y) Z = np.sin(np.sqrt(X**2 + Y**2)) surf = ax.plot_surface(X, Y, Z, cmap='viridis') canvas = FigureCanvasTkAgg(fig, master=root) canvas_widget = canvas.get_tk_widget() canvas_widget.pack(fill=tk.BOTH, expand=True) toolbar = NavigationToolbar2Tk(canvas, root) toolbar.update() canvas_widget.pack(fill=tk.BOTH, expand=True) def update_plot(): global Z Z = np.sin(np.sqrt(X**2 + Y**2) + np.random.rand()) ax.clear() ax.plot_surface(X, Y, Z, cmap='viridis') canvas.draw() update_button = tk.Button(root, text="Update Plot", command=update_plot) update_button.pack() root.mainloop()
Output:
This code adds an “Update Plot” button that, when clicked, updates the Z values with a random offset and redraws the plot.
Add Additional Tkinter Widgets
You can add sliders to control plot parameters dynamically:
import tkinter as tk from tkinter import ttk from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy as np root = tk.Tk() root.title("3D Plot in Tkinter") root.geometry("800x600") fig = plt.figure(figsize=(6, 4), dpi=100) ax = fig.add_subplot(111, projection='3d') x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x, y) Z = np.sin(np.sqrt(X**2 + Y**2)) surf = ax.plot_surface(X, Y, Z, cmap='viridis') canvas = FigureCanvasTkAgg(fig, master=root) canvas_widget = canvas.get_tk_widget() canvas_widget.pack(fill=tk.BOTH, expand=True) toolbar = NavigationToolbar2Tk(canvas, root) toolbar.update() canvas_widget.pack(fill=tk.BOTH, expand=True) def update_plot(frequency): global Z Z = np.sin(frequency * np.sqrt(X**2 + Y**2)) ax.clear() ax.plot_surface(X, Y, Z, cmap='viridis') canvas.draw() frequency_slider = ttk.Scale(root, from_=0.1, to=2, orient=tk.HORIZONTAL, command=lambda x: update_plot(float(x))) frequency_slider.set(1) frequency_slider.pack() root.mainloop()
Output:
The slider controls the frequency of the sinusoidal function so you can modify the plot.
Zooming and Rotation
Matplotlib 3D plots already support zooming and rotation using the mouse.
However, you can add custom controls for finer adjustments:
import tkinter as tk from tkinter import ttk from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy as np root = tk.Tk() root.title("3D Plot in Tkinter") root.geometry("800x600") fig = plt.figure(figsize=(6, 4), dpi=100) ax = fig.add_subplot(111, projection='3d') x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x, y) Z = np.sin(np.sqrt(X**2 + Y**2)) surf = ax.plot_surface(X, Y, Z, cmap='viridis') canvas = FigureCanvasTkAgg(fig, master=root) canvas_widget = canvas.get_tk_widget() canvas_widget.pack(fill=tk.BOTH, expand=True) toolbar = NavigationToolbar2Tk(canvas, root) toolbar.update() canvas_widget.pack(fill=tk.BOTH, expand=True) def update_view(elev, azim): ax.view_init(elev=elev, azim=azim) canvas.draw() elev_slider = ttk.Scale(root, from_=0, to=90, orient=tk.HORIZONTAL) elev_slider.set(30) elev_slider.pack() azim_slider = ttk.Scale(root, from_=0, to=360, orient=tk.HORIZONTAL) azim_slider.set(45) azim_slider.pack() elev_slider.config(command=lambda x: update_view(float(x), azim_slider.get())) azim_slider.config(command=lambda x: update_view(elev_slider.get(), float(x))) root.mainloop()
Output:
This code adds two sliders to control the elevation and azimuth angles of the 3D plot.
Integrate with Data Sources for Real-time Plotting
You can integrate real-time data sources to update the plot continuously:
import tkinter as tk from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy as np root = tk.Tk() root.title("Real-time 3D Plot in Tkinter") root.geometry("800x600") fig = plt.figure(figsize=(6, 4), dpi=100) ax = fig.add_subplot(111, projection='3d') x = np.linspace(-5, 5, 50) y = np.linspace(-5, 5, 50) X, Y = np.meshgrid(x, y) Z = np.zeros_like(X) surf = ax.plot_surface(X, Y, Z, cmap='viridis') canvas = FigureCanvasTkAgg(fig, master=root) canvas_widget = canvas.get_tk_widget() canvas_widget.pack(fill=tk.BOTH, expand=True) toolbar = NavigationToolbar2Tk(canvas, root) toolbar.update() canvas_widget.pack(fill=tk.BOTH, expand=True) def update_plot(): global Z Z = np.sin(np.sqrt(X**2 + Y**2) + np.random.rand()) ax.clear() ax.plot_surface(X, Y, Z, cmap='viridis') canvas.draw() root.after(100, update_plot) # Schedule the next update update_plot() # Start the update loop root.mainloop()
This code simulates real-time data by updating the plot every 100 milliseconds with new random data.
Save and Load Plots
You can add functionality to save and load plot data:
import tkinter as tk from tkinter import filedialog from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy as np import pickle root = tk.Tk() root.title("3D Plot in Tkinter with Save/Load") root.geometry("800x600") fig = plt.figure(figsize=(6, 4), dpi=100) ax = fig.add_subplot(111, projection='3d') x = np.linspace(-5, 5, 50) y = np.linspace(-5, 5, 50) X, Y = np.meshgrid(x, y) Z = np.sin(np.sqrt(X**2 + Y**2)) surf = ax.plot_surface(X, Y, Z, cmap='viridis') canvas = FigureCanvasTkAgg(fig, master=root) canvas_widget = canvas.get_tk_widget() canvas_widget.pack(fill=tk.BOTH, expand=True) toolbar = NavigationToolbar2Tk(canvas, root) toolbar.update() canvas_widget.pack(fill=tk.BOTH, expand=True) def save_plot(): file_path = filedialog.asksaveasfilename(defaultextension=".pkl", filetypes=[("Pickle files", "*.pkl")]) if file_path: # Retrieve current elevation and azimuth angles elev = ax.elev azim = ax.azim # Save Z data and angles in a dictionary plot_data = {'Z': Z, 'elev': elev, 'azim': azim} with open(file_path, 'wb') as file: pickle.dump(plot_data, file) def load_plot(): file_path = filedialog.askopenfilename(filetypes=[("Pickle files", "*.pkl")]) if file_path: with open(file_path, 'rb') as file: plot_data = pickle.load(file) global Z Z = plot_data['Z'] elev = plot_data['elev'] azim = plot_data['azim'] ax.clear() ax.plot_surface(X, Y, Z, cmap='viridis') # Restore the elevation and azimuth angles ax.view_init(elev=elev, azim=azim) canvas.draw() save_button = tk.Button(root, text="Save Plot", command=save_plot) save_button.pack(side=tk.LEFT) load_button = tk.Button(root, text="Load Plot", command=load_plot) load_button.pack(side=tk.LEFT) root.mainloop()
Output:
This code adds “Save Plot” and “Load Plot” buttons to the Tkinter window.
The save_plot
function now retrieves the current elevation and azimuth angles using ax.elev
and ax.azim
.
It stores these angles along with the Z
data in a dictionary, which is then pickled and saved to a file.
The load_plot
function loads the dictionary from the file, extracts the Z
data, elevation, and azimuth angles, and updates the plot.
It uses ax.view_init(elev=elev, azim=azim)
to restore the view angles.
Mokhtar is the founder of LikeGeeks.com. He is a seasoned technologist and accomplished author, with expertise in Linux system administration and Python development. Since 2010, Mokhtar has built an impressive career, transitioning from system administration to Python development in 2015. His work spans large corporations to freelance clients around the globe. Alongside his technical work, Mokhtar has authored some insightful books in his field. Known for his innovative solutions, meticulous attention to detail, and high-quality work, Mokhtar continually seeks new challenges within the dynamic field of technology.