为什么Python仍在垃圾收集我的Tkinter图像?

问题描述 投票:2回答:1

我知道这是此站点上之前曾提出的问题。但是,我已经做出了诚实的尝试来实现这些答案中提出的解决方案,但我仍然遇到相同的问题:Python似乎一直在垃圾收集我的图像,而我在我的窗口中应有图片的空白位置,如随附的屏幕截图所示。

screenshot

这是我尝试导入图像的代码的一部分:

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

[您会注意到,我已经尝试过通过将图像锚定到标签对象的属性上来保留图像的技巧。您还可能从所附的屏幕截图中注意到,对于此窗口,使用相同的图像文件导入自定义图标没有问题。

我很乐意根据需要共享该程序中的更多代码。


几个人建议我多分享一些代码。这是上面的代码所在的整个文件:

### 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 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()
python tkinter
1个回答
3
投票

PhotoImage("...")的参数是错误的。应该是PhotoImage(file="...")

© www.soinside.com 2019 - 2024. All rights reserved.