pythontkintersqlalchemypython-imaging-libraryphotoimage

Using a generated image in a tkinter GUI with PIL


I am trying to use a tkinter button to display a ERD diagram using sqlalchemy and PIL. I have managed to get this to work by saving the picture generated to a file and then re-opening that file in order to display in a label.

Is there a way to get the image to display without having to save it first?

import tkinter as tk
from sqlalchemy_schemadisplay import create_schema_graph
import urllib
from sqlalchemy import MetaData, create_engine
from PIL import Image, ImageTk 

root = tk.Tk()

def erd_gen(pr):
    global erd_img
    conn = create_engine("mssql+pyodbc:///?odbc_connect={}".format(pr))
    grph_data = MetaData(bind=conn)
    graph = create_schema_graph(metadata=grph_data, show_datatypes=False, show_indexes=False, rankdir='LR', concentrate=False)
    graph.write_png('dbschema.png') # write file to folder 
    erd_img = ImageTk.PhotoImage(Image.open("dbschema.png")) #open file from folder, would like to do this by just referring to 'graph' and not the saved file
    panel.configure(image = erd_img)
    conn.close()


params = urllib.parse.quote_plus("DRIVER={SQL Server Native Client 11.0};"
                                    "SERVER=(localdb)\ProjectsV13;"
                                    "DATABASE=Test;"
                                    "Trusted_Connection=yes")  

tk.Button(root, text='Generate', command=lambda: erd_gen(params)).pack()

panel = tk.Label(root)
panel.pack()

root.mainloop()

Solution

  • You can use create_png() instead of write_png() to create a PNG image data buffer, then use io.BytesIO to simulate a file input stream:

    from io import BytesIO
    
    def erd_gen(pr):
        global erd_img
        conn = create_engine("mssql+pyodbc:///?odbc_connect={}".format(pr))
        grph_data = MetaData(bind=conn)
        graph = create_schema_graph(metadata=grph_data, show_datatypes=False, show_indexes=False, rankdir='LR', concentrate=False)
        iostream = BytesIO(graph.create_png())
        erd_img = ImageTk.PhotoImage(file=iostream)
        panel.configure(image=erd_img)
        conn.dispose()