beeware / toga

A Python native, OS native GUI toolkit.
https://toga.readthedocs.io/en/latest/
BSD 3-Clause "New" or "Revised" License
4.19k stars 655 forks source link

Add support for background and session-based apps #2651

Closed freakboy3742 closed 4 days ago

freakboy3742 commented 2 weeks ago

Builds on #2649; that PR should be reviewed and merged before reviewing this one.

Derived from #2244.

Adds support for 2 new application types:

Includes a new statusiconapp example to demonstrate background apps. This is a bit of a stub until full status icon support is added in a subsequent PR. The key detail is that a background app is configured by setting main_window = toga.App.BACKGROUND.

The documentapp example is used as an example of session-based apps; the addition from this PR is that configuring the app requires setting main_window = None.

This PR also adds a runnning() method on apps that can be overridden by subclasses; this is guaranteed to be executed as soon as the main event loop starts. This simplifies a common use case of using startup() to add a background task. The statusiconapp contains a simple example of usage.

This PR uses the document-based app as a testing exemplar of session-based apps. Testing the session-based API requires loosening some of the test coverage related to document handling; these tests will be made more robust in a subsequent PR that finalises the Document API.

Two notable minor changes:

  1. On Winforms, the main window is no longer set as the application's context. This was needed prior to #2112, but is now redundant; there's also a side effect that assigning a window as the application context forces the window to be shown. Fixes #2653.
  2. On GTK, the call to set the "role" of the window has been removed. This call is only useful on X11; and even then, is only used to provide a position restoration hint if the app's window name isn't unique.

Refs #2209. Refs #97.

PR Checklist:

mhsmith commented 5 days ago

This PR also adds a runnning() method on apps that can be overridden by subclasses; this is guaranteed to be executed as soon as the main event loop starts. This simplifies a common use case of using startup() to add a background task.

As we discussed starting at https://github.com/beeware/toga/issues/2099#issuecomment-1705652449, this can now be written as self.loop.create_task(my_async_function()). This seems simple enough already, and is more flexible because it supports any number of tasks, created at any time, with any arguments.

freakboy3742 commented 5 days ago

This PR also adds a runnning() method on apps that can be overridden by subclasses; this is guaranteed to be executed as soon as the main event loop starts. This simplifies a common use case of using startup() to add a background task.

As we discussed starting at #2099 (comment), this can now be written as self.loop.create_task(my_async_function()). This seems simple enough already, and is more flexible because it supports any number of tasks, created at any time, with any arguments.

I agree that it can be spelled like this; but "logic I want to run as soon as the event loop is running" is a very common pattern; and that logic won't always be async. To my mind, providing explicit support for this use case is worth it, even if it can be replicated with other primitives - if only for the beginner use case, because it means we can expose the capability without needing to give a full primer on what a "task" is in the context of asyncio.

freakboy3742 commented 4 days ago

See #2678 for the discussed changes around on_exit and running.