2

I just put together this basic retirement savings calculator using python. While this works, I had a couple of questions:

  1. Is there a way to embed the plot directly, i.e. without saving it as a PNG and then loading it again?
  2. Line 30 reads img.image = render. While I understand that this updates the image attribute for the label defined on line 29, I am confused why this line is required, since we already call out image = render on line 29 itself. Why twice?
from tkinter import *
import pandas as pd
import matplotlib.pyplot as plt
from PIL import ImageTk, Image

def generate_plot():
    rate = float(entry_1.get())
    years_saving = int(entry_2.get())
    initial_savings = float(entry_3.get())
    yearly_contribution = float(entry_4.get())

    model = pd.DataFrame({'time': range(years_saving)})
    model['simple_exp'] = [initial_savings*rate**year for year in model['time']]
    model['yearly_invest'] = model['simple_exp'] + [yearly_contribution*(rate**year - 1)/(rate-1) for year in model['time']]

    final = model['yearly_invest'].sort_values(ascending= False).iloc[0]

    label_5 = Label(frame, text=f"You would have saved INR {final} by retirement")
    label_5.grid(row=0, column=0)

    plt.plot(model['time'], model['yearly_invest'])
    plt.title('Retirement Savings')
    plt.xlabel('Time in Years)')
    plt.ylabel('INR (Lacs)')
    plt.savefig('plot.png')

    load = Image.open('plot.png')
    render = ImageTk.PhotoImage(load)
    img = Label(frame, image = render)
    img.image = render
    img.grid(row=1, column=0)   
    # my_label = Label(frame, image = my_img)
    # my_label.grid(row=1, column=0)

    # img = ImageTk.PhotoImage(Image.open('plot.png'))
    # img_label = Label(frame, image = img)
    # img_label.grid(row=1, column=0)

root = Tk()

label_1 = Label(root, text = 'INTEREST RATE(%)')
label_2 = Label(root, text = 'NUMBER OF YEARS IN SAVINGS')
label_3 = Label(root, text = 'INITIAL CORPUS (INR LACS)')
label_4 = Label(root, text = 'YEARLY CONTRIBUTION (INR LACS')
frame = Frame(root, width=300, height=300)
button = Button(root, text="GENERATE PLOT", command = generate_plot, padx = 5, pady=5)

entry_1 = Entry(root)
entry_2 = Entry(root)
entry_3 = Entry(root)
entry_4 = Entry(root)

label_1.grid(row=0, column=0, pady=5, padx=5)
entry_1.grid(row=0, column=1, pady=5, padx=5)

label_2.grid(row=1, column=0, pady=5, padx=5)
entry_2.grid(row=1, column=1, pady=5, padx=5)

label_3.grid(row=2, column=0, pady=5, padx=5)
entry_3.grid(row=2, column=1, pady=5, padx=5)

label_4.grid(row=3, column=0, pady=5, padx=5)
entry_4.grid(row=3, column=1, pady=5, padx=5)

button.grid(row=4,column=0, columnspan=2, pady=20, padx=5)

frame.grid(row=5, column=0, columnspan = 2, padx = 5, pady = 5)

root.mainloop()

1
  • This line: img.image = render is useless if you aren't going to keep a reference to img. Make img global or add it to a global list Commented May 22, 2021 at 10:28

1 Answer 1

2

You can try saving to a stream using BytesIO:

from tkinter import *
from io import BytesIO
import pandas as pd
import matplotlib.pyplot as plt
from PIL import ImageTk, Image

def generate_plot():
    rate = float(entry_1.get())
    years_saving = int(entry_2.get())
    initial_savings = float(entry_3.get())
    yearly_contribution = float(entry_4.get())

    model = pd.DataFrame({'time': range(years_saving)})
    model['simple_exp'] = [initial_savings*rate**year for year in model['time']]
    model['yearly_invest'] = model['simple_exp'] + [yearly_contribution*(rate**year - 1)/(rate-1) for year in model['time']]

    final = model['yearly_invest'].sort_values(ascending= False).iloc[0]

    label_5 = Label(frame, text=f"You would have saved INR {final} by retirement")
    label_5.grid(row=0, column=0)

    plt.plot(model['time'], model['yearly_invest'])
    plt.title('Retirement Savings')
    plt.xlabel('Time in Years)')
    plt.ylabel('INR (Lacs)')

    img_data = BytesIO()
    plt.savefig(img_data)

    load = Image.open(img_data)
    render = ImageTk.PhotoImage(load)
    img = Label(frame, image = render)
    img.image = render # This is needed to keep a reference to the image, see the link below
    img.grid(row=1, column=0)   
    # my_label = Label(frame, image = my_img)
    # my_label.grid(row=1, column=0)

    # img = ImageTk.PhotoImage(Image.open('plot.png'))
    # img_label = Label(frame, image = img)
    # img_label.grid(row=1, column=0)

root = Tk()

label_1 = Label(root, text = 'INTEREST RATE(%)')
label_2 = Label(root, text = 'NUMBER OF YEARS IN SAVINGS')
label_3 = Label(root, text = 'INITIAL CORPUS (INR LACS)')
label_4 = Label(root, text = 'YEARLY CONTRIBUTION (INR LACS')
frame = Frame(root, width=300, height=300)
button = Button(root, text="GENERATE PLOT", command = generate_plot, padx = 5, pady=5)

entry_1 = Entry(root)
entry_2 = Entry(root)
entry_3 = Entry(root)
entry_4 = Entry(root)

label_1.grid(row=0, column=0, pady=5, padx=5)
entry_1.grid(row=0, column=1, pady=5, padx=5)

label_2.grid(row=1, column=0, pady=5, padx=5)
entry_2.grid(row=1, column=1, pady=5, padx=5)

label_3.grid(row=2, column=0, pady=5, padx=5)
entry_3.grid(row=2, column=1, pady=5, padx=5)

label_4.grid(row=3, column=0, pady=5, padx=5)
entry_4.grid(row=3, column=1, pady=5, padx=5)

button.grid(row=4,column=0, columnspan=2, pady=20, padx=5)

frame.grid(row=5, column=0, columnspan = 2, padx = 5, pady = 5)

root.mainloop()

Reference: Why do my Tkinter images not appear?

Sign up to request clarification or add additional context in comments.

10 Comments

This isn't going to work because img.image goes out of scope so it is deleted by python's garbage collector.
Seems to work when I try it.... Tinker should have a copy of the image data no?
Look at this. Basically if the PhotoImage goes out of scope the image should be deleted in tkinter's world. tkinter doesn't keep a reference to the image
I don't see the issue tbh, it seems to work and the image is already rendered when it goes out of scope, if you can elaborate more I will change the answer.
Yeah so there was no issue at all haha, Ill add a reference to that link :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.