Repository: spyder-ide/spyder
Pull request: PR: Prevent collapsing splitter widgets in the Plots plugin
<!--- Make sure to read the Contributing Guidelines:                   --->
<!--- https://github.com/spyder-ide/spyder/blob/master/CONTRIBUTING.md --->
<!--- and follow PEP 8, PEP 257 and Spyder's code style:               --->
<!--- https://github.com/spyder-ide/spyder/wiki/Dev:-Coding-Style      --->

## Description of Changes

This fixes a `ValueError: math domain error` reported by a user when the figure viewer had zero width.


<!--- Explain what you've done and why --->




### Issue(s) Resolved

<!--- List the issue(s) below, in the form "Fixes #1234"; one per line --->

Fixes #23677 


### Affirmation

By submitting this Pull Request or typing my (user)name below,
I affirm the [Developer Certificate of Origin](https://developercertificate.org)
with respect to all commits and content included in this PR,
and understand I am releasing the same under Spyder's MIT (Expat) license.

<!--- TYPE YOUR USER/NAME AFTER THE FOLLOWING: --->
I certify the above statement is true and correct:
@jsbautista 
<!--- Thanks for your help making Spyder better for everyone! --->


Problem statement:
`ValueError: math domain error` in the Plots pane
## Description

### What steps will reproduce the problem?

<!--- You can use Markdown here --->

I keep getting a "internal problem" error. I dont know why.It says i have to report it. 

### Traceback
```python-traceback

Traceback (most recent call last):
  File "C:\Users\hanna\anaconda3\envs\DataScienceProgramming\Lib\site-packages\spyder\plugins\plots\widgets\figurebrowser.py", line 278, in add_figure
    self.thumbnails_sb.add_thumbnail(fig, fmt)
  File "C:\Users\hanna\anaconda3\envs\DataScienceProgramming\Lib\site-packages\spyder\plugins\plots\widgets\figurebrowser.py", line 999, in add_thumbnail
    self.set_current_thumbnail(thumbnail)
  File "C:\Users\hanna\anaconda3\envs\DataScienceProgramming\Lib\site-packages\spyder\plugins\plots\widgets\figurebrowser.py", line 1089, in set_current_thumbnail
    self.figure_viewer.load_figure(
  File "C:\Users\hanna\anaconda3\envs\DataScienceProgramming\Lib\site-packages\spyder\plugins\plots\widgets\figurebrowser.py", line 463, in load_figure
    self.auto_fit_plotting = self.current_thumbnail.auto_fit
    ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hanna\anaconda3\envs\DataScienceProgramming\Lib\site-packages\spyder\plugins\plots\widgets\figurebrowser.py", line 422, in auto_fit_plotting
    self.scale_image()
  File "C:\Users\hanna\anaconda3\envs\DataScienceProgramming\Lib\site-packages\spyder\plugins\plots\widgets\figurebrowser.py", line 630, in scale_image
    self.scalefactor = self.get_scale_factor()
                       ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hanna\anaconda3\envs\DataScienceProgramming\Lib\site-packages\spyder\plugins\plots\widgets\figurebrowser.py", line 645, in get_scale_factor
    return math.log(self.get_scaling() / 100) / math.log(self._scalestep)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: math domain error
```

## Versions

* Spyder version: 6.0.1  (conda)
* Python version: 3.12.8 64-bit
* Qt version: 5.15.2
* PyQt5 version: 5.15.10
* Operating System: Windows-11-10.0.26100-SP0

### Dependencies

```
# Mandatory:
aiohttp >=3.9.3                  :  3.11.10 (OK)
asyncssh >=2.14.0,<3.0.0         :  2.17.0 (OK)
atomicwrites >=1.2.0             :  1.4.0 (OK)
chardet >=2.0.0                  :  4.0.0 (OK)
cloudpickle >=0.5.0              :  3.0.0 (OK)
cookiecutter >=1.6.0             :  2.6.0 (OK)
diff_match_patch >=20181111      :  20200713 (OK)
github >=2.3.0                   :  2.4.0 (OK)
importlib_metadata >=4.6.0       :  8.5.0 (OK)
intervaltree >=3.0.2             :  3.1.0 (OK)
IPython >=8.13.0,<9.0.0,!=8.17.1 :  8.30.0 (OK)
jedi >=0.17.2,<0.20.0            :  0.19.2 (OK)
jellyfish >=0.7                  :  1.1.3 (OK)
jsonschema >=3.2.0               :  4.23.0 (OK)
keyring >=17.0.0                 :  24.3.1 (OK)
nbconvert >=4.0                  :  7.16.4 (OK)
numpydoc >=0.6.0                 :  1.7.0 (OK)
parso >=0.7.0,<0.9.0             :  0.8.4 (OK)
pexpect >=4.4.0                  :  4.8.0 (OK)
pickleshare >=0.4                :  0.7.5 (OK)
psutil >=5.3                     :  5.9.0 (OK)
pygments >=2.0                   :  2.15.1 (OK)
pylint >=3.1,<4                  :  3.3.3 (OK)
pylint_venv >=3.0.2              :  3.0.3 (OK)
pyls_spyder >=0.4.0              :  0.4.0 (OK)
pylsp >=1.12.0,<1.13.0           :  1.12.0 (OK)
pylsp_black >=2.0.0,<3.0.0       :  2.0.0 (OK)
pyuca >=1.2                      :  1.2 (OK)
qdarkstyle >=3.2.0,<3.3.0        :  3.2.3 (OK)
qstylizer >=0.2.2                :  0.2.2 (OK)
qtawesome >=1.3.1,<1.4.0         :  1.3.1 (OK)
qtconsole >=5.6.0,<5.7.0         :  5.6.1 (OK)
qtpy >=2.4.0                     :  2.4.1 (OK)
rtree >=0.9.7                    :  1.0.1 (OK)
setuptools >=49.6.0              :  75.8.0 (OK)
sphinx >=0.6.6                   :  7.3.7 (OK)
spyder_kernels >=3.0.0,<3.1.0    :  3.0.2 (OK)
superqt >=0.6.2,<1.0.0           :  0.6.7 (OK)
textdistance >=4.2.0             :  4.6.2 (OK)
three_merge >=0.1.1              :  0.1.1 (OK)
watchdog >=0.10.3                :  4.0.1 (OK)
yarl >=1.9.4                     :  1.18.0 (OK)
zmq >=24.0.0                     :  26.2.0 (OK)

# Optional:
cython >=0.21                    :  None (NOK)
matplotlib >=3.0.0               :  3.10.0 (OK)
numpy >=1.7                      :  2.2.2 (OK)
pandas >=1.1.1                   :  2.2.3 (OK)
scipy >=0.17.0                   :  None (NOK)
sympy >=0.7.3                    :  None (NOK)
```

### Environment

<details><summary>Environment</summary>

```
# packages in environment at C:\Users\hanna\anaconda3\envs\DataScienceProgramming:
#
# Name                    Version                   Build  Channel
aiohappyeyeballs          2.4.4           py312haa95532_0  
aiohttp                   3.11.10         py312h827c3e9_0  
aiosignal                 1.2.0              pyhd3eb1b0_0  
alabaster                 0.7.16          py312haa95532_0  
annotated-types           0.7.0                    pypi_0    pypi
arrow                     1.3.0           py312haa95532_0  
astroid                   3.3.8           py312haa95532_0  
asttokens                 2.0.5              pyhd3eb1b0_0  
asyncssh                  2.17.0          py312haa95532_0  
atomicwrites              1.4.0                      py_0  
attrs                     24.3.0          py312haa95532_0  
autopep8                  2.0.4              pyhd3eb1b0_0  
babel                     2.16.0          py312haa95532_0  
beautifulsoup4            4.12.3          py312haa95532_0  
betterproto               2.0.0b6                  pypi_0    pypi
binaryornot               0.4.4              pyhd3eb1b0_1  
black                     24.10.0         py312haa95532_0  
bleach                    6.2.0           py312haa95532_0  
brotli-python             1.0.9           py312h5da7b33_9  
bs4                       0.0.2                    pypi_0    pypi
bzip2                     1.0.8                h2bbff1b_6  
ca-certificates           2024.12.31           haa95532_0  
certifi                   2025.1.31       py312haa95532_0  
cffi                      1.17.1          py312h827c3e9_1  
chardet                   4.0.0           py312haa95532_1003  
charset-normalizer        3.3.2              pyhd3eb1b0_0  
click                     8.1.7           py312haa95532_0  
cloudpickle               3.0.0           py312haa95532_0  
colorama                  0.4.6           py312haa95532_0  
comm                      0.2.1           py312haa95532_0  
contourpy                 1.3.1                    pypi_0    pypi
cookiecutter              2.6.0           py312haa95532_0  
cryptography              43.0.3          py312hbd6ee87_1  
cycler                    0.12.1                   pypi_0    pypi
debugpy                   1.8.11          py312h5da7b33_0  
decorator                 5.1.1              pyhd3eb1b0_0  
defusedxml                0.7.1              pyhd3eb1b0_0  
deprecated                1.2.13          py312haa95532_0  
diff-match-patch          20200713           pyhd3eb1b0_0  
dill                      0.3.8           py312haa95532_0  
dnspython                 2.7.0                    pypi_0    pypi
docstring-to-markdown     0.11            py312haa95532_0  
docutils                  0.18.1          py312haa95532_3  
email-validator           2.2.0                    pypi_0    pypi
executing                 0.8.3              pyhd3eb1b0_0  
expat                     2.6.4                h8ddb27b_0  
flake8                    7.1.1           py312haa95532_0  
fonttools                 4.56.0                   pypi_0    pypi
frozenlist                1.5.0           py312h827c3e9_0  
grpclib                   0.4.7                    pypi_0    pypi
h11                       0.14.0                   pypi_0    pypi
h2                        4.2.0                    pypi_0    pypi
hpack                     4.1.0                    pypi_0    pypi
html5lib                  1.1                      pypi_0    pypi
hyperframe                6.1.0                    pypi_0    pypi
icu                       73.1                 h6c2663c_0  
id                        1.5.0                    pypi_0    pypi
idna                      3.7             py312haa95532_0  
imagesize                 1.4.1           py312haa95532_0  
importlib-metadata        8.5.0           py312haa95532_0  
in-toto-attestation       0.9.3                    pypi_0    pypi
inflection                0.5.1           py312haa95532_1  
intervaltree              3.1.0              pyhd3eb1b0_0  
ipykernel                 6.29.5          py312haa95532_1  
ipython                   8.30.0          py312haa95532_0  
isort                     5.13.2          py312haa95532_1  
jaraco.classes            3.2.1              pyhd3eb1b0_0  
jedi                      0.19.2          py312haa95532_0  
jellyfish                 1.1.3           py312h8ecf97c_0  
jinja2                    3.1.5           py312haa95532_0  
jpeg                      9e                   h827c3e9_3  
jsonschema                4.23.0          py312haa95532_0  
jsonschema-specifications 2023.7.1        py312haa95532_0  
jupyter_client            8.6.3           py312haa95532_0  
jupyter_core              5.7.2           py312haa95532_0  
jupyterlab_pygments       0.2.2           py312haa95532_0  
kaggle                    1.6.17                   pypi_0    pypi
kagglehub                 0.3.7                    pypi_0    pypi
keyring                   24.3.1          py312haa95532_0  
kiwisolver                1.4.8                    pypi_0    pypi
krb5                      1.20.1               h5b6d351_0  
libclang                  14.0.6          default_hb5a9fac_2  
libclang13                14.0.6          default_h8e68704_2  
libffi                    3.4.4                hd77b12b_1  
libpng                    1.6.39               h8cc25b3_0  
libpq                     17.2                 h70ee33d_0  
libsodium                 1.0.18               h62dcd97_0  
libspatialindex           1.9.3                h6c2663c_0  
lxml                      5.3.0                    pypi_0    pypi
lz4-c                     1.9.4                h2bbff1b_1  
markdown-it-py            2.2.0           py312haa95532_1  
markupsafe                3.0.2           py312h827c3e9_0  
matplotlib                3.10.0                   pypi_0    pypi
matplotlib-inline         0.1.6           py312haa95532_0  
mccabe                    0.7.0              pyhd3eb1b0_0  
mdurl                     0.1.0           py312haa95532_0  
mistune                   2.0.4           py312haa95532_0  
model-signing             0.2.0                    pypi_0    pypi
more-itertools            10.3.0          py312haa95532_0  
multidict                 6.1.0           py312h827c3e9_0  
mypy_extensions           1.0.0           py312haa95532_0  
nbclient                  0.8.0           py312haa95532_0  
nbconvert                 7.16.4          py312haa95532_0  
nbformat                  5.10.4          py312haa95532_0  
nest-asyncio              1.6.0           py312haa95532_0  
numpy                     2.2.2                    pypi_0    pypi
numpydoc                  1.7.0           py312haa95532_0  
openssl                   3.0.15               h827c3e9_0  
outcome                   1.3.0.post0              pypi_0    pypi
packaging                 24.2            py312haa95532_0  
pandas                    2.2.3                    pypi_0    pypi
pandocfilters             1.5.0              pyhd3eb1b0_0  
parso                     0.8.4           py312haa95532_0  
pathspec                  0.10.3          py312haa95532_0  
pexpect                   4.8.0              pyhd3eb1b0_3  
pickleshare               0.7.5           pyhd3eb1b0_1003  
pillow                    11.1.0                   pypi_0    pypi
pip                       25.0            py312haa95532_0  
platformdirs              4.3.6                    pypi_0    pypi
pluggy                    1.5.0           py312haa95532_0  
ply                       3.11            py312haa95532_1  
prompt-toolkit            3.0.43          py312haa95532_0  
prompt_toolkit            3.0.43               hd3eb1b0_0  
propcache                 0.2.0           py312h827c3e9_0  
protobuf                  5.29.3                   pypi_0    pypi
psutil                    5.9.0           py312h827c3e9_1  
ptyprocess                0.7.0              pyhd3eb1b0_2  
pure_eval                 0.2.2              pyhd3eb1b0_0  
pyasn1                    0.6.1                    pypi_0    pypi
pycodestyle               2.12.1          py312haa95532_0  
pycparser                 2.21               pyhd3eb1b0_0  
pydantic                  2.10.6                   pypi_0    pypi
pydantic-core             2.27.2                   pypi_0    pypi
pydocstyle                6.3.0           py312haa95532_0  
pyflakes                  3.2.0           py312haa95532_0  
pygithub                  2.4.0           py312haa95532_0  
pygments                  2.15.1          py312haa95532_1  
pyjwt                     2.10.1          py312haa95532_0  
pylint                    3.3.3           py312haa95532_0  
pylint-venv               3.0.3           py312haa95532_0  
pyls-spyder               0.4.0              pyhd3eb1b0_0  
pymongo                   4.11                     pypi_0    pypi
pynacl                    1.5.0           py312h7edc060_1  
pyopenssl                 25.0.0                   pypi_0    pypi
pyparsing                 3.2.1                    pypi_0    pypi
pyqt                      5.15.10         py312h5da7b33_1  
pyqt5-sip                 12.13.0         py312h827c3e9_1  
pyqtwebengine             5.15.10         py312h5da7b33_1  
pysocks                   1.7.1           py312haa95532_0  
python                    3.12.8               h14ffc60_0  
python-dateutil           2.9.0post0      py312haa95532_2  
python-fastjsonschema     2.20.0          py312haa95532_0  
python-lsp-black          2.0.0           py312haa95532_0  
python-lsp-jsonrpc        1.1.2              pyhd3eb1b0_0  
python-lsp-server         1.12.0          py312hfc267ef_0  
python-slugify            5.0.2              pyhd3eb1b0_0  
pytoolconfig              1.2.6           py312haa95532_0  
pytz                      2024.2                   pypi_0    pypi
pyuca                     1.2             py312haa95532_1  
pywin32                   308             py312h5da7b33_0  
pywin32-ctypes            0.2.2           py312haa95532_0  
pyyaml                    6.0.2           py312h827c3e9_0  
pyzmq                     26.2.0          py312h5da7b33_0  
qdarkstyle                3.2.3              pyhd3eb1b0_0  
qstylizer                 0.2.2           py312haa95532_0  
qt-main                   5.15.2              h19c9488_11  
qt-webengine              5.15.9               h5bd16bc_7  
qtawesome                 1.3.1           py312haa95532_0  
qtconsole                 5.6.1           py312haa95532_0  
qtpy                      2.4.1           py312haa95532_0  
referencing               0.30.2          py312haa95532_0  
requests                  2.32.3          py312haa95532_1  
rfc3161-client            0.1.2                    pypi_0    pypi
rfc8785                   0.1.4                    pypi_0    pypi
rich                      13.9.4          py312haa95532_0  
rope                      1.12.0          py312haa95532_0  
rpds-py                   0.22.3          py312h636fa0f_0  
rtree                     1.0.1           py312h2eaa2aa_0  
securesystemslib          1.2.0                    pypi_0    pypi
selenium                  4.28.1                   pypi_0    pypi
setuptools                75.8.0          py312haa95532_0  
sigstore                  3.6.1                    pypi_0    pypi
sigstore-protobuf-specs   0.3.2                    pypi_0    pypi
sigstore-rekor-types      0.0.18                   pypi_0    pypi
sip                       6.7.12          py312h5da7b33_1  
six                       1.17.0                   pypi_0    pypi
sniffio                   1.3.1                    pypi_0    pypi
snowballstemmer           2.2.0              pyhd3eb1b0_0  
sortedcontainers          2.4.0              pyhd3eb1b0_0  
soupsieve                 2.5             py312haa95532_0  
sphinx                    7.3.7           py312h827c3e9_0  
sphinxcontrib-applehelp   1.0.2              pyhd3eb1b0_0  
sphinxcontrib-devhelp     1.0.2              pyhd3eb1b0_0  
sphinxcontrib-htmlhelp    2.0.0              pyhd3eb1b0_0  
sphinxcontrib-jsmath      1.0.1              pyhd3eb1b0_0  
sphinxcontrib-qthelp      1.0.3              pyhd3eb1b0_0  
sphinxcontrib-serializinghtml 1.1.10          py312haa95532_0  
spyder                    6.0.1           py312haa95532_0  
spyder-kernels            3.0.2           py312hfc267ef_0  
sqlite                    3.45.3               h2bbff1b_0  
stack_data                0.2.0              pyhd3eb1b0_0  
superqt                   0.6.7           py312hfc267ef_0  
tabulate                  0.9.0           py312haa95532_0  
text-unidecode            1.3                pyhd3eb1b0_0  
textdistance              4.6.3           py312hfc267ef_0  
three-merge               0.1.1              pyhd3eb1b0_0  
tinycss2                  1.4.0           py312haa95532_0  
tk                        8.6.14               h0416ee5_0  
tomli                     2.0.1           py312haa95532_1  
tomlkit                   0.13.2          py312haa95532_0  
tornado                   6.4.2           py312h827c3e9_0  
tqdm                      4.67.1                   pypi_0    pypi
traitlets                 5.14.3          py312haa95532_0  
trio                      0.28.0                   pypi_0    pypi
trio-websocket            0.11.1                   pypi_0    pypi
tuf                       5.1.0                    pypi_0    pypi
typing-extensions         4.12.2          py312haa95532_0  
typing_extensions         4.12.2          py312haa95532_0  
tzdata                    2025.1                   pypi_0    pypi
ujson                     5.10.0          py312h5da7b33_1  
unidecode                 1.3.8           py312haa95532_0  
urllib3                   2.3.0           py312haa95532_0  
vc                        14.40                haa95532_2  
vs2015_runtime            14.42.34433          h9531ae6_2  
watchdog                  4.0.1           py312haa95532_0  
wcwidth                   0.2.5              pyhd3eb1b0_0  
webencodings              0.5.1           py312haa95532_2  
websocket-client          1.8.0                    pypi_0    pypi
whatthepatch              1.0.2           py312haa95532_0  
wheel                     0.44.0          py312haa95532_0  
win_inet_pton             1.1.0           py312haa95532_0  
wrapt                     1.17.0          py312h827c3e9_0  
wsproto                   1.2.0                    pypi_0    pypi
xz                        5.4.6                h8cc25b3_1  
yaml                      0.2.5                he774522_0  
yapf                      0.40.2          py312haa95532_0  
yarl                      1.18.0          py312h827c3e9_0  
zeromq                    4.3.5                hd77b12b_0  
zipp                      3.21.0          py312haa95532_0  
zlib                      1.2.13               h8cc25b3_1  
zstd                      1.5.6                h8880b57_0  

```
</details>

Hints from issue discussion:
Hi @b19hanru thank you for the report! I think I was able to replicate this! Seems like this error happens when you completely decrease the size of the place where the selected plot is shown and try to selected another plot while having the `Fit plot to the pane size` option active:

![Image](https://github.com/user-attachments/assets/203d810c-6612-4eb7-92db-fef64a8ce8fa)

Seems like we need to add a minimum size to the place where the currently selected plot is shown @ccordoba12 ?

> Seems like we need to add a minimum size to the place where the currently selected plot is shown @ccordoba12 ?

Or perhaps a max width to the thumbnail bar. I mean, it doesn't make much sense to make it reach the full width of the pane (half of it should be good enough).

Commit message: fix resize plots

Patch to review:
diff --git a/spyder/plugins/plots/widgets/figurebrowser.py b/spyder/plugins/plots/widgets/figurebrowser.py
index ece97f0b937..2cb2d4cac01 100644
--- a/spyder/plugins/plots/widgets/figurebrowser.py
+++ b/spyder/plugins/plots/widgets/figurebrowser.py
@@ -194,6 +194,7 @@ def __init__(self, parent=None, background_color=None):
               "like Bokeh, Plotly or Altair.")
         )
 
+
         # Create the layout.
         self.splitter = splitter = QSplitter(parent=self)
         splitter.addWidget(self.figviewer)
@@ -203,6 +204,8 @@ def __init__(self, parent=None, background_color=None):
         splitter.setStyleSheet(
             f"border-radius: {SpyderPalette.SIZE_BORDER_RADIUS}"
         )
+        self.splitter.setChildrenCollapsible(False)
+        self.splitter.splitterMoved.connect(self.on_splitter_moved)
 
         self.stack_layout = QStackedLayout()
         self.stack_layout.addWidget(splitter)
@@ -212,6 +215,15 @@ def __init__(self, parent=None, background_color=None):
         self.stack_layout.setSpacing(0)
         self.setContentsMargins(0, 0, 0, 0)
 
+    def on_splitter_moved(self):
+        total_width = self.splitter.width()
+
+        min_width_percentage = 0.5
+
+        min_width = total_width * min_width_percentage
+
+        self.figviewer.setMinimumWidth(min_width)
+
     def _update_zoom_value(self, value):
         """
         Used in testing.


Code review comments:
diff_hunk: @@ -212,6 +215,15 @@ def __init__(self, parent=None, background_color=None):
         self.stack_layout.setSpacing(0)
         self.setContentsMargins(0, 0, 0, 0)
 
+    def on_splitter_moved(self):
+        total_width = self.splitter.width()
+
+        min_width_percentage = 0.5
+
+        min_width = total_width * min_width_percentage
+
path: spyder/plugins/plots/widgets/figurebrowser.py
text: @user1:
```suggestion
    def _on_splitter_moved(self):
        total_width = self.splitter.width()
        min_width_percentage = 0.55
        min_width = int(total_width * min_width_percentage)
```

* Make method private.
* Remove unnecessary blank lines.
* Make `figviewer` slightly bigger than the thumbnails bar.
* `min_width` needs to be an `int` in Python 3.10+

diff_hunk: @@ -203,6 +204,8 @@ def __init__(self, parent=None, background_color=None):
         splitter.setStyleSheet(
             f"border-radius: {SpyderPalette.SIZE_BORDER_RADIUS}"
         )
+        self.splitter.setChildrenCollapsible(False)
+        self.splitter.splitterMoved.connect(self.on_splitter_moved)
path: spyder/plugins/plots/widgets/figurebrowser.py
text: @user1:
```suggestion
        self.splitter.splitterMoved.connect(self._on_splitter_moved)
```

Let's use a private method for this.