Skip to content

Command

A menu item or toolbar action.

Screenshot not available

Screenshot not available

Screenshot not available

Screenshot not available

Screenshot not available

Screenshot not available

Screenshot not available

Screenshot not available

Usage

Aside from event handlers on widgets, most GUI toolkits also provide other ways for the user to give instructions to an app. In Toga, these UI patterns are supported by the Command class.

A command encapsulates a piece of functionality that the user can invoke - no matter how they invoke it. It doesn't matter if they select a menu item, press a button on a toolbar, or use a key combination - the functionality is wrapped up in a Command.

Adding commands

Commands are added to an app using the properties toga.App.commands and toga.MainWindow.toolbar. Toga then takes control of ensuring that the command is exposed to the user in a way that they can access. On desktop platforms, this may result in a command being added to a menu.

Commands can be organized into a Group of similar commands. Groups are hierarchical, so a group can contain a sub-group, which can contain a sub-group, and so on. Inside a group, commands can be organized into sections.

For example:

import toga

def callback(sender, **kwargs):
    print("Command activated")

stuff_group = toga.Group('Stuff', order=40)

cmd1 = toga.Command(
    callback,
    text='Example command',
    tooltip='Tells you when it has been activated',
    shortcut=toga.Key.MOD_1 + 'k',
    icon='icons/pretty.png',
    group=stuff_group,
    section=0
)
cmd2 = toga.Command(
    ...
)
...

app.commands.add(cmd1, cmd4, cmd3)
app.main_window.toolbar.add(cmd2, cmd3)

This code defines a command cmd1 that will be placed in the first section of the "Stuff" group. It can be activated by pressing CTRL-k (or CMD-K on a Mac).

The definitions for cmd2, cmd3, and cmd4 have been omitted, but would follow a similar pattern.

It doesn't matter what order you add commands to the app - the group, section and order will be used to display the commands in the right order.

If a command is added to a toolbar, it will automatically be added to the app as well. It isn't possible to have functionality exposed on a toolbar that isn't also exposed by the app. So, cmd2 will be added to the app, even though it wasn't explicitly added to the app commands.

Removing commands

Commands can be removed using set-like and dictionary-like APIs. The set-like APIs use the command instance; the dictionary-like APIs use the command ID:

# Remove the app using the instance
app.commands.remove(cmd_1)

# Remove a command by ID
del app.commands["Some-Command-ID"]

Standard commands

Each command has an id attribute. This is set when the command is defined; if no ID is provided, a random ID will be generated for the Command. This identifier can be used to retrieve a command from toga.App.commands and toga.MainWindow.toolbar.

These command IDs are also used to create standard commands. These are commands that are expected functionality in most applications, such as ABOUT and EXIT, as well as document management commands such as NEW, OPEN and SAVE.

These commands are automatically added to your app, depending on platform requirements and app definition. For example, mobile apps won't have an Exit command as mobile apps don't have a concept of "exiting". Document management commands will be automatically added if your app defines document types.

The label, shortcut, grouping and ordering of these commands is platform dependent. For example, on macOS, the EXIT command will be labeled "Quit My App", and have a shortcut of Command-q; on Windows, the command will be labeled "Exit", and won't have a keyboard shortcut.

Any automatically added standard commands will be installed before your app's startup() method is invoked. If you wish to remove or modify and a standard app command, you can use the standard command's ID to retrieve the command instance from toga.App.commands. If you wish to add or override a standard command that hasn't been installed by default (for example, to add an Open command without defining a document type), you can use the toga.Command.standard() method to create an instance of the standard command, and add that command to your app:

import toga

class MyApp(toga.app):
    def startup(self):
        ...
        # Delete the default Preferences command
        del self.commands[toga.Command.PREFERENCES]

        # Modify the text of the "About" command
        self.commands[toga.Command.ABOUT].text = "I'm Customized!!"

        # Add an Open command
        custom_open = toga.Command.standard(
            self,
            toga.Command.OPEN,
            action=self.custom_open
        )

        self.commands.add(custom_open)

Reference

Source code in core/src/toga/command.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
class Command:
    ABOUT: str = "about"
    """
    An identifier for the standard "About" menu item. This command is always
    installed by default. Uses [`toga.App.about()`][toga.App.about] as the default
    action.
    """
    EXIT: str = "request_exit"
    """
    An identifier for the standard "Exit" menu item. This command may be installed by
    default, depending on platform requirements. Uses
    [`toga.App.request_exit()`][toga.App.request_exit]
    as the default action.
    """
    NEW: str = "documents.new"
    """
    An identifier for the standard "New" menu item. This constant will be used for
    the default document type for your app; if you specify more than one document
    type, the command for the subsequent commands will have a colon and the first
    extension for that data type appended to the ID. Uses
    [`toga.documents.DocumentSet.new()`][toga.documents.DocumentSet.new] as the
    default action.
    """
    OPEN: str = "documents.request_open"
    """
    An identifier for the standard "Open" menu item. This command will be
    automatically installed if your app declares any document types. Uses
    [`toga.documents.DocumentSet.request_open()`][toga.documents.DocumentSet.request_open]
    as the default action.
    """
    PREFERENCES: str = "preferences"
    """
    An identifier for the standard "Preferences" menu item. The Preferences item is
    not installed by default. If you install it manually, it will attempt to use
    `toga.App.preferences()` as the default action; your app will need to define
    this method, or provide an explicit value for the action.
    """
    SAVE: str = "documents.save"
    """
    An identifier for the standard "Save" menu item. This command will be
    automatically installed if your app declares any document types. Uses
    [`toga.documents.DocumentSet.save()`][toga.documents.DocumentSet.save] as the
    default action.
    """
    SAVE_AS: str = "documents.save_as"
    """
    An identifier for the standard "Save As..." menu item. This command will be
    automatically installed if your app declares any document types. Uses
    [`toga.documents.DocumentSet.save_as()`][toga.documents.DocumentSet.save_as] as the
    default action.
    """
    SAVE_ALL: str = "documents.save_all"
    """
    An identifier for the standard "Save All" menu item. This command will be
    automatically installed if your app declares any document types. Uses
    [`toga.documents.DocumentSet.save_all()`][toga.documents.DocumentSet.save_all] as
    the default action.
    """
    VISIT_HOMEPAGE: str = "visit_homepage"
    """
    An identifier for the standard "Visit Homepage" menu item. This command may be
    installed by default, depending on platform requirements. Uses
    [`toga.App.visit_homepage()`][toga.App.visit_homepage] as the default action.
    """

    def __init__(
        self,
        action: ActionHandler | None,
        text: str,
        *,
        shortcut: str | Key | None = None,
        tooltip: str | None = None,
        icon: IconContentT | None = None,
        group: Group = Group.COMMANDS,
        section: int = 0,
        order: int = 0,
        enabled: bool = True,
        id: str = None,
    ):
        """
        Create a new Command.

        Commands may not use all the arguments - for example, on some platforms, menus
        will contain icons; on other platforms they won't.

        :param action: A handler to invoke when the command is activated. If this is
            `None`, the command will be disabled.
        :param text: A label for the command.
        :param shortcut: A key combination that can be used to invoke the command.
        :param tooltip: A short description of what the command will do.
        :param icon: The [icon content][toga.icons.IconContentT] that can be used to
            decorate the command if the platform requires.
        :param group: The group to which this command belongs.
        :param section: The section where the command should appear within its group.
        :param order: The position where the command should appear within its section.
            If multiple items have the same group, section and order, they will be
            sorted alphabetically by their text.
        :param enabled: Is the Command currently enabled?
        :param id: A unique identifier for the command.
        """
        self._id = f"cmd-{_py_id(self)}" if id is None else id
        self.text = text

        self.shortcut = shortcut
        self.tooltip = tooltip
        self.icon = icon

        self.group = group
        self.section = section
        self.order = order

        self.action = action

        self.factory = get_factory()
        self._impl = self.factory.Command(interface=self)

        self._enabled = True
        self.enabled = enabled

    @classmethod
    def standard(cls, app: App, id, **kwargs):
        """Create an instance of a standard command for the provided app.

        The default action for the command will be constructed using the value of the
        command's ID as an attribute of the app object. If a method or co-routine
        matching that name doesn't exist, a value of `None` will be used as the
        default action.

        :param app: The app for which the standard command will be created.
        :param id: The ID of the standard command to create.
        :param kwargs: Overrides for any default properties of the standard command.
            Accepts the same arguments as the [`Command`][toga.Command] constructor.
        """
        # The value of the ID constant is the method on the app instance
        cmd_kwargs = {"id": id}
        try:
            attrs = id.split(".")
            action = getattr(app, attrs[0])
            for attr in attrs[1:]:
                action = getattr(action, attr)
            cmd_kwargs["action"] = simple_handler(action)
        except AttributeError:
            cmd_kwargs["action"] = None

        # Get the platform-specific keyword arguments for the command
        factory = get_factory()
        platform_kwargs = factory.Command.standard(app, id)

        if platform_kwargs:
            cmd_kwargs.update(platform_kwargs)
            cmd_kwargs.update(kwargs)

            # Return the command instance
            return Command(**cmd_kwargs)
        else:
            # Standard command doesn't exist on the platform.
            return None

    @property
    def id(self) -> str:
        """A unique identifier for the command."""
        return self._id

    @property
    def key(self) -> tuple[tuple[int, int, str], ...]:
        """A unique tuple describing the path to this command.

        Each element in the tuple describes the (section, order, text) for the
        groups that must be navigated to invoke this action.
        """
        return self.group.key + ((self.section, self.order, self.text),)

    @property
    def enabled(self) -> bool:
        """Is the command currently enabled?"""
        return self._enabled

    @enabled.setter
    def enabled(self, value: bool) -> None:
        self._enabled = value and getattr(self.action, "_raw", True) is not None
        self._impl.set_enabled(value)

    @property
    def icon(self) -> Icon | None:
        """The Icon for the command.

        Can be specified as any valid [icon content][toga.icons.IconContentT].
        """
        return self._icon

    @icon.setter
    def icon(self, icon_or_name: IconContentT | None) -> None:
        if isinstance(icon_or_name, Icon) or icon_or_name is None:
            self._icon = icon_or_name
        else:
            self._icon = Icon(icon_or_name)

    @property
    def action(self) -> ActionHandler | None:
        """The Action attached to the command."""
        return self._action

    @action.setter
    def action(self, action: ActionHandler | None) -> None:
        """Set the action attached to the command

        Needs to be a valid ActionHandler or `None`
        """
        self._action = wrapped_handler(self, action)

    def __lt__(self, other: object) -> bool:
        if not isinstance(other, Group | Command):
            return False
        return self.key < other.key

    def __gt__(self, other: object) -> bool:
        if not isinstance(other, Group | Command):
            return False
        return other < self

    def __repr__(self) -> str:
        return (
            f"<Command text={self.text!r} "
            f"group={self.group} "
            f"section={self.section} "
            f"order={self.order}>"
        )

ABOUT = 'about' class-attribute instance-attribute

An identifier for the standard "About" menu item. This command is always installed by default. Uses toga.App.about() as the default action.

EXIT = 'request_exit' class-attribute instance-attribute

An identifier for the standard "Exit" menu item. This command may be installed by default, depending on platform requirements. Uses toga.App.request_exit() as the default action.

NEW = 'documents.new' class-attribute instance-attribute

An identifier for the standard "New" menu item. This constant will be used for the default document type for your app; if you specify more than one document type, the command for the subsequent commands will have a colon and the first extension for that data type appended to the ID. Uses toga.documents.DocumentSet.new() as the default action.

OPEN = 'documents.request_open' class-attribute instance-attribute

An identifier for the standard "Open" menu item. This command will be automatically installed if your app declares any document types. Uses toga.documents.DocumentSet.request_open() as the default action.

PREFERENCES = 'preferences' class-attribute instance-attribute

An identifier for the standard "Preferences" menu item. The Preferences item is not installed by default. If you install it manually, it will attempt to use toga.App.preferences() as the default action; your app will need to define this method, or provide an explicit value for the action.

SAVE = 'documents.save' class-attribute instance-attribute

An identifier for the standard "Save" menu item. This command will be automatically installed if your app declares any document types. Uses toga.documents.DocumentSet.save() as the default action.

SAVE_ALL = 'documents.save_all' class-attribute instance-attribute

An identifier for the standard "Save All" menu item. This command will be automatically installed if your app declares any document types. Uses toga.documents.DocumentSet.save_all() as the default action.

SAVE_AS = 'documents.save_as' class-attribute instance-attribute

An identifier for the standard "Save As…" menu item. This command will be automatically installed if your app declares any document types. Uses toga.documents.DocumentSet.save_as() as the default action.

VISIT_HOMEPAGE = 'visit_homepage' class-attribute instance-attribute

An identifier for the standard "Visit Homepage" menu item. This command may be installed by default, depending on platform requirements. Uses toga.App.visit_homepage() as the default action.

action property writable

The Action attached to the command.

enabled property writable

Is the command currently enabled?

icon property writable

The Icon for the command.

Can be specified as any valid icon content.

id property

A unique identifier for the command.

key property

A unique tuple describing the path to this command.

Each element in the tuple describes the (section, order, text) for the groups that must be navigated to invoke this action.

__init__(action, text, *, shortcut=None, tooltip=None, icon=None, group=Group.COMMANDS, section=0, order=0, enabled=True, id=None)

Create a new Command.

Commands may not use all the arguments - for example, on some platforms, menus will contain icons; on other platforms they won't.

:param action: A handler to invoke when the command is activated. If this is None, the command will be disabled. :param text: A label for the command. :param shortcut: A key combination that can be used to invoke the command. :param tooltip: A short description of what the command will do. :param icon: The icon content that can be used to decorate the command if the platform requires. :param group: The group to which this command belongs. :param section: The section where the command should appear within its group. :param order: The position where the command should appear within its section. If multiple items have the same group, section and order, they will be sorted alphabetically by their text. :param enabled: Is the Command currently enabled? :param id: A unique identifier for the command.

Source code in core/src/toga/command.py
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
def __init__(
    self,
    action: ActionHandler | None,
    text: str,
    *,
    shortcut: str | Key | None = None,
    tooltip: str | None = None,
    icon: IconContentT | None = None,
    group: Group = Group.COMMANDS,
    section: int = 0,
    order: int = 0,
    enabled: bool = True,
    id: str = None,
):
    """
    Create a new Command.

    Commands may not use all the arguments - for example, on some platforms, menus
    will contain icons; on other platforms they won't.

    :param action: A handler to invoke when the command is activated. If this is
        `None`, the command will be disabled.
    :param text: A label for the command.
    :param shortcut: A key combination that can be used to invoke the command.
    :param tooltip: A short description of what the command will do.
    :param icon: The [icon content][toga.icons.IconContentT] that can be used to
        decorate the command if the platform requires.
    :param group: The group to which this command belongs.
    :param section: The section where the command should appear within its group.
    :param order: The position where the command should appear within its section.
        If multiple items have the same group, section and order, they will be
        sorted alphabetically by their text.
    :param enabled: Is the Command currently enabled?
    :param id: A unique identifier for the command.
    """
    self._id = f"cmd-{_py_id(self)}" if id is None else id
    self.text = text

    self.shortcut = shortcut
    self.tooltip = tooltip
    self.icon = icon

    self.group = group
    self.section = section
    self.order = order

    self.action = action

    self.factory = get_factory()
    self._impl = self.factory.Command(interface=self)

    self._enabled = True
    self.enabled = enabled

standard(app, id, **kwargs) classmethod

Create an instance of a standard command for the provided app.

The default action for the command will be constructed using the value of the command's ID as an attribute of the app object. If a method or co-routine matching that name doesn't exist, a value of None will be used as the default action.

:param app: The app for which the standard command will be created. :param id: The ID of the standard command to create. :param kwargs: Overrides for any default properties of the standard command. Accepts the same arguments as the Command constructor.

Source code in core/src/toga/command.py
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
@classmethod
def standard(cls, app: App, id, **kwargs):
    """Create an instance of a standard command for the provided app.

    The default action for the command will be constructed using the value of the
    command's ID as an attribute of the app object. If a method or co-routine
    matching that name doesn't exist, a value of `None` will be used as the
    default action.

    :param app: The app for which the standard command will be created.
    :param id: The ID of the standard command to create.
    :param kwargs: Overrides for any default properties of the standard command.
        Accepts the same arguments as the [`Command`][toga.Command] constructor.
    """
    # The value of the ID constant is the method on the app instance
    cmd_kwargs = {"id": id}
    try:
        attrs = id.split(".")
        action = getattr(app, attrs[0])
        for attr in attrs[1:]:
            action = getattr(action, attr)
        cmd_kwargs["action"] = simple_handler(action)
    except AttributeError:
        cmd_kwargs["action"] = None

    # Get the platform-specific keyword arguments for the command
    factory = get_factory()
    platform_kwargs = factory.Command.standard(app, id)

    if platform_kwargs:
        cmd_kwargs.update(platform_kwargs)
        cmd_kwargs.update(kwargs)

        # Return the command instance
        return Command(**cmd_kwargs)
    else:
        # Standard command doesn't exist on the platform.
        return None
Source code in core/src/toga/command.py
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 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
class Group:
    def __init__(
        self,
        text: str,
        *,
        parent: Group | None = None,
        section: int = 0,
        order: int = 0,
        id: str | None = None,
    ):
        """
        A collection of commands to display together.

        :param text: A label for the group.
        :param parent: The parent of this group; use `None` to make a root group.
        :param section: The section where the group should appear within its parent. A
            section cannot be specified unless a parent is also specified.
        :param order: The position where the group should appear within its section.
            If multiple items have the same group, section and order, they will be
            sorted alphabetically by their text.
        :param id: A unique identifier for the group.
        """
        self._id = f"group-{_py_id(self)}" if id is None else id
        self._text = text
        self.order = order
        if parent is None and section != 0:
            raise ValueError("Section cannot be set without parent group")
        self.section = section

        # Prime the underlying value of _parent so that the setter has a current value
        # to work with
        self._parent: Group | None = None
        self.parent = parent

    @property
    def id(self) -> str:
        """A unique identifier for the group."""
        return self._id

    @property
    def text(self) -> str:
        """A text label for the group."""
        return self._text

    @property
    def parent(self) -> Group | None:
        """The parent of this group; returns `None` if the group is a root group."""
        return self._parent

    @parent.setter
    def parent(self, parent: Group | None) -> None:
        if parent is None:
            self._parent = None
        elif parent == self:
            raise ValueError("A group cannot be it's own parent")
        elif self.is_parent_of(parent):
            raise ValueError(
                f"Cannot set parent; {self.text!r} is an ancestor of {parent.text!r}."
            )
        else:
            self._parent = parent

    @property
    def root(self) -> Group:
        """The root group for this group.

        This will be `self` if the group *is* a root group."""
        if self.parent is None:
            return self
        return self.parent.root

    def is_parent_of(self, child: Group | None) -> bool:
        """Is this group a parent of the provided group, directly or indirectly?

        :param child: The potential child to check
        :returns: True if this group is a parent of the provided child.
        """
        if child is None:
            return False
        if child.parent is None:
            return False
        if child.parent == self:
            return True
        return self.is_parent_of(child.parent)

    def is_child_of(self, parent: Group | None) -> bool:
        """Is this group a child of the provided group, directly or indirectly?

        :param parent: The potential parent to check
        :returns: True if this group is a child of the provided parent.
        """
        if parent is None:
            return False
        return parent.is_parent_of(self)

    def __hash__(self) -> int:
        return hash(self.key)

    def __lt__(self, other: object) -> bool:
        if not isinstance(other, Group | Command):
            return False
        return self.key < other.key

    def __gt__(self, other: object) -> bool:
        if not isinstance(other, Group | Command):
            return False
        return other < self

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Group | Command):
            return False
        return self.key == other.key

    def __repr__(self) -> str:
        parent_string = (
            f" parent={self.parent} section={self.section}"
            if self.parent is not None
            else ""
        )
        return f"<Group text={self.text!r} order={self.order}{parent_string}>"

    @property
    def key(self) -> tuple[tuple[int, int, str], ...]:
        """A unique tuple describing the path to this group."""
        self_tuple = (self.section, self.order, self.text)
        if self.parent is None:
            return (self_tuple,)
        return self.parent.key + (self_tuple,)

    # Standard groups - docstrings can only be provided within the `class` statement,
    # but the objects can't be instantiated here.
    APP: Group
    """Application-level commands"""
    FILE: Group
    """File commands"""
    EDIT: Group
    """Editing commands"""
    VIEW: Group
    """Content appearance commands"""
    COMMANDS: Group
    """Default group for user-provided commands"""
    WINDOW: Group
    """Window management commands"""
    HELP: Group
    """Help commands"""
    SETTINGS: Group
    """Preferences commands (used only for Qt backend by default)"""

APP instance-attribute

Application-level commands

COMMANDS instance-attribute

Default group for user-provided commands

EDIT instance-attribute

Editing commands

FILE instance-attribute

File commands

HELP instance-attribute

Help commands

SETTINGS instance-attribute

Preferences commands (used only for Qt backend by default)

VIEW instance-attribute

Content appearance commands

WINDOW instance-attribute

Window management commands

id property

A unique identifier for the group.

key property

A unique tuple describing the path to this group.

parent property writable

The parent of this group; returns None if the group is a root group.

root property

The root group for this group.

This will be self if the group is a root group.

text property

A text label for the group.

__init__(text, *, parent=None, section=0, order=0, id=None)

A collection of commands to display together.

:param text: A label for the group. :param parent: The parent of this group; use None to make a root group. :param section: The section where the group should appear within its parent. A section cannot be specified unless a parent is also specified. :param order: The position where the group should appear within its section. If multiple items have the same group, section and order, they will be sorted alphabetically by their text. :param id: A unique identifier for the group.

Source code in core/src/toga/command.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def __init__(
    self,
    text: str,
    *,
    parent: Group | None = None,
    section: int = 0,
    order: int = 0,
    id: str | None = None,
):
    """
    A collection of commands to display together.

    :param text: A label for the group.
    :param parent: The parent of this group; use `None` to make a root group.
    :param section: The section where the group should appear within its parent. A
        section cannot be specified unless a parent is also specified.
    :param order: The position where the group should appear within its section.
        If multiple items have the same group, section and order, they will be
        sorted alphabetically by their text.
    :param id: A unique identifier for the group.
    """
    self._id = f"group-{_py_id(self)}" if id is None else id
    self._text = text
    self.order = order
    if parent is None and section != 0:
        raise ValueError("Section cannot be set without parent group")
    self.section = section

    # Prime the underlying value of _parent so that the setter has a current value
    # to work with
    self._parent: Group | None = None
    self.parent = parent

is_child_of(parent)

Is this group a child of the provided group, directly or indirectly?

:param parent: The potential parent to check :returns: True if this group is a child of the provided parent.

Source code in core/src/toga/command.py
103
104
105
106
107
108
109
110
111
def is_child_of(self, parent: Group | None) -> bool:
    """Is this group a child of the provided group, directly or indirectly?

    :param parent: The potential parent to check
    :returns: True if this group is a child of the provided parent.
    """
    if parent is None:
        return False
    return parent.is_parent_of(self)

is_parent_of(child)

Is this group a parent of the provided group, directly or indirectly?

:param child: The potential child to check :returns: True if this group is a parent of the provided child.

Source code in core/src/toga/command.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
def is_parent_of(self, child: Group | None) -> bool:
    """Is this group a parent of the provided group, directly or indirectly?

    :param child: The potential child to check
    :returns: True if this group is a parent of the provided child.
    """
    if child is None:
        return False
    if child.parent is None:
        return False
    if child.parent == self:
        return True
    return self.is_parent_of(child.parent)

Bases: MutableSet[Command], MutableMapping[str, Command]

Source code in core/src/toga/command.py
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
class CommandSet(MutableSet[Command], MutableMapping[str, Command]):
    def __init__(
        self,
        on_change: CommandSetChangeHandler | None = None,
        app: App | None = None,
    ):
        """
        A collection of commands.

        This is used as an internal representation of Menus, Toolbars, and any other
        graphical manifestations of commands. You generally don't need to construct a
        CommandSet of your own; you should use existing app or window level CommandSet
        instances.

        The `in` operator can be used to evaluate whether a [`Command`][toga.Command] is
        a member of the CommandSet, using either an instance of a Command, or the ID of
        a command.

        Commands can be retrieved from the CommandSet using `[]` notation with the
        requested command's ID.

        When iterated over, a CommandSet returns [`Command`][toga.Command] instances in
        their sort order, with [`Separator`][toga.command.Separator] instances inserted
        between groups.

        :param on_change: A method that should be invoked when this CommandSet changes.
        :param app: The app this CommandSet is associated with, if it is not the app's
            own CommandSet.
        """
        self._app = app
        self._commands: dict[str:Command] = {}
        self.on_change = on_change

    def add(self, *commands: Command | None):
        """Add a collection of commands to the command set.

        A command value of `None` will be ignored. This allows you to add standard
        commands to a command set without first validating that the platform provides an
        implementation of that command.

        :param commands: The commands to add to the command set.
        """
        if self.app:
            self.app.commands.add(*commands)
        self._commands.update({cmd.id: cmd for cmd in commands if cmd is not None})
        if self.on_change:
            self.on_change()

    def clear(self) -> None:
        """Remove all commands from the command set."""
        self._commands = {}
        if self.on_change:
            self.on_change()

    @property
    def app(self) -> App | None:
        """The app this CommandSet is associated with.

        Returns None if this is the app's CommandSet.
        """
        return self._app

    def __contains__(self, obj: str | Command) -> Command:
        if isinstance(obj, Command):
            return obj in self._commands.values()
        else:
            return obj in self._commands

    def __getitem__(self, id: str) -> Command:
        return self._commands[id]

    def __setitem__(self, id: str, command: Command) -> Command:
        if id != command.id:
            raise ValueError(f"Command has id {command.id!r}; can't add as {id!r}")

        self.add(command)

    def __delitem__(self, id: str) -> Command:
        del self._commands[id]
        if self.on_change:
            self.on_change()

    def discard(self, command: Command):
        try:
            self._commands.pop(command.id)
            if self.on_change:
                self.on_change()
        except KeyError:
            pass

    def __len__(self) -> int:
        return len(self._commands)

    def __iter__(self) -> Iterator[Command | Separator]:
        cmd_iter = iter(sorted(self._commands.values()))

        def descendant(group: Group, ancestor: Group) -> Group | None:
            # Return the immediate descendant of ancestor used by this group.
            if group.parent == ancestor:
                return group
            if group.parent:
                return descendant(group.parent, ancestor)
            return None

        # The iteration over commands tells us the exact order of commands, but doesn't
        # tell us anything about menu and submenu structure. In order to insert section
        # breaks in the right place (including before and after submenus), we need to
        # iterate over commands inside each group, dealing with each subgroup as an
        # independent iteration.
        #
        # The iterator over commands is maintained external to this recursive iteration,
        # because we may need to inspect the command at multiple group levels, but we
        # can't `peek` at the top element of an iterator, `push` an item back on after
        # it has been consumed, or pass the consumed item as a return value in addition
        # to the generator result.
        def _iter_group(parent):
            nonlocal command
            nonlocal finished
            section = None

            def _section_break(obj):
                # Utility method that will insert a section break, if required.
                # A section break is needed if the section for the object we're
                # processing (either a command, or a group acting as a submenu)
                # has a section ID different to the previous object processed at
                # this level, excluding the very first object (as there's no need
                # for a section break before the first command/submenu).
                nonlocal section
                if section is not None:
                    if section != obj.section:
                        yield Separator(parent)
                        section = obj.section
                else:
                    section = obj.section

            while not finished:
                if parent is None:
                    # Handle root-level menus
                    yield from _iter_group(command.group.root)
                elif command.group == parent:
                    # A normal command at this level of the group.
                    yield from _section_break(command)
                    yield command

                    # Consume the next item on the iterator; if we run out, mark the
                    # sentinel that says we've finished. We can't just raise
                    # StopIteration, because that stops the *generator* we're creating.
                    try:
                        command = next(cmd_iter)
                    except StopIteration:
                        finished = True
                else:
                    # The command isn't in this group. If the command is in descendant
                    # group, yield items from the group. If it's not a descendant, then
                    # there are no more commands in this group; we can return to the
                    # previous group for processing.
                    subgroup = descendant(command.group, parent)
                    if subgroup:
                        yield from _section_break(subgroup)
                        yield from _iter_group(subgroup)
                    else:
                        return

        # Prime the initial command into the command iterator
        try:
            command = next(cmd_iter)
        except StopIteration:
            pass
        else:
            finished = False
            yield from _iter_group(None)

app property

The app this CommandSet is associated with.

Returns None if this is the app's CommandSet.

__init__(on_change=None, app=None)

A collection of commands.

This is used as an internal representation of Menus, Toolbars, and any other graphical manifestations of commands. You generally don't need to construct a CommandSet of your own; you should use existing app or window level CommandSet instances.

The in operator can be used to evaluate whether a Command is a member of the CommandSet, using either an instance of a Command, or the ID of a command.

Commands can be retrieved from the CommandSet using [] notation with the requested command's ID.

When iterated over, a CommandSet returns Command instances in their sort order, with Separator instances inserted between groups.

:param on_change: A method that should be invoked when this CommandSet changes. :param app: The app this CommandSet is associated with, if it is not the app's own CommandSet.

Source code in core/src/toga/command.py
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
def __init__(
    self,
    on_change: CommandSetChangeHandler | None = None,
    app: App | None = None,
):
    """
    A collection of commands.

    This is used as an internal representation of Menus, Toolbars, and any other
    graphical manifestations of commands. You generally don't need to construct a
    CommandSet of your own; you should use existing app or window level CommandSet
    instances.

    The `in` operator can be used to evaluate whether a [`Command`][toga.Command] is
    a member of the CommandSet, using either an instance of a Command, or the ID of
    a command.

    Commands can be retrieved from the CommandSet using `[]` notation with the
    requested command's ID.

    When iterated over, a CommandSet returns [`Command`][toga.Command] instances in
    their sort order, with [`Separator`][toga.command.Separator] instances inserted
    between groups.

    :param on_change: A method that should be invoked when this CommandSet changes.
    :param app: The app this CommandSet is associated with, if it is not the app's
        own CommandSet.
    """
    self._app = app
    self._commands: dict[str:Command] = {}
    self.on_change = on_change

add(*commands)

Add a collection of commands to the command set.

A command value of None will be ignored. This allows you to add standard commands to a command set without first validating that the platform provides an implementation of that command.

:param commands: The commands to add to the command set.

Source code in core/src/toga/command.py
475
476
477
478
479
480
481
482
483
484
485
486
487
488
def add(self, *commands: Command | None):
    """Add a collection of commands to the command set.

    A command value of `None` will be ignored. This allows you to add standard
    commands to a command set without first validating that the platform provides an
    implementation of that command.

    :param commands: The commands to add to the command set.
    """
    if self.app:
        self.app.commands.add(*commands)
    self._commands.update({cmd.id: cmd for cmd in commands if cmd is not None})
    if self.on_change:
        self.on_change()

clear()

Remove all commands from the command set.

Source code in core/src/toga/command.py
490
491
492
493
494
def clear(self) -> None:
    """Remove all commands from the command set."""
    self._commands = {}
    if self.on_change:
        self.on_change()
Source code in core/src/toga/command.py
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
class Separator:
    def __init__(self, group: Group | None = None):
        """A representation of a separator between sections in a Group.

        :param group: The group that contains the separator.
        """
        self.group = group

    def __repr__(self) -> str:
        return f"<Separator group={None if self.group is None else self.group.text}>"

    def __eq__(self, other: object) -> bool:
        if isinstance(other, Separator):
            return self.group == other.group
        return False

__init__(group=None)

A representation of a separator between sections in a Group.

:param group: The group that contains the separator.

Source code in core/src/toga/command.py
417
418
419
420
421
422
def __init__(self, group: Group | None = None):
    """A representation of a separator between sections in a Group.

    :param group: The group that contains the separator.
    """
    self.group = group

Bases: Protocol

Source code in core/src/toga/command.py
177
178
179
180
181
182
183
184
class ActionHandler(Protocol):
    def __call__(self, command: Command, **kwargs) -> bool:
        """A handler that will be invoked when a Command is invoked.

        :param command: The command that triggered the action.
        :param kwargs: Ensures compatibility with additional arguments introduced in
            future versions.
        """

__call__(command, **kwargs)

A handler that will be invoked when a Command is invoked.

:param command: The command that triggered the action. :param kwargs: Ensures compatibility with additional arguments introduced in future versions.

Source code in core/src/toga/command.py
178
179
180
181
182
183
184
def __call__(self, command: Command, **kwargs) -> bool:
    """A handler that will be invoked when a Command is invoked.

    :param command: The command that triggered the action.
    :param kwargs: Ensures compatibility with additional arguments introduced in
        future versions.
    """

Bases: Protocol

Source code in core/src/toga/command.py
433
434
435
436
437
438
439
class CommandSetChangeHandler(Protocol):
    def __call__(self, **kwargs) -> object:
        """A handler that will be invoked when a Command or Group is added to the
        CommandSet.

        :param kwargs: Ensures compatibility with arguments added in future versions.
        """

__call__(**kwargs)

A handler that will be invoked when a Command or Group is added to the CommandSet.

:param kwargs: Ensures compatibility with arguments added in future versions.

Source code in core/src/toga/command.py
434
435
436
437
438
439
def __call__(self, **kwargs) -> object:
    """A handler that will be invoked when a Command or Group is added to the
    CommandSet.

    :param kwargs: Ensures compatibility with arguments added in future versions.
    """