pythontkintersvgtksvg

Tkinter - Modify fill option when using tksvg


Thanks to this question, I discovered tksvg.

I already know how to display an svg file:

import tkinter as tk
import tksvg

window = tk.Tk()
svg_image = tksvg.SvgImage(file="tests/orb.svg")
label = tk.Label(image=svg_image)
label.pack()
window.mainloop()

and also how to do the same using svg data/string:

import tkinter as tk
import tksvg

svg_string = """
<svg aria-hidden="true" focusable="false" role="img" viewBox="0 0 24 24" class="" fill="none" stroke-width="2" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><g stroke-width="1.5px" stroke="#B8B8B8" fill="none"><path stroke="none" d="M0 0h24v24H0z" fill="none" stroke-width="1.5px"></path><line x1="4" y1="20" x2="7" y2="20" stroke="#B8B8B8" fill="none" stroke-width="1.5px"></line><line x1="14" y1="20" x2="21" y2="20" stroke="#B8B8B8" fill="none" stroke-width="1.5px"></line><line x1="6.9" y1="15" x2="13.8" y2="15" stroke="#B8B8B8" fill="none" stroke-width="1.5px"></line><line x1="10.2" y1="6.3" x2="16" y2="20" stroke="#B8B8B8" fill="none" stroke-width="1.5px"></line><polyline points="5 20 11 4 13 4 20 20" stroke="#B8B8B8" fill="none" stroke-width="1.5px"></polyline></g></svg>
"""
window = tk.Tk()
svg_image = tksvg.SvgImage(data=svg_string)
label = tk.Label(image=svg_image)
label.pack()
window.mainloop()

but I'm wondering, how do I modify the fill option of the svg element (based on the svg data).

Only way I thought of was to redraw/delete and create the svg element with a different color used in fill. Here is my attempt:

import tkinter as tk
import tksvg

svg_string = '<svg viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="red"/></svg>'


def change_color(event):
    global svg_string
    fill_color = "blue" if "red" in svg_string else "red"
    svg_string = '<svg viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="{}"/></svg>'.format(fill_color)
    canvas.delete(image_store[0])
    svg_store.pop(0)
    svg_store.append(tksvg.SvgImage(data=svg_string))
    image_store[0] = canvas.create_image(100, 100, image=svg_store[0])

window = tk.Tk()
canvas = tk.Canvas(window, width=200, height=200)
canvas.pack()

image_store = []
svg_store = []
svg_store.append(tksvg.SvgImage(data=svg_string))
image_store.append(canvas.create_image(100, 100, image=svg_store[0]))

canvas.tag_bind(image_store[0], "<Button-1>", change_color)
window.mainloop()

Here I tried to switch the color back and forth, so that when it is blue it becomes red, and when red, it becomes blue. But in this case, the color only seems to change once.

I'm only trying to make a workaround since I don't think this is supported yet (feel free to correct me if I'm wrong, as I tried to look at the code, with my best attempt at understanding it at the official github repository).

How can I do this? either using the above workaround (redraw element) or with a better alternative?

I'm on Windows 10, Python version 3.8.10, Tkinter 8.6.9


Solution

  • In addition to the answer of "acw1668". Creating a new TkImage is costly and you should just update your old image with the new data. Therefore it makes sense to use image.configure(data=data_string), example below:

    def change_color(event):
        global svg_string
        fill_color = "blue" if "red" in svg_string else "red"
        svg_string = '<svg viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="{}"/></svg>'.format(fill_color)
        id_, img = image_store[0], svg_store[0]
        img.configure(data=svg_string)
        canvas.itemconfig(id_, image=img)