I have a variety of SVG images, their filenames are in uniformed number format, such as 1.svg, 2.svg. I want to rename each file based on image content.
I coded a python tkinter app for me to do this, which it has a previous and next button and a text area, which allows me to go back and go previous and edit the filename.
Currently I succeed in displaying svg content by writing svg content into a temporary png file, then display the png file. However, I had trouble switching svg content.
Please have a look of following code:
# 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()
What I want to do, is whenever I call refresh_image() function, tkinter canvas change the svg content based on at index, which rewrite current svg file into the temp.png file and display the png.
However, I find it wired that, it works if I write the inner part of refresh_image() function to global scope, but if it's in functional scope, it stops working!
the code example I provided above does not work, which it calls refresh_image() function. The following is the working version, instead of calling the function, I executed the code block in refresh_image() once at the end:
'''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()
Please follow the testing instruction:
- I have put the code and 3 svg file examples in github, run following command
git clone https://github.com/Weilory/svg_renamer
- if you haven't install the packages:
pip install svglib
pip install Pillow
pip install tkinter
pip install reportlib
pip install lxml
direct terminal to directory containing SVG folder and renamer.py, open renamer.py, assign
directoryvariable to the absolute path of your SVG folder.run the code through terminal
python renamer.py
the renamer.py on github is the functional scope, which you should see NO image displaying in tkinter canvas.
- hash the
refresh_image()function and the calling part, unhash the global single execution part at the end of file, run again.
python renamer.py
the image will show up. but without calling the function, I won't be able to switch svg content.
This issue has been annoying me all day, I will be so appreciated if someone offers me a solution or some suggestions.