Skip to content

App

The main application object.

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

The App class is the top level representation of all application activity. It is a singleton object - any given process can only have a single App. That application may manage multiple windows, but it will generally have at least one window (called the main_window).

The application is started by calling main_loop(). This will invoke the startup() method of the app.

import toga

app = toga.App("Simplest App", "com.example.simplest")
app.main_loop()

You can populate an app's main window by passing a callable as the startup argument to the toga.App constructor. This startup method must return the content that will be added to the main window of the app.

import toga

def create_content(app):
    return toga.Box(children=[toga.Label("Hello!")])

app = toga.App("Simple App", "com.example.simple", startup=create_content)
app.main_loop()

This approach to app construction is most useful with simple apps. For most complex apps, you should subclass toga.App, and provide an implementation of startup(). This implementation must assign a value to main_window for the app. The possible values are discussed below; most apps will assign an instance of toga.MainWindow:

import toga

class MyApp(toga.App):
    def startup(self):
        self.main_window = toga.MainWindow()
        self.main_window.content = toga.Box(children=[`toga.Label("Hello!")])
        self.main_window.show()

if __name__ == '__main__':
    app = MyApp("Realistic App", "org.beeware.realistic")
    app.main_loop()

Every app must have a formal name (a human readable name), and an app ID (a machine-readable identifier - usually a reversed domain name). In the examples above, these are provided as constructor arguments. However, you can also provide these details, along with many of the other constructor arguments, as packaging metadata in a format compatible with [importlib.metadata][]. If you deploy your app with Briefcase, this will be done automatically.

A Toga app will install a number of default commands to reflect core application functionality (such as the Quit/Exit menu item, and the About menu item). The IDs for these commands are defined as constants on the Command class. These commands are automatically installed before startup() is invoked. If you wish to customize the menu items exposed by your app, you can add or remove commands in your startup() implementation.

As part of application startup, apps will also ensure that the locale has been set to match the language settings of the operating system.

Assigning a main window

An app must assign main_window as part of the startup process. However, the value that is assigned as the main window will affect the behavior of the app.

toga.Window

Most apps will assign an instance of toga.Window (or a subclass, such as toga.MainWindow) as the main window. This window will control the life cycle of the app. When the window assigned as the main window is closed, the app will exit.

If you create an App by passing a startup argument to the constructor, a MainWindow will be automatically created and assigned to main_window.

None

If your app doesn't have a single "main" window, but instead has multiple windows that are equally important (e.g., a document editor, or a web browser), you can assign a value of None to main_window. The resulting behavior is slightly different on each platform, reflecting platform differences.

On macOS, the app is allowed to continue running without having any open windows. The app can open and close windows as required; the app will keep running until explicitly exited. If you give the app focus when it has no open windows, a file dialog will be displayed prompting you to select a file to open. If the file is already open, the existing representation for the document will be given focus.

On Linux and Windows, when an app closes the last window it is managing, the app will automatically exit. Attempting to close the last window will trigger any app-level on_exit() handling in addition to any window-specific on_close() handling.

Mobile, web and console platforms must define a main window.

toga.App.BACKGROUND

Assigning a value of toga.App.BACKGROUND as the main window will allow your app to persist even if it doesn't have any open windows. It will also hide any app-level icon from your taskbar.

Background apps are not supported on mobile, web and console platforms.

Life cycle of an app

Regardless of what an application does, every application goes through the same life cycle of starting, running, and shutting down.

Application startup is handled by the startup() method described above. startup() cannot be an asynchronous method, as it runs before the App's event loop is started.

All other events in the life cycle of the app can be managed with event handlers. toga.App defines the following event handlers:

  • on_running() occurs as soon as the app's event loop has started.
  • on_exit() occurs when the user tries to exit. The handler for this event must return a Boolean value: True if the app is allowed to exit; False otherwise. This allows an app to abort the exit process (for example, to prevent exit if there are unsaved changes).

Event handlers can be defined by subclassing toga.App and overriding the event handler method, by assigning a value to the event handler when the app instance is constructed, or by assigning the event handler attribute on an existing app instance. When the event handler is set by assigning a value to the event handler, the handler method must accept an app argument. This argument is not required when subclassing, as the app instance can be implied. Regardless of how they are defined, event handlers can be defined as async methods.

Managing documents

When you create an App instance, you can declare the type of documents that your app is able to manage by providing a value for document_types. When an app declares that it can manage document types, the app will automatically create file management menu items (such as New, Open and Save), and the app will process command line arguments, creating a toga.Document instance for each argument matching a registered document type.

For details on how to define and register document types, refer to the documentation on document handling.

Notes

  • On macOS, menus are tied to the app, not the window; and a menu is mandatory. Therefore, a macOS app will always have a menu with the default menu items, regardless of the window being used as the main window.
  • Apps executed under Wayland on Linux environment may not show the app's formal name correctly. Wayland considers many aspects of app operation to be the domain of the windowing environment, not the app; as a result, some API requests will be ignored under a Wayland environment. Correctly displaying the app's formal name requires the use of a desktop metadata that Wayland can read. Packaging your app with Briefcase is one way to produce this metadata.

Reference

Source code in core/src/toga/app.py
 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
 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
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 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
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
class App:
    app: App | None = None
    """
    The currently running [`App`][toga.App]. Since there can only be one running
    Toga app in a process, this is available as a class property via
    `toga.App.app`. If no app has been created yet, this is set to `None`.
    """
    _impl: Any
    _camera: Camera
    _location: Location
    _main_window: Window | str | None
    _running_tasks: set[asyncio.Task] = set()

    BACKGROUND: str = "background app"
    """A constant that can be used as the main window to indicate that an app will
    run in the background without a main window."""

    _UNDEFINED: str = "<main window not assigned>"

    def __init__(
        self,
        formal_name: str | None = None,
        app_id: str | None = None,
        app_name: str | None = None,
        *,
        icon: IconContentT | None = None,
        author: str | None = None,
        version: str | None = None,
        home_page: str | None = None,
        description: str | None = None,
        startup: AppStartupMethod | None = None,
        document_types: list[type[Document]] | None = None,
        on_running: OnRunningHandler | None = None,
        on_exit: OnExitHandler | None = None,
    ):
        """Create a new App instance.

        Once the app has been created, you should invoke the
        [`App.main_loop()`][toga.App.main_loop] method, which will start the event loop
        of your App.

        :param formal_name: The human-readable name of the app. If not provided, the
            metadata key `Formal-Name` must be present.
        :param app_id: The unique application identifier. This will usually be a
            reversed domain name, e.g. `org.beeware.myapp`. If not provided, the
            metadata key `App-ID` must be present.
        :param app_name: The name of the distribution used to load metadata with
            [`importlib.metadata`][]. If `app_name` is not provided, the last segment of
            the `app_id` argument will be used as a default value (e.g., `my-app` if an
            app ID of `com.example.my-app` is provided). If `app_id` is not provided,
            and the `__main__` module for the app is contained in a package, that
            package's name will be used. As a last resort, the name `toga` will be used.
        :param icon: The [icon][toga.icons.IconContentT] for the app. Defaults to
            [`toga.Icon.APP_ICON`][].
        :param author: The person or organization to be credited as the author of the
            app. If not provided, the metadata key `Author` will be used.
        :param version: The version number of the app.  If not provided, the metadata
            key `Version` will be used.
        :param home_page: The URL of a web page for the app. Used in auto-generated help
            menu items. If not provided, the metadata key `Home-page` will be used.
        :param description: A brief (one line) description of the app. If not provided,
            the metadata key `Summary` will be used.
        :param startup: A callable to run before starting the app.
        :param on_running: The initial [`on_running`][toga.App.on_running] handler.
        :param on_exit: The initial [`on_exit`][toga.App.on_exit] handler.
        :param document_types: A list of [`Document`][toga.Document] classes that this
            app can manage.
        """
        # Sets Python's locale settings to the language settings of the system.
        # If the local is unknown (e.g. en-FR), catch the error, log and ignore,
        # as there's not much else we can do.
        try:
            locale.setlocale(locale.LC_ALL, "")
        except locale.Error:  # pragma: no cover
            print(
                f"Unable to set locale to match system value of {os.getenv('LANG')!r}"
            )

        # Initialize empty widgets registry
        self._widgets = WidgetRegistry()

        # Keep an accessible copy of the app singleton instance
        App.app = self

        self._app_name = None
        # Try deconstructing the distribution name from the app ID
        if app_name:
            # We have an explicitly provided app name
            self._app_name = app_name
        elif app_id:
            # We can guess the app name from the provided app ID
            app_name = app_id.split(".")[-1]
        else:
            # We need a distribution name to load app metadata.
            #
            # If the code is contained in appname.py, and you start the app using
            # `python -m appname`, then __main__.__package__ will be an empty string.
            #
            # If the code is contained in appname.py, and you start the app using
            # `python appname.py`, then __main__.__package__ will be None - unless
            # the app has been run under pdb, in which case `__package__` doesn't exist.
            #
            # If the code is contained in appname/__main__.py, and you start the app
            # using `python -m appname`, then __main__.__package__ will be "appname".
            try:
                main_module_pkg = getattr(sys.modules["__main__"], "__package__", None)
                if main_module_pkg:
                    app_name = main_module_pkg
            except KeyError:
                # If there's no __main__ module, we're probably in a test.
                pass

            # If we still don't have a distribution name, fall back to `toga` as a
            # last resort.
            if app_name is None:
                app_name = "toga"

        # Try to load the app metadata with our best guess of the distribution name.
        try:
            self.metadata = importlib.metadata.metadata(app_name)
            # If the app name has been explicitly set, keep that name. Otherwise, if the
            # app metadata provides an app name, use it. Fall back to whatever name
            # has been derived from other sources (app_id, module name, or "toga")
            if self._app_name is None:
                self._app_name = self.metadata.get("Name", app_name)
        except (importlib.metadata.PackageNotFoundError, ValueError):
            self.metadata = {}
            # If the app name has been explicitly set, keep that name. Otherwise, fall
            # back to whatever name has been derived from other sources (app_id, module
            # name, or "toga")
            if self._app_name is None:
                self._app_name = app_name

        # If a formal name has been provided, use it; otherwise, look to
        # the metadata. However, a formal name *must* be provided.
        if formal_name:
            self._formal_name = formal_name
        else:
            self._formal_name = self.metadata.get("Formal-Name")
        if self._formal_name is None:
            raise RuntimeError("Toga application must have a formal name")

        # If an app_id has been provided, use it; otherwise, look to
        # the metadata. However, an app_id *must* be provided
        if app_id:
            self._app_id = app_id
        else:
            self._app_id = self.metadata.get("App-ID")
        if self._app_id is None:
            raise RuntimeError("Toga application must have an app ID")

        # Other metadata may be passed to the constructor, or loaded with importlib.
        if author:
            self._author = author
        else:
            self._author = self.metadata.get("Author", None)

        if version:
            self._version = version
        else:
            self._version = self.metadata.get("Version", None)

        if home_page:
            self._home_page = home_page
        else:
            self._home_page = self.metadata.get("Home-page", None)

        if description:
            self._description = description
        else:
            self._description = self.metadata.get("Summary", None)

        # Get a platform factory.
        self.factory = get_factory()

        # Instantiate the paths instance for this app.
        self._paths = Paths()

        if icon is None:
            self.icon = Icon.APP_ICON
        else:
            self.icon = icon

        # Set up the document types and collection of documents being managed.
        self._documents = DocumentSet(
            self,
            types=[] if document_types is None else document_types,
        )

        # Install the lifecycle handlers. If passed in as an argument, or assigned using
        # `app.on_event = my_handler`, the event handler will take the app as the first
        # argument. If we're using the default value, or we're subclassing app, the app
        # can be safely implied; so we wrap the method as a simple handler.
        if on_running:
            self.on_running = on_running
        else:
            self.on_running = simple_handler(self.on_running)

        if on_exit:
            self.on_exit = on_exit
        else:
            self.on_exit = simple_handler(self.on_exit)

        # We need the command set to exist so that startup et al. can add commands;
        # but we don't have an impl yet, so we can't set the on_change handler
        self._commands = CommandSet()
        self._status_icons = StatusIconSet()

        self._startup_method = startup

        self._main_window = App._UNDEFINED
        self._windows = WindowSet(self)

        # Create the implementation. This will trigger any startup logic.
        self.factory.App(interface=self)

    ######################################################################
    # App properties
    ######################################################################

    @property
    def app_name(self) -> str:
        """The name of the distribution used to load metadata with
        [`importlib.metadata`][] (read-only)."""
        return self._app_name

    @property
    def app_id(self) -> str:
        """The unique application identifier (read-only). This will usually be a
        reversed domain name, e.g. `org.beeware.myapp`.
        """
        return self._app_id

    @property
    def author(self) -> str | None:
        """The person or organization to be credited as the author of the app
        (read-only)."""
        return self._author

    @property
    def description(self) -> str | None:
        """A brief (one line) description of the app (read-only)."""
        return self._description

    @property
    def formal_name(self) -> str:
        """The human-readable name of the app (read-only)."""
        return self._formal_name

    @property
    def home_page(self) -> str | None:
        """The URL of a web page for the app (read-only). Used in auto-generated help
        menu items."""
        return self._home_page

    @property
    def dark_mode(self) -> bool | None:
        """Whether the user has dark mode enabled in their environment (read-only).

        :returns: A Boolean describing if the app is in dark mode; `None` if Toga
            cannot determine if the app is in dark mode.
        """
        return self._impl.get_dark_mode_state()

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

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

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

        try:
            self._impl.set_icon(self._icon)
        except AttributeError:
            # The first time the icon is set, it is *before* the impl has been created,
            # so that the app instance can be instantiated with the correct icon.
            pass

    @property
    def version(self) -> str | None:
        """The version number of the app (read-only)."""
        return self._version

    @property
    def is_bundled(self) -> bool:
        """Has the app been bundled as a standalone binary,
        or is it running as a Python script?"""
        return Path(sys.executable).stem not in {
            "python",
            f"python{sys.version_info.major}",
            f"python{sys.version_info.major}.{sys.version_info.minor}",
        }

    ######################################################################
    # App lifecycle
    ######################################################################

    def request_exit(self):
        """Request an exit from the application.

        This method will call the [`App.on_exit()`][toga.App.on_exit] handler to confirm
        if the app should be allowed to exit; if that handler confirms the action,
        the app will exit.
        """

        def cleanup(app, should_exit):
            if should_exit:
                app.exit()

        # Wrap on_exit to ensure that an async handler is turned into a task,
        # then immediately invoke.
        wrapped_handler(self, self.on_exit, cleanup=cleanup)()

    def exit(self) -> None:
        """Unconditionally exit the application.

        This *does not* invoke the `on_exit` handler; the app will be immediately
        and unconditionally closed.
        """
        self._impl.exit()

    @property
    def loop(self) -> asyncio.AbstractEventLoop:
        """The [event loop](https://docs.python.org/3/library/asyncio-eventloop.html)
        of the app's main thread (read-only)."""
        return self._impl.loop

    def main_loop(self) -> None:
        """Start the application.

        On desktop platforms, this method will block until the application has exited.
        On mobile and web platforms, it returns immediately.
        """
        # Modify signal handlers to make sure Ctrl-C is caught and handled.
        signal.signal(signal.SIGINT, signal.SIG_DFL)

        self._impl.main_loop()

    def _install_task_factory_wrapper(self):
        """Wrap task creation to track pending and running tasks.

        When tasks are created, asyncio only maintains a weak reference to the task.
        This allows for the possibility that the garbage collector destroys the task
        in the midst of its execution. To avoid this, a strong reference is stored on
        the app and a task callback removes the reference after the task completes.
        Upstream issue tracked at python/cpython#91887.
        """
        platform_task_factory = self.loop.get_task_factory()

        def factory(loop, coro, **kwargs):
            if platform_task_factory is not None:
                task = platform_task_factory(loop, coro, **kwargs)
            else:
                task = asyncio.Task(coro, loop=loop, **kwargs)

            self._running_tasks.add(task)
            task.add_done_callback(self._running_tasks.discard)
            return task

        self.loop.set_task_factory(factory)

    @property
    def main_window(self) -> Window | str | None:
        """The main window for the app.

        See [the documentation on assigning a main window][assigning-main-window]
        for values that can be used for this attribute.
        """
        if self._main_window is App._UNDEFINED:
            raise ValueError("Application has not set a main window.")

        return self._main_window

    @main_window.setter
    def main_window(self, window: MainWindow | str | None) -> None:
        if window is None or window is App.BACKGROUND or isinstance(window, Window):
            # The main window must be closable
            if isinstance(window, Window) and not window.closable:
                raise ValueError("The window used as the main window must be closable.")

            old_window = self._main_window
            self._main_window = window
            try:
                self._impl.set_main_window(window)
            except Exception as e:
                # If the main window could not be changed, revert to the previous value
                # then reraise the exception
                if old_window is not App._UNDEFINED:
                    self._main_window = old_window
                raise e
        else:
            raise ValueError(f"Don't know how to use {window!r} as a main window.")

    def _open_initial_document(self, filename: Path | str) -> bool:
        """Internal utility method for opening a document provided at the command line.

        This is abstracted so that backends that have their own management of command
        line arguments can share the same error handling.

        :param filename: The filename passed as an argument, as a string.
        :returns: `True` if a document was successfully loaded; `False` otherwise.
        """
        try:
            self.documents.open(filename)
            return True
        except FileNotFoundError:
            print(f"Document {filename} not found")
            return False
        except Exception as e:
            print(f"{filename}: {e}")
            return False

    def _create_standard_commands(self):
        """Internal utility method to create the standard commands for the app."""
        for cmd_id in [
            Command.ABOUT,
            Command.EXIT,
            Command.VISIT_HOMEPAGE,
        ]:
            self.commands.add(Command.standard(self, cmd_id))

        if self.documents.types:
            default_document_type = self.documents.types[0]
            command = Command.standard(
                self,
                Command.NEW,
                action=simple_handler(self.documents.new, default_document_type),
            )
            if command:
                if len(self.documents.types) == 1:
                    # There's only 1 document type. The new command can be used as is.
                    self.commands.add(command)
                else:
                    # There's more than one document type. Create a new command for each
                    # document type, updating the title of the command to disambiguate,
                    # and modifying the shortcut, order and ID of the document types 2+
                    for i, document_class in enumerate(self.documents.types):
                        command = Command.standard(
                            self,
                            Command.NEW,
                            action=simple_handler(self.documents.new, document_class),
                        )
                        command.text = command.text + f" {document_class.description}"
                        if i > 0:
                            command.shortcut = None
                            command._id = f"{command.id}:{document_class.extensions[0]}"
                            command.order = command.order + i

                        self.commands.add(command)

            for cmd_id in [
                Command.OPEN,
                Command.SAVE,
                Command.SAVE_AS,
                Command.SAVE_ALL,
            ]:
                self.commands.add(Command.standard(self, cmd_id))

    def _create_initial_windows(self):
        """Internal utility method for creating initial windows based on command line
        arguments.

        If document types are defined, and the backend doesn't have native command line
        handling, try to open every argument on the command line as a document (unless
        the backend manages the command line arguments).

        If, after processing all command line arguments, the app doesn't have at least
        one window, the app's default initial document handling will be triggered.
        """
        # Process command line arguments if the backend doesn't handle them
        if not self._impl.HANDLES_COMMAND_LINE:
            if self.documents.types:
                for filename in sys.argv[1:]:
                    self._open_initial_document(filename)

        # Ensure there is at least one window
        if self.main_window is None and len(self.windows) == 0:
            if self.documents.types:
                if self._impl.CLOSE_ON_LAST_WINDOW:
                    # Pass in the first document type as the default
                    self.documents.new(self.documents.types[0])
                else:
                    self.loop.create_task(self.documents.request_open())
            else:
                # No document types defined.
                raise RuntimeError(
                    "App didn't create any windows, or register any document types."
                )

    def _startup(self) -> None:
        # Wrap the platform's event loop's task factory for task tracking
        self._install_task_factory_wrapper()

        # Install the standard commands. This is done *before* startup so the user's
        # code has the opportunity to remove/change the default commands.
        self._create_standard_commands()
        self._impl.create_standard_commands()

        # Install the standard status icon commands. Again, this is done *before*
        # startup so that the user's code can remove/change the defaults.
        self.status_icons._create_standard_commands()

        # Invoke the user's startup method (or the default implementation)
        self.startup()

        # Validate that the startup requirements have been met.
        # Accessing the main window attribute will raise an exception if the app hasn't
        # defined a main window.
        _ = self.main_window

        # Create any initial windows
        self._create_initial_windows()

        # Manifest the initial state of the menus. This will cascade down to all
        # open windows if the platform has window-based menus. Then install the
        # on-change handler for menus to respond to any future changes.
        self._impl.create_menus()
        self.commands.on_change = self._impl.create_menus

        # Manifest the initial state of the status icons, then install an on-change
        # handler so that any future changes will be reflected in the GUI.
        self.status_icons._impl.create()
        self.status_icons.commands.on_change = self.status_icons._impl.create

        # Manifest the initial state of toolbars (on the windows that have
        # them), then install a change listener so that any future changes to
        # the toolbar cause a change in toolbar items.
        for window in self.windows:
            if hasattr(window, "toolbar"):
                window._impl.create_toolbar()
                window.toolbar.on_change = window._impl.create_toolbar

        # Queue a task to run as soon as the event loop starts.
        self.loop.call_soon_threadsafe(wrapped_handler(self, self.on_running))

    def startup(self) -> None:
        """Create and show the main window for the application.

        Subclasses can override this method to define customized startup behavior;
        however, any override *must* ensure the
        [`main_window`][toga.App.main_window] has
        been assigned before it returns.
        """
        self.main_window = MainWindow(title=self.formal_name, id="main")

        if self._startup_method:
            content = self._startup_method(self)
            if content is None:
                raise ValueError(
                    "Your app's startup method has not provided any content for your "
                    "app's main window. Did you remember to return the main content "
                    "container in your startup method?"
                )
            self.main_window.content = content

        self.main_window.show()

    ######################################################################
    # App resources
    ######################################################################

    @property
    def camera(self) -> Camera:
        """A representation of the device's camera (or cameras)."""
        try:
            return self._camera
        except AttributeError:
            # Instantiate the camera instance for this app on first access
            # This will raise an exception if the platform doesn't implement
            # the Camera API.
            from .hardware.camera import Camera

            self._camera = Camera(self)
            return self._camera

    @property
    def commands(self) -> CommandSet:
        """The commands available in the app."""
        return self._commands

    @property
    def documents(self) -> DocumentSet:
        """The list of documents associated with this app."""
        return self._documents

    @property
    def location(self) -> Location:
        """A representation of the device's location service."""
        try:
            return self._location
        except AttributeError:
            # Instantiate the location service for this app on first access
            # This will raise an exception if the platform doesn't implement
            # the Location API.
            from .hardware.location import Location

            self._location = Location(self)
            return self._location

    @property
    def paths(self) -> Paths:
        """Paths for platform-appropriate locations on the user's file system.

        Some platforms do not allow access to any file system location other than these
        paths. Even when arbitrary file access is allowed, there are preferred locations
        for each type of content.
        """
        return self._paths

    @property
    def screens(self) -> list[Screen]:
        """Returns a list of available screens."""
        return [screen.interface for screen in self._impl.get_screens()]

    @property
    def status_icons(self) -> StatusIconSet:
        """The status icons displayed by the app."""
        return self._status_icons

    @property
    def widgets(self) -> WidgetRegistry:
        """The widgets managed by the app, over all windows.

        Can be used to look up widgets by ID over the entire app (e.g.,
        `app.widgets["my_id"]`).

        Only returns widgets that are currently part of a layout. A widget that has been
        created, but not assigned as part of window content will not be returned by
        widget lookup.
        """
        return self._widgets

    @property
    def windows(self) -> WindowSet:
        """The windows managed by the app. Windows are automatically added to the app
        when they are created, and removed when they are closed."""
        return self._windows

    ######################################################################
    # App capabilities
    ######################################################################

    def about(self) -> None:
        """Display the About dialog for the app.

        Default implementation shows a platform-appropriate about dialog using app
        metadata. Override if you want to display a custom About dialog.
        """
        self._impl.show_about_dialog()

    def beep(self) -> None:
        """Play the default system notification sound."""
        self._impl.beep()

    async def dialog(self, dialog: Dialog) -> Coroutine[None, None, Any]:
        """Display a dialog to the user in the app context.

        :param dialog: The [dialog](/reference/api/application/dialogs.md) to
            display to the user.
        :returns: The result of the dialog.
        """
        return await dialog._show(None)

    def visit_homepage(self) -> None:
        """Open the application's [`home_page`][toga.App.home_page] in the default
        browser.

        This method is invoked as a handler by the "Visit homepage" default menu item.
        If the [`home_page`][toga.App.home_page] is `None`, this is a no-op, and the
        default menu item will be disabled.
        """
        if self.home_page is not None:
            webbrowser.open(self.home_page)

    ######################################################################
    # Cursor control
    ######################################################################

    def hide_cursor(self) -> None:
        """Hide cursor from view."""
        self._impl.hide_cursor()

    def show_cursor(self) -> None:
        """Make the cursor visible."""
        self._impl.show_cursor()

    ######################################################################
    # Window control
    ######################################################################

    @property
    def current_window(self) -> Window | None:
        """Return the currently active window."""
        window = self._impl.get_current_window()
        if window is None:
            return None
        return window.interface

    @current_window.setter
    def current_window(self, window: Window) -> None:
        """Set a window into current active focus."""
        self._impl.set_current_window(window)

    ######################################################################
    # Presentation mode controls
    ######################################################################

    @property
    def in_presentation_mode(self) -> bool:
        """Is the app currently in presentation mode?"""
        return any(window.state == WindowState.PRESENTATION for window in self.windows)

    def enter_presentation_mode(
        self,
        windows: list[Window] | dict[Screen, Window],
    ) -> None:
        """Enter into presentation mode with one or more windows on different screens.

        Presentation mode is not the same as "Full Screen" mode; presentation mode is
        when window borders, other window decorations, app menu and toolbars are no
        longer visible.

        :param windows: A list of windows, or a dictionary
            mapping screens to windows, to go into presentation, in order of
            allocation to screens. If the number of windows exceeds the number
            of available displays, those windows will not be visible. The windows
            must have a content set on them.

        :raises ValueError: If the presentation layout supplied is not a list of
            windows or a dict mapping windows to screens, or if any window does
            not have content.
        """
        if windows:
            # Exit any existing presentation mode before entering with new windows.
            # Presentation mode is not cumulative: a new call replaces the previous one.
            if self.in_presentation_mode:
                self.exit_presentation_mode()

            screen_window_dict = {}
            if isinstance(windows, list):
                for window, screen in zip(windows, self.screens, strict=False):
                    screen_window_dict[screen] = window
            elif isinstance(windows, dict):
                screen_window_dict = windows
            else:
                raise ValueError(
                    "Presentation layout should be a list of windows,"
                    " or a dict mapping windows to screens."
                )

            for screen, window in screen_window_dict.items():
                window._impl._before_presentation_mode_screen = window.screen
                window.screen = screen
                window._impl.set_window_state(WindowState.PRESENTATION)

    def exit_presentation_mode(self) -> None:
        """Exit presentation mode."""
        # Guard against recursion: backend set_window_state guards may call
        # exit_presentation_mode() when they see other windows still in
        # presentation mode during the exit loop.
        if self._impl._exiting_presentation:
            return
        self._impl._exiting_presentation = True

        try:
            for window in self.windows:
                if window.state == WindowState.PRESENTATION:
                    window._impl.set_window_state(WindowState.NORMAL)
        finally:
            self._impl._exiting_presentation = False

    ######################################################################
    # App events
    ######################################################################

    def on_exit(self) -> bool:
        """The event handler that will be invoked when the app is about to exit.

        The return value of this method controls whether the app is allowed to exit.
        This can be used to prevent the app exiting with unsaved changes, etc.

        If necessary, the overridden method can be defined as an `async` coroutine.

        :returns: `True` if the app is allowed to exit; `False` if the app is not
            allowed to exit.
        """
        # Always allow exit
        return True

    def on_running(self) -> None:
        """The event handler that will be invoked
        when the app's event loop starts running.

        If necessary, the overridden method can be defined as an `async` coroutine.
        """

    ######################################################################
    # 2024-06: Backwards compatibility for <= 0.4.5
    ######################################################################

    def add_background_task(self, handler: BackgroundTask) -> None:
        """**DEPRECATED** – Use [`asyncio.create_task`][], or override/assign
        [`App.on_running()`][toga.App.on_running]."""
        warnings.warn(
            (
                "App.add_background_task is deprecated. Use asyncio.create_task(), "
                "or set an App.on_running() handler"
            ),
            DeprecationWarning,
            stacklevel=2,
        )

        self.loop.call_soon_threadsafe(wrapped_handler(self, handler))

    ######################################################################
    # 2024-12: Backwards compatibility for < 0.5.0
    ######################################################################

    def exit_full_screen(self) -> None:
        """**DEPRECATED** – Use
        [`App.exit_presentation_mode()`][toga.App.exit_presentation_mode]."""
        warnings.warn(
            (
                "`App.exit_full_screen()` is deprecated. "
                "Use `App.exit_presentation_mode()` instead."
            ),
            DeprecationWarning,
            stacklevel=2,
        )
        if self.in_presentation_mode:
            self.exit_presentation_mode()

    @property
    def is_full_screen(self) -> bool:
        """**DEPRECATED** – Use
        [`App.in_presentation_mode`][toga.App.in_presentation_mode]."""
        warnings.warn(
            (
                "`App.is_full_screen` is deprecated. "
                "Use `App.in_presentation_mode` instead."
            ),
            DeprecationWarning,
            stacklevel=2,
        )
        return self.in_presentation_mode

    def set_full_screen(self, *windows: Window) -> None:
        """**DEPRECATED** – Use
        [`App.enter_presentation_mode()`][toga.App.enter_presentation_mode] and
        [`App.exit_presentation_mode()`][toga.App.exit_presentation_mode]."""
        warnings.warn(
            (
                "`App.set_full_screen()` is deprecated. "
                "Use `App.enter_presentation_mode()` instead."
            ),
            DeprecationWarning,
            stacklevel=2,
        )
        self.exit_presentation_mode()
        if windows:
            self.enter_presentation_mode(list(windows))

BACKGROUND = 'background app' class-attribute instance-attribute

A constant that can be used as the main window to indicate that an app will run in the background without a main window.

app = None class-attribute instance-attribute

The currently running App. Since there can only be one running Toga app in a process, this is available as a class property via toga.App.app. If no app has been created yet, this is set to None.

app_id property

The unique application identifier (read-only). This will usually be a reversed domain name, e.g. org.beeware.myapp.

app_name property

The name of the distribution used to load metadata with [importlib.metadata][] (read-only).

author property

The person or organization to be credited as the author of the app (read-only).

camera property

A representation of the device's camera (or cameras).

commands property

The commands available in the app.

current_window property writable

Return the currently active window.

dark_mode property

Whether the user has dark mode enabled in their environment (read-only).

:returns: A Boolean describing if the app is in dark mode; None if Toga cannot determine if the app is in dark mode.

description property

A brief (one line) description of the app (read-only).

documents property

The list of documents associated with this app.

formal_name property

The human-readable name of the app (read-only).

home_page property

The URL of a web page for the app (read-only). Used in auto-generated help menu items.

icon property writable

The Icon for the app.

Can be specified as any valid icon content.

in_presentation_mode property

Is the app currently in presentation mode?

is_bundled property

Has the app been bundled as a standalone binary, or is it running as a Python script?

is_full_screen property

DEPRECATED – Use App.in_presentation_mode.

location property

A representation of the device's location service.

loop property

The event loop of the app's main thread (read-only).

main_window property writable

The main window for the app.

See the documentation on assigning a main window for values that can be used for this attribute.

paths property

Paths for platform-appropriate locations on the user's file system.

Some platforms do not allow access to any file system location other than these paths. Even when arbitrary file access is allowed, there are preferred locations for each type of content.

screens property

Returns a list of available screens.

status_icons property

The status icons displayed by the app.

version property

The version number of the app (read-only).

widgets property

The widgets managed by the app, over all windows.

Can be used to look up widgets by ID over the entire app (e.g., app.widgets["my_id"]).

Only returns widgets that are currently part of a layout. A widget that has been created, but not assigned as part of window content will not be returned by widget lookup.

windows property

The windows managed by the app. Windows are automatically added to the app when they are created, and removed when they are closed.

__init__(formal_name=None, app_id=None, app_name=None, *, icon=None, author=None, version=None, home_page=None, description=None, startup=None, document_types=None, on_running=None, on_exit=None)

Create a new App instance.

Once the app has been created, you should invoke the App.main_loop() method, which will start the event loop of your App.

:param formal_name: The human-readable name of the app. If not provided, the metadata key Formal-Name must be present. :param app_id: The unique application identifier. This will usually be a reversed domain name, e.g. org.beeware.myapp. If not provided, the metadata key App-ID must be present. :param app_name: The name of the distribution used to load metadata with [importlib.metadata][]. If app_name is not provided, the last segment of the app_id argument will be used as a default value (e.g., my-app if an app ID of com.example.my-app is provided). If app_id is not provided, and the __main__ module for the app is contained in a package, that package's name will be used. As a last resort, the name toga will be used. :param icon: The icon for the app. Defaults to toga.Icon.APP_ICON. :param author: The person or organization to be credited as the author of the app. If not provided, the metadata key Author will be used. :param version: The version number of the app. If not provided, the metadata key Version will be used. :param home_page: The URL of a web page for the app. Used in auto-generated help menu items. If not provided, the metadata key Home-page will be used. :param description: A brief (one line) description of the app. If not provided, the metadata key Summary will be used. :param startup: A callable to run before starting the app. :param on_running: The initial on_running handler. :param on_exit: The initial on_exit handler. :param document_types: A list of Document classes that this app can manage.

Source code in core/src/toga/app.py
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
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
def __init__(
    self,
    formal_name: str | None = None,
    app_id: str | None = None,
    app_name: str | None = None,
    *,
    icon: IconContentT | None = None,
    author: str | None = None,
    version: str | None = None,
    home_page: str | None = None,
    description: str | None = None,
    startup: AppStartupMethod | None = None,
    document_types: list[type[Document]] | None = None,
    on_running: OnRunningHandler | None = None,
    on_exit: OnExitHandler | None = None,
):
    """Create a new App instance.

    Once the app has been created, you should invoke the
    [`App.main_loop()`][toga.App.main_loop] method, which will start the event loop
    of your App.

    :param formal_name: The human-readable name of the app. If not provided, the
        metadata key `Formal-Name` must be present.
    :param app_id: The unique application identifier. This will usually be a
        reversed domain name, e.g. `org.beeware.myapp`. If not provided, the
        metadata key `App-ID` must be present.
    :param app_name: The name of the distribution used to load metadata with
        [`importlib.metadata`][]. If `app_name` is not provided, the last segment of
        the `app_id` argument will be used as a default value (e.g., `my-app` if an
        app ID of `com.example.my-app` is provided). If `app_id` is not provided,
        and the `__main__` module for the app is contained in a package, that
        package's name will be used. As a last resort, the name `toga` will be used.
    :param icon: The [icon][toga.icons.IconContentT] for the app. Defaults to
        [`toga.Icon.APP_ICON`][].
    :param author: The person or organization to be credited as the author of the
        app. If not provided, the metadata key `Author` will be used.
    :param version: The version number of the app.  If not provided, the metadata
        key `Version` will be used.
    :param home_page: The URL of a web page for the app. Used in auto-generated help
        menu items. If not provided, the metadata key `Home-page` will be used.
    :param description: A brief (one line) description of the app. If not provided,
        the metadata key `Summary` will be used.
    :param startup: A callable to run before starting the app.
    :param on_running: The initial [`on_running`][toga.App.on_running] handler.
    :param on_exit: The initial [`on_exit`][toga.App.on_exit] handler.
    :param document_types: A list of [`Document`][toga.Document] classes that this
        app can manage.
    """
    # Sets Python's locale settings to the language settings of the system.
    # If the local is unknown (e.g. en-FR), catch the error, log and ignore,
    # as there's not much else we can do.
    try:
        locale.setlocale(locale.LC_ALL, "")
    except locale.Error:  # pragma: no cover
        print(
            f"Unable to set locale to match system value of {os.getenv('LANG')!r}"
        )

    # Initialize empty widgets registry
    self._widgets = WidgetRegistry()

    # Keep an accessible copy of the app singleton instance
    App.app = self

    self._app_name = None
    # Try deconstructing the distribution name from the app ID
    if app_name:
        # We have an explicitly provided app name
        self._app_name = app_name
    elif app_id:
        # We can guess the app name from the provided app ID
        app_name = app_id.split(".")[-1]
    else:
        # We need a distribution name to load app metadata.
        #
        # If the code is contained in appname.py, and you start the app using
        # `python -m appname`, then __main__.__package__ will be an empty string.
        #
        # If the code is contained in appname.py, and you start the app using
        # `python appname.py`, then __main__.__package__ will be None - unless
        # the app has been run under pdb, in which case `__package__` doesn't exist.
        #
        # If the code is contained in appname/__main__.py, and you start the app
        # using `python -m appname`, then __main__.__package__ will be "appname".
        try:
            main_module_pkg = getattr(sys.modules["__main__"], "__package__", None)
            if main_module_pkg:
                app_name = main_module_pkg
        except KeyError:
            # If there's no __main__ module, we're probably in a test.
            pass

        # If we still don't have a distribution name, fall back to `toga` as a
        # last resort.
        if app_name is None:
            app_name = "toga"

    # Try to load the app metadata with our best guess of the distribution name.
    try:
        self.metadata = importlib.metadata.metadata(app_name)
        # If the app name has been explicitly set, keep that name. Otherwise, if the
        # app metadata provides an app name, use it. Fall back to whatever name
        # has been derived from other sources (app_id, module name, or "toga")
        if self._app_name is None:
            self._app_name = self.metadata.get("Name", app_name)
    except (importlib.metadata.PackageNotFoundError, ValueError):
        self.metadata = {}
        # If the app name has been explicitly set, keep that name. Otherwise, fall
        # back to whatever name has been derived from other sources (app_id, module
        # name, or "toga")
        if self._app_name is None:
            self._app_name = app_name

    # If a formal name has been provided, use it; otherwise, look to
    # the metadata. However, a formal name *must* be provided.
    if formal_name:
        self._formal_name = formal_name
    else:
        self._formal_name = self.metadata.get("Formal-Name")
    if self._formal_name is None:
        raise RuntimeError("Toga application must have a formal name")

    # If an app_id has been provided, use it; otherwise, look to
    # the metadata. However, an app_id *must* be provided
    if app_id:
        self._app_id = app_id
    else:
        self._app_id = self.metadata.get("App-ID")
    if self._app_id is None:
        raise RuntimeError("Toga application must have an app ID")

    # Other metadata may be passed to the constructor, or loaded with importlib.
    if author:
        self._author = author
    else:
        self._author = self.metadata.get("Author", None)

    if version:
        self._version = version
    else:
        self._version = self.metadata.get("Version", None)

    if home_page:
        self._home_page = home_page
    else:
        self._home_page = self.metadata.get("Home-page", None)

    if description:
        self._description = description
    else:
        self._description = self.metadata.get("Summary", None)

    # Get a platform factory.
    self.factory = get_factory()

    # Instantiate the paths instance for this app.
    self._paths = Paths()

    if icon is None:
        self.icon = Icon.APP_ICON
    else:
        self.icon = icon

    # Set up the document types and collection of documents being managed.
    self._documents = DocumentSet(
        self,
        types=[] if document_types is None else document_types,
    )

    # Install the lifecycle handlers. If passed in as an argument, or assigned using
    # `app.on_event = my_handler`, the event handler will take the app as the first
    # argument. If we're using the default value, or we're subclassing app, the app
    # can be safely implied; so we wrap the method as a simple handler.
    if on_running:
        self.on_running = on_running
    else:
        self.on_running = simple_handler(self.on_running)

    if on_exit:
        self.on_exit = on_exit
    else:
        self.on_exit = simple_handler(self.on_exit)

    # We need the command set to exist so that startup et al. can add commands;
    # but we don't have an impl yet, so we can't set the on_change handler
    self._commands = CommandSet()
    self._status_icons = StatusIconSet()

    self._startup_method = startup

    self._main_window = App._UNDEFINED
    self._windows = WindowSet(self)

    # Create the implementation. This will trigger any startup logic.
    self.factory.App(interface=self)

about()

Display the About dialog for the app.

Default implementation shows a platform-appropriate about dialog using app metadata. Override if you want to display a custom About dialog.

Source code in core/src/toga/app.py
787
788
789
790
791
792
793
def about(self) -> None:
    """Display the About dialog for the app.

    Default implementation shows a platform-appropriate about dialog using app
    metadata. Override if you want to display a custom About dialog.
    """
    self._impl.show_about_dialog()

add_background_task(handler)

DEPRECATED – Use [asyncio.create_task][], or override/assign App.on_running().

Source code in core/src/toga/app.py
945
946
947
948
949
950
951
952
953
954
955
956
957
def add_background_task(self, handler: BackgroundTask) -> None:
    """**DEPRECATED** – Use [`asyncio.create_task`][], or override/assign
    [`App.on_running()`][toga.App.on_running]."""
    warnings.warn(
        (
            "App.add_background_task is deprecated. Use asyncio.create_task(), "
            "or set an App.on_running() handler"
        ),
        DeprecationWarning,
        stacklevel=2,
    )

    self.loop.call_soon_threadsafe(wrapped_handler(self, handler))

beep()

Play the default system notification sound.

Source code in core/src/toga/app.py
795
796
797
def beep(self) -> None:
    """Play the default system notification sound."""
    self._impl.beep()

dialog(dialog) async

Display a dialog to the user in the app context.

:param dialog: The dialog to display to the user. :returns: The result of the dialog.

Source code in core/src/toga/app.py
799
800
801
802
803
804
805
806
async def dialog(self, dialog: Dialog) -> Coroutine[None, None, Any]:
    """Display a dialog to the user in the app context.

    :param dialog: The [dialog](/reference/api/application/dialogs.md) to
        display to the user.
    :returns: The result of the dialog.
    """
    return await dialog._show(None)

enter_presentation_mode(windows)

Enter into presentation mode with one or more windows on different screens.

Presentation mode is not the same as "Full Screen" mode; presentation mode is when window borders, other window decorations, app menu and toolbars are no longer visible.

:param windows: A list of windows, or a dictionary mapping screens to windows, to go into presentation, in order of allocation to screens. If the number of windows exceeds the number of available displays, those windows will not be visible. The windows must have a content set on them.

:raises ValueError: If the presentation layout supplied is not a list of windows or a dict mapping windows to screens, or if any window does not have content.

Source code in core/src/toga/app.py
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
def enter_presentation_mode(
    self,
    windows: list[Window] | dict[Screen, Window],
) -> None:
    """Enter into presentation mode with one or more windows on different screens.

    Presentation mode is not the same as "Full Screen" mode; presentation mode is
    when window borders, other window decorations, app menu and toolbars are no
    longer visible.

    :param windows: A list of windows, or a dictionary
        mapping screens to windows, to go into presentation, in order of
        allocation to screens. If the number of windows exceeds the number
        of available displays, those windows will not be visible. The windows
        must have a content set on them.

    :raises ValueError: If the presentation layout supplied is not a list of
        windows or a dict mapping windows to screens, or if any window does
        not have content.
    """
    if windows:
        # Exit any existing presentation mode before entering with new windows.
        # Presentation mode is not cumulative: a new call replaces the previous one.
        if self.in_presentation_mode:
            self.exit_presentation_mode()

        screen_window_dict = {}
        if isinstance(windows, list):
            for window, screen in zip(windows, self.screens, strict=False):
                screen_window_dict[screen] = window
        elif isinstance(windows, dict):
            screen_window_dict = windows
        else:
            raise ValueError(
                "Presentation layout should be a list of windows,"
                " or a dict mapping windows to screens."
            )

        for screen, window in screen_window_dict.items():
            window._impl._before_presentation_mode_screen = window.screen
            window.screen = screen
            window._impl.set_window_state(WindowState.PRESENTATION)

exit()

Unconditionally exit the application.

This does not invoke the on_exit handler; the app will be immediately and unconditionally closed.

Source code in core/src/toga/app.py
458
459
460
461
462
463
464
def exit(self) -> None:
    """Unconditionally exit the application.

    This *does not* invoke the `on_exit` handler; the app will be immediately
    and unconditionally closed.
    """
    self._impl.exit()

exit_full_screen()

DEPRECATED – Use App.exit_presentation_mode().

Source code in core/src/toga/app.py
963
964
965
966
967
968
969
970
971
972
973
974
975
def exit_full_screen(self) -> None:
    """**DEPRECATED** – Use
    [`App.exit_presentation_mode()`][toga.App.exit_presentation_mode]."""
    warnings.warn(
        (
            "`App.exit_full_screen()` is deprecated. "
            "Use `App.exit_presentation_mode()` instead."
        ),
        DeprecationWarning,
        stacklevel=2,
    )
    if self.in_presentation_mode:
        self.exit_presentation_mode()

exit_presentation_mode()

Exit presentation mode.

Source code in core/src/toga/app.py
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
def exit_presentation_mode(self) -> None:
    """Exit presentation mode."""
    # Guard against recursion: backend set_window_state guards may call
    # exit_presentation_mode() when they see other windows still in
    # presentation mode during the exit loop.
    if self._impl._exiting_presentation:
        return
    self._impl._exiting_presentation = True

    try:
        for window in self.windows:
            if window.state == WindowState.PRESENTATION:
                window._impl.set_window_state(WindowState.NORMAL)
    finally:
        self._impl._exiting_presentation = False

hide_cursor()

Hide cursor from view.

Source code in core/src/toga/app.py
823
824
825
def hide_cursor(self) -> None:
    """Hide cursor from view."""
    self._impl.hide_cursor()

main_loop()

Start the application.

On desktop platforms, this method will block until the application has exited. On mobile and web platforms, it returns immediately.

Source code in core/src/toga/app.py
472
473
474
475
476
477
478
479
480
481
def main_loop(self) -> None:
    """Start the application.

    On desktop platforms, this method will block until the application has exited.
    On mobile and web platforms, it returns immediately.
    """
    # Modify signal handlers to make sure Ctrl-C is caught and handled.
    signal.signal(signal.SIGINT, signal.SIG_DFL)

    self._impl.main_loop()

on_exit()

The event handler that will be invoked when the app is about to exit.

The return value of this method controls whether the app is allowed to exit. This can be used to prevent the app exiting with unsaved changes, etc.

If necessary, the overridden method can be defined as an async coroutine.

:returns: True if the app is allowed to exit; False if the app is not allowed to exit.

Source code in core/src/toga/app.py
920
921
922
923
924
925
926
927
928
929
930
931
932
def on_exit(self) -> bool:
    """The event handler that will be invoked when the app is about to exit.

    The return value of this method controls whether the app is allowed to exit.
    This can be used to prevent the app exiting with unsaved changes, etc.

    If necessary, the overridden method can be defined as an `async` coroutine.

    :returns: `True` if the app is allowed to exit; `False` if the app is not
        allowed to exit.
    """
    # Always allow exit
    return True

on_running()

The event handler that will be invoked when the app's event loop starts running.

If necessary, the overridden method can be defined as an async coroutine.

Source code in core/src/toga/app.py
934
935
936
937
938
939
def on_running(self) -> None:
    """The event handler that will be invoked
    when the app's event loop starts running.

    If necessary, the overridden method can be defined as an `async` coroutine.
    """

request_exit()

Request an exit from the application.

This method will call the App.on_exit() handler to confirm if the app should be allowed to exit; if that handler confirms the action, the app will exit.

Source code in core/src/toga/app.py
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
def request_exit(self):
    """Request an exit from the application.

    This method will call the [`App.on_exit()`][toga.App.on_exit] handler to confirm
    if the app should be allowed to exit; if that handler confirms the action,
    the app will exit.
    """

    def cleanup(app, should_exit):
        if should_exit:
            app.exit()

    # Wrap on_exit to ensure that an async handler is turned into a task,
    # then immediately invoke.
    wrapped_handler(self, self.on_exit, cleanup=cleanup)()

set_full_screen(*windows)

DEPRECATED – Use App.enter_presentation_mode() and App.exit_presentation_mode().

Source code in core/src/toga/app.py
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
def set_full_screen(self, *windows: Window) -> None:
    """**DEPRECATED** – Use
    [`App.enter_presentation_mode()`][toga.App.enter_presentation_mode] and
    [`App.exit_presentation_mode()`][toga.App.exit_presentation_mode]."""
    warnings.warn(
        (
            "`App.set_full_screen()` is deprecated. "
            "Use `App.enter_presentation_mode()` instead."
        ),
        DeprecationWarning,
        stacklevel=2,
    )
    self.exit_presentation_mode()
    if windows:
        self.enter_presentation_mode(list(windows))

show_cursor()

Make the cursor visible.

Source code in core/src/toga/app.py
827
828
829
def show_cursor(self) -> None:
    """Make the cursor visible."""
    self._impl.show_cursor()

startup()

Create and show the main window for the application.

Subclasses can override this method to define customized startup behavior; however, any override must ensure the main_window has been assigned before it returns.

Source code in core/src/toga/app.py
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
def startup(self) -> None:
    """Create and show the main window for the application.

    Subclasses can override this method to define customized startup behavior;
    however, any override *must* ensure the
    [`main_window`][toga.App.main_window] has
    been assigned before it returns.
    """
    self.main_window = MainWindow(title=self.formal_name, id="main")

    if self._startup_method:
        content = self._startup_method(self)
        if content is None:
            raise ValueError(
                "Your app's startup method has not provided any content for your "
                "app's main window. Did you remember to return the main content "
                "container in your startup method?"
            )
        self.main_window.content = content

    self.main_window.show()

visit_homepage()

Open the application's home_page in the default browser.

This method is invoked as a handler by the "Visit homepage" default menu item. If the home_page is None, this is a no-op, and the default menu item will be disabled.

Source code in core/src/toga/app.py
808
809
810
811
812
813
814
815
816
817
def visit_homepage(self) -> None:
    """Open the application's [`home_page`][toga.App.home_page] in the default
    browser.

    This method is invoked as a handler by the "Visit homepage" default menu item.
    If the [`home_page`][toga.App.home_page] is `None`, this is a no-op, and the
    default menu item will be disabled.
    """
    if self.home_page is not None:
        webbrowser.open(self.home_page)

Bases: Protocol

Source code in core/src/toga/app.py
37
38
39
40
41
42
43
44
45
46
47
class AppStartupMethod(Protocol):
    def __call__(self, app: App, **kwargs: Any) -> Widget:
        """The startup method of the app.

        Called during app startup to set the initial main window content.

        :param app: The app instance that is starting.
        :param kwargs: Ensures compatibility with additional arguments introduced in
            future versions.
        :returns: The widget to use as the main window content.
        """

__call__(app, **kwargs)

The startup method of the app.

Called during app startup to set the initial main window content.

:param app: The app instance that is starting. :param kwargs: Ensures compatibility with additional arguments introduced in future versions. :returns: The widget to use as the main window content.

Source code in core/src/toga/app.py
38
39
40
41
42
43
44
45
46
47
def __call__(self, app: App, **kwargs: Any) -> Widget:
    """The startup method of the app.

    Called during app startup to set the initial main window content.

    :param app: The app instance that is starting.
    :param kwargs: Ensures compatibility with additional arguments introduced in
        future versions.
    :returns: The widget to use as the main window content.
    """

Bases: Protocol

Source code in core/src/toga/app.py
75
76
77
78
79
80
81
82
class BackgroundTask(Protocol):
    def __call__(self, app: App, **kwargs: Any) -> object:
        """Code that should be executed as a background task.

        :param app: The app that is handling the background task.
        :param kwargs: Ensures compatibility with additional arguments introduced in
            future versions.
        """

__call__(app, **kwargs)

Code that should be executed as a background task.

:param app: The app that is handling the background task. :param kwargs: Ensures compatibility with additional arguments introduced in future versions.

Source code in core/src/toga/app.py
76
77
78
79
80
81
82
def __call__(self, app: App, **kwargs: Any) -> object:
    """Code that should be executed as a background task.

    :param app: The app that is handling the background task.
    :param kwargs: Ensures compatibility with additional arguments introduced in
        future versions.
    """

Bases: Protocol

Source code in core/src/toga/app.py
50
51
52
53
54
55
56
57
class OnRunningHandler(Protocol):
    def __call__(self, app: App, **kwargs: Any) -> None:
        """A handler to invoke when the app event loop is running.

        :param app: The app instance that is running.
        :param kwargs: Ensures compatibility with additional arguments introduced in
            future versions.
        """

__call__(app, **kwargs)

A handler to invoke when the app event loop is running.

:param app: The app instance that is running. :param kwargs: Ensures compatibility with additional arguments introduced in future versions.

Source code in core/src/toga/app.py
51
52
53
54
55
56
57
def __call__(self, app: App, **kwargs: Any) -> None:
    """A handler to invoke when the app event loop is running.

    :param app: The app instance that is running.
    :param kwargs: Ensures compatibility with additional arguments introduced in
        future versions.
    """

Bases: Protocol

Source code in core/src/toga/app.py
60
61
62
63
64
65
66
67
68
69
70
71
72
class OnExitHandler(Protocol):
    def __call__(self, app: App, **kwargs: Any) -> bool:
        """A handler to invoke when the app is about to exit.

        The return value of this callback controls whether the app is allowed to exit.
        This can be used to prevent the app exiting with unsaved changes, etc.

        :param app: The app instance that is exiting.
        :param kwargs: Ensures compatibility with additional arguments introduced in
            future versions.
        :returns: `True` if the app is allowed to exit; `False` if the app is not
            allowed to exit.
        """

__call__(app, **kwargs)

A handler to invoke when the app is about to exit.

The return value of this callback controls whether the app is allowed to exit. This can be used to prevent the app exiting with unsaved changes, etc.

:param app: The app instance that is exiting. :param kwargs: Ensures compatibility with additional arguments introduced in future versions. :returns: True if the app is allowed to exit; False if the app is not allowed to exit.

Source code in core/src/toga/app.py
61
62
63
64
65
66
67
68
69
70
71
72
def __call__(self, app: App, **kwargs: Any) -> bool:
    """A handler to invoke when the app is about to exit.

    The return value of this callback controls whether the app is allowed to exit.
    This can be used to prevent the app exiting with unsaved changes, etc.

    :param app: The app instance that is exiting.
    :param kwargs: Ensures compatibility with additional arguments introduced in
        future versions.
    :returns: `True` if the app is allowed to exit; `False` if the app is not
        allowed to exit.
    """