Repository: napari/napari

Issue:
Slider widget resizing when changing axes labels
## 🐛 Bug

Sliders are resizing when adjusted with `ViewerModel.dims.axis_labels` (thanks @andy-sweet for pointing out the source of the bug).

Here is an example of what it looks like.

Starting off normally:
<img width="266" alt="image" src="https://user-images.githubusercontent.com/400105/215795041-be635a13-1a64-4acd-aa25-e59cd90fddcd.png">

When typing the label the slider resizes:
<img width="392" alt="image" src="https://user-images.githubusercontent.com/400105/215795285-0564fdb0-8de8-4ab3-aee6-a2182114fefe.png">

_Originally posted by @kephale in https://github.com/andy-sweet/napari-metadata/issues/8#issuecomment-1410515298_

@andy-sweet made [some comments](https://github.com/andy-sweet/napari-metadata/pull/8#issuecomment-1410749226) about this bug:

> Good catch. That's a core napari bug, which you can reproduce by setting `ViewerModel.dims.axis_labels`. I suspect it's due to a slider widget existing for each axis/dimension and being laid out, but also being hidden. There's nothing we can do about it here, but might be worth creating an issue on napari/napari.
            
## To Reproduce

Steps to reproduce the behavior:

1. Open an image that has a slider, like cells 3d
2. Set `ViewerModel.dims.axis_labels` 

## Expected behavior

The slider shouldn't resize





Discussion:
Hi, I was giving this a check to find a fix and seems like this is the way is documented to work. You can see the method handling the resize behavior at:

https://github.com/napari/napari/blob/9e911040148d232b76d768ed74ff90ac0fa6f707/napari/_qt/widgets/qt_dims.py#L131-L147

So, as it is, you see:

![axis_labels_now](https://user-images.githubusercontent.com/16781833/224390317-bdbe7636-b3c1-44c2-b73b-25fec3a5a938.gif)

To modify that, the resize handler method could be reimplemented to something like:

```python
    def _resize_axis_labels(self):
        """When any of the labels get updated, this method updates all label
        widths to a minimum size. This allows the full label to be
        visible at all times, with minimal space, without setting stretch on
        the layout.
        """
        fm = QFontMetrics(QFont("", 0))
        labels = self.findChildren(QLineEdit, 'axis_label')
        maxwidth = max(fm.boundingRect(lab.text()).width() for lab in labels)

        if any(self._displayed_sliders):
            # set maximum width to no more than 20% of slider width
            maxwidth = int(self.slider_widgets[0].width() * 0.2)

        for labl in labels:
            newwidth = min(
                [int(fm.boundingRect(labl.text()).width()) + 10, maxwidth]
            )
            labl.setFixedWidth(newwidth)
```

And then you get something like this when setting the `axis_labels`:

![axis_labels](https://user-images.githubusercontent.com/16781833/224389503-de42558e-3e4f-4b3e-87e1-1bbb5de9d701.gif)

Is okay to do such a change or maybe the expected behavior should be something different from the one proposed and the current one?
Cool @dalthviz! Q: if you have two sliders (a 4D dataset), are they aligned in the new implementation? If so then I like this idea!
Thank you for the feedback @jni ! I checked with a 4D example and they are not aligned:

![imagen](https://user-images.githubusercontent.com/16781833/224757346-37529376-4e1c-4541-9fa7-d5ba80461c9e.png)

I will explorer if there is a way to keep them aligned (maybe defining a minimum size using the longest visible label instead of the longest label could do the trick :) ) 
New update! I think something like this could work:

```python
    def _resize_axis_labels(self):
        """When any of the labels get updated, this method updates all label
        widths to a minimum size. This allows the full label to be
        visible at all times, with minimal space, without setting stretch on
        the layout.
        """
        displayed_sliders_idx = [
            idx
            for idx, displayed in enumerate(self._displayed_sliders)
            if displayed
        ]
        if displayed_sliders_idx:
            fm = QFontMetrics(QFont("", 0))
            labels = self.findChildren(QLineEdit, 'axis_label')
            displayed_labels = [
                self.slider_widgets[slider_idx].axis_label
                for slider_idx in displayed_sliders_idx
            ]
            maxwidth = max(
                [int(fm.boundingRect(lab.text()).width()) for lab in labels]
            )
            newwidth = max(
                [
                    int(fm.boundingRect(dlab.text()).width())
                    for dlab in displayed_labels
                ]
            )

            if any(self._displayed_sliders):
                # set maximum width to no more than 20% of slider width
                maxwidth = int(self.slider_widgets[0].width() * 0.2)

            for labl in labels:
                labl_width = min([newwidth + 10, maxwidth])
                labl.setFixedWidth(labl_width)
```
While also adding a new call to `_resize_axis_labels` at `_update_display`:

https://github.com/napari/napari/blob/45044be5c04f79a7a59e2ce229e8ee97248ef4fc/napari/_qt/widgets/qt_dims.py#L129-L130

With that and a 4D dataset things look something like this:

![axis_labels_change](https://user-images.githubusercontent.com/16781833/225389852-bff347ec-ae33-4a21-8f0b-85541e909e85.gif)


Resolving patch:
diff --git a/napari/_qt/widgets/qt_dims.py b/napari/_qt/widgets/qt_dims.py
--- a/napari/_qt/widgets/qt_dims.py
+++ b/napari/_qt/widgets/qt_dims.py
@@ -116,6 +116,7 @@ def _update_display(self):
         nsliders = np.sum(self._displayed_sliders)
         self.setMinimumHeight(nsliders * self.SLIDERHEIGHT)
         self._resize_slice_labels()
+        self._resize_axis_labels()
 
     def _update_nsliders(self):
         """Updates the number of sliders based on the number of dimensions."""
@@ -130,20 +131,31 @@ def _update_nsliders(self):
 
     def _resize_axis_labels(self):
         """When any of the labels get updated, this method updates all label
-        widths to the width of the longest label. This keeps the sliders
-        left-aligned and allows the full label to be visible at all times,
-        with minimal space, without setting stretch on the layout.
+        widths to a minimum size. This allows the full label to be
+        visible at all times, with minimal space, without setting stretch on
+        the layout.
         """
-        fm = QFontMetrics(QFont("", 0))
-        labels = self.findChildren(QLineEdit, 'axis_label')
-        newwidth = max(fm.boundingRect(lab.text()).width() for lab in labels)
-
-        if any(self._displayed_sliders):
+        displayed_labels = [
+            self.slider_widgets[idx].axis_label
+            for idx, displayed in enumerate(self._displayed_sliders)
+            if displayed
+        ]
+        if displayed_labels:
+            fm = QFontMetrics(QFont("", 0))
+            labels = self.findChildren(QLineEdit, 'axis_label')
             # set maximum width to no more than 20% of slider width
-            maxwidth = self.slider_widgets[0].width() * 0.2
-            newwidth = min([newwidth, maxwidth])
-        for labl in labels:
-            labl.setFixedWidth(int(newwidth) + 10)
+            maxwidth = int(self.slider_widgets[0].width() * 0.2)
+            # set new base width to the width of the longest label being displayed
+            newwidth = max(
+                [
+                    int(fm.boundingRect(dlab.text()).width())
+                    for dlab in displayed_labels
+                ]
+            )
+
+            for labl in labels:
+                labl_width = min([newwidth + 10, maxwidth])
+                labl.setFixedWidth(labl_width)
 
     def _resize_slice_labels(self):
         """When the size of any dimension changes, we want to resize all of the


Test patch: