在 Python Tkinter 中使用图像的问题

我正在使用 Tkinter 创建 Blackjack GUI 游戏,但遇到了一个问题:当添加新牌时,交易按钮会从屏幕上清除旧牌的图像。我有根据的猜测是,当我再次使用该函数时,card_image该函数的内部deal()正在覆盖自身。如果是这种情况,为什么会这样?最好的解决办法是什么?谢谢。


import random

from tkinter import *

from PIL import Image, ImageTk


root =Tk()

root.title('21 Blackjack')

root.iconbitmap('images/21_cards.ico')

root.geometry('1280x750')

root.configure(bg='green')


cards = []


suits = ['hearts', 'clubs', 'diamonds', 'spades']

face_cards = ['ace', 'jack', 'queen', 'king']


extension = 'png'


for y in suits:

    for x in range(2, 11):

        name = 'images/{}-{}.{}'.format(str(x), y, extension)

        cards.append(name)


    for x in face_cards:

        name = 'images/{}-{}.{}'.format(str(x), y, extension)

        cards.append(name)


print(cards)

print(len(cards))

random.shuffle(cards)

print(cards[0])


hand = []


def deal():

    global card_image, card_label, hand

    card_image = ImageTk.PhotoImage(Image.open(cards[0]).resize((180, 245), Image.ANTIALIAS))

    card_label = Label(root, image=card_image, relief="raised").pack(side="left")

    hand += cards[:1]

    cards.pop(0)

    print(hand)


deal_button = Button(root, text="deal", command=deal).pack()


root.mainloop()


交互式爱情
浏览 143回答 3
3回答

一只甜甜圈

正如人们指出的那样,我需要添加card_label.image = card_image该函数并删除card_image和card_label全局以阻止图像被删除。但出于某种原因,Python 不喜欢在我这样做之前打包图像。该函数现在看起来像这样。    global hand    card_image = ImageTk.PhotoImage(Image.open(cards[0]).resize((180, 245), Image.ANTIALIAS))    card_label = Label(root, image=card_image, relief="raised")    card_label.image = card_image    card_label.pack(side="left")    hand += cards[:1]    cards.pop(0)    print(hand)

尚方宝剑之说

代替card_image = ImageTk.PhotoImage(Image.open(cards[0]).resize((180, 245), Image.ANTIALIAS))card_label = Label(root, image=card_image, relief="raised").pack(side="left")做:card_label = Label(root, image=card_image, relief="raised").pack(side="left")card_image = ImageTk.PhotoImage(Image.open(cards[0]).resize((180, 245), Image.ANTIALIAS))card_label.image = card_image #Keeps reference to the image so it's not garbage collected请注意,当您想使用照片时,请使用变量card_image

沧海一幻觉

您应该使用图像池。你很幸运,我这里有一个import osfrom glob import globfrom PIL import Image, ImageTkfrom typing import List, Tuple, Union, Dict, Typefrom dataclasses import dataclass@dataclassclass Image_dc:&nbsp; &nbsp; image&nbsp; :Image.Image&nbsp; &nbsp; rotate :int&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 0&nbsp; &nbsp; photo&nbsp; :ImageTk.PhotoImage = None&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; def size(self) -> Tuple[int]:&nbsp; &nbsp; &nbsp; &nbsp; if not self.photo is None:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return self.photo.width(), self.photo.height()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return self.image.width, self.image.heightclass ImagePool(object):&nbsp; &nbsp; ##__> PRIVATE INTERFACE <__##&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; __PATHS&nbsp; = dict()&nbsp; &nbsp; __IMAGES = dict()&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; @staticmethod&nbsp; &nbsp; def __name(path) -> str:&nbsp; &nbsp; &nbsp; &nbsp; return os.path.basename(os.path.splitext(path)[0])&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; @staticmethod&nbsp; &nbsp; def __unique(path:str, prefix:str='') -> str:&nbsp; &nbsp; &nbsp; &nbsp; name = f'{prefix}{ImagePool.__name(path)}'&nbsp; &nbsp; &nbsp; &nbsp; if name in ImagePool.names():&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sol = 'using a prefix' if not prefix else f'changing your prefix ({prefix})'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; msg = ("WARNING:\n"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f"{name} was not loaded due to a same-name conflict.\n"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f"You may want to consider {sol}.\n\n")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print(msg)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return None&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return name&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; @staticmethod&nbsp; &nbsp; def __request(name:str) -> Image_dc:&nbsp; &nbsp; &nbsp; &nbsp; if name in ImagePool.__PATHS:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; path = ImagePool.paths(name)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if os.path.isfile(path):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if name not in ImagePool.__IMAGES:&nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ImagePool.__IMAGES[name] = Image_dc(Image.open(path))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return ImagePool.__IMAGES[name]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; raise ValueError(f'ImagePool::__request - Path Error:\n\tpath is not a valid file\n\t{path}')&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; raise NameError(f'ImagePool::__request - Name Error:\n\t"{name}" does not exist')&nbsp; &nbsp; &nbsp; &nbsp; return None&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; @staticmethod&nbsp; &nbsp; def __size(iw:int, ih:int, w:int=None, h:int=None, scale:float=1.0) -> Tuple[int]:&nbsp; &nbsp; &nbsp; &nbsp; if not w is None and not h is None:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if iw>ih:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ih = ih*(w/iw)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; r = h/ih if (ih/h) > 1 else 1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iw, ih = w*r, ih*r&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iw = iw*(h/ih)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; r = w/iw if (iw/w) > 1 else 1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iw, ih = iw*r, h*r&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return int(iw*scale), int(ih*scale)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; ##__> PUBLIC INTERFACE <__##&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; @staticmethod&nbsp; &nbsp; def names(prefix:str='') -> List[str]:&nbsp; &nbsp; &nbsp; &nbsp; names = [*ImagePool.__PATHS]&nbsp; &nbsp; &nbsp; &nbsp; return names if not prefix else list(filter(lambda name, pre=prefix: name.startswith(pre), names))&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; @staticmethod&nbsp; &nbsp; def paths(name:str=None) -> Union[Dict, str]:&nbsp; &nbsp; &nbsp; &nbsp; if name is None:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return ImagePool.__PATHS&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; if name in ImagePool.__PATHS:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return ImagePool.__PATHS[name]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; raise NameError(f'ImagePool::paths - Name Error:\n\tname "{name}" does not exist')&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; @staticmethod&nbsp; &nbsp; def images(name:str=None, prefix:str='') -> Union[Dict, Image.Image]:&nbsp; &nbsp; &nbsp; &nbsp; if name is None:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return {name:ImagePool.__request(name).image for name in self.names(prefix)}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return ImagePool.__request(name).image&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; @staticmethod&nbsp; &nbsp; def photos(name:str=None, prefix:str='') -> Union[Dict, ImageTk.PhotoImage]:&nbsp; &nbsp; &nbsp; &nbsp; if name is None:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return {name:ImagePool.__request(name).photo for name in self.names(prefix)}&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return ImagePool.__request(name).photo&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; @staticmethod&nbsp; &nbsp; def append_file(path:str, prefix:str='') -> Type:&nbsp; &nbsp; &nbsp; &nbsp; if not os.path.isfile(path):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; raise ValueError(f'ImagePool::append_file - Value Error:\n\tpath is not valid\n\t{path}')&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; name = ImagePool.__unique(path, prefix)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; if name:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ImagePool.__PATHS[name] = path&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return ImagePool&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; @staticmethod&nbsp; &nbsp; def append_directory(directory:str, filters=['*.png', '*.jpg'], prefix:str='') -> Type:&nbsp; &nbsp; &nbsp; &nbsp; if not os.path.isdir(directory):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; raise ValueError(f'ImagePool::append_directory - Value Error:\n\tdirectory is not valid\n\t{directory}')&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; filters = filters if isinstance(filters, (List, Tuple)) else [filters]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; for filter in filters:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for path in glob(f'{directory}/{filter}'):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ImagePool.append_file(path, prefix)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return ImagePool&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; @staticmethod&nbsp; &nbsp; def photo(name:str, width:int=None, height:int=None, scale:float=1.00, rotate:int=None) -> ImageTk.PhotoImage:&nbsp; &nbsp; &nbsp; &nbsp; image_t = ImagePool.__request(name)&nbsp; &nbsp; &nbsp; &nbsp; size&nbsp; &nbsp; = ImagePool.__size(*image_t.size(), width, height, scale)&nbsp; &nbsp; &nbsp; &nbsp; rotate&nbsp; = image_t.rotate if rotate is None else rotate&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; #only resize if the new size or rotation is different than the current photo size or rotation&nbsp; &nbsp; &nbsp; &nbsp; #however, a small margin for error must be considered for the size&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; diff = tuple(map(lambda i, j: i-j, image_t.size(), size))&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; if (diff > (1, 1)) or (diff < (-1, -1)) or (image_t.rotate != rotate):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; image_t.rotate = rotate&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; image_t.photo&nbsp; = ImageTk.PhotoImage(image_t.image.resize(size, Image.LANCZOS).rotate(rotate))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return image_t.photo&nbsp; &nbsp;&nbsp;使用该文件您可以非常轻松地做很多事情。您可以将所有卡片图像放入一个文件夹中,并通过以下方式获取它们:ImagePool.append_directory(path_to_folder)然后,您可以获取任何卡并强制其适合其父级(无论如何):somelabel['image'] = ImagePool.photo(image_name, allotted_width, allotted_height)要不就:somelabel['image'] = ImagePool.photo(image_name)你可以得到池中每个名字的列表deck = ImagePool.names()或者在附加目录的同时抓取它deck = ImagePool.append_directory(path_to_folder).names()这对您非常有帮助,因为您可以简单地洗牌并弹出该列表作为游戏中的官方“牌组”。像这样:somecard['image'] = ImagePool.photo(deck.pop(0))假设您不仅拥有卡片图形,但又不想在创建牌组时获得所有图像,那么也有一个解决方案。首先在附加卡片图像目录时提供前缀,然后在调用names(). 只要卡片图像目录中只有卡片图像,则只会返回卡片名称。deck = ImagePool.append_directory(path_to_folder, prefix='cards_').names('cards_')笔记:allotted区域仅被分配,绝不应被假定为代表实际图像的最终宽度和/或高度。图像将尽一切努力适应分配的空间,而不会丢失其原始的高度和宽度比例。如果您不提供分配的宽度和高度,图像将是完整尺寸。所有图像和照片参考都会自动维护在ImagePool. 没有理由存储您自己的参考资料整个类是静态的,因此可以在任何地方访问所有图像和照片的引用,而无需ImagePool实例。换句话说,您永远不需要这样做:images = ImagePool()因此永远不需要弄清楚如何进入images某些class或其他文档。在您开始请求图像之前,不会实际加载图像,然后它会自动发生。这是一件好事。你有 52 张卡,但如果你在第一场比赛中只使用 ex: 6 ~ 只有 6 张会完全加载。最终,所有卡牌都会被玩完并被完全加载,并且您在游戏中不会出现尝试一次完全创建 52 张卡牌的情况。该类ImagePool具有更多功能,但此处解释的功能是您的游戏所需的全部功能。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python