Reputation: 69
So I've done the hardest part of actually displaying the SVG with the help of Giovanni Gatto(credit: https://pythonprogramming.altervista.org/tkinter-shows-an-svg-file/)
I've modified the code to fit my needs, but after implementation, rather than it assigning the SVG to each button individually, it will only display it on the last button on the frame.
I can create a function for each button, but that would be inefficient, what is the best way to approach this?
Import
from PIL import Image, ImageTk
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
from io import BytesIO
import PIL.Image
Function This is within a class, but for readability convenience here is the function that converts converts.
def imgToButton(image):
global img
svgfile = svg2rlg(image)
bytespng = BytesIO()
renderPM.drawToFile(svgfile, bytespng, bg=0x393A4C, fmt="PNG")
img = PIL.Image.open(bytespng)
img = img.resize((20, 20), PIL.Image.ANTIALIAS)
img.image = img
img = ImageTk.PhotoImage(img)
return img
Object
self.button1 = tk.Button(self.labelframe1)
self.button1.configure(activebackground='#393a4c', activeforeground='#000000', background='#393a4c', font='{Century Gothic} 13 {bold}')
self.button1.configure(foreground='#000000', highlightbackground='#393a4c', highlightcolor='#393a4c', highlightthickness='100')
self.button1.configure(justify='center', text='')
self.button1.configure(image=imgToButton("/Users/giuseppemarziano/Desktop/Scripts/Personal Projects/Auto Click GM/ACGM 0.04/ACGM Images/plus.svg"))
self.button1.place(height='25', relx='0.0', rely='0.0', width='25', x='5', y='3')
self.button1.bind('<1>', self.addSeq, add='+')
Apologies for using the full path, I am currently on an M1 MacBook Pro, which is an absolute nightmare for Python or coding in general
Ideally, I would like for each button to call the function, asign the image to the button, then repeat the process, rather than not doing that
I have also used self.button1.update()
, but that still yields the same result.
FYI: if you are struggling with low-res .PNG images on Tkinter and want to use .SVG for high-res smaller images, you can implement this code to display an SVG image on a button or any object, all you need to do is make sure you are able to import svglib
and import reportlab
, you should already have pillow, if not: https://pillow.readthedocs.io/en/stable/installation.html
Upvotes: 1
Views: 4306
Reputation: 142651
I can't test it but problem can be bug in PhotoImage
which removes it when it is assigned to local variable.
Normally you could assign `PhotoImage to global variable or to other class
photo = ImageTk.PhotoImage(img)
img.photo = photo
not img.image = PIL.Image.open(bytespng)
But you run it many times so you assign new image to the same variable so previous image is removed from variable and then bug in PhotoImage removes it from memory.
You should keep all PhotoImage
in different variables or on list
all_photoimages = [] # global list
def imgToButton(image):
svgfile = svg2rlg(image)
bytespng = BytesIO()
renderPM.drawToFile(svgfile, bytespng, bg=0x393A4C, fmt="PNG")
img = PIL.Image.open(bytespng)
img = img.resize((20, 20), PIL.Image.ANTIALIAS)
photo = ImageTk.PhotoImage(img)
#img.photo = photo
all_photoimages.append(photo) # keep in global list
return photo
EDIT:
As @TheLizzard noticed in comment you can also assign PhotoImage
to Button
and then you don't need list all_photoimages
because every PhotoImage
will be assigned to different Button
And this method seems cleaner - because imgToButton
doesn't have to use external list.
# get photoimage
img = imgToButton(...)
# use it with button
self.button1.configure(image=img)
# assign to button to resolve problem with bug
self.button1.tk_img = img
# -----
def imgToButton(image):
svgfile = svg2rlg(image)
bytespng = BytesIO()
renderPM.drawToFile(svgfile, bytespng, bg=0x393A4C, fmt="PNG")
img = PIL.Image.open(bytespng)
img = img.resize((20, 20), PIL.Image.ANTIALIAS)
return ImageTk.PhotoImage(img)
Upvotes: 2