Search code examples
pythonpython-3.xmatplotlibtkinterembed

How to imbed figure object when plotting occurs outside tkinter environment


There are plenty of web examples (1,2,3,4) and threads (1,2,3) about imbedding a plot into a tkinter window, but very few that address plotting in a separate environment and importing the resulting graph to the tkinter window.

In a nutshell, I have a program that calculates many different values, and exports those values to a separate file that creates a large number of plots. My tkinter application accepts parameters in Entry boxes, before applying them to the main file that does all the calculations. Typically, I would just follow the examples I linked, but with such a large number of plots being generated and the need to be able to select any particular graph I need at a given time, this would be inefficient and time consuming to brute-force. There must be a better way!

Below is a simplified example of how I am trying to accomplish this task:


import tkinter as tk
from matplotlib import pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, 
NavigationToolbar2Tk)

import numpy as np


def example_plot(A):
    # Plot generated outside of tkinter environment, but controlled by 
    # variable within tkinter window.
    x = np.linspace(0, 10, 50)
    y = A*x**2
    
    fig, ax = plt.subplots()
    ax.plot(x,y)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    
    return fig


window = tk.Tk()
window.geometry('256x256')
variableEntry = tk.Entry(width = 10)
variableLabel = tk.Label(window, text = "A")


variableEntry.grid(row = 0, column = 0)
variableLabel.grid(row = 0, column = 1)


def plotButton():
    A = variableEntry.get()
    A = int(A)
    
    figure = Figure(figsize = (1,1), dpi = 128)
    add = figure.add_subplot(1,1,1)
    
    example = example_plot(A)
    
    add.imshow(example)
    
    canvas = FigureCanvasTkAgg(figure)
    canvas.get_tk_widget().grid(row = 2, column = 0)
    
    toolbar = NavigationToolbar2Tk(canvas)
    toolbar.update()
    canvas._tkcanvas.grid(row = 3 , column = 0)
    canvas.show()
    

applyButton = tk.Button(master = window, text = "Apply", command = plotButton)
applyButton.grid(row = 1,column = 0)

window.mainloop()

When I run this, set A to some integer and press apply, I get an error

TypeError: Image data of dtype object cannot be converted to float

It seems that add.imshow() doesn't like that I fed it the figure. Is there some way to obtain the figure (ie: example = example_plot(A)) and store it to display later?


Solution

  • Try this:

    import tkinter as tk
    from matplotlib import pyplot as plt
    from matplotlib.figure import Figure
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, \
                                                  NavigationToolbar2Tk
    
    import numpy as np
    
    
    def example_plot(A):
        # Plot generated outside of tkinter environment, but controlled by 
        # variable within tkinter window.
        x = np.linspace(0, 10, 50)
        y = A*x*x # This should run slightly faster
    
        fig, ax = plt.subplots()
        ax.plot(x,y)
        ax.set_xlabel("x")
        ax.set_ylabel("y")
        
        return fig
    
    
    window = tk.Tk()
    frame = tk.Frame(window)
    frame.pack()
    variable_entry = tk.Entry(frame, width=10)
    variable_label = tk.Label(frame, text="A")
    
    
    variable_entry.pack(side="left", fill="x")
    variable_label.pack(side="left")
    
    
    def plot():
        A = int(variable_entry.get())
        
        figure = Figure(figsize=(1, 1), dpi=128)
        add = figure.add_subplot(1, 1, 1)
        
        figure = example_plot(A)
        
        canvas = FigureCanvasTkAgg(figure)
        canvas.get_tk_widget().pack()
        
        toolbar = NavigationToolbar2Tk(canvas, window)
        toolbar.update()
        canvas.get_tk_widget().pack()
        # canvas.show() # There is no need for this
        
    
    apply_button = tk.Button(window, text="Apply", command=plot)
    apply_button.pack(fill="x")
    
    window.mainloop()
    

    Your example_plot returns a Figure so you can use figure = example_plot(A) and then FigureCanvasTkAgg(figure). I also added a frame and tried to make everything look better.