Python ove(事件x、事件y) def set_moving_false(事件=无): 对于obj_lst中的obj: 如果对象允许移动: obj.allow_move=False def create_obj(事件=无): 对象附加(圆(画布、事件x、事件y)) def保存(事件=无): 打开('untitled.canvas','w')作为文件: obj_dict={f'{obj.type}{id}':(obj.x,obj.y)表示id,枚举中的obj(obj_lst)} dump(obj_dict,文件) root=Tk() 画布=画布(根,宽度=500,高度=400) canvas.pack() obj_type_dict={'circle':circle} 尝试: 打开('untitled.canvas')作为文件: obj_dict=json.load(文件) obj_lst=[] 对于obj_类型,obj_dict.items()中的属性: obj=obj_type_dict[obj_type.split()[0]](画布,属性[0],属性[1]) 对象附加(obj) 除FileNotFoundError外: 打开('untitled.canvas','w')作为文件: 通过 obj_lst=[] canvas.bind(“”,检查坐标) canvas.bind(“”,移动) canvas.bind(“”,set\u moving\u false) canvas.bind(“”,创建对象) root.bind(“”,保存) root.mainloop()

Python ove(事件x、事件y) def set_moving_false(事件=无): 对于obj_lst中的obj: 如果对象允许移动: obj.allow_move=False def create_obj(事件=无): 对象附加(圆(画布、事件x、事件y)) def保存(事件=无): 打开('untitled.canvas','w')作为文件: obj_dict={f'{obj.type}{id}':(obj.x,obj.y)表示id,枚举中的obj(obj_lst)} dump(obj_dict,文件) root=Tk() 画布=画布(根,宽度=500,高度=400) canvas.pack() obj_type_dict={'circle':circle} 尝试: 打开('untitled.canvas')作为文件: obj_dict=json.load(文件) obj_lst=[] 对于obj_类型,obj_dict.items()中的属性: obj=obj_type_dict[obj_type.split()[0]](画布,属性[0],属性[1]) 对象附加(obj) 除FileNotFoundError外: 打开('untitled.canvas','w')作为文件: 通过 obj_lst=[] canvas.bind(“”,检查坐标) canvas.bind(“”,移动) canvas.bind(“”,set\u moving\u false) canvas.bind(“”,创建对象) root.bind(“”,保存) root.mainloop(),python,tkinter,canvas,save,load,Python,Tkinter,Canvas,Save,Load,首先从导入开始,我选择使用json,因为这是可能的,而且pickle有一些安全问题,所以为什么不使用json。和tkinter中的内容 然后我定义了一个类,该类将解释圆形对象(一种大小的简单对象,没有什么特别之处) 然后使用绑定和一些函数(只是一些移动的东西)来处理大部分问题 重要的部分是读取和创建文件 因此绑定到Control+s的是一个保存文件的函数(save())。首先,它从屏幕上的对象列表中读取。(通过create_obj()函数将它们附加到那里)它将它们的类型和坐标存储在字典中,然后将

首先从导入开始,我选择使用
json
,因为这是可能的,而且
pickle
有一些安全问题,所以为什么不使用
json
。和
tkinter
中的内容

然后我定义了一个类,该类将解释圆形对象(一种大小的简单对象,没有什么特别之处)

然后使用绑定和一些函数(只是一些移动的东西)来处理大部分问题

重要的部分是读取和创建文件

因此绑定到
Control+s
的是一个保存文件的函数(
save()
)。首先,它从屏幕上的对象列表中读取。(通过
create_obj()
函数将它们附加到那里)它将它们的类型和坐标存储在字典中,然后将其转储到文件中

从文件中读取也很有趣(
try/except
在文件不存在的情况下存在)。读取时,它加载字典,并在循环中使用
obj_type_dict
字典确定对象的类型,将项目添加到列表中。在创建对象的同时,也会绘制对象

控制:

  • Ctrl+s
    -保存
  • 单击并用鼠标按钮拖动1
    -移动对象
  • 单击鼠标按钮3(右)
    -创建一个对象
建议:

  • 改进文件保存(为用户提供选项/使用
    filedialog
  • 改进对象添加到屏幕(测试时,您会注意到对象相对于鼠标的位置)
  • 添加将此保存为图像的零件

无论如何,这只是一个例子,说明了如何做到这一点,同样对于图像,如果要加载图像,您必须添加一个保存图像路径的属性,并进行其他调整,因为
json
无法序列化图像(我想).

既然您似乎不明白如何在评论中使用我的建议,下面是一个可运行的概念证明,以证明它执行我建议的操作的可行性,即定义一个
Canvas
子类,该子类记录在其上绘制的每个对象的信息,并能够将其保存并加载到文件中。为了简单起见,我决定将数据保存在JSON格式中,因为该文件是人类可读的(Python为它的标准库提供了支持,但正如我所说,选择是您的)(正如@ Matiiss在他的回答中建议的那样,将是另一个可行的替代方案来考虑和更紧凑)。 由于这只是一个演示,我只实现了对
Canvas
线条和矩形的支持,但这应该足以让您全面了解如何实现
Canvas
小部件的其余部分,如椭圆、多边形等等。请注意,可能无法全部实现,但我认为这不会是一个问题因为你可能不需要,甚至不用它们

除此之外,值得注意的是图像,因为下面显示的用于保存信息的模式不起作用,因为它们的
image=
选项参数。要处理它们,您需要保存图像文件的路径或其中的数据,但后一个选项需要将大量数据复制到保存文件中,并且需要将其编码和解码为JSON可序列化格式(如果您要使用该格式)

已更新

为了澄清我所说的图像是一种特殊情况(因为这可能是我们希望能够处理的事情,所以我修改了代码来展示一种方法,即将图像信息嵌入保存的画布文件中,其中包括图像文件的路径)。这似乎是与JSON格式结合使用时最干净的方法。这意味着保存的画布实际上并不包含图像数据本身

这是代码中使用的图像文件

import json
from PIL import Image, ImageTk
import tkinter as tk
from tkinter.constants import *


class MemoCanvas(tk.Canvas):
    ''' Canvas subclass that remembers the items drawn on it. '''
    def __init__(self, parent, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.memo = {}  # Database of Canvas item information.

    # Canvas object constructors.
    def create_line(self, *args, **kwargs):
        id = super().create_line(*args, **kwargs)
        self.memo[id] = dict(type='line', args=args, kwargs=kwargs)
        return id

    def create_rectangle(self, *args, **kwargs):
        id = super().create_rectangle(*args, **kwargs)
        self.memo[id] = dict(type='rectangle', args=args, kwargs=kwargs)
        return id

    def create_image(self, *args, imageinfo=None, **kwargs):
        id = super().create_image(*args, **kwargs)
        if not imageinfo:
            raise RuntimeError('imageinfo dictionary must be supplied')
        del kwargs['image']  # Don't store in memo.
        self.memo[id] = dict(type='image', args=args, imageinfo=imageinfo, kwargs=kwargs)
        return id

    # General methods on Canvas items (not fully implemented - some don't update memo).
    def move(self, *args):
        super().move(*args)

    def itemconfigure(self, *args, **kwargs):
        super().itemconfigure(*args, **kwargs)

    def delete(self, tag_or_id):
        super().delete(tag_or_id)
        if isinstance(tag_or_id, str) and tag_or_id.lower() != 'all':
            if self.memo[tag_or_id]['type'] == 'image':
                del self.imagerefs[tag_or_id]
            del self.memo[tag_or_id]
        else:
            try:
                self.memo.clear()
                del self.imagerefs
            except AttributeError:
                pass


# General purpose utility.
def setdefaultattr(obj, name, value):
    ''' If obj has attribute, return it, otherwise create and assign the value
        value to it first. '''
    try:
        return getattr(obj, name)
    except AttributeError:
        setattr(obj, name, value)
    return value


# Demo app functions.
def draw_objects(canvas):
    canvas.create_line(100, 200, 300, 400, fill='red')
    canvas.create_rectangle(100, 200, 300, 400, outline='green')

    # Add an image to canvas.
    imageinfo = dict(imagepath=IMAGEPATH, hsize=100, vsize=100, resample=Image.ANTIALIAS)
    imagepath, hsize, vsize, resample = imageinfo.values()

    image = Image.open(imagepath).resize((hsize, vsize), resample)
    image = ImageTk.PhotoImage(image)

    id = canvas.create_image(450, 150, image=image, imageinfo=imageinfo)
    imagerefs = setdefaultattr(canvas, 'imagerefs', {})
    imagerefs[id] = image  # Save reference to image created.

def clear_objects(canvas):
    canvas.delete('all')
    canvas.memo.clear()
    try:
        del canvas.imagerefs
    except AttributeError:
        pass

def save_objects(canvas, filename):
    with open(filename, 'w') as file:
        json.dump(canvas.memo, file, indent=4)

def load_objects(canvas, filename):
    clear_objects(canvas)  # Remove current contents.
    # Recreate each saved canvas item.
    with open(filename, 'r') as file:
        items = json.load(file)
    for item in items.values():
        if item['type'] == 'line':
            canvas.create_line(*item['args'], **item['kwargs'])

        elif item['type'] == 'rectangle':
            canvas.create_rectangle(*item['args'], **item['kwargs'])

        elif item['type'] == 'image':
            # Recreate image item from imageinfo.
            imageinfo = item['imageinfo']
            imagepath, hsize, vsize, resample = imageinfo.values()
            image = Image.open(imagepath).resize((hsize, vsize), resample)
            image = ImageTk.PhotoImage(image)
            item['kwargs']['image'] = image
            id = canvas.create_image(*item['args'], imageinfo=imageinfo, **item['kwargs'])
            imagerefs = setdefaultattr(canvas, 'imagerefs', {})
            imagerefs[id] = image  # Save reference to recreated image.

        else:
            raise TypeError(f'Unknown canvas item type: {type!r}')

# Main
WIDTH, HEIGHT = 640, 480
FILEPATH = 'saved_canvas.json'
IMAGEPATH = '8-ball-tbgr.png'
CMDS = dict(Draw=lambda: draw_objects(canvas),
            Save=lambda: save_objects(canvas, FILEPATH),
            Clear=lambda: clear_objects(canvas),
            Load=lambda: load_objects(canvas, FILEPATH),
            Quit=lambda: root.quit())

root = tk.Tk()
root.title('Save and Load Canvas Demo')
root.geometry(f'{WIDTH}x{HEIGHT}')

canvas = MemoCanvas(root)
canvas.pack(fill=BOTH, expand=YES)

btn_frame = tk.Frame(root)
btn_frame.pack()

for text, command in CMDS.items():
    btn = tk.Button(btn_frame, text=text, command=command)
    btn.pack(side=LEFT)

root.mainloop()
下面是它运行的屏幕截图:

下面是它创建的JSON格式文件的截断内容(无法发布完整内容,因为现在图像数据太大)


您可以使用字典将对象与其坐标一起保存在其中,读取文件时只需将对象放在坐标处即可。我不会尝试保存画布对象本身,而是保存创建每个对象所需的信息(以您喜欢的任何格式)。一种方法是定义自己的
Canvas
子类,该子类记录在画布上绘制的所有对象。下面是一个子类化的示例(用于不同的目的)在@Bryan Oakley对这个问题的回答中。@Matiiss谢谢你的回答。你能给我一个代码的例子吗?我很困惑,因为对象的位置和大小以及图片都应该保存以加载。@martineau谢谢你的回答,但我无法将
import json
from PIL import Image, ImageTk
import tkinter as tk
from tkinter.constants import *


class MemoCanvas(tk.Canvas):
    ''' Canvas subclass that remembers the items drawn on it. '''
    def __init__(self, parent, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.memo = {}  # Database of Canvas item information.

    # Canvas object constructors.
    def create_line(self, *args, **kwargs):
        id = super().create_line(*args, **kwargs)
        self.memo[id] = dict(type='line', args=args, kwargs=kwargs)
        return id

    def create_rectangle(self, *args, **kwargs):
        id = super().create_rectangle(*args, **kwargs)
        self.memo[id] = dict(type='rectangle', args=args, kwargs=kwargs)
        return id

    def create_image(self, *args, imageinfo=None, **kwargs):
        id = super().create_image(*args, **kwargs)
        if not imageinfo:
            raise RuntimeError('imageinfo dictionary must be supplied')
        del kwargs['image']  # Don't store in memo.
        self.memo[id] = dict(type='image', args=args, imageinfo=imageinfo, kwargs=kwargs)
        return id

    # General methods on Canvas items (not fully implemented - some don't update memo).
    def move(self, *args):
        super().move(*args)

    def itemconfigure(self, *args, **kwargs):
        super().itemconfigure(*args, **kwargs)

    def delete(self, tag_or_id):
        super().delete(tag_or_id)
        if isinstance(tag_or_id, str) and tag_or_id.lower() != 'all':
            if self.memo[tag_or_id]['type'] == 'image':
                del self.imagerefs[tag_or_id]
            del self.memo[tag_or_id]
        else:
            try:
                self.memo.clear()
                del self.imagerefs
            except AttributeError:
                pass


# General purpose utility.
def setdefaultattr(obj, name, value):
    ''' If obj has attribute, return it, otherwise create and assign the value
        value to it first. '''
    try:
        return getattr(obj, name)
    except AttributeError:
        setattr(obj, name, value)
    return value


# Demo app functions.
def draw_objects(canvas):
    canvas.create_line(100, 200, 300, 400, fill='red')
    canvas.create_rectangle(100, 200, 300, 400, outline='green')

    # Add an image to canvas.
    imageinfo = dict(imagepath=IMAGEPATH, hsize=100, vsize=100, resample=Image.ANTIALIAS)
    imagepath, hsize, vsize, resample = imageinfo.values()

    image = Image.open(imagepath).resize((hsize, vsize), resample)
    image = ImageTk.PhotoImage(image)

    id = canvas.create_image(450, 150, image=image, imageinfo=imageinfo)
    imagerefs = setdefaultattr(canvas, 'imagerefs', {})
    imagerefs[id] = image  # Save reference to image created.

def clear_objects(canvas):
    canvas.delete('all')
    canvas.memo.clear()
    try:
        del canvas.imagerefs
    except AttributeError:
        pass

def save_objects(canvas, filename):
    with open(filename, 'w') as file:
        json.dump(canvas.memo, file, indent=4)

def load_objects(canvas, filename):
    clear_objects(canvas)  # Remove current contents.
    # Recreate each saved canvas item.
    with open(filename, 'r') as file:
        items = json.load(file)
    for item in items.values():
        if item['type'] == 'line':
            canvas.create_line(*item['args'], **item['kwargs'])

        elif item['type'] == 'rectangle':
            canvas.create_rectangle(*item['args'], **item['kwargs'])

        elif item['type'] == 'image':
            # Recreate image item from imageinfo.
            imageinfo = item['imageinfo']
            imagepath, hsize, vsize, resample = imageinfo.values()
            image = Image.open(imagepath).resize((hsize, vsize), resample)
            image = ImageTk.PhotoImage(image)
            item['kwargs']['image'] = image
            id = canvas.create_image(*item['args'], imageinfo=imageinfo, **item['kwargs'])
            imagerefs = setdefaultattr(canvas, 'imagerefs', {})
            imagerefs[id] = image  # Save reference to recreated image.

        else:
            raise TypeError(f'Unknown canvas item type: {type!r}')

# Main
WIDTH, HEIGHT = 640, 480
FILEPATH = 'saved_canvas.json'
IMAGEPATH = '8-ball-tbgr.png'
CMDS = dict(Draw=lambda: draw_objects(canvas),
            Save=lambda: save_objects(canvas, FILEPATH),
            Clear=lambda: clear_objects(canvas),
            Load=lambda: load_objects(canvas, FILEPATH),
            Quit=lambda: root.quit())

root = tk.Tk()
root.title('Save and Load Canvas Demo')
root.geometry(f'{WIDTH}x{HEIGHT}')

canvas = MemoCanvas(root)
canvas.pack(fill=BOTH, expand=YES)

btn_frame = tk.Frame(root)
btn_frame.pack()

for text, command in CMDS.items():
    btn = tk.Button(btn_frame, text=text, command=command)
    btn.pack(side=LEFT)

root.mainloop()
{
    "1": {
        "type": "line",
        "args": [
            100,
            200,
            300,
            400
        ],
        "kwargs": {
            "fill": "red"
        }
    },
    "2": {
        "type": "rectangle",
        "args": [
            100,
            200,
            300,
            400
        ],
        "kwargs": {
            "outline": "green"
        }
    },
    "3": {
        "type": "image",
        "args": [
            450,
            150
        ],
        "imageinfo": {
            "imagepath": "8-ball-tbgr.png",
            "hsize": 100,
            "vsize": 100,
            "resample": 1
        },
        "kwargs": {}
    }
}