Icon
Usage
Icons and Images are not the same!
Toga draws a distinction between an Icon and an Image. An Icon is small, square, and might vary between platforms. It is a visual element that is often used as part of an interactive element such as a button, toolbar item, or tab selector - but the Icon itself isn't an interactive element.
An Image, on the other hand, can have an arbitrary size or aspect ratio, and is not platform dependent - the same image will be used on every platform. An Image is not an interactive element, because there is no visual cue to the user that the image can be interacted with.
If you are looking for a widget that the user can click on, you're looking for a widget configured to use an Icon (probably Button), not an on_press handler on an Image or ImageView.
The filename specified for an icon should be specified without an extension; the platform will determine an appropriate extension, and may also modify the name of the icon to include a platform and/or size qualifier.
The following formats are supported (in order of preference):
- Android - PNG
- iOS - ICNS, PNG, BMP, ICO
- macOS - ICNS, PNG, PDF
- GTK - PNG, ICO, ICNS; 512, 256, 128, 72, 64, 32, and 16px variants of each icon can be provided;
- Windows - ICO, PNG, BMP
The first matching icon of the most specific platform, with the most specific size will be used. For example, on Windows, specifying an icon of myicon will cause Toga to look for (in order):
myicon-windows.ico
myicon.ico
myicon-windows.png
myicon.png
myicon-windows.bmp
myicon.bmp
On GTK, Toga will perform this lookup for each variant size, falling back to a name without a size specifier if a size-specific variant has not been provided. For example, when resolving the 32px variant, Toga will look for (in order):
myicon-linux-32.png
myicon-32.png
myicon-linux-32.ico
myicon-32.ico
myicon-linux-32.icns
myicon-32.icns
myicon.png
myicon.ico
Any icon that is found will be resized to the required size. Toga will generate any GTK icon variants that are not available from the highest resolution provided (e.g., if no 128px variant can be found, one will be created by scaling the highest resolution variant that is available).
An icon is guaranteed to have an implementation, regardless of the path specified. If you specify a path and no matching icon can be found, Toga will output a warning to the console, and return DEFAULT_ICON. The only exception to this is if an icon file is found, but it cannot be loaded (e.g., due to a file format or permission error). In this case, an error will be raised.
When you provide a relative path, Toga resolves it relative to the module that defines your toga.App subclass. If you instantiate toga.App directly (without subclassing), there is no separate “app module”, so the relative path is resolved relative to the Toga installation instead of your project.
Reference
Source code in core/src/toga/icons.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209 | class Icon:
APP_ICON = CachedIcon(_APP_ICON)
"""The application icon.
The application icon will be loaded from `resources/<app name>` (where `<app
name>` is the value of [`toga.App.app_name`][]).
If this resource cannot be found, and the app has been packaged as a binary, the
icon from the application binary will be used as a fallback.
Otherwise, [`Icon.DEFAULT_ICON`][toga.Icon.DEFAULT_ICON] will be used.
"""
DEFAULT_ICON = CachedIcon("toga", system=True)
"""The default icon used as a fallback - Toga's "Tiberius the yak" icon."""
OPTION_CONTAINER_DEFAULT_TAB_ICON = CachedIcon("optioncontainer-tab", system=True)
"""The default icon used to decorate option container tabs."""
def __init__(
self,
path: str | Path,
*,
system: bool = False, # Deliberately undocumented; for internal use only
):
"""Create a new icon.
:param path: Base filename for the icon. Should not contain an extension
(If an extension is specified, it will be ignored). Paths can be absolute
or relative. Relative paths start from the folder where your [`toga.App`][]
subclass is defined.
If the icon cannot be found, the default icon will be
[Icon.DEFAULT_ICON][toga.Icon.DEFAULT_ICON].
If icon is found, but cannot be loaded (due to a file format
or permission error), a warning will be emitted and
[Icon.DEFAULT_ICON][toga.Icon.DEFAULT_ICON] will be used.
:param system: **For internal use only**
"""
self.factory = get_factory()
try:
# Try to load the icon with the given path snippet. If the request is for
# the app icon, use `resources/<app name>` as the path.
if path is _APP_ICON:
self.path = Path(f"resources/{toga.App.app.app_name}")
else:
self.path = Path(path)
self.system = system
if self.system:
if isinstance(self.factory, Factory):
resource_path = Path(self.factory.resources.__file__).parent
else:
# doesn't get covered in core tests
resource_path = (
Path(self.factory.__file__).parent / "resources"
) # pragma: no cover
else:
resource_path = toga.App.app.paths.app
full_path: dict[str, Path] | Path
if self.factory.Icon.SIZES:
full_path = {}
for size in self.factory.Icon.SIZES:
try:
full_path[size] = self._full_path(
size=size,
extensions=self.factory.Icon.EXTENSIONS,
resource_path=resource_path,
)
except FileNotFoundError:
# This size variant wasn't found; we can skip it
pass
else:
full_path = self._full_path(
size=None,
extensions=self.factory.Icon.EXTENSIONS,
resource_path=resource_path,
)
self._impl = self.factory.Icon(interface=self, path=full_path)
except (FileNotFoundError, ValueError) as exc:
# Icon path couldn't be resolved or loaded. If the path is the sentinel
# for the app icon, and this isn't running as a script, fall back to the
# application binary.
if path is _APP_ICON:
if isinstance(exc, ValueError):
fallback = (
"application binary icon"
if toga.App.app.is_bundled
else "default icon"
)
print(
f"WARNING: Unable to load app icon {self.path}; "
f"falling back to {fallback}"
)
if toga.App.app.is_bundled:
try:
# Use the application binary's icon
self._impl = self.factory.Icon(interface=self, path=None)
except (FileNotFoundError, ValueError):
# Can't find the application binary's icon.
print(
"WARNING: Can't find app icon; falling back to default icon"
)
self._impl = self.DEFAULT_ICON._impl
else:
self._impl = self.DEFAULT_ICON._impl
else:
if isinstance(exc, FileNotFoundError):
print(
f"WARNING: Can't find icon {self.path}; "
f"falling back to default icon"
)
else:
print(
f"WARNING: Unable to load icon {self.path}; "
f"falling back to default icon"
)
self._impl = self.DEFAULT_ICON._impl
def _full_path(
self,
size: str | None,
extensions: Iterable[str],
resource_path: Path,
) -> Path:
platform = toga.platform.current_platform
if size:
for extension in extensions:
for filename in [
f"{self.path.stem}-{platform}-{size}{extension}",
f"{self.path.stem}-{size}{extension}",
]:
icon_path = resource_path / self.path.parent / filename
if icon_path.exists():
return icon_path
# Look for size-less alternatives
for extension in extensions:
for filename in [
f"{self.path.stem}-{platform}{extension}",
f"{self.path.stem}{extension}",
]:
icon_path = resource_path / self.path.parent / filename
if icon_path.exists():
return icon_path
raise FileNotFoundError(f"Can't find icon {self.path}")
def __eq__(self, other: object) -> bool:
return isinstance(other, Icon) and other._impl.path == self._impl.path
|
APP_ICON = CachedIcon(_APP_ICON)
class-attribute
instance-attribute
The application icon.
The application icon will be loaded from resources/<app name> (where <app
name> is the value of toga.App.app_name).
If this resource cannot be found, and the app has been packaged as a binary, the
icon from the application binary will be used as a fallback.
Otherwise, Icon.DEFAULT_ICON will be used.
DEFAULT_ICON = CachedIcon('toga', system=True)
class-attribute
instance-attribute
The default icon used as a fallback - Toga's "Tiberius the yak" icon.
OPTION_CONTAINER_DEFAULT_TAB_ICON = CachedIcon('optioncontainer-tab', system=True)
class-attribute
instance-attribute
The default icon used to decorate option container tabs.
__init__(path, *, system=False)
Create a new icon.
:param path: Base filename for the icon. Should not contain an extension
(If an extension is specified, it will be ignored). Paths can be absolute
or relative. Relative paths start from the folder where your toga.App
subclass is defined.
If the icon cannot be found, the default icon will be
[Icon.DEFAULT_ICON][toga.Icon.DEFAULT_ICON].
If icon is found, but cannot be loaded (due to a file format
or permission error), a warning will be emitted and
[Icon.DEFAULT_ICON][toga.Icon.DEFAULT_ICON] will be used.
:param system: For internal use only
Source code in core/src/toga/icons.py
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177 | def __init__(
self,
path: str | Path,
*,
system: bool = False, # Deliberately undocumented; for internal use only
):
"""Create a new icon.
:param path: Base filename for the icon. Should not contain an extension
(If an extension is specified, it will be ignored). Paths can be absolute
or relative. Relative paths start from the folder where your [`toga.App`][]
subclass is defined.
If the icon cannot be found, the default icon will be
[Icon.DEFAULT_ICON][toga.Icon.DEFAULT_ICON].
If icon is found, but cannot be loaded (due to a file format
or permission error), a warning will be emitted and
[Icon.DEFAULT_ICON][toga.Icon.DEFAULT_ICON] will be used.
:param system: **For internal use only**
"""
self.factory = get_factory()
try:
# Try to load the icon with the given path snippet. If the request is for
# the app icon, use `resources/<app name>` as the path.
if path is _APP_ICON:
self.path = Path(f"resources/{toga.App.app.app_name}")
else:
self.path = Path(path)
self.system = system
if self.system:
if isinstance(self.factory, Factory):
resource_path = Path(self.factory.resources.__file__).parent
else:
# doesn't get covered in core tests
resource_path = (
Path(self.factory.__file__).parent / "resources"
) # pragma: no cover
else:
resource_path = toga.App.app.paths.app
full_path: dict[str, Path] | Path
if self.factory.Icon.SIZES:
full_path = {}
for size in self.factory.Icon.SIZES:
try:
full_path[size] = self._full_path(
size=size,
extensions=self.factory.Icon.EXTENSIONS,
resource_path=resource_path,
)
except FileNotFoundError:
# This size variant wasn't found; we can skip it
pass
else:
full_path = self._full_path(
size=None,
extensions=self.factory.Icon.EXTENSIONS,
resource_path=resource_path,
)
self._impl = self.factory.Icon(interface=self, path=full_path)
except (FileNotFoundError, ValueError) as exc:
# Icon path couldn't be resolved or loaded. If the path is the sentinel
# for the app icon, and this isn't running as a script, fall back to the
# application binary.
if path is _APP_ICON:
if isinstance(exc, ValueError):
fallback = (
"application binary icon"
if toga.App.app.is_bundled
else "default icon"
)
print(
f"WARNING: Unable to load app icon {self.path}; "
f"falling back to {fallback}"
)
if toga.App.app.is_bundled:
try:
# Use the application binary's icon
self._impl = self.factory.Icon(interface=self, path=None)
except (FileNotFoundError, ValueError):
# Can't find the application binary's icon.
print(
"WARNING: Can't find app icon; falling back to default icon"
)
self._impl = self.DEFAULT_ICON._impl
else:
self._impl = self.DEFAULT_ICON._impl
else:
if isinstance(exc, FileNotFoundError):
print(
f"WARNING: Can't find icon {self.path}; "
f"falling back to default icon"
)
else:
print(
f"WARNING: Unable to load icon {self.path}; "
f"falling back to default icon"
)
self._impl = self.DEFAULT_ICON._impl
|
When specifying an Icon, you can
provide:
- a string specifying an absolute or relative path;
- an absolute or relative [
pathlib.Path][]
object; or
- an instance of
toga.Icon.
If a relative path is provided, it will be anchored relative to the
module that defines your Toga application class.