Better error handling for capturing as an image
- Dominant language
- Python
- Stars
- 5.4k
- Forks
- 827
- Avg merge
- 15h 39m
- Merged PRs (30d)
- 40
Description
### Describe the bug
Failing to capture a screenshot can result in unexpected failure modes.
For instance, on Linux under Wayland, it is not possible to screenshot the entire screen:
```python
[helloworld] Starting app...
===========================================================================
(helloworld:3383114): dbind-WARNING **: 11:52:50.660: Couldn't connect to accessibility bus: Failed to connect to socket /run/user/1000/at-spi/bus_1: Connection refused
window1.as_image()=
/home/russell/tmp/beeware/helloworld/build/helloworld/pop/jammy/helloworld-0.0.1/usr/lib/helloworld/app_packages/toga/__init__.py:57: NotImplementedWarning: [GTK] Not implemented: Screen.get_image_data() on Wayland
warnings.warn(NotImplementedWarning(f"[{platform}] Not implemented: {feature}"))
Traceback (most recent call last):
File "/home/russell/tmp/beeware/helloworld/build/helloworld/pop/jammy/helloworld-0.0.1/usr/lib/helloworld/app_packages/toga_gtk/app.py", line 67, in gtk_startup
self.interface._startup()
File "/home/russell/tmp/beeware/helloworld/build/helloworld/pop/jammy/helloworld-0.0.1/usr/lib/helloworld/app_packages/toga/app.py", line 641, in _startup
self.startup()
File "/home/russell/tmp/beeware/helloworld/build/helloworld/pop/jammy/helloworld-0.0.1/usr/lib/helloworld/app/helloworld/app.py", line 37, in startup
print(f"{self.screens[0].as_image()=}")
File "/home/russell/tmp/beeware/helloworld/build/helloworld/pop/jammy/helloworld-0.0.1/usr/lib/helloworld/app_packages/toga/screens.py", line 43, in as_image
return Image(self._impl.get_image_data()).as_format(format)
File "/home/russell/tmp/beeware/helloworld/build/helloworld/pop/jammy/helloworld-0.0.1/usr/lib/helloworld/app_packages/toga/images.py", line 155, in __init__
raise TypeError("Unsupported source type for Image")
TypeError: Unsupported source type for Image
```
Instead, you get a runtime `NotImplementedWarning` (that's also part of a realllly long line of text) followed by a `TypeError`. This is because the screenshot API expects the backend to always return valid image data; in this case, `None` is returned.
This is also true of `toga.widgets.Canvas.as_image()` and `toga.window.Window.as_image()`.
In a slightly different way, `toga.widgets.ImageView.as_image()` is also affected.
```python
[helloworld] Starting app...
===========================================================================
(helloworld:3401633): dbind-WARNING **: 12:05:35.097: Couldn't connect to accessibility bus: Failed to connect to socket /run/user/1000/at-spi/bus_1: Connection refused
window1.as_image()=
self.screens[0].as_image()=
Traceback (most recent call last):
File "/home/russell/tmp/beeware/helloworld/build/helloworld/pop/jammy/helloworld-0.0.1/usr/lib/helloworld/app_packages/toga_gtk/app.py", line 67, in gtk_startup
self.interface._startup()
File "/home/russell/tmp/beeware/helloworld/build/helloworld/pop/jammy/helloworld-0.0.1/usr/lib/helloworld/app_packages/toga/app.py", line 641, in _startup
self.startup()
File "/home/russell/tmp/beeware/helloworld/build/helloworld/pop/jammy/helloworld-0.0.1/usr/lib/helloworld/app/helloworld/app.py", line 39, in startup
print(f"{toga.ImageView().as_image()=}")
File "/home/russell/tmp/beeware/helloworld/build/helloworld/pop/jammy/helloworld-0.0.1/usr/lib/helloworld/app_packages/toga/widgets/imageview.py", line 139, in as_image
return self.image.as_format(format)
AttributeError: 'NoneType' object has no attribute 'as_format'
```
Here, users can create an empty `ImageView` and end up with an `AttributeError` if it doesn't actually contain an image.
### Steps to reproduce
Sample app:
```python
import toga
class HelloWorld(toga.App):
def startup(self):
self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.show()
print(f"{self.main_window.as_image()=}")
print(f"{self.screens[0].as_image()=}")
print(f"{toga.ImageView().as_image()=}")
def main():
return HelloWorld(
```
### Expected behavior
While some of these failure modes are user induced, others are quite subtle and may be entirely unknown to developers at build time. Toga could be more resilient to these failure modes and provide better error handling when they occur.
### Screenshots
_No response_
### Environment
- Operating System: pop os 22.04
- Python version: 3.10.14
- Software versions:
- Briefcase: `0.3.19`
- Toga: `0.4.6.dev52+g10608425a`
### Logs
_No response_
### Additional context
On a side note, there are also likely to be interesting issues if you use these APIs while the window is not being shown. I think this may be getting a little in to particularly contrived failure modes, though.
Contributor guide
Research direction
Start by reading toga/screens.py, toga/images.py, toga/widgets/imageview.py, and the corresponding Window and Canvas as_image implementations shown in the traceback. Check how None or an empty ImageView is handled, then run the existing image-related tests if available; done means these APIs report a clear, consistent failure instead of exposing TypeError or AttributeError details.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux, python
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100