【发布时间】:2021-02-01 18:34:15
【问题描述】:
我有各种 SVG 图片,它们的文件名都是统一的数字格式,例如1.svg、2.svg。我想根据图像内容重命名每个文件。
我为我编写了一个 python tkinter 应用程序来执行此操作,它有一个上一个和下一个按钮以及一个文本区域,它允许我返回和上一个并编辑文件名。
目前我通过将svg内容写入临时png文件来成功显示svg内容,然后显示png文件。但是,我在切换 svg 内容时遇到了麻烦。
请看下面的代码:
# renamer.py
'''sh
pip install svglib
pip install Pillow
pip install tkinter
pip install reportlib
pip install lxml
'''
import os
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPDF, renderPM
from tkinter import *
from PIL import Image, ImageTk
# rename file by abs path
def rename(abs_path, new_name):
os.rename(abs_path, new_name)
tk = Tk()
# ---------------------------------------- fill your path here
# SVG directory absolute path
directory = "";
svg_paths = []
# iterate .svg file and append to collector
for filename in os.listdir(directory):
svg_paths.append(os.path.join(directory, filename));
# current manipulating index
at = 1
# feedback
# how many finished
# how many to go
def percent():
return f"{at} / {len(svg_paths)}"
vl = StringVar(tk, value=percent())
def go_previous():
global at
at -= 1
if at == 0:
# no more previous
at += 1
else:
vl.set(percent())
def go_next():
global at
at += 1
if at == len(svg_paths):
# no more next
at -= 1
else:
vl.set(percent())
label = Label(tk, textvariable=vl, font="Helvetica 30")
label.pack()
# tkinter doesnot support svg, therefore create a temp.png file
# in same level as SVG folder as converted png file to display
canvas = Canvas(tk, width=500, height=500)
canvas.pack()
last_img = None
'''
update png is achieved by Tkinter.Canvas widget
canvas.delete() => delete last image content
canvas.create_image() => set up new image content
the file name is always temp.png however the content is different
since new svg will be overwritten into the same file name
'''
'''
if you call following function at the end, it doesnot work,
which the png will not be displayed on tkinter
but if we exec these code in global scope instead of function
scope, it turns out working! I must use the function to update
the image content when either previous or next button is clicked
'''
def refresh_image():
global last_img, canvas
if last_img is not None:
canvas.delete(last_img)
drawing = svg2rlg(svg_paths[at-1])
renderPM.drawToFile(drawing, "temp.png", fmt="PNG")
img = Image.open('temp.png')
img = img.resize((400, 400), Image.ANTIALIAS)
pimg = ImageTk.PhotoImage(img)
last_img = canvas.create_image(0, 0, anchor='nw',image=pimg)
# display file name in input box
va = StringVar(tk, value='')
entry = Entry(tk, textvariable=va, font="Helvetica 30")
entry.pack(ipady=3)
# update file name
def refresh_entry():
va.set(svg_paths[at-1].split('/')[-1])
# rename file
def assign_name(new_name):
nm = directory + '/' + new_name
rename(svg_paths[at-1], nm)
# update recorded paths
svg_paths[at] = nm
previous = Button(tk, text="previous", command=go_previous)
_next = Button(tk, text="next", command=go_next)
previous.pack()
_next.pack()
refresh_image()
'''
As I mentioned in refresh_image(),
if you hash the function above and unhash the code below,
it works.
I want to update image content according to svg_paths at `at` index
'''
# drawing = svg2rlg(svg_paths[at-1])
# renderPM.drawToFile(drawing, "temp.png", fmt="PNG")
# img = Image.open('temp.png')
# img = img.resize((400, 400), Image.ANTIALIAS)
# pimg = ImageTk.PhotoImage(img)
# last_img = canvas.create_image(0, 0, anchor='nw',image=pimg)
refresh_entry()
tk.mainloop()
我想做的是,每当我调用refresh_image()函数时,tkinter canvas会根据at索引更改svg内容,将当前svg文件重写为temp.png文件并显示png。
但是,我发现它是有线的,如果我将 refresh_image() 函数的内部部分写入全局范围,它可以工作,但如果它在函数范围内,它就会停止工作!
我上面提供的代码示例不起作用,它调用refresh_image() 函数。以下是工作版本,我没有调用函数,而是在最后执行了refresh_image()中的代码块:
'''sh
pip install svglib
pip install Pillow
pip install tkinter
pip install reportlib
pip install lxml
'''
import os
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPDF, renderPM
from tkinter import *
from PIL import Image, ImageTk
# rename file by abs path
def rename(abs_path, new_name):
os.rename(abs_path, new_name)
tk = Tk()
# ---------------------------------------- fill your path here
# SVG directory absolute path
directory = "";
svg_paths = []
# iterate .svg file and append to collector
for filename in os.listdir(directory):
svg_paths.append(os.path.join(directory, filename));
# current manipulating index
at = 1
# feedback
# how many finished
# how many to go
def percent():
return f"{at} / {len(svg_paths)}"
vl = StringVar(tk, value=percent())
def go_previous():
global at
at -= 1
if at == 0:
# no more previous
at += 1
else:
vl.set(percent())
def go_next():
global at
at += 1
if at == len(svg_paths):
# no more next
at -= 1
else:
vl.set(percent())
label = Label(tk, textvariable=vl, font="Helvetica 30")
label.pack()
# tkinter doesnot support svg, therefore create a temp.png file
# in same level as SVG folder as converted png file to display
canvas = Canvas(tk, width=500, height=500)
canvas.pack()
last_img = None
'''
update png is achieved by Tkinter.Canvas widget
canvas.delete() => delete last image content
canvas.create_image() => set up new image content
the file name is always temp.png however the content is different
since new svg will be overwritten into the same file name
'''
'''
if you call following function at the end, it doesnot work,
which the png will not be displayed on tkinter
but if we exec these code in global scope instead of function
scope, it turns out working! I must use the function to update
the image content when either previous or next button is clicked
'''
# def refresh_image():
# global last_img, canvas
# if last_img is not None:
# canvas.delete(last_img)
# drawing = svg2rlg(svg_paths[at-1])
# renderPM.drawToFile(drawing, "temp.png", fmt="PNG")
# img = Image.open('temp.png')
# img = img.resize((400, 400), Image.ANTIALIAS)
# pimg = ImageTk.PhotoImage(img)
# last_img = canvas.create_image(0, 0, anchor='nw',image=pimg)
# display file name in input box
va = StringVar(tk, value='')
entry = Entry(tk, textvariable=va, font="Helvetica 30")
entry.pack(ipady=3)
# update file name
def refresh_entry():
va.set(svg_paths[at-1].split('/')[-1])
# rename file
def assign_name(new_name):
nm = directory + '/' + new_name
rename(svg_paths[at-1], nm)
# update recorded paths
svg_paths[at] = nm
previous = Button(tk, text="previous", command=go_previous)
_next = Button(tk, text="next", command=go_next)
previous.pack()
_next.pack()
# refresh_image()
'''
As I mentioned in refresh_image(),
if you hash the function above and unhash the code below,
it works.
I want to update image content according to svg_paths at `at` index
'''
drawing = svg2rlg(svg_paths[at-1])
renderPM.drawToFile(drawing, "temp.png", fmt="PNG")
img = Image.open('temp.png')
img = img.resize((400, 400), Image.ANTIALIAS)
pimg = ImageTk.PhotoImage(img)
last_img = canvas.create_image(0, 0, anchor='nw',image=pimg)
refresh_entry()
tk.mainloop()
请按照测试说明进行:
- 我已经把代码和 3 个 svg 文件示例放在了 github,运行以下命令
git clone https://github.com/Weilory/svg_renamer
- 如果您尚未安装软件包:
pip install svglib
pip install Pillow
pip install tkinter
pip install reportlib
pip install lxml
-
将终端指向包含 SVG 文件夹和 renamer.py 的目录,打开 renamer.py,将
directory变量分配给 SVG 文件夹的绝对路径。 -
通过终端运行代码
python renamer.py
github 上的renamer.py 是功能范围,您应该在 tkinter 画布中看到没有图像显示。
- 对
refresh_image()函数和调用部分进行hash,对文件末尾的全局单执行部分进行unhash,再次运行。
python renamer.py
图像将显示出来。但是如果不调用该函数,我将无法切换 svg 内容。
这个问题困扰了我一整天,如果有人给我解决方案或建议,我将不胜感激。
【问题讨论】:
-
如果运行
renamer.py时出现错误lxml cannot import etree,只需重新安装它:pip uninstall lxml,pip install lxml -
我确信在功能范围内该错误隐藏在这一行
pimg = ImageTk.PhotoImage(img)