DUO Labs
DUO Labs

Reputation: 159

How to open SVG files in tkinter?

I want to be able to open SVG files in tkinter. The answer I'm using right now converts the SVG into a PNG before showing it in tkinter, but since I want to be to resize it without losing quality, I need a better answer.

Upvotes: 3

Views: 3243

Answers (2)

j_4321
j_4321

Reputation: 16169

Some basic svg support will be part of tk 8.7 (https://core.tcl-lang.org/tips/doc/trunk/tip/507.md).

But to add svg support to tk 8.6, it is necessary to install an extra package like tksvg. So, first install tksvg, following the instructions from here. Then you can create a PhotoImage from a svg file but you need to load tksvg in the tcl interpreter. I wrote a quick python wrapper for tksvg:

import tkinter as tk

class SvgImage(tk.PhotoImage):
    """Widget which can display images in PGM, PPM, GIF, PNG format."""
    _tksvg_loaded = False
    _svg_options = ['scale', 'scaletowidth', 'scaletoheight']

    def __init__(self, name=None, cnf={}, master=None, **kw):
        # load tksvg
        if not SvgImage._tksvg_loaded:
            if master is None:
                master = tk._default_root
                if not master:
                    raise RuntimeError('Too early to create image')
            master.tk.eval('package require tksvg')
            SvgImage._tksvg_loaded = True
        # remove specific svg options from keywords
        svgkw = {opt: kw.pop(opt, None) for opt in self._svg_options}
        tk.PhotoImage.__init__(self, name, cnf, master, **kw)
        # pass svg options
        self.configure(**svgkw)

    def configure(self, **kw):
        svgkw = {opt: kw.pop(opt) for opt in self._svg_options if opt in kw}
        # non svg options
        if kw:
            tk.PhotoImage.configure(self, **kw)
        # svg options
        options = ()
        for k, v in svgkw.items():
            if v is not None:
                options = options + ('-'+k, str(v))
        self.tk.eval('%s configure -format {svg %s}' % (self.name, ' '.join(options)))

So SvgImage is like a PhotoImage but it has the additional options (only one of them can be specified since they are three different way of specifying the image size):

  • scale
  • scaletowidth: width in pixel
  • scaletoheight: height in pixel

Here is an example:

root = tk.Tk()

def rescale():
    global scale
    scale += 0.5
    img.configure(scale=scale)

scale = 0.5
img = SvgImage(master=root, file='/path/to/file.svg', scale=scale)
tk.Button(root, image=img, command=rescale).pack()
root.mainloop()

Upvotes: 5

Flavio Moraes
Flavio Moraes

Reputation: 1351

I think that there is no way of doing this without losing quality resolutions, but if svglib was not good enough maybe cairo can do it better. Try the code bellow. It has the advantage that you don't really need the image as a png file, but since PIL does not handle svg you have to convert it anyway:

from io import BytesIO
import cairosvg
from PIL import Image

out = BytesIO()
cairosvg.svg2png(url='path/to/svg', write_to=out)
image = Image.open(out)

Upvotes: 1

Related Questions