Python:Canvas中动态创建图像问题

问题遇到的现象和发生背景

tkinter中利用Canvas创建图像,这个倒是正常,如果静态的纯手工写死几个文件路径,倒也能正常加载,但现在的问题,我通过askopenfilename这个函数调用文件对话框,实现让使用者自定义选择图片

self.filepath = filedialog.askopenfilename(filetypes=[('png', '.png')])

通过该功能获取文件路径和文件名,再通过cv.create_image的形式加载该图片,该功能倒也算正常,不过当打开添加第二张图。第N张图时,永远只显示出来最后一次加载的图片,之前的图片全部显示不出来。在网上查了好多资料,说是垃圾回收机制的原因,需要通过临时局部变量添加引用的形式,我也加了引用

cvImageID = self.cv.create_image(self.w / 2, self.h / 2, image=photo)
self.imageList.append(cvImageID)

self.iamgeList是在初始化时就创建的一个集合
但是我通过直接循环创建同一个图片,又能正常:

    def createImage(self, photo):
        for i in range(5):
            cvImageID = self.cv.create_image(self.w / 2, self.h / 2, image=photo)
            self.imageList.append(cvImageID)
            print(self.imageList)

img

但此处又不可能单一的循环某一张图,仅作为测试用。
如果按照选一次图加载一张图出来,却永远只显示最后一张

img

下面给出主要的代码实现:

# -*- coding: utf-8 -*-

# @Author : SiriBen

# @Time : 2022/5/13 10:57

# @File : main.py

# @Descripts: 主界面

import tkinter as tk
from tkinter import filedialog
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

from PIL import Image, ImageGrab, ImageTk

# img = None
# imgT = None
# photo = None
class APP:




    def __init__(self):
        self.w = 1440
        self.h = 900
        self.frameTop = ttk.Frame(root)
        self.frameTop.pack()
        self.frameFooter = ttk.Frame(root)
        self.frameFooter.pack()

        self.str1 = ttk.StringVar()

        self.imageList = []

        self.main()

    def button1(self, event):
        #global filepath
        button_text = event.widget['text']
        print('触发按钮'.format(button_text))
        #folderpath = filedialog.askdirectory()
        try:
            self.filepath = filedialog.askopenfilename(filetypes=[('png', '.png')])
            print('选择文件为:{}'.format(self.filepath))
            self.str1.set(self.filepath)

        except Exception as e:
            print(e)
            return

    def loadingImage(self, event):
        global img, imgT, photo, cv
        filepath2 = self.filepath

        img = Image.open(filepath2)
        print(img)
        imgT = img.resize((int(img.size[0] * 0.5), int(img.size[1] * 0.5)), Image.ANTIALIAS)
        print(imgT)
        photo = ImageTk.PhotoImage(imgT)
        print(photo)

        self.createImage(photo)


    def createImage(self, photo):
        #for i in range(5):
        cvImageID = self.cv.create_image(self.w / 2, self.h / 2, image=photo)
        self.imageList.append(cvImageID)
        print(self.imageList)

        self.cv.bind("<ButtonPress-1>", self.StartMove)  # 绑定鼠标左键按下事件
        self.cv.bind("<ButtonRelease-1>", self.StopMove)  # 绑定鼠标左键松开事件
        self.cv.bind("<B1-Motion>", self.OnMotion)  # 绑定鼠标左键被按下时移动鼠标事件

    def StartMove(self, event):
        global first_x, first_y, clickID
        allID = self.cv.find_closest(event.x, event.y)  # halo =3容易找到细直线
        print(allID)
        if len(allID) > 0:
            clickID = allID[0]
            first_x, first_y = event.x, event.y

    def StopMove(self, event):
        global first_x, first_y, clickID
        # cv.move(clickID,event.x-first_x,event.y-first_y)
        # cv.delete(clickID)#删除对象ID的画布

        clickID = -1

    def OnMotion(self, event):
        global first_x, first_y, clickID

        if clickID != -1:
            self.cv.move(clickID, event.x - first_x, event.y - first_y)
            first_x, first_y = event.x, event.y

    def main(self):


        b1 = ttk.Button(self.frameTop, text="选择", bootstyle="success")
        b1.pack(side=LEFT, padx=5, pady=10)
        b1.bind('<Button-1>', self.button1)


        b2 = ttk.Button(self.frameTop, text="加载", bootstyle="info-outline")
        b2.pack(side=LEFT, padx=5, pady=10)
        b2.bind('<Button-1>', self.loadingImage)

        lable1 = ttk.Label(self.frameTop, text='当前文件路径:')
        lable1.pack()

        self.lable2 = ttk.Label(self.frameTop, text='路径', textvariable=self.str1)
        self.lable2.pack()


        self.cv = ttk.Canvas(root, width=self.w, height=self.h)
        #cv = tk.Canvas(root, height=1440, width=2560, bg='#FFFFFF')  # silver
        self.cv.pack(side=LEFT, padx=5, pady=10)



if __name__ == '__main__':


    w = 1440
    h = 900
    root = ttk.Window(

        title='设计预览',
        themename='superhero',  #主题
        size=(w, h),  #大小
        #resizable=None, #是否更改大小
        alpha=1.0,  #透明度
        position=(50, 50)   #位置
    )

    # style = ttk.Style()
    # theme_names = style.theme_names()  # 以列表形式返回多个主题名

    # w = root.winfo_screenwidth()
    # h = root.winfo_screenheight()
    # root.geometry('%dx%d' % (w, h))


    app = APP()

    root.mainloop()



你要创建一个列表,保存图片的路径目录,这样每次刷新的时候,循环调用创建图片。这也是为什么同一张图片可以放好几次,因为路径是同一个,但是如果你不保存之前图片的路径,刷新的时候就丢失了。记住:所谓的刷新,其实就是重画。位置信息也是一样,如果要保持不变,也要建一个列表保存起来,下次刷新重新在那个位置画。时间紧,稍微这样改了一下,你试试:

class APP:
    def __init__(self):

        self.pathList = []
        self.imageList = []

    def loadingImage(self, event):
        global img, imgT, photo, cv
        filepath2 = self.filepath
        self.pathList.append(filepath2)
        img = []
        photo = []
        for i in range(len(self.pathList)):
            img.append(Image.open(self.pathList[i]))
            photo.append(ImageTk.PhotoImage(img[i].resize((int(img[i].size[0] * 0.5), int(img[i].size[1] * 0.5)), Image.ANTIALIAS)))
        self.createImage(photo)

    def createImage(self, photo):
        for i in photo:
            cvImageID = self.cv.create_image(self.w / 2, self.h / 2, image=i)
            self.imageList.append(cvImageID)