I am building a series of mutli-element "buttons" based on a dynamic data set.
I have the list generating and all elements are correctly being placed in their respective TTK elements -- except the logos. These are only appearing in the last multi-element button.
Below is my menu.py file. I have added a comment about the images in question.
import globals
import os
from data import Data
from utils import Image as imageUtils
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
class CreateMenu(tk.Canvas):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
menuItems = Data.getSchedule().games
# ---------------------------------------------------------------------
self.canvas = tk.Canvas(self, bg = '#000000', width = globals.screenWidth, height = globals.screenHeight)
self.canvas.pack()
for item in menuItems:
self.childFrame = tk.Frame(self.canvas, width=(buttonWidth - padding), height=buttonHeight, bg='#888888')
# ---------------------------------------------------------------------
self.awayLogo = imageUtils.webImage(item.away.logo)
print(self.awayLogo)
self.awayLogoImageFile = Image.open(self.awayLogo)
self.awayLogoImageFile = self.awayLogoImageFile.resize((thumbnailWidth - 16, thumbnailHeight - 16), Image.ANTIALIAS)
self.awayLogoImage = ImageTk.PhotoImage(self.awayLogoImageFile, format='png', width=thumbnailWidth, height=thumbnailHeight)
# The image is only getting added in the last instance of "item".
# In other items, the cards exist.
# I have confirmed that the images exist in the source location.
# The button below is placed, but is missing the image (except in the last instance of the "item" where it is rendered correctly)
self.awayLogoButton = ttk.Button(self.childFrame, compound=tk.CENTER, image=self.awayLogoImage, command=lambda link=item.link: selectGame(link))
self.awayLogoButton.pack(ipady = 1)
self.awayLogoButton.place(x=0, y=0, width=thumbnailWidth, height=thumbnailHeight)
self.awayNameButton = ttk.Button(self.childFrame, text=item.away.name, command=lambda link=item.link: selectGame(link))
self.awayNameButton.pack(ipady = 1)
self.awayNameButton.place(x=thumbnailWidth, y=0, height=buttonHeight/2, width=teamTextWidth)
# ---------------------------------------------------------------------
self.childFrame.bind("<Return>", lambda event, link=item.link: selectGame(link))
self.childFrame.focus_set()
self.childFrame.pack(ipady = 1)
self.childFrame.place(x = x, y = y)
if (x == (globals.screenWidth - buttonWidth)):
x = 0
y = y + buttonHeight
else:
x = x + buttonWidth
Any ideas on how to correct the images not appearing on all the other instances of item?
The issue is that python garbage collects all of your images and deletes them from python's memory as soon as you can no longer hold a reference to them.
To fix your issue you will need to add all of the images to a list like this:
self.tk_images = []
for ... in ...:
tk_image = ImageTk.PhotoImage(...)
...
self.tk_images.append(tk_image)
That way you can keep a reference to those images so python doesn't garbage collect them.
Related
I am new to Tkinter. I want to control the movement of the image with a cursor and a small window:
enter image description here
I tried with this code, but the result is not really what I'm looking for.
import tkinter as tk
from io import BytesIO
import requests
from PIL import Image , ImageTk
def full_dimensions(imag_fs):
top = tk.Toplevel(root)
img = tk.Label(top, image=imag_fs)
img.pack()
def get_image():
_url = 'https://i.imgur.com/4m7AHVu.gif'
_img = requests.get(_url)
if _img.status_code == 200:
_content = BytesIO(_img.content)
else:
_content = 'error.gif'
print('image loaded')
return _content
root = tk.Tk()
_content = get_image()
_x = Image.open(_content)
imag_fs = ImageTk.PhotoImage(_x)
_x.thumbnail((100, 100), Image.ANTIALIAS)
imag = ImageTk.PhotoImage(_x)
img = tk.Button(root, image=imag, command=lambda: full_dimensions(imag_fs))
img.grid(column=3, row=1)
root.mainloop()
I tested a window, but when I import an image I can't control
import tkinter as tk
main_window = tk.Tk()
def check_hand_enter():
canvas.config(cursor="hand1")
def check_hand_leave():
canvas.config(cursor="")
canvas = tk.Canvas(width=200, height=200)
tag_name = "polygon"
canvas.create_polygon((25, 25), (25, 100), (125, 100), (125, 25), outline='black', fill="", tag=tag_name)
canvas.tag_bind(tag_name, "<Enter>", lambda event: check_hand_enter())
canvas.tag_bind(tag_name, "<Leave>", lambda event: check_hand_leave())
canvas.pack()
main_window.mainloop()
So here is how I would do this (a simple example):
# import all necessary modules and classes
from tkinter import Tk, Canvas, Frame
from PIL import Image, ImageTk
import requests
# checking if the file exists, if it doesn't exist download it, if can't download it, exit the program
try:
open('space.jpg')
except FileNotFoundError:
url = 'https://images5.alphacoders.com/866/866360.jpg'
img = requests.get(url)
if img.status_code == 200:
with open('space.jpg', 'wb') as file:
file.write(img.content)
print('File not found. Downloaded the necessary file.')
else:
print('File not found. Could not download the necessary file.')
exit()
class MovableImage(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
# dictionary for storing information about movement
self.start_coords = {'x': 0, 'y': 0, 'move': False}
# dictionary for storing information about movement
self.start_coords_main = {'x': 0, 'y': 0, 'move': False}
# loads the image
self.image = Image.open('space.jpg')
# sets the images to their corresponding variables so that they can be referenced later
# resizes the smaller image to fit the navigation window
self.main_image = ImageTk.PhotoImage(self.image)
self.nav_image = ImageTk.PhotoImage(self.image.resize((200, 100), Image.ANTIALIAS))
# creates the canvas to store the bigger image on
self.main_canvas = Canvas(self, width=700, height=500, highlightthickness=0)
self.main_canvas.pack()
# puts image on canvas
self.main_image_id = self.main_canvas.create_image((0, 0), image=self.main_image, anchor='nw', tags='main_image')
# creates the smaller canvas that will be used for navigation
self.nav_canvas = Canvas(self.main_canvas, width=200, height=100, highlightthickness=0)
# adds the smaller canvas as a window to the main_canvas
self.main_canvas.create_window((500, 400), window=self.nav_canvas, anchor='nw', tags='nav_canvas')
# adds the resized image to nav_canvas
self.nav_canvas.create_image((0, 0), image=self.nav_image, anchor='nw')
# creates a rectangle to indicate the current view of the image
self.nav_box = self.nav_canvas.create_rectangle((0, 0, 70, 50), outline='white')
# binds functions
self.main_canvas.bind('<Button-1>', self.set_start_coords_main)
self.main_canvas.bind('<B1-Motion>', self.move_coords_main)
# binds functions
self.nav_canvas.bind('<Button-1>', self.set_start_coords)
self.nav_canvas.bind('<B1-Motion>', self.move_coords)
# function that sets the starting coords so that they can be referenced later, also sets whether the box can be moved at all
def set_start_coords(self, event):
x1, y1, x2, y2 = self.nav_canvas.coords(self.nav_box)
if x1 < event.x < x2 and y1 < event.y < y2:
self.start_coords['x'] = event.x - x1
self.start_coords['y'] = event.y - y1
self.start_coords['move'] = True
else:
self.start_coords['move'] = False
# the moving part, this takes reference from the starting coords and uses them for calculation
# basic border checks and then the actual moving
def move_coords(self, event):
if not self.start_coords['move']:
return
dx = self.start_coords['x']
dy = self.start_coords['y']
x = event.x - dx
y = event.y - dy
if x < 0:
x = 0
elif x + 70 > 200:
x = 130
if y < 0:
y = 0
elif y + 50 > 100:
y = 50
self.nav_canvas.coords(self.nav_box, x, y, x + 70, y + 50)
self.main_canvas.coords(self.main_image_id, -x * 10, -y * 10)
# function that sets the starting coords so that they can be referenced later, also sets whether the box can be moved at all
def set_start_coords_main(self, event):
x1, y1, x2, y2 = self.main_canvas.bbox('main_image')
if x1 < event.x < x2 and y1 < event.y < y2:
self.start_coords_main['x'] = event.x - x1
self.start_coords_main['y'] = event.y - y1
self.start_coords_main['move'] = True
else:
self.start_coords_main['move'] = False
# the moving part, this takes reference from the starting coords and uses them for calculation
# basic border checks and then the actual moving
def move_coords_main(self, event):
if not self.start_coords_main['move']:
return
dx = self.start_coords_main['x']
dy = self.start_coords_main['y']
x = event.x - dx
y = event.y - dy
if x < -1300:
x = -1300
elif x > 0:
x = 0
if y < -500:
y = -500
elif y > 0:
y = 0
self.nav_canvas.coords(self.nav_box, -x / 10, -y / 10, -x / 10 + 70, -y / 10 + 50)
self.main_canvas.coords(self.main_image_id, x, y)
# basic Tk() instance and afterwards the root.mainloop()
root = Tk()
MovableImage(root).pack()
# mainloop
root.mainloop()
Few things to mention: this is a pretty hardcoded sample that pretty much works well with only images whose resolution is 2000x1000 pixels, other images may not be shown correctly or resizing won't look as good. With this issue You will have to deal Yourself or ask another question about what issues You encountered while trying to make this adjustable. So it would be great if any image could be put up there and it would work.
About the code, it is pretty simple:
Import modules
check for file (there could be a better way but this works too), then if the file does not exist just download it, if that can't be done, well just exit the program.
Then setting up some references (a dictionary so that global doesn't have to be used and also I would say that a dictionary is better for other reasons too like all the necessary variables are in one place and for example x and y variable names are not taken globally).
Then setting the first function which will register the mouse click. This is done to get the position of mouse relative to the moveable square (basically where is the mouse on the square if the square was an independent window or sth) and it is made possible to move, but before that the function checks if the mouse is inside the square at all. If not moving is disabled so that for example if You were to click outside of the square and start moving it does not do that.
Then the moving function is defined. There the relative x and y coords are retrieved but first it checks if it can move and if it cannot, it stops the execution of that function. Then some more calculations and checking for border (again this is pretty hardcoded and should be changed to a more dynamic function so that it can detect this based on picture). Then comes the moving part which just move both the navigation box and the actual picture to their according spots (this is somewhat hardcoded but basically if You were to keep the scale to 1/10 of the smaller box, then this specific part would work with different images).
Then comes the basic Tk() initiation and later the .mainloop().
In the middle just open the image and set it to 2 variables and resize the one that will go to the navigation box.
Then create the main canvas where the main image will be shown.
Then add that image to canvas and keep the id reference so that the image can be later moved.
Create the smaller canvas and don't pack it or anything but add the instance as a window to the main canvas so that it is on top of it (as in the photo in Your question). Again it is placed in a hardcoded manner.
Then add the navigation image to the navigation canvas and add the small box that will be moved too.
Then bind functions to mouse activity in nav_canvas.
If You have questions ask. Note that the try/except is not necessary if You are using Your own images and it will always come with the program. It is there because You may not have this exact picture with those exact dimensions with that exact name so it is kinda temporary for testing purposes.
EDIT: put code in a class that inherits from Frame so that this can be placed as a widget.
I am preparing data for deep running. So I have to get certain pixel coordinates for each picture. Only one coordinate per photo is required.
So when I use PIL to input one click, I try to implement coordinates so that I can go to the next picture.
However, when I write the code as below, the coordinates are output in duplicate to only one picture, and the next picture does not appear on the screen.
How can I make sure that only one coordinate is recorded on a single picture?
from PIL import Image, ImageTk
import tkinter
import os
URL = './SavedImage/'
imgList = os.listdir(URL)
print(imgList)
width = 852
height = 480
stepW = 852/4
stepH = 480/5
def callback(event):
print("clicked at: ", event.x, event.y)
window = tkinter.Tk(className='pla')
for file in sorted(imgList):
a=True
image = Image.open(os.path.join(URL, file))
print(image)
canvas = tkinter.Canvas(window, width=image.size[0], height=image.size[1])
canvas.pack()
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
canvas.bind("<Button-1>", callback)
tkinter.mainloop()
I am not 100% sure I understand what you need but it looks like to me you are trying to get one set of cord's for each image in a list of images.
I would do this by creating a function and a tracking variable to loop through each image on at a time and on click update a new list with the image and cord's then loop to next image.
Let me know if you have any questions.
Example:
from PIL import Image, ImageTk
import tkinter
import os
URL = './SavedImage/'
imgList = os.listdir(URL)
width = 852
height = 480
stepW = 852/4
stepH = 480/5
tracker = 0
list_images_with_cords = [] # added list for final results
def callback(event):
# Added global's.
global tracker,list_images_with_cords
# Used to append final results to list.
list_images_with_cords.append([imgList[tracker], event.x, event.y])
# This tracker lets us go through each item on the list.
tracker += 1
# After appending list go to next image.
open_next()
window = tkinter.Tk(className='pla')
# Creates just one canvas that we can update later.
canvas = tkinter.Canvas(window)
canvas.pack()
def open_next():
# Adding global's.
global image, canvas, image_tk, tracker
# Clearing canvas before drawing new image.
canvas.delete("all")
# Checking for valid index in list.
if tracker < len(imgList):
image = Image.open(os.path.join(URL, imgList[tracker]))
# use config() to update canvas.
canvas.config(width=image.size[0], height=image.size[1])
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
canvas.bind("<Button-1>", callback)
else:
# This else statement is just for when we run out of images.
# It will display all the results in a textbox.
canvas.destroy()
txt = tkinter.Text(window, width=25)
txt.pack()
for item in list_images_with_cords:
txt.insert("end", "{}\n\n".format(item))
open_next()
tkinter.mainloop()
I'm looking for a way to move multiple images together with the background. Moving the background image works fine, but I can't figure out how to add two images on top (they disappear immediately) and then move together with the background. I guess there is an easy way to do that?
I appreciate any hint!
from tkinter import *
import time
tk = Tk()
w = 1000
h = 800
pos = 0
canvas = Canvas(tk, width=w, height=h)
canvas.pack()
tk.update()
background_image = PhotoImage(file="bg.gif")
background_label = Label(tk, image=background_image)
background_label.place(x=0, y=0)
tk.update()
def addImages(files):
for f in files:
image = PhotoImage(file=f)
label = Label(tk, image=image)
label.place(x=files[f][0],y=files[f][1])
tk.update()
def move(xPos):
pos = background_label.winfo_x() - xPos
while background_label.winfo_x() > pos:
background_label.place(x=background_label.winfo_x()-25)
tk.update()
time.sleep(0.001)
img = {"file1.gif": [10,10], "file2.gif": [50,50]}
addImages(img)
move(100)
tk.mainloop()
I'm having difficulty in understanding your code. Why create a canvas and then not use it? You have also littered your code with tk.update(), most of which are unnecessary. But, the described problem is because you create the labels inside a function and the association between label and image gets garbage collected when the function exits. You have to explicitly remember this association:
def addImages(files):
for f in files:
image = PhotoImage(file=f)
label = Label(tk, image=image)
label.image = image # Lets the label remember the image outside the function
label.place(x=files[f][0],y=files[f][1])
If you are then going to move these labels you might want to keep some kind of reference to them or you won't be able to address them.
Complete example
I changed tk to root because tk is the name usually used as alias for tkinter (eg. import tkinter as tk) which gets confusing.
I'm creating a image_list to hold references to the labels containing images. Later I use the list to loop through the labels and move them.
After I have built the GUI I wait 1000 milliseconds before starting the move function. Also I move the images just 1 pixel at a time to clearer see the action.
from tkinter import *
import time
root = Tk()
root.geometry('800x600') # Setting window size instead of usin canvas to do that
pos = 0
background_image = PhotoImage(file="bg.gif")
background_label = Label(root, image=background_image)
background_label.place(x=0, y=0)
image_list = [] # List for holding references to labels with images
def addImages(files):
for f in files:
image = PhotoImage(file=f)
label = Label(root, image=image)
label.image = image # Remember the image outside the function
label.place(x=files[f][0],y=files[f][1])
image_list.append(label) # Append created Label to the list
def move(xPos):
pos = background_label.winfo_x() - xPos
while background_label.winfo_x() > pos:
background_label.place(x=background_label.winfo_x()-1)
for image in image_list: # Loop over labels in list
image.place(x=image.winfo_x()-1) # Move the label
root.update()
time.sleep(0.001)
img = {"file1.gif": [10,10], "file2.gif": [50,50]}
addImages(img)
root.after(1000, move, 100) # Waits 1 second and then moves images
root.mainloop()
By the way; after is a function much preferred over sleep as sleep suspends the program until its finished, whereas after works by making a call after a time and the program runs meanwhile. But if you are ok with that the program freezes during the move, then why not.
My aim is to create a random country generator, and the flag of the country that is picked will appear. However, if the image file is bigger than the predetermined size of the label, then only part of the image is displayed. Is there a way of resizing the image to fit the label? (All other questions like this which I have seen have been answered, mentioning the PIL or Image modules. I tested them both, and they both came up with this error:
Traceback (most recent call last):
File "C:\python\country.py", line 6, in
import PIL
ImportError: No module named 'PIL'
This is my code, if it helps:
import tkinter
from tkinter import *
import random
flags = ['England','Wales','Scotland','Northern Ireland','Republic of Ireland']
def newcountry():
country = random.choice(flags)
flagLabel.config(text=country)
if country == "England":
flagpicture.config(image=England)
elif country == "Wales":
flagpicture.config(image=Wales)
elif country == "Scotland":
flagpicture.config(image=Scotland)
elif country == "Northern Ireland":
flagpicture.config(image=NorthernIreland)
else:
flagpicture.config(image=Ireland)
root = tkinter.Tk()
root.title("Country Generator")
England = tkinter.PhotoImage(file="england.gif")
Wales = tkinter.PhotoImage(file="wales.gif")
Scotland = tkinter.PhotoImage(file="scotland.gif")
NorthernIreland = tkinter.PhotoImage(file="northern ireland.gif")
Ireland = tkinter.PhotoImage(file="republic of ireland.gif")
blackscreen = tkinter.PhotoImage(file="black screen.gif")
flagLabel = tkinter.Label(root, text="",font=('Helvetica',40))
flagLabel.pack()
flagpicture = tkinter.Label(root,image=blackscreen,height=150,width=150)
flagpicture.pack()
newflagButton = tkinter.Button(text="Next Country",command=newcountry)
newflagButton.pack()
The code works perfectly fine apart from only showing part of the image. Is there a way to resize the images within the code itself?(I am using Python 3.5.1)
If you don't have PIL installed, first you need to install
pip install pillow
Once installed you can now import from PIL:
from PIL import Image, ImageTk
Tk's PhotoImage can only display .gif's, whereas PIL's ImageTk will let us display various image formats in tkinter and PIL's Image class has a resize method we can use to resize the image.
I trimmed your code down some.
You can resize the image and then just configure the label, the label will expand to be the size of the image. If you gave the label a specific height and width, lets say height=1 and width=1 and you resized the image to be 500x500 and then configured the widget. It would still display a 1x1 label since you've set these attributes explicitly.
In the below code, modifiying the dict, it is not okay to modify a dict while iterating over it. dict.items() returns a copy of the dict.
There's various ways to do this, I just though a dict was convenient here.
Link to an image that's over the height / width limit - kitty.gif
from tkinter import *
import random
from PIL import Image, ImageTk
WIDTH, HEIGHT = 150, 150
flags = {
'England': 'england.gif',
'Wales': 'wales.gif',
'Kitty': 'kitty.gif'
}
def batch_resize():
for k, v in flags.items():
v = Image.open(v).resize((WIDTH, HEIGHT), Image.ANTIALIAS)
flags[k] = ImageTk.PhotoImage(v)
def newcountry():
country = random.choice(list(flags.keys()))
image = flags[country]
flagLabel['text'] = country
flagpicture.config(image=image)
if __name__ == '__main__':
root = Tk()
root.configure(bg='black')
batch_resize()
flagLabel = Label(root, text="", bg='black', fg='cyan', font=('Helvetica',40))
flagLabel.pack()
flagpicture = Label(root)
flagpicture.pack()
newflagButton = Button(root, text="Next Country", command=newcountry)
newflagButton.pack()
root.mainloop()
Instead of randomly selecting a country to display its flag, we loop through the flags dictionary that is key-sorted. Unlike the random choice which will inevitably repeat the flags, this scheme runs through the countries in alphabetical order. Meanwhile, we resize all the images to a fixed pixel size based on the width and height of the root window multiplied by a scale factor. Below is the code:
import tkinter as tk
from PIL import Image, ImageTk
class Flags:
def __init__(self, flags):
self.flags = flags
self.keyList = sorted(self.flags.keys()) # sorted(flags)
self.length = len(self.keyList)
self.index = 0
def resize(self, xy, scale):
xy = [int(x * y) for (x, y) in zip(xy, scale)]
for k, v in self.flags.items():
v = Image.open(r'C:/Users/user/Downloads/' + v)
v = v.resize((xy[0], xy[1]), Image.ANTIALIAS)
self.flags[k] = ImageTk.PhotoImage(v)
def newCountry(self, lbl_flag, lbl_pic):
country = self.keyList[self.index]
lbl_flag["text"] = country
img = self.flags[country]
lbl_pic.config(image = img)
self.index = (self.index + 1) % self.length # loop around the flags dictionary
def rootSize(root):
# Find the size of the root window
root.update_idletasks()
width = int(root.winfo_width() * 1.5) # 200 * m
height = int(root.winfo_height() * 1.0) # 200 * m
return width, height
def centerWsize(root, wh):
root.title("Grids layout manager")
width, height = wh
# Find the (x,y) to position it in the center of the screen
x = int((root.winfo_screenwidth() / 2) - width/2)
y = int((root.winfo_screenheight() / 2) - height/2)
root.geometry("{}x{}+{}+{}".format(width, height, x, y))
if __name__ == "__main__":
flags = {
"Republic of China": "taiwan_flag.png",
"United States of America": "america_flag.gif",
"America": "america_flag.png",
}
root = tk.Tk()
wh = rootSize(root)
centerWsize(root, wh)
frame = tk.Frame(root, borderwidth=5, relief=tk.GROOVE)
frame.grid(column=0, row=0, rowspan=3)
flag = Flags(flags)
zoom = (0.7, 0.6) # Resizing all the flags to a fixed size of wh * zoom
flag.resize(wh, zoom)
lbl_flag = tk.Label(frame, text = "Country name here", bg = 'white', fg = 'magenta', font = ('Helvetica', 12), width = 30)
lbl_flag.grid(column = 0, row = 0)
pic_flag = tk.Label(frame, text = "Country flag will display here")
pic_flag.grid(column = 0, row = 1)
btn_flag = tk.Button(frame, text = "Click for next Country Flag",
bg = "white", fg = "green", command = lambda : flag.newCountry(lbl_flag, pic_flag))
btn_flag.grid(column = 0, row = 2)
root.mainloop()
You can use the PIL(pillow module to resize the image)
But in order to resize the images to exactly to the widget size, You can do the following. (Assuming that you are familiar with basic tkinter syntax structure)
your_widget.update() #We are calling the update method in-order to update the
#widget size after it's creartion, Other wise, it will just print '1' and '1'
#rather than the pixel size.
height=your_widget.winfo_height()
width=your_widget.winfo_width()
print(height,weight)
Now you can use the height and width information to create a new image that you can size it perfectly to your widget.
But if you have your image is already created, you can use the PIL module to resize the image to your size
first open your image with
flag_temp = Image.open("file location")
next resize the image to your size
flag_new = flag_temp.resize((10,10))# example size
make your final image to add in your widget
flag_final = ImageTk.PhotoImage(flag_new)
now you can use the 'flag final' varible in your widget.
IF YOUR APP HAS TO BE RESIZED AT ANY POINT, you can use the height and width varible created in the first code para, to dynamically resize the image
But you should make sure that the function is called regularly to update it.
You should also pass in the height and width variable in the place of (10,10) something like this
flag_new = flag_temp.resize((height,widht)
Hopefully this was helpful, I think the answer is bit long for your question, If you have any problems pls comment below.
I am trying to display two image objects in my Text widget. Initially, both objects showed up as empty images. After some research, I learned that I need to keep a reference to the image. I added an extra line to my code. I now see the 2nd image displayed correctly, however the 1st image is still blank.
I suspect my "reference" is being over written during the for loop. Is this the case? And if so, how would I maintain multiple references if I need to display multiple image objects inside a Text widget?
Here is my code so far:
from Tkinter import *
from PIL import Image, ImageTk
import os
class TestApp:
def __init__(self, parent):
self.myParent = parent
self.main_container = Frame(parent)
self.main_container.pack()
self.text_box = Text(self.main_container, height=50, width=80)
self.text_box.pack()
image_names = ['img1.jpg', 'img2.jpg']
for name in image_names:
img = Image.open(os.getcwd() + '\\' + name)
img_obj = ImageTk.PhotoImage(img)
self.text_box.image_create(INSERT, image=img_obj)
self.text_box.insert(END, "\n")
# Added this extra reference
self.text_box.image = img_obj
root = Tk()
root.title("This is a test app")
mainapp = TestApp(root)
root.mainloop()
Yes, your reference is being overwritten.
The simplest might be to just add the reference to a list. For example:
class TestApp:
def __init__(...):
...
self.images = []
...
for name in image_names:
...
img_obj = ImageTk.PhotoImage(img)
self.images.append(img_obj)