用Python shelve将canvas图形图像保存为文件及从文件读出图形图像重新显示的方法
Posted geng_zhaoying
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用Python shelve将canvas图形图像保存为文件及从文件读出图形图像重新显示的方法相关的知识,希望对你有一定的参考价值。
除了处理位图的程序,例如win10画图程序,还有一类程序是处理图形图像类实例,例如画线路板的PCB EDA程序。电子产品都有印刷电路板(PCB),其用途是将所需元器件焊接到PCB上,用PCB上覆铜导线将元器件连接起来。设计印刷电路板的程序称为PCB EDA程序。根据产品的外形,PCB尺寸是限定的,例如手机对PCB尺寸要求是很苛刻的。为了在有限面积上摆放所有元器件,元器件摆放位置和连接元器件的线要不断调整。为了移动方便,PCB EDA设计图上的元器件和连线都必须是图形或图像类实例。在PCB EDA设计未完成前,需要保存已完成图形图像为文件,以便以后从文件取出已完成图形图像,在此基础上继续设计工作,只有在设计完成后才会生成位图文件。
用Python设计一个大型EDA程序可能不现实,但设计一些简单的类似EDA程序还是可能的。例如设计海报、广告和电子板报等,现在一般不用手工完成,而是采用软件设计,用现成素材,加上相应文字,然后排版后完成。为了排版方便,素材和文字应是类实例。python可以实现这个功能,在tkinter Canvas上的图形图像,它们都是图形或图像类实例,很容易移动、放大、缩小等。虽然python可将Canvas上的图形图像保存为postscript类型文件,用于高质量打印,但python并没有提供读该类型文件方法。本文提供一种方法,用shelve将canvas上已完成的图形图像保存到文件中,并能从文件取出已完成的图形图像重新在空Canvas上显示,方便继续工作。
实现的基本思路是:首先得到某图形或图像类型,例如类型"oval"是椭圆(圆);然后得到其坐标;最后得到其配置,例如:“fill”、“tags”、“image"和"anchor"等;用shelve把这些数据保存到文件。重新显示时打开用shelve保存的文件,根据保存的图形或图像类型调用Canvas的相应创建图形或图像方法,该方法使用文件中坐标参数和配置参数在空Canvas上重画这个图形或图像。
现在介绍程序。程序在Canvas上创建了一个椭圆、一条线段和一个小猫图像。增加3个按钮,按钮标题分别是:保存、打开和删除所有图形。单击保存按钮,调用事件处理函数saveImage,用shelve将Canvas上椭圆、线段和小猫图像等实例类型、坐标和配置保存到文件中。单击删除所有图形按钮,删除Canvas上椭圆、线段和小猫图像。单击打开按钮,调用事件处理函数loadImage,取出用shelve保存的文件,根据该文件中类型、坐标和配置数据用Canvas方法重新创建被保存的椭圆、线段和小猫图像。
事件处理函数saveImage中(第12到33行),用shelve保存Canvas上所有图形和图像数据到文件中。首先创建列表data,列表的每一项都包括类型、坐标和配置3个键值对(第16到20行)。坐标键值对的值是坐标元组,长度可为2(例如图形中心点坐标)、4(例如矩形2个顶点坐标)或多个(例如多条线段);配置键值对的值包括若干(个数不定)键值对。注意,shelve模块中,key(键)必须为字符串,值可以是python所有数据类型。如图形或图像类实例类型为"image”(第15行),用第16行语句为列表data增加1项,如是其它类型,用第24行语句为列表data增加1项。最后用shelve将列表data保存为文件。
事件处理函数loadImage中(第37到51行),从用shelve保存的文件中读取Canvas上所有图形图像数据,并在空canvas上重新显示。这里*args可以看做是多个变量组成的列表(list),参数个数决定于列表长度。**args可以看做是个字典,参数个数决定于字典长度。需注意,第18行配置中的"image",并没有把图形数据保存到文件中,感觉是保存了变量名,因此重新创建图像时,第10行语句必须存在。
如在第24行语句的"config"键值对中增加"image" 和"anchor"键值对,删除第14到23行,仅用第24行语句将椭圆、线段和小猫图像3个类实例的数据增加到列表data中,将会报错。这是因为椭圆和线段没有属性(即方法的参数) “image"和"anchor”,当在列表data中增加椭圆或线段数据时,使用itemcget得到"image" 和"anchor"值将报错,小猫image图像没有属性"fill"和 “dash”,使用itemcget得到"fill"和"dash"值也将报错。因此,当不同类型的图形使用相同语句增加数据到列表中时,如某图形使用了属性A,例如第8条语句的画线方法的dash属性,使用相同语句增加数据的其它图形(例如第9条语句的画椭圆方法)也必须有属性A(例如dash),即使创建其它图形时,并没有使用属性A,在其它图形使用itemcget得到属性A时,将得到默认值,不会影响其它图形的正确显示。不过,canvas只有8个创建图形和图像的方法,如用8个if或elif语句,每条语句单独为某种类型图形或图像在列表增加数据,也就不用考虑这些问题了。
用canvas方法创建图形或图像,每个方法都有很多参数,例如椭圆(圆)有22个参数,它们都有默认值。如果仅用来画图,使用的参数就少多了,常用的有dash、fill、 outline、stipple和width等。因此在类似EDA画图程序中为保存图形图像类实例,可编写一个通用保存数据方法,使用8个if或elif语句,每个if或elif语句仅将某一种类型图形或图像常用参数保存到列表中,如果实际调用Canvas方法时,某个参数并没使用,则取默认值。
另外,图形图像之间可能会出现重叠。canvas为了记录这种重叠关系,建立一个显示列表,该列表决定当两个图形图像重叠时是如何覆盖的(默认情况下新创建的会覆盖旧的图形图像的重叠部分,即位于显示列表上方的图形图像将覆盖下方那个)。当然,显示列表中的图形图像可以被重新排序。而find_all方法按照显示列表的顺序返回所有图形图像的ID,返回格式是一个元组,按照显示列表依次重画图形图像,将能正确建立原图的重叠关系。
完整程序如下。被拷贝程序运行前,要先创建一个300*300、文件名为"cat.gif"的文件,并保存到被拷贝程序所在文件夹中。
import tkinter as tk
import shelve
root = tk.Tk()
root.geometry('400x350')
cv = tk.Canvas(root, width=400, height=300,bg='white')
cv.pack()
filepath = "myData"
cv.create_line(10,120,90,210,fill = "green",tags=('L'),dash=(3,5))
cv.create_oval(10,10,100,100,fill = "red",tags=('L'))
p = tk.PhotoImage(file = "cat.gif") #支持png和gif格式,不支持jpg格式,当调用loadImage重新显示,该语句必须存在
cv.create_image(100, 10, anchor="nw", image=p,tags=('L'))
def saveImage():
data = []
for itemID in cv.find_all(): #得到canvas上所有图形和图像类实例的ID
if cv.type(itemID) == "image":
data.append({"type": cv.type(itemID),"coords": cv.coords(itemID),
"config": {
"image": cv.itemcget(itemID,"image"),
"anchor": cv.itemcget(itemID,"anchor"),
"tags": cv.itemcget(itemID,"tags"),
}
})
else:
data.append({ #每个画布上图形类实例的数据为1个列表项
"type": cv.type(itemID),#返回画布上图形类实例类型,例如椭圆为"oval"
#返回画布上图形类实例坐标元组,长度可为2(例如图形位置)、4(例如矩形2个顶点坐标)或多个(例如多个线段)
"coords": cv.coords(itemID),#shelve模块中,key必须为字符串,值可以是python所有数据类型。
"config": {
"fill": cv.itemcget(itemID,"fill"),
"tags": cv.itemcget(itemID,"tags"),
"dash": cv.itemcget(itemID,"dash"),
}
})
with shelve.open('myData') as f:
f['cvData'] = data # 保存列表
def loadImage():
#data=[]
with shelve.open('myData') as f:
data=f.get('cvData')
for item in data:
#根据type的种类而调用Canvas的不同方法
if item["type"] == "oval":
#itemID = cv.create_oval(*item["coords"])
#cv.itemconfig(itemID, **item["config"])
#*item和**item用于函数参数个数不能确定时。*item可以看做是多个变量组成的list。**item可以看做是个字典
cv.create_oval(*item["coords"], **item["config"])
if item["type"] == "image":
cv.create_image(*item["coords"], **item["config"])
if item["type"] == "line":
cv.create_line(*item["coords"], **item["config"])
def deleteAllImage():
cv.delete('L')
but1=tk.Button(root,command=saveImage,text='保存')
but1.pack(side="left")
but2=tk.Button(root,command=loadImage,text='打开')
but2.pack(side="left")
but3=tk.Button(root,command=deleteAllImage,text='删除所有图形')
but3.pack(side="left")
root.mainloop()
以上是关于用Python shelve将canvas图形图像保存为文件及从文件读出图形图像重新显示的方法的主要内容,如果未能解决你的问题,请参考以下文章