Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for background and session-based apps #2651

Merged
merged 21 commits into from
Jun 26, 2024

Conversation

freakboy3742
Copy link
Member

@freakboy3742 freakboy3742 commented Jun 14, 2024

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

Derived from #2244.

Adds support for 2 new application types:

  • Background apps: Apps that run, but aren't required to have any windows. This is the precursor to having system tray apps.
  • Session-based apps: Apps that don't have a single "main" window, but multiple windows each of which manages a "session". What was previously called a "document-based" app would be a session-based app (with each session being a document); web browsers and file managers would also be session-based (where each session is an open URL, or a view on a directory). Session-based apps can have multiple open windows. Behavior when the last window is closed varies by platform; on macOS, the app persists; on Windows/GTK, closing the last window closes the app.

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 Decoupled winforms event loop from AppContext. #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 On Windows, the creation of MainWindow renders it in the display without calling show() #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:

  • All new features have been tested
  • All new features have been documented
  • I have read the CONTRIBUTING.md file
  • I will abide by the code of conduct

changes/2653.bugfix.rst Show resolved Hide resolved
docs/reference/api/app.rst Outdated Show resolved Hide resolved
docs/reference/api/app.rst Outdated Show resolved Hide resolved
docs/reference/api/app.rst Outdated Show resolved Hide resolved
docs/reference/api/app.rst Outdated Show resolved Hide resolved
core/src/toga/window.py Outdated Show resolved Hide resolved
docs/reference/api/app.rst Outdated Show resolved Hide resolved
iOS/src/toga_iOS/app.py Outdated Show resolved Hide resolved
core/src/toga/app.py Outdated Show resolved Hide resolved
@mhsmith
Copy link
Member

mhsmith commented Jun 25, 2024

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.

docs/reference/api/app.rst Outdated Show resolved Hide resolved
docs/reference/api/app.rst Outdated Show resolved Hide resolved
Comment on lines 90 to 91
:class:`toga.Window`
~~~~~~~~~~~~~~~~~~~~
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hiding the module name would make the page more readable. In fact, I think we should be doing this more generally in the docs.

Suggested change
:class:`toga.Window`
~~~~~~~~~~~~~~~~~~~~
:class:`Window`
~~~~~~~~~~~~~~~

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed; however, AIUI, the ~ is the preferred way to do this; that keeps the Sphinx reference explicit, while hiding the context.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes a difference when there are more than two components. For example, :any:~toga.App.exit will display as exit, while :any:App.exit will display as App.exit.

If you use the :any: syntax, Sphinx doesn't require names to be absolute, it just searches for a match among all the targets in the docs, and gives an error if it doesn't find exactly one. This usually allows you to write the names in the RST exactly as you want them to appear in the HTML.

I'm not suggesting any more changes to this PR, just that in future it's probably better to avoid littering the docs with toga everywhere. Note that toga is already hidden in the right sidebar, and in argument and return types.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another advantage of using :any: is that it gives an error if it doesn't find a valid target, while :class:, :meth:, etc. all fail silently and output the text with no hyperlink.

docs/reference/api/app.rst Outdated Show resolved Hide resolved
freakboy3742 and others added 2 commits June 26, 2024 06:04
Co-authored-by: Malcolm Smith <smith@chaquo.com>
@freakboy3742
Copy link
Member Author

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.

docs/reference/api/app.rst Outdated Show resolved Hide resolved
docs/reference/api/app.rst Outdated Show resolved Hide resolved
Copy link
Member

@mhsmith mhsmith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discussion, we agreed to do the following in a separate PR:

  • Change running into an event handler called on_running. Like other event handlers, this will support being assigned as a property or a constructor argument.
  • Since the App class is designed to be subclassed, we'll also support overriding the event property with a synchronous or asynchronous method.
  • Do the same with on_exit.

@mhsmith mhsmith merged commit 9d3be1c into beeware:main Jun 26, 2024
32 of 35 checks passed
@freakboy3742 freakboy3742 deleted the other-app-types branch June 26, 2024 23:15
@freakboy3742
Copy link
Member Author

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

@freakboy3742 freakboy3742 mentioned this pull request Jul 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

On Windows, the creation of MainWindow renders it in the display without calling show()
2 participants