Reputation: 722
I'm aware that this is a question that has been asked before on this site. However, I've made an honest attempt to implement the solutions put forward in those answers, and I'm still running into the same problem: Python seems to keep garbage-collecting my image, and I get an empty slot in my window where the image ought to be, as shown in the attached screenshot.
This is the part of my code where I attempt to import an image:
def make_top_title(self):
result = Frame(self.top)
text_lbl = Label(result, text="Proserpine",
font=(main_font, title_pt, "bold"))
arms_image = PhotoImage("icon.png")
arms_lbl = Label(result, image=arms_image, height=200)
arms_lbl.image = arms_image
arms_lbl.pack()
text_lbl.pack()
return result
You'll notice that I've already attempted to use the trick of preserving the image by anchoring it onto a property of a label object. You may also notice, from the attached screenshot, that I have no problem importing a custom icon - using the same image file - for this window.
I'm happy to share any more code from this program, as required.
A couple of people have suggested I share a bit more of my code. This is the whole file in which the code above is located:
### This code holds a class which allows the user to inspect a the coldstore
### object attached to its parent.
# GUI imports.
from tkinter import *
# Custom imports.
from sibstructures import data_storage
from sibstructures.numerals import index_to_label_column, \
index_to_label_row, \
index_to_label_layer
# Imports
import time
from threading import Thread
# Local constants.
main_font = "Arial"
title_pt = 60
subtitle_pt = 30
big_pt = 20
standard_pt = 15
diddy_pt = 10
standard_pad = 10
inner_pad = 5
tile_width = 15
details_height = 10
details_width = 30
grid_border = 5
spot_stack_width = tile_width+5
##############
# MAIN CLASS #
##############
# The class in question.
class CS_Viewer:
def __init__(self, parent):
self.parent = parent
self.parent_window = self.parent.get_top()
self.code = self.parent.coldstore.code
self.parent.coldstore.reconstruct()
self.max_max_layers = self.parent.coldstore.get_max_max_layers()
self.layer = 0
self.top = Frame(self.parent_window)
self.top_title = self.make_top_title()
self.subtitle = Label(self.top, text="code="+self.code,
font=(main_font, subtitle_pt, "bold"))
self.main_container = Frame(self.top)
self.spot_grid = Spot_Grid(self, self.main_container)
self.box_details = Text(self.main_container,
height=details_height, width=details_width,
state=DISABLED)
self.spot_stack = Spot_Stack(self.main_container, None,
self.box_details)
self.add_headings()
self.arrange()
# Ronseal.
def make_top_title(self):
result = Frame(self.top)
text_lbl = Label(result, text="Proserpine",
font=(main_font, title_pt, "bold"))
arms_image = PhotoImage("icon.png")
arms_lbl = Label(result, image=arms_image, height=200)
arms_lbl.image = arms_image
arms_lbl.pack()
text_lbl.pack()
return result
# Add headings to the holster widgets.
def add_headings(self):
spot_grid_label = Label(self.main_container, text="Coldstore",
font=(main_font, big_pt, "bold"))
spot_stack_label = Label(self.main_container, text="Spot",
font=(main_font, big_pt, "bold"),
width=spot_stack_width)
box_details_label = Label(self.main_container, text="Box",
font=(main_font, big_pt, "bold"))
spot_grid_label.grid(column=0, row=0)
spot_stack_label.grid(column=1, row=0)
box_details_label.grid(column=2, row=0)
# Ronseal.
def place_spot_stack(self):
self.spot_stack.get_top().grid(column=1, row=1,
padx=standard_pad, pady=standard_pad)
# Arrange the object's elements.
def arrange(self):
self.top_title.pack()
self.subtitle.pack()
self.spot_grid.get_top().grid(column=0, row=1, sticky=N,
padx=standard_pad, pady=standard_pad,
ipadx=inner_pad, ipady=inner_pad)
self.place_spot_stack()
self.box_details.grid(column=2, row=1, sticky=N,
padx=standard_pad, pady=standard_pad)
self.main_container.pack()
# Replace the spot stack widget.
def replace_spot_stack(self):
self.spot_stack.get_top().grid_forget()
self.place_spot_stack()
# Ronseal.
def get_top(self):
return self.top
################################
# HELPER CLASSES AND FUNCTIONS #
################################
# A class which holds the grid of spots.
class Spot_Grid:
def __init__(self, parent, parent_window):
self.parent = parent
self.parent_window = parent_window
self.top = Frame(self.parent_window, borderwidth=grid_border,
relief="solid")
Thread(target=self.make_grid).start()
# Fill the grid with boxes.
def make_grid(self):
cs = self.parent.parent.coldstore
for i in range(len(cs.columns)):
column_label = Label(self.top, text=str(index_to_label_column(i)),
font=(main_font, big_pt, "bold"))
column_label.grid(column=(i+1), row=0, padx=standard_pad)
for j in range(len(cs.columns[0].spots)):
if i == 0:
row_label = Label(self.top, text=str(index_to_label_row(j)),
font=(main_font, big_pt, "bold"))
row_label.grid(column=0, row=(j+1), padx=standard_pad)
tile = Spot_Tile(self, self.parent, cs.columns[i].spots[j],
self.parent.box_details)
tile.get_top().grid(column=(i+1), row=(j+1))
# Ronseal.
def get_top(self):
return self.top
# A class which holds a clickable representation of a spot.
class Spot_Tile:
def __init__(self, parent, main_ref, spot_obj, box_details_ref):
self.parent = parent
self.main_ref = main_ref
self.spot_obj = spot_obj
self.box_details_ref = box_details_ref
self.parent_window = self.parent.get_top()
self.top = Frame(self.parent_window)
Thread(target=self.make_filling).start()
# Fill the object with either a tile or a label.
def make_filling(self):
if self.spot_obj.max_layers == 0:
filling = Label(self.top, text="VOID", font=(main_font, diddy_pt),
width=tile_width)
elif self.spot_obj.layers() == 0:
filling = Button(self.top, text="free", command=None,
font=(main_font, diddy_pt, "italic"),
width=tile_width, state=DISABLED)
else:
filling = self.make_filling_button()
filling.pack()
# Make the filling object if it is a button.
def make_filling_button(self):
result = Button(self.top, text=self.make_filling_button_text(),
command=self.inspect,
font=(main_font, diddy_pt), width=tile_width)
return result
# Make the text portion of the filling button.
def make_filling_button_text(self):
growers = set()
varieties = set()
fields = set()
for box in self.spot_obj.boxes:
current_epc = box.epc
current_data = data_storage.fetch_most_recent_crop(current_epc)
growers.add(current_data["grower"])
varieties.add(current_data["variety"])
fields.add(current_data["field"])
result = (set_to_string(growers)+"\n"+set_to_string(varieties)+"\n"+
set_to_string(fields)+"\n"+str(len(self.spot_obj.boxes)))
return result
# Inspect a given spot.
def inspect(self):
self.main_ref.spot_stack = Spot_Stack(self.main_ref.main_container,
self.spot_obj,
self.box_details_ref)
self.main_ref.replace_spot_stack()
# Ronseal.
def get_top(self):
return self.top
# A class which holds a representation of the boxes on a given spot.
class Spot_Stack:
def __init__(self, parent_window, spot_obj, box_details_ref):
self.parent_window = parent_window
self.spot_obj = spot_obj
self.box_details_ref = box_details_ref
self.top = Frame(self.parent_window)
if self.spot_obj is None:
self.fill_empty()
else:
Thread(target=self.add_boxes).start()
# "Fill in" the representation if the spot object is empty.
def fill_empty(self):
label = Label(self.top, text="Select spot",
font=(main_font, standard_pt, "italic"), width=tile_width)
label.pack()
# Add representations of the spot's boxes.
def add_boxes(self):
no_of_boxes = len(self.spot_obj.boxes)
if no_of_boxes == 0:
empty_label = Label(self.top, text="Empty spot",
font=(main_font, standard_pt, "italic"),
width=tile_width)
empty_label.pack()
else:
for i in range(no_of_boxes):
backwards_index = (no_of_boxes-1)-i
box_tile = Box_Tile(self.top, self.spot_obj.boxes[backwards_index],
backwards_index, self.box_details_ref)
box_tile.get_top().pack()
# Ronseal.
def get_top(self):
return self.top
# A class which holds a clickable representation of a box.
class Box_Tile:
def __init__(self, parent_window, box, index, box_details_ref):
self.parent_window = parent_window
self.box = box
self.index = index
self.box_details_ref = box_details_ref
self.top = Frame(self.parent_window)
self.make_filling()
# Fill the object with either a tile or a label.
def make_filling(self):
label = Label(self.top, text=str(index_to_label_layer(self.index)),
font=(main_font, standard_pt))
filling = Button(self.top, text=self.box.epc, command=self.inspect,
font=(main_font, standard_pt), width=tile_width)
label.grid(column=0, row=0, padx=standard_pad)
filling.grid(column=1, row=0)
# Ronseal.
def get_top(self):
return self.top
# Inspect the data for this particular box in more detail.
def inspect(self):
text_to_insert = data_storage.fetch_most_recent_crop(self.box.epc)
self.box_details_ref.config(state=NORMAL)
self.box_details_ref.delete("1.0", END)
self.box_details_ref.insert(END, text_to_insert)
self.box_details_ref.config(state=DISABLED)
# Turns a set into a string, with items thereof separated by commas.
def set_to_string(the_set):
result = ""
the_list = list(the_set)
# Reversal is necessary, since .add() seems to add items to the FRONT of
# the set.
the_list.reverse()
for item in the_list:
if the_list.index(item) == 0:
result = item
else:
result = result+", "+item
return result
This is the other file, which, with its fellow, makes up the whole program:
### This code holds a class which manages transitions between windows, and
### also oversees their interactions with the Coldstore object.
# Imports.
from pathlib import Path
# GUI imports.
from tkinter import *
# Custom imports.
from sibstructures.coldstore import Coldstore
# Local imports.
from cs_viewer import CS_Viewer
# Constants.
path_to_db = str(Path.home())+"/cseye/source/proserpine/data.db"
##############
# MAIN CLASS #
##############
# The class in question.
class Comptroller:
def __init__(self):
self.coldstore = Coldstore(proserpine_mode=True,
proserpine_path=path_to_db)
self.gui = Tk()
self.top = Frame(self.gui)
self.window = CS_Viewer(self)
self.arrange()
# Return the top-level GUI object.
def get_top(self):
return self.top
# Arrange the widgets.
def arrange(self):
self.window.get_top().pack()
self.top.pack()
# Run the "mainloop" method on the GUI object.
def run_me(self):
self.gui.title("Proserpine")
self.gui.iconphoto(True, PhotoImage(file="icon.png"))
self.gui.mainloop()
###################
# RUN AND WRAP UP #
###################
def run():
comptroller = Comptroller()
comptroller.run_me()
if __name__ == "__main__":
run()
Upvotes: 3
Views: 514
Reputation: 46707
The argument to PhotoImage("...")
is wrong. It should be PhotoImage(file="...")
.
Upvotes: 3