t.o.
t.o.

Reputation: 879

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?

Upvotes: 0

Views: 255

Answers (1)

TheLizzard
TheLizzard

Reputation: 7680

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.

Upvotes: 1

Related Questions