Trouble with addon changing image filepath

i’m attempting to make an addon for the first time (just a basic one that changes the images of different anki icons) and i’ve looked at how some simple addons work and came up with this (all i’m doing is (theoretically) copying a function from the file, changing the filepath within that function, then overwriting the original function in

from aqt.deckbrowser import DeckTreeNode, RenderDeckNodeContext, DeckBrowser

import os
addon_path = os.path.dirname(__file__)
addonfoldername = os.path.basename(addon_path)

def _render_deck_node(self, node: DeckTreeNode, ctx: RenderDeckNodeContext) -> str:
        if node.collapsed:
            prefix = "+"
            prefix = "-"

        due = node.review_count + node.learn_count

        def indent() -> str:
            return " " * 6 * (node.level - 1)

        if node.deck_id == ctx.current_deck_id:
            klass = "deck current"
            klass = "deck"

        buf = "<tr class='%s' id='%d'>" % (klass, node.deck_id)
        # deck link
        if node.children:
            collapse = (
                "<a class=collapse href=# onclick='return pycmd(\"collapse:%d\")'>%s</a>"
                % (node.deck_id, prefix)
            collapse = "<span class=collapse></span>"
        if node.filtered:
            extraclass = "filtered"
            extraclass = ""
        buf += """
        <td class=decktd colspan=5>%s%s<a class="deck %s"
        href=# onclick="return pycmd('open:%d')">%s</a></td>""" % (
        # due counts
        def nonzeroColour(cnt: int, klass: str) -> str:
            if not cnt:
                klass = "zero-count"
            return f'<span class="{klass}">{cnt}</span>'

        buf += "<td align=right>%s</td><td align=right>%s</td>" % (
            nonzeroColour(due, "review-count"),
            nonzeroColour(node.new_count, "new-count"),
        # options
        buf += (
            "<td align=center class=opts><a onclick='return pycmd(\"opts:%d\");'>"
            f"<img src='/_addons/{addonfoldername}/files/gears.svg' class=gears></a></td></tr>" % node.deck_id
        # children
        if not node.collapsed:
            for child in node.children:
                buf += self._render_deck_node(child, ctx)
        return buf

DeckBrowser._render_deck_node = _render_deck_node

instead of replacing the gear icon with the one in the addon folder, the gear icon just disappears. i’ve used webinspector to check if the filepath is correct, and it seems to be fine (i’ve tried using the full filepath rather than starting from "/_addons/“, but the image still doesn’t show up)

any thoughts as to why it doesnt work?

update: other icons such as the bold text icon dont seem to work either (image disappears rather than changes to the new image)

In order for webview to load external files located within the addon directory, such as css, js, and images, you have to give it permission to access those files by mw.addonManager.setWebExports(). Try adding the following:

from aqt import mw

mw.addonManager.setWebExports(__name__, r"files/.*\.svg")

By the way, if you just want to replace the icon image, I think it might be better to do that by adding a css file using the webview_will_set_content hook rather than monkey-patching a function.

└── files
    ├── gears-icon.css
    └── gears.svg


.gears {
    content: url("gears.svg");

from typing import Any, Optional
import aqt, r"files/.*\.(css|svg)")
addon_package =

def on_webview_will_set_content(
    web_content: aqt.webview.WebContent, context: Optional[Any]
) -> None:
    if isinstance(context, aqt.deckbrowser.DeckBrowser):


yep the fix worked, thanks :slight_smile:

and thanks for the extra suggestions too !