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": {}
}
}