【问题标题】:What's the best way to change an image multiple times in many labels?在许多标签中多次更改图像的最佳方法是什么?
【发布时间】:2019-09-01 04:07:23
【问题描述】:

我需要一些关于如何处理这个项目的建议。多年前我有一些编程经验,但最近几周才开始接触 Python。

这个 Python 3 程序有一个 tkinter 屏幕,上面有 150 个图标。图标是标签小部件,根据从文本文件读入程序的设备状态显示三个 20x20 png 图形之一。每个图标每天都需要更换几次。

任何关于在这种情况下显示这些图标的方法的建议将不胜感激。

我已经尝试了几次。理想情况下,StringVar()/textvariable 类型的方法会很好(这对于更改标签中的文本非常有效),但我找不到类似的图像方法。只是一遍又一遍地将相同的标签推到屏幕上是行不通的,因为程序最终会“内存不足”崩溃。

编辑: 我已经使用键/值对解决了这个问题,因此每个循环都不会创建新标签。该方法适用于单个实例,但是一旦我将其放入此循环结构中,它就会崩溃并出现“TypeError: 'str' object does not support item assignment”。我创建了这个简单的例程,它反映了我在大型项目中遇到的问题。我怎样才能让它工作?

import tkinter as tk
import time

window = tk.Tk()
window.title("PiStatus")
window.geometry("500x500+0+0")

ct = 0
# You can use any small images for this, mine are 20x20 pixels.
pc_on_icon = tk.PhotoImage(file="1.png")
pc_active_icon = tk.PhotoImage(file="2.png")
pc_off_icon = tk.PhotoImage(file="0.png")

# This loop creates the base 15x10 grid of labels, each with a unique name.
for ypos in range(10):
    for xpos in range(15):
        label_name = "icon" + str(xpos) + "-" + str(ypos)
        label_name = tk.Label(window, image=pc_on_icon)
        label_name.grid(row=ypos, column=xpos)

while True:

# These statements cycle through the 3 images
    if ct == 0:
        turn = pc_off_icon
    elif ct == 1:
        turn = pc_on_icon
    else:
        turn = pc_active_icon

# This loop references each label giving it a different image each time around.
    for ypos in range(10):
        for xpos in range(15):
            label_name = "icon" + str(xpos) + "-" + str(ypos)
            label_name['image'] = turn  # This is where the error occurs.

    ct += 1

    if ct == 3:
        ct = 0

    window.update()

window.mainloop()

【问题讨论】:

  • 您好,欢迎来到 SO。我建议您提供您正在处理的代码示例,即使您正在寻求建议,它也可能有助于更好地理解上下文。
  • 为所有状态创建标签,并且只使与当前设备状态对应的那些可见。
  • 非常感谢丹。我还没有遇到自定义小部件。这种方法看起来很有趣,我需要一些时间来消化并将这种方法应用到我的代码中。我会告诉你进展如何。
  • 没问题。您实际上并不需要 Frame 派生类,您可以将所有 450 个标签推入一个列表并进行基本数学计算以计算正确的索引......这似乎是构建代码的更好方法.
  • 不幸的是,这看起来不适合丹。检查代码看起来每个循环仍在创建一个新的标签实例。我运行了一个小时,它在运行时消耗了 14M 的可用内存。我在树莓派上运行它,所以内存并不是那么大。完全披露,我没有加载 pil 模块,也不需要显示 png 文件,所以我从你的代码中删除了它。但我认为这不会影响这些结果。我希望它无需每天重新启动即可运行以释放内存。

标签: python tkinter


【解决方案1】:

我们可以创建一个自定义小部件来封装为一个特定设备显示多个状态图标的功能。我们称它为MultiStateIcon 并从tk.Frame 派生它。

此小部件将包含一组子 tk.Label 小部件,每个可能的状态/图像一个。所有标签都将重叠填充,但只有一个(对应于当前状态的标签)可见(其他标签可以使用 pack_forget() 隐藏)。

然后你只需要创建一个MultiStateIcons 的网格(或任何你想要的布局),然后按照文本文件的指示改变它们的状态。


为了演示,我让应用程序定期(每 10 毫秒)将一个随机选择的设备的状态设置为随机选择的状态。

示例脚本:

import Tkinter as tk
import PIL.Image, PIL.ImageTk
from random import randrange


class MultiStateIcon(tk.Frame):
    def __init__(self, parent, images, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        
        self.images = images
        self.labels = []
        
        for image in images:
            l = tk.Label(self, image = image)
            l.pack()
            self.labels.append(l)
            
        self.set_state(0)
        
    def set_state(self, n):
        for i in range(len(self.labels)):
            if i == n:
                self.labels[i].pack()
            else:
                self.labels[i].pack_forget()
        
class TestApp(tk.Tk):
    def __init__(self, image_paths, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
       
        self.title("Test")
        self.frame = tk.Frame(self)
        self.frame.pack()
        
        self.images = []
        for image_path in image_paths:
            img = PIL.ImageTk.PhotoImage(PIL.Image.open(image_path))
            self.images.append(img)
        
        self.icons = []
        for r in range(10):
            for c in range(15):
                icon_lbl = MultiStateIcon(self.frame, self.images)
                icon_lbl.grid(row=r, column=c)
                self.icons.append(icon_lbl)
                
        self.after(1000, self.change_random_icon)
    
    def change_random_icon(self):
        n = randrange(0, len(self.icons))
        state = randrange(0, len(self.images))
        self.icons[n].set_state(state)
        self.after(10, self.change_random_icon)

def run():
    app = TestApp(["multiicon_0.png", "multiicon_1.png", "multiicon_2.png"])    
    app.mainloop()
    
run()

我使用的图片:

应用程序的屏幕截图(开始时的一个,稍等片刻后的另一个 - 内存使用情况似乎很稳定):

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多