Repository: napari/napari

Issue:
UI icons shift when saving screenshot with flash
## 🐛 Bug

I have a napari instance with 228733 shapes. When I use `File \ Save screenshot with viewer` my UI briefly displays as follows (pardon the screenshot it requires quick clicking):

<img width="566" alt="image" src="https://github.com/napari/napari/assets/400105/c61e8015-b616-4885-9a44-e45e42436165">

then returns to normal:

![image](https://github.com/napari/napari/assets/400105/86751171-92c5-4aaf-98b6-e8ac3a62eb1c)

[Edit: when I hard code `flash=False` in qt_main_window.py this doesn't happen]

## To Reproduce

Steps to reproduce the behavior:

1. I ran https://github.com/kephale/nesoi/blob/b34621c5e7a0e5b88e063424d7fc471da2c75e2e/examples/penrose_shapes.py#L94 with `num_generations=9`
2. `File \ Save screenshot with viewer`

## Expected behavior

UI should stay consistent during the screenshot.

## Environment

```
napari: 0.5.0a2.dev384+g2e1d7c12
Platform: macOS-13.5-arm64-arm-64bit
System: MacOS 13.5
Python: 3.10.8 | packaged by conda-forge | (main, Nov 22 2022, 08:25:13) [Clang 14.0.6 ]
Qt: 5.15.8
PyQt5: 5.15.9
NumPy: 1.24.3
SciPy: 1.11.1
Dask: 2023.8.0
VisPy: 0.13.1.dev53
magicgui: 0.7.2
superqt: unknown
in-n-out: 0.1.8
app-model: 0.2.0
npe2: 0.7.2

OpenGL:
- GL version: 2.1 Metal - 83.1
- MAX_TEXTURE_SIZE: 16384

Screens:
- screen 1: resolution 1440x2560, scale 2.0
- screen 2: resolution 1728x1117, scale 2.0
- screen 3: resolution 1440x2560, scale 2.0

Settings path:
- /Users/kharrington/Library/Application Support/napari/nesoi_c9e19b2bd7fa30acdc87b97eea38f175233a048f/settings.yaml
```




Discussion:
I think this is expected behaviour, to give the user both an audio and visual clue that the screenshot did in fact happen. 

Is your complaint that the saved screenshot image is not an accurate representation? Or do you just prefer that the flash/colour change is not on by default?

FYI, you can also pass `flash=False' to the viewer screenshot method (obviously the file menu just gives you the default kwargs, but if you are using napari programmatically you can turn it off)
https://github.com/napari/napari/blob/18dee538c0fc8e584884594ba1d42c485602a215/napari/viewer.py#L100

My concern is with the change in layer control icon placement during the flash's visual effect. In the first screenshot you can see that the spacing between icons becomes large enough that they display over the canvas.
Hi, checked this for a little bit and seems like the buttons showing a strange layout comes from having applied another `QGraphicsEffect` to the layer controls buttons. Using as an example the Shapes layer controls class, if I comment out the following lines (those lines apply an opacity effect over the buttons):

https://github.com/napari/napari/blob/c68d25f51524284898080eed53e5b77e6f1629cc/napari/_qt/layer_controls/qt_shapes_controls.py#L451-L455

And then launch napari and try to use `Save screenshot with Viewer` I don't see the buttons layout changing:

<details>

![controls_flash_buttons](https://github.com/napari/napari/assets/16781833/0b5fb761-77da-436b-bb19-95db7e72e60b)

</details>

For reference, this is what I see without commenting the lines (the layer buttons layout changing while the effect used for the flash argument is in place):

<details>

![current_controls_flash_buttons](https://github.com/napari/napari/assets/16781833/1c629d81-4abf-4f47-bc09-387483aa30ca)

</details>
Quick update, seems like disabling the opacity effect when unneeded makes the screenshot flash/color effect work without changing the buttons layout :tada: :

![fix_controls_flash_buttons](https://github.com/napari/napari/assets/16781833/f3dfd0fb-80ae-426f-b932-fedf5aaac396)

For that, some changes could be done to the `set_widgets_enabled_with_opacity` function. So changing the code at:

https://github.com/napari/napari/blob/c68d25f51524284898080eed53e5b77e6f1629cc/napari/_qt/utils.py#L144-L152

For something like:

```python
def set_widgets_enabled_with_opacity(
    parent: QWidget, widgets: Iterable[QWidget], enabled: bool
):
    """Set enabled state on some widgets. If not enabled, decrease opacity."""
    for widget in widgets:
        op = QGraphicsOpacityEffect(parent)
        op.setOpacity(0.5)
        # Only enable opacity effect when needed. That prevents layout changes
        # when setting the color effect for the whole window with the flash
        # animation option.
        # See https://github.com/napari/napari/issues/6147
        op.setEnabled(not enabled)
        widget.setEnabled(enabled)
        widget.setGraphicsEffect(op)
```

If that seems like a good enough fix let me know and I can submit a PR with the change :)



> If that seems like a good enough fix let me know and I can submit a PR with the change :)

Seems like a good idea to me @dalthviz 

Resolving patch:
diff --git a/napari/_qt/utils.py b/napari/_qt/utils.py
--- a/napari/_qt/utils.py
+++ b/napari/_qt/utils.py
@@ -146,9 +146,14 @@ def set_widgets_enabled_with_opacity(
 ):
     """Set enabled state on some widgets. If not enabled, decrease opacity."""
     for widget in widgets:
-        widget.setEnabled(enabled)
         op = QGraphicsOpacityEffect(parent)
-        op.setOpacity(1 if enabled else 0.5)
+        op.setOpacity(0.5)
+        # Only enable opacity effect when needed. That prevents layout changes
+        # when setting the color effect for the whole window with the flash
+        # animation option.
+        # See https://github.com/napari/napari/issues/6147
+        op.setEnabled(not enabled)
+        widget.setEnabled(enabled)
         widget.setGraphicsEffect(op)
 
 


Test patch: