Knowledge Base

Preserving for the future: Shell scripts, AoC, and more

Display svg in tkinter python3

The Internet has a guide for displaying svgs in Tkinter in Python 2: http://code.activestate.com/lists/python-list/595078/ However, I have not found any guides for the process for python3. And since I don't want to move backwards, I had to come up with something. Here is my solution. (link) Screenshot of tkinter window with svgs
rendered as the images on buttons, one scaled and one
unscaled.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env python3
# File: svgs-tkinter-python3.py
# License: CC-BY-SA 4.0
# Author: bgstack15
# Startdate: 2019-06-21 10:09
# Title: 
# Purpose: 
# History:
# Usage:
# References:
#    http://effbot.org/tkinterbook/button.htm
#    http://effbot.org/tkinterbook/tkinter-application-windows.htm
#    http://effbot.org/tkinterbook/
#    https://stackoverflow.com/questions/18537918/set-window-icon#18538416
#    https://pillow.readthedocs.io/en/stable/reference/ImageTk.html
# Improve:
# Dependencies:
#    devuan: python3-tk python3-pil.imagetk python3-cairosvg
#    el7: python36-tkinter python36-pillow-tk ( pip3 install cairosvg )

import re
import tkinter as tk
from PIL import Image, ImageTk, PngImagePlugin

LM_USE_SVG = 0
try:
   from cairosvg import svg2png
   LM_USE_SVG = 1
except:
   print("WARNING: Unable to import cairosvg. No svg images will be displayed.")
   LM_USE_SVG = 0

# graphical classes and functions
print("Loading graphics...")

def photoimage_from_svg(filename = "",size = "48"):
   # this one works, but does not allow me to set the size.
   # this is kept as an example of how to open a svg without saving to a file.
   # open svg
   item = svg2png(url=filename, parent_width = size, parent_height = size)
   return ImageTk.PhotoImage(data=item)

def empty_photoimage(size=24):
   photo = Image.new("RGBA",[size,size])
   return ImageTk.PhotoImage(image=photo)

def image_from_svg(filename = "",size = 0):
   # open svg
   if LM_USE_SVG == 1:
      if size == 0:
         # unscaled
         svg2png(url=filename,write_to="/tmp/example_temp_image.png")
      else:
         svg2png(url=filename,write_to="/tmp/example_temp_image.png",parent_width = size,parent_height = size)
      photo = Image.open("/tmp/example_temp_image.png")
   else:
      photo = Image.new("RGBA",[size,size])
   return photo

def get_scaled_icon(iconfilename, size = 0):

   try:
      print("Opening icon file",iconfilename)
      # try an svg
      if re.compile(".*\.svg").match(iconfilename):
         photo = image_from_svg(filename=iconfilename, size=size)
      else:
         photo = Image.open(iconfilename)
   except Exception as f:
      print("Error with icon file:", f)
      return empty_photoimage()

   if size != 0 and (type(photo) is Image or type(photo) is PngImagePlugin.PngImageFile):
      photo.thumbnail(size=[size, size])

   if not type(photo) is ImageTk.PhotoImage:
      try:
         photo = ImageTk.PhotoImage(photo)
      except Exception as e:
         print("Error was ",e)
   return photo

class App:
   def __init__(self, master):
      frame = tk.Frame(master)
      frame.grid(row=0)

      self.photo1 = get_scaled_icon("/usr/share/icons/Numix/128/actions/system-log-out.svg", 20)
      self.button1 = tk.Button(frame, text="Scaled to 24x24", image=self.photo1, compound=tk.LEFT)
      self.button1.grid(row=0,column=0)

      self.photo2 = get_scaled_icon("/usr/share/icons/Numix/128/actions/system-log-out.svg")
      self.button2 = tk.Button(frame, text="Unscaled", image=self.photo2, compound=tk.LEFT)
      self.button2.grid(row=0,column=1)

      self.buttonCancel = tk.Button(frame, text="Cancel", underline=0, command=self.quitaction)
      self.buttonCancel.grid(row=1,columnspan=8,sticky=tk.W+tk.E)

   def quitaction(self,b=None):
      print("Closing the window...")
      root.destroy()

root = tk.Tk()

# MAIN LOOP
root.title("SVG examples")
imgicon = get_scaled_icon("/usr/share/icons/Numix/128/actions/system-log-out.svg", 24)
root.tk.call('wm','iconphoto', root._w, imgicon)
app = App(root)
root.mainloop()
try:
   root.destroy()
except:
   pass

Comments