1

I am using Tkinter, Python and PIL to attempt to create a simple photo editing application.

My troubles currently are in the "add_text_function" all the way at the bottom. I am editing an image using PIL ImagdeDraw.Draw and .text to add text to the image.

However, when I try to load the edited image onto the canvas it does not work :( If I do a simple current_image.show() then it will show the edits made but in a another window. I'd like for it to update my canvas so everything takes place in the application. The image_container that I am trying to update can be found in the edit_screen function and is a global variable.

Your help would be greatly appreciated. Please find my code pasted below:

from tkinter import *
from tkinter.filedialog import askopenfilename, asksaveasfilename
from PIL import Image, ImageTk, ImageFont, ImageDraw, ImageFilter

# GLOBAL VARIABLES
# INITIALIZE WINDOW
home_window = Tk()
# INITIALIZE IMAGE STORAGE VARIABLE
all_files = list()
# INITIALIZE IMAGE BUTTON COUNT VARIABLE
add_images_button_count = 0
# INITIALIZE X AND Y POSITION FOR IMAGE PLACEMENT
x = 0
y = 0
# INITIALIZE MAX IMAGE COUNT VARIABLE
max_image = []
# CREATE CANVAS
canvas = Canvas(home_window, width=600, height=400, bg="white")
canvas.place(rely=0.1)
# CURRENT IMAGE
current_image = None
# IMAGE COUNTER
image_counter = 0
# IMAGE CONTAINER FOR CANVAS
image_container = None


# HOME SCREEN
def home_screen():
    # REFERENCE GLOBAL WINDOW
    global home_window

    # SPECIFY WINDOW SIZE
    home_window.geometry("600x450")

    # SPECIFY WINDOW TITLE
    home_window.title("Foto Editor")

    # CREATES TOP FRAME AND DOESN'T ALLOW WINDOW SHRINKAGE
    header_frame = Frame(home_window, width=600, height=50, bd=5, bg="grey")
    header_frame.grid(row=0)

    # BUTTONS
    close_app = Button(header_frame, text="Close App", bg="white", fg="blue", command=home_window.destroy)
    close_app.place(relx=0.015, rely=0.2, anchor="nw")

    back = Button(header_frame, text="Back", bg="white", fg="blue", state="disabled")
    back.place(relx=0.13, rely=0.2, anchor="nw")

    add_images = Button(header_frame, text="Add Images", bg="white", fg="blue", command=add_button)
    add_images.place(relx=0.45, rely=0.2, anchor="n")

    clear = Button(header_frame, text="Clear", bg="white", fg="blue", command=clear_button)
    clear.place(relx=0.55, rely=0.2, anchor="n")

    next_step = Button(header_frame, text="Next Step", bg="blue", fg="white", command=next_step_button)
    next_step.place(relx=0.985, rely=0.2, anchor="ne")

    # RUN A NON RESIZEABLE TKINTER WINDOW
    home_window.resizable(False, False)
    home_window.mainloop()


# PHOTO EDITING SCREEN
def edit_screen():
    # REFERENCE GLOBAL WINDOW
    global home_window, current_image, image_counter, all_files, image_container, canvas

    # CLEAR PREVIOUS WINDOW CANVAS
    clear_screen()

    # SPECIFY WINDOW SIZE
    home_window.geometry("600x450")

    # CREATES TOP FRAME AND DOESN'T ALLOW WINDOW SHRINKAGE
    header_frame = Frame(home_window, width=600, height=50, bd=5, bg="grey")
    header_frame.grid(row=0)

    # BUTTONS
    close_app = Button(header_frame, text="Close App", bg="white", fg="blue", command=home_window.destroy)
    close_app.place(relx=0.015, rely=0.2, anchor="nw")

    back = Button(header_frame, text="Back", bg="white", fg="blue", command=back_button)
    back.place(relx=0.13, rely=0.2, anchor="nw")

    add_text = Button(header_frame, text="Add Text", bg="white", fg="blue", command=add_text_button)
    add_text.place(relx=0.4, rely=0.2, anchor="nw")

    clear = Button(header_frame, text="Clear", bg="white", fg="blue")
    clear.place(relx=0.605, rely=0.2, anchor="n")

    save = Button(header_frame, text="Save", bg="blue", fg="white", command=save_button)
    save.place(relx=0.985, rely=0.2, anchor="ne")

    # LOGIC TO DISPLAY EACH PICTURE
    # ACCESS CURRENT IMAGE
    f = all_files[image_counter]
    # OPEN AS IMAGE
    current_image = Image.open(f)
    # TK PHOTO
    resized_img = ImageTk.PhotoImage(current_image.resize((500, 400)))
    # PLACE IMAGE ON CANVAS
    image_container = canvas.create_image(300, 210, image=resized_img)

    # RUN A NON RESIZEABLE TKINTER WINDOW
    home_window.resizable(False, False)
    home_window.mainloop()


# BACK BUTTON
def back_button():
    # REFERENCE GLOBAL VARIABLES
    global canvas
    # CLEAR IMAGES
    for child in canvas.winfo_children():
        child.destroy()
    # LOAD HOME SCREEN
    home_screen()


# ADD IMAGES BUTTON
def add_button():
    # BUTTON COUNTER
    global home_window, canvas, add_images_button_count, x, y, max_image, all_files
    # SPECIFY FILE TYPES
    filetypes = [('JPG Files', '*.jpg'), ('PNG Files', '*.png')]
    # OPEN FILE EXPLORER AND ALLOW USER TO SELECT PHOTO
    filename = askopenfilename(multiple=True, filetypes=filetypes)
    # STORE ALL FILES
    all_files.extend(filename)
    # APPEND NEW FILE TO MAX IMAGE COUNTER
    max_image.append(filename)
    # CHECK IF MAX IMAGE AMOUNT OF 6 IS REACHED, INFORM USER
    if len(max_image) > 6:
        max_image_warning()
        return
    # FIRST CLICK
    if add_images_button_count == 0:
        # START POSITION FOR IMAGE
        x = 0.1
        y = 0.25
        # INCREMENT BUTTON COUNTER
        add_images_button_count += 1
    # NOT FIRST CLICK
    else:
        # IMAGE POSITIONING LOGIC
        # START NEW ROW AFTER THIRD COLUMN
        if x == 0.7:
            x = 0.1
            y = 0.6
        # ELSE CONTINUE TO INCREMENT COLUMN
        else:
            x += 0.3
    # MULTIPLE IMAGES METHOD
    for f in filename:
        # OPEN AS IMAGE
        img = Image.open(f)
        # RESIZE IMAGE
        img_resized = img.resize((100, 100))
        # TK PHOTO
        img = ImageTk.PhotoImage(img_resized)
        # CREATE LABEL TO DISPLAY IMAGE
        panel = Label(canvas)
        # IMAGE POSITION
        panel.place(relx=x, rely=y)
        # ASSIGN IMAGE TO LABEL DIRECTLY
        panel.image = img
        # GARBAGE COLLECTION
        panel['image'] = img


# CLEAR BUTTON
def clear_button():
    # REFERENCE GLOBAL VARIABLE
    global canvas, add_images_button_count, all_files
    # RESET ALL FILES VARIABLE
    all_files = []
    # CLEAR IMAGES
    for child in canvas.winfo_children():
        child.destroy()
    # RESET BUTTON COUNTER TO ZERO
    add_images_button_count = 0


# AUTOMATICALLY CLEAR SCREEN
def clear_screen():
    # REFERENCE GLOBAL VARIABLE
    global canvas, add_images_button_count
    # CLEAR IMAGES
    for child in canvas.winfo_children():
        child.destroy()
    # RESET BUTTON COUNTER TO ZERO
    add_images_button_count = 0


# NEXT STEP BUTTON
def next_step_button():
    # REFERENCE GLOBAL VARIABLES
    global home_window, all_files
    # TRY TO NAVIGATE TO EDIT SCREEN
    # try:
    try:
        edit_screen()
    # CATCH EXCEPTION WHERE USER HASN'T SELECTED PHOTOS YET
    except TypeError:
        # PROMPT USER TO SELECT PHOTOS BEFORE CONTINUING
        # INITIALIZE POP-UP WINDOW
        pop = Toplevel(home_window)
        # SET POP-UP WINDOW SIZE
        pop.geometry("300x200")
        # SET TITLE FOR WINDOW
        pop.title("Warning")
        # SET WARNING MESSAGE
        Label(pop, text="Please select at least one photo to continue!").pack(pady=20)
        # CREATE BUTTON
        Button(pop, text="Understood", command=pop.destroy).pack(pady=40)
        # REROUTE BACK TO HOME SCREEN
        home_screen()
    # CATCH EXCEPTION WHERE USER HASN'T SELECTED PHOTOS YET
    except IndexError:
        # PROMPT USER TO SELECT PHOTOS BEFORE CONTINUING
        # INITIALIZE POP-UP WINDOW
        pop = Toplevel(home_window)
        # SET POP-UP WINDOW SIZE
        pop.geometry("300x200")
        # SET TITLE FOR WINDOW
        pop.title("Warning")
        # SET WARNING MESSAGE
        Label(pop, text="Please select at least one photo to continue!").pack(pady=20)
        # CREATE BUTTON
        Button(pop, text="Understood", command=pop.destroy).pack(pady=40)
        # REROUTE BACK TO HOME SCREEN
        home_screen()


# SAVE BUTTON
def save_button():
    # SPECIFY FILE TYPES
    filetypes = [('JPG Files', '*.jpg'), ('PNG Files', '*.png')]
    # STORE SAVED NAME
    file = asksaveasfilename(defaultextension=".jpg", filetypes=filetypes)
    # SAVE IMAGE
    current_image.save(file)


# MAX IMAGE WARNING
def max_image_warning():
    global home_window
    pop = Toplevel(home_window)
    pop.geometry("300x200")
    pop.title("Warning")
    Label(pop, text="Max amount of images reached!").pack(pady=20)
    Button(pop, text="Understood", command=pop.destroy).pack(pady=40)


# ADD TEXT
def add_text_button():
    # INITIALIZE POP-UP WINDOW
    pop = Toplevel(home_window)
    # SET POP-UP WINDOW SIZE
    pop.geometry("400x375")
    # SET TITLE FOR WINDOW
    pop.title("Add Text")
    # OBTAIN X POSITION
    Label(pop, text="X position:").grid(row=0, column=0, padx=50, pady=25)
    x_text_position = Entry(pop, width=10)
    x_text_position.grid(row=0, column=1)
    # OBTAIN Y POSITION
    Label(pop, text="Y position:").grid(row=1, column=0, padx=25)
    y_text_position = Entry(pop, width=10)
    y_text_position.grid(row=1, column=1)
    # OBTAIN DESIRED TEXT
    Label(pop, text="Text:").grid(row=2, column=0, padx=20, pady=30)
    text = Entry(pop, width=25)
    text.grid(row=2, column=1)
    # OBTAIN DESIRED TEXT COLOR
    # DATATYPE OF MENU TEXT
    variable = StringVar(home_window)
    # DEFAULT VALUE
    variable.set("Black")
    Label(pop, text="Text Color").grid(row=3, column=0)
    text_color = OptionMenu(pop, variable, "Red", "Blue", "Green", "Yellow", "Orange")
    text_color.grid(row=3, column=1)
    # OBTAIN DESIRED FONT SIZE
    # SET SCALE DATATYPE
    v1 = IntVar()
    Label(pop, text="Text Size").grid(row=4, column=0)
    text_size = Scale(pop, variable=v1, from_=1, to=100, orient=HORIZONTAL)
    text_size.grid(row=4, column=1, pady=25)
    # SAVE BUTTON
    save = Button(pop, text="Impose Edits", width=10,
                  command=lambda : add_text_function(pop=pop,
                                                     x_text_position=x_text_position,
                                                     y_text_position=y_text_position,
                                                     text=text,
                                                     text_color=variable,
                                                     text_size=text_size))
    save.grid(row=5, columnspan=3, pady=40)


def add_text_function(pop, x_text_position, y_text_position, text, text_color, text_size):
    # REFERENCE GLOBAL VARIABLES
    global current_image, canvas, image_container
    # ENABLE IMAGE EDITING
    edited_image = ImageDraw.Draw(current_image)
    # CUSTOM FONT SIZE
    myFont = ImageFont.truetype(font="arial.ttf", size=text_size.get())
    # ADD TEXT IMAGE
    final_image = edited_image.text((int(x_text_position.get()), int(y_text_position.get())), text.get(), fill=text_color.get(), font=myFont)
    # CLOSE POP WINDOW
    pop.destroy()
    # UPDATE CANVAS WITH FINAL IMAGE
    canvas.itemconfigure(image_container, image=final_image)
    # THIS METHOD WORKS BUT WOULD PREFER NOT TO USE IT 
    current_image.show()



# RUN HOME SCREEN
home_screen()
  • This is far too much code. Please create a [mcve] that is focused on the problem you're asking about. The problem is related to this, which gets asked about a couple of times every week: https://stackoverflow.com/questions/16424091 – Bryan Oakley Oct 07 '22 at 22:17

0 Answers0