ui.appicon

The application’s own icon: the artist’s mark, on its own ground.

This is the commissioned mark, not a stand-in. docs/132-logo.md records the two stand-ins that came before it – Font Awesome’s hat-wizard, then game-icons.net’s pointy-hat – each chosen only because no artist existed yet. One delivered on 2026-09-05: a gold pentacle inside a ring, on a dark square it already carries as its own ground. Donald chose this over a transparent glyph and over a light/dark pair, on the grounds that a square that supplies its own background reads the same on a light desktop, a dark one and in a macOS dock – nothing has to guess what is behind it.

The drawing stopped being generated, and that is a deliberate trade. Every icon before this one was painted at run time from ui/icons.py’s path data, specifically so no raster could drift from the glyph the program painted elsewhere. There is nowhere else in the program that paints this mark, so that property bought nothing here, and the mark is a fixed, committed SVG instead: assets/logo/mark.svg, the artist’s file, verbatim, beside his own PNG exports of it at 80, 150, 200 and 500.

Below 500 pixels the icon is his PNG scaled down, not his SVG rendered. image() has the reason and the measurement; the short form is that Qt’s SVG renderer drops the mark’s hairline rings at a taskbar size and the artist’s exporter did not. Every size is still made on its own terms from the smallest delivered file no smaller than it, the way tools/genicons.py always insisted on – so a 16 is never a squeezed 256 of ours.

The assets are not modified. Resizing a delivered file into the sizes a taskbar or a .ico wants is placement, not art; nothing here moves a point of the artist’s drawing or a pixel of his export.

This mark carries no CC BY obligation. It is Donald’s own commissioned work, not lifted from a licensed set, so there is no attribution to discharge here the way pointy-hat’s belonged to Lorc at game-icons.net – see ui/icons.py’s own docstring, where that credit still lives for the icons still drawn from it.

Module Attributes

ASSET

The artist's own file, committed rather than left under work/, which is gitignored and has been lost twice.

RASTERS

The artist's own PNG exports of the same mark, by side, byte for byte as he delivered them (Marks/Color/Color Mark NxN.png, 2026-08-31).

WINDOW_SIZES

What setWindowIcon gets.

Functions

app_icon()

The window and taskbar icon, one pixmap per size.

image(size)

The icon at size: the artist's smallest PNG no smaller than it, scaled down whole; the SVG only above his largest PNG.

pixmap(size)

The icon for a widget, drawn at the ratio the application is running at.

raster_side(size)

Which delivered PNG a size square is scaled from: the smallest no smaller than it, so a raster is only ever scaled down.

render_svg(size)

The SVG rendered whole into a size square.

source(size)

The file the icon at size is made from.

ui.appicon.ASSET = PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/wish-goldbox/checkouts/latest/assets/logo/mark.svg')

The artist’s own file, committed rather than left under work/, which is gitignored and has been lost twice. Resolved through goldbox.assets so a frozen build finds it under sys._MEIPASS – a path built from __file__ here is what shipped a black square on the Windows taskbar, #351 (The Windows build shows no logo in About and a black square on the taskbar, because the artist's SVGs are not in the package).

ui.appicon.RASTERS: dict[int, pathlib.Path] = {80: PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/wish-goldbox/checkouts/latest/assets/logo/mark-80.png'), 150: PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/wish-goldbox/checkouts/latest/assets/logo/mark-150.png'), 200: PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/wish-goldbox/checkouts/latest/assets/logo/mark-200.png'), 500: PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/wish-goldbox/checkouts/latest/assets/logo/mark-500.png')}

The artist’s own PNG exports of the same mark, by side, byte for byte as he delivered them (Marks/Color/Color Mark NxN.png, 2026-08-31). Four separate literal calls rather than a loop, because tests/test_assets.py reads asset_path(...) calls out of the source to check each file is in wish.spec’s datas, and a path assembled from a variable is one it cannot see.

ui.appicon.WINDOW_SIZES = (16, 20, 24, 32, 40, 48, 64, 128, 256)

What setWindowIcon gets. Qt picks the nearest entry and scales the rest, so the sizes the title bar, Alt-Tab and the taskbar button ask for are all present as their own square, each scaled from a delivered file.

ui.appicon.app_icon()[source]

The window and taskbar icon, one pixmap per size.

Return type:

PyQt6.QtGui.QIcon

ui.appicon.image(size)[source]

The icon at size: the artist’s smallest PNG no smaller than it, scaled down whole; the SVG only above his largest PNG.

Do not simplify this back to rendering the SVG at every size. It was that, and it looked right in a checkout and wrong on a Windows taskbar: the mark’s two rings are embedded hairline bitmaps in the artist’s SVG, and Qt’s renderer drops most of each below 48 pixels, leaving scattered dots where a circle should be. His own PNG export kept them as a faint circle. Measured on 2026-09-06 around the outer ring at 24 pixels, the scaled 80 lights 72 of 72 sample points and the SVG render lights 20; tests/test_appicon.py::test_the_ring_is_a_circle_at_the_taskbar_sizes keeps both figures. A taskbar button is 24 logical pixels, 32 at 150 % scaling, so those two sizes are the whole reason for the rule, and the rule is applied at every size so no size is a downscale of a render that lost something. Donald chose it off the comparison sheet as row B (docs/132-logo.md §7).

A raster that is missing decodes to a null QImage, and scaling one gives an empty square – the black-square failure #351 (The Windows build shows no logo in About and a black square on the taskbar, because the artist's SVGs are not in the package) was about. The SVG stands in for it here so a player still gets a mark; tests/test_assets.py is what keeps the PNGs in the package, so the stand-in should never run.

Parameters:

size (int)

Return type:

PyQt6.QtGui.QImage

ui.appicon.pixmap(size)[source]

The icon for a widget, drawn at the ratio the application is running at.

iconpaint.icon_pixmap does the same for the small glyphs, and for the same reason: on a 2x display a 64-logical picture asked for as 64 device pixels is a soft one.

Parameters:

size (int)

Return type:

PyQt6.QtGui.QPixmap

ui.appicon.raster_side(size)[source]

Which delivered PNG a size square is scaled from: the smallest no smaller than it, so a raster is only ever scaled down. None above the largest, where the SVG is the only source there is.

Parameters:

size (int)

Return type:

int | None

ui.appicon.render_svg(size)[source]

The SVG rendered whole into a size square. What image uses above the largest PNG, and kept callable on its own so a test can show what the renderer does to the rings at a taskbar size.

Parameters:

size (int)

Return type:

PyQt6.QtGui.QImage

ui.appicon.source(size)[source]

The file the icon at size is made from.

Parameters:

size (int)

Return type:

pathlib.Path