# Icons and metadata

> Give a packaged Pyzed app its name, icon (.ico, .icns and the window icon made from one PNG), version, publisher, bundle identifier and file properties on Windows, macOS and Linux.

Source: https://albakore.github.io/pyzed/packaging/icons-metadata/

An app's identity is a few small things: its **name**, its **icon**, its **version**, who **publishes** it and, on
macOS, its **bundle identifier**. All of them are set in `[tool.pyzed.build]` of `pyproject.toml`, by hand or from the
[Export dialog](https://albakore.github.io/pyzed/editor/exporting/#general) of Pyzed Design, and `pyzed build` writes them where each system
looks.

## Name, version and details

```toml title="pyproject.toml"
[tool.pyzed.build]
name = "Shop"                   # Shop.exe, Shop.app, dist/Shop/
version = "1.2.0"               # default: [project] version
publisher = "Acme"
identifier = "com.acme.shop"    # default: com.<publisher>.<name>
description = "Point of sale"   # default: [project] description
copyright = "© 2026 Acme"
```

| What | Windows | macOS | Linux |
| --- | --- | --- | --- |
| `name` | `Shop.exe`, the product name | `Shop.app`, `CFBundleDisplayName` | The executable |
| `version` | File and product version (Details tab) | `CFBundleShortVersionString` and `CFBundleVersion` | - |
| `publisher` | Company name | - | - |
| `identifier` | - | `CFBundleIdentifier` | The windows' app id (see [The window icon](#the-window-icon)) |
| `description` | File description | - | - |
| `copyright` | Legal copyright | `NSHumanReadableCopyright` | - |

With PyInstaller, `pyzed build` writes a Windows version file (`build/pyzed/version.txt`) and fills in the bundle's
`Info.plist` after the build; Nuitka gets the same values as options.

Use a reverse domain you own as the `identifier`. macOS keeps the app's preferences and privacy permissions under it,
so do not change it between releases.

The **window title** is not here: it is the form's **Title** in the designer, or `title` of your `pz.App`.

If your app shows its version or [checks for updates](https://albakore.github.io/pyzed/packaging/updates/), keep the version in your code too,
for example in your package's `__init__.py`, and update it together with `pyproject.toml`:

```python title="src/shop/__init__.py"
__version__ = "1.2.0"
```

**Why not importlib.metadata?:**

`importlib.metadata.version("shop")` reads the installed package's metadata, which PyInstaller does not copy into the
bundle unless you ask it to (`args = ["--copy-metadata", "shop"]`). A constant in your code always works.

## The icon

Give `icon` one square picture, ideally a PNG of 1024 × 1024 pixels with a transparent background:

```toml title="pyproject.toml"
[tool.pyzed.build]
icon = "assets/icon.png"
```

`pyzed build` makes every file the systems want from it, with Pyzed's own image code (no Pillow, `sips` or
`iconutil` needed), in `build/pyzed/`:

| File | Sizes inside | Used for |
| --- | --- | --- |
| `app-icon.ico` | 16, 20, 24, 32, 40, 48, 64, 128, 256 px | The Windows `.exe`: Explorer, the taskbar, Alt+Tab |
| `app-icon.icns` | 16 to 1024 px, with Retina sizes | The macOS bundle: Finder and the Dock |
| `app-icon.png` | 256 px | The icon of the app's windows (shipped inside the app) |

Small sizes are made by scaling the big picture down. If the 16 and 32 pixel versions look blurry, draw a simpler
version for them yourself (below), or keep the design bold. The [Export dialog](https://albakore.github.io/pyzed/editor/exporting/#icon)
previews every size.

An `.ico` or `.icns` file given as `icon` is used as it is, for its own system.

### Making the icon files yourself

For full control over each size, make the files with your favorite tool from a 1024 × 1024 PNG:

**Windows .ico (Python):**

```python
from PIL import Image  # pip install pillow

Image.open("icon.png").save("icon.ico", sizes=[(s, s) for s in (16, 24, 32, 48, 64, 128, 256)])
```

**macOS .icns:**

```bash
mkdir icon.iconset
for s in 16 32 128 256 512; do
  sips -z $s $s icon.png --out icon.iconset/icon_${s}x${s}.png
  sips -z $((s*2)) $((s*2)) icon.png --out icon.iconset/icon_${s}x${s}@2x.png
done
iconutil -c icns icon.iconset   # writes icon.icns (macOS only)
```

## The window icon

The `.exe` and `.app` icons are the ones of the **file**. A packaged app also gives its **windows** the icon (the title
bar and the taskbar on Windows and Linux): the launcher sets `pz.Window.icon` to `app-icon.png`. macOS always shows
the bundle's icon in the Dock.

In your own code, any window can have its icon, a path to an image or its bytes:

```python
import pyzed as pz

class Shop(pz.App):
    icon = "assets/icon.png"
    app_id = "com.acme.shop"   # Linux: matches the .desktop file
```

On Linux, `app_id` names the app to the desktop. Wayland takes the icon of the taskbar and the dock from the
`.desktop` file whose name matches it (`com.acme.shop.desktop`, see
[Linux](https://albakore.github.io/pyzed/packaging/linux/#add-it-to-the-application-menu)); X11 uses `icon` directly.

## Windows file properties by hand

To write a version resource of your own instead of the generated one (more fields, other languages), capture one from an
existing `.exe` with `pyi-grab_version` and pass it to PyInstaller:

```toml title="pyproject.toml"
[tool.pyzed.build.pyinstaller]
args = ["--version-file", "assets/version.txt"]
```

Paths in `args` reach PyInstaller unchanged, and PyInstaller writes its spec file to `build/pyzed/`: if it cannot find
the file, give its absolute path.
