Repository: spyder-ide/spyder

Issue:
Low quality matplotlib inline plots on hidpi display
<!--- **PLEASE READ:** When submitting here, please ensure you've completed the following checklist and checked the boxes to confirm. Issue reports without it may be closed. Thanks! --->

### Issue Report Checklist

* [x] Searched the [issues page](https://github.com/spyder-ide/spyder/issues?q=is%3Aissue) for similar reports
* [x] Read the relevant sections of the [Spyder Troubleshooting Guide](https://github.com/spyder-ide/spyder/wiki/Troubleshooting-Guide-and-FAQ) and followed its advice
* [x] Reproduced the issue after updating with ``conda update spyder`` (or ``pip``, if not using Anaconda)
* [ ] Could not reproduce inside ``jupyter qtconsole`` (if console-related)
* [x] Tried basic troubleshooting (if a bug/error)
    * [x] Restarted Spyder
    * [x] Reset preferences with ``spyder --reset``
    * [ ] Reinstalled the latest version of [Anaconda](https://www.anaconda.com/download/)
    * [x] Tried the other applicable steps from the Troubleshooting Guide
* [x] Completed the **Problem Description**, **Steps to Reproduce** and **Version** sections below


## Problem Description



### What steps reproduce the problem?

1. set a custom high DPI scalling (default 1.5 or any larger value) in spyder Preferences -> General
2. plot a line with matplotlib
```
import numpy as np
from matplotlib import pyplot as pp
pp.figure()
pp.plot(np.arange(100), 'k')
pp.grid()
```

3. change inline backend figure format to retina
```
%config InlineBackend.figure_format = 'retina'
```
4. repeat step 2.

### What is the expected output? What do you see instead?

Expected the output to be smooth high quality DPI inline plot after change in inline figure format but instead blurry jagged low quality version is displayed as before

![spyder-matplotlib-inline-plot](https://user-images.githubusercontent.com/2661785/56022205-dc1a8c80-5d13-11e9-923e-01ddf9b2ec64.png)

Jupyter notebook run shows correct smooth plot after inline figure format change

![jupyter-ipython-inline-plot](https://user-images.githubusercontent.com/2661785/56022206-dc1a8c80-5d13-11e9-90ec-3cb18b0aff0b.png)



## Versions
<!--- You can get this information from Help > About Spyder...
or (if Spyder won't launch) the "conda list" command
from the Anaconda Prompt/Terminal/command line. --->

* Spyder version: 3.3.4
* Python version: 3.6.7 64-bit
* Qt version: 5.12.2
* PyQt version: 5.12.1
* Operating System name/version: Linux Mint 19.1 Cinnamon 4.0.10

### Dependencies
<!--- Please go to the menu entry Help > Dependencies,
press the Copy to clipboard button and paste below --->

```
IPython >=4.0     :  7.4.0 (OK)
cython >=0.21     :  0.29.6 (OK)
jedi >=0.9.0      :  0.13.3 (OK)
matplotlib >=2.0.0:  3.0.3 (OK)
nbconvert >=4.0   :  5.4.1 (OK)
numpy >=1.7       :  1.16.2 (OK)
pandas >=0.13.1   :  0.24.2 (OK)
psutil >=0.3      :  5.6.1 (OK)
pycodestyle >=2.3 :  2.5.0 (OK)
pyflakes >=0.6.0  :  2.1.1 (OK)
pygments >=2.0    :  2.3.1 (OK)
pylint >=0.25     :  2.3.1 (OK)
qtconsole >=4.2.0 :  4.4.3 (OK)
rope >=0.9.4      :  0.14.0 (OK)
sphinx >=0.6.6    :  2.0.1 (OK)
sympy >=0.7.3     :  1.4 (OK)
```



Discussion:
@dalthviz, please take a look at this one. We need to add a new option in the Inline backend section of our preferences called

    [ ] Display images in high resolution

And pass that option to the kernel.
using Kubuntu 18.10 installation there is no such a problem even with default png backend inline format
`Python 3.6.7 64-bit | Qt 5.12.2 | PyQt5 5.12.1 | Linux 4.18.0-17-generic `
Then I'd say it's a Qt issue and not an Spyder one.
> Display images in high resolution

If this is still a correction needed, why not make it a default (instead of adding the option?) 
Because in non-HDPI screens plots will look huge.
>Because in non-HDPI screens plots will look huge.

Got it thanks!

Is there an overall High DPI setting for spyder? Could we link to that one?
There's what Rafa added in

    Preferences > General > Screen resolution

But linking both things (a change in that option with a change in the inline backend) is not work for Spyder 3.

Should we leave this for Spyder 4? It's not that critical.
>Should we leave this for Spyder 4? It's not that critical.

Yes, I believe is the right place to fix it. 
> Then I'd say it's a Qt issue and not an Spyder one.

There are similar issues with Gwenview PNG images opened in Kubuntu 18.10 HIDPI monitor with desktop scaled to fractional scale as in Spyder running on Mint 19.1. PNG images in Gwenview once opened looked exactly blurry and jagged and that issue is known and reported.
@goanpeca, do you need to activate our option in

    Preferences > General > Screen resolution

in your Retina Display Mac for things to look good there?

That's to know if Qt adjusts automatically to HiDPI in some cases (like macOS).

If you don't need to activate that option, then we really need to add another option to the console preferences (as proposed at the beginning) to tell the kernel that Spyder is running in a HiDPI screen.
Some useful links:

* Detect change of monitor in Qt: https://stackoverflow.com/a/27277493/438386
* Detect screen size in PyQt: https://stackoverflow.com/a/17475065/438386
Notes:

* Add support to run `%config InlineBackend.figure_format = <option>`.
* Add an option in the Plots plugin to enable a certain figure format (probably the option in the preferences of the `IPython Console` should be migrated to not have multiple preferences for the same thing - format figure of the inline backend).
* Probably, when setting the figure format option in the Plots plugin, we should ask if the format figure preference needs to be applied to all the available consoles or just the current one.


@dalthviz, the work you're planning to do to solve this one could be unnecessary if PR #12219 is merged in the future. So please take a look at that work before starting this one.
Has there been any developments on this?

I see that a fix was aimed for Spyder 4...

> > Should we leave this for Spyder 4? It's not that critical.
> 
> Yes, I believe is the right place to fix it.

I still have this issue with Spyder 5.0.5. My inline matplotlib plots on my MacBook Pro (hidpi display) look fuzzy/blurry no matter how high I increase matplotlib's DPI setting.

Edit:
Example:
![image](https://user-images.githubusercontent.com/15970250/127406004-0427b093-d30c-4113-a50b-0c18b4cf0ef1.png)

No, this hasn't been addressed yet, but we'll try to do it in the next couple of months.

For now, you need to increase your DPI manually by going to the menu

    Tools > Preferences > IPython console > Graphics > Inline Backend

and the increasing the `Resolution` there.
> No, this hasn't been addressed yet, but we'll try to do it in the next couple of months.
> 
> For now, you need to increase your DPI manually by going yo the menu
> 
> ```
> Tools > Preferences > IPython console > Graphics > Inline Backend
> ```
> 
> and the increasing the `Resolution` there.

Thank you for revisiting! Much appreciated!
This discussion runs over several years and it's not clear to me what needs to be done now. This is my understanding of the current situation. 

The images in the plots pane can be fixed by increasing the resolution (DPI) in the preferences, so the only thing that we maybe need to do is to change the default resolution, perhaps by taking the high-DPI scaling factor into account.

The images in the IPython console can not be fixed by increasing the DPI in the preferences, because this will simply enlarge the image and the resolution remains the same. One possible fix here is to support the "Retina" format for plots like Jupyter, along the lines of PR #12128. However, that is a bit of a kludge.
> One possible fix here is to support the "Retina" format for plots like Jupyter, along the lines of PR https://github.com/spyder-ide/spyder/pull/12128. However, that is a bit of a kludge.

In fact, I think that would be the simplest thing to do, i.e. allow users to control the retina display option with a Spyder one. To do that, I propose the following:

* Add an option to the Plots options menu called `Enable high resolution plots`, checked by default.
* That option should set/unset the retina display option in the Inline backend.
* If users uncheck the `Mute inline plotting` option, `Enable high resolution plots` should be unchecked and disabled so plots appear in the console at an appropriate size.
* When `Mute inline plotting` is unchecked, a message should be displayed in the console to warn users that plots will be shown in low resolution.
I have now a slightly better idea what the "Retina" format for plots does. It changes two things: it doubles the matploblib resolution (in dpi) from what the user sets, and it shrinks plots in the IPython console by a factor of two.

In more detail, the standard settings are for plots to be 6 inches wide and 4 inches high with a resolution of 72 dpi. This produces images of 6 * 72 = 432 pixels wide and 4 * 72 = 288 pixels high (actually, the images are slightly smaller if tight layout is enabled, which it is by default). If the plot is displayed in the IPython console (which if the option "Mute inline plotting" is unchecked), then it is displayed as an image of 432x288 pixels. By default, the plot is displayed in the Plot pane with "Fit plots to window" checked. If the area where the plot is displayed is bigger than 432x288 (which corresponds to the Spyder window being bigger than about 1000x800 pixels), then the image is magnified and since it is PNG that will give a low quality plot.

If the user turns on retina (which can currently be done with `%config InlineBackend.figure_format = 'retina'`) then the plots are generated with 2 * 72 = 144 dpi, so the image is 864x576. This probably looks better in the Plots pane (I say "probably" because the zoom factor is non-integer so the plot never looks very good unless "Fit plots to window" is unchecked). However, in the IPython console the image is shrunk by a factor of two, so it will be displayed as 432x288 pixels, just as before. This does not give a better image on my screen, even if I set a custom high DPI scaling of 1.5 in Spyder. Of course, this may depend on OS or physical monitor.

So at least for me, retina does not improve the appearance in the IPython console and while is does improve the appearance in the Plots pane, the same effect can be achieved more transparently by increasing the DPI value in the Spyder preferences. 
> So at least for me, retina does not improve the appearance in the IPython console and while is does improve the appearance in the Plots pane, the same effect can be achieved more transparently by increasing the DPI value in the Spyder preferences.

Ok, thanks for the analysis @jitseniesen. Could post a screenshot of how plots look with an increased DPI (the one you consider it'd really improve things) in the console and Plots pane?
Here is a plot in the Plots pane in the default resolution of 72 dpi. I reconfigured the layout on Spyder somewhat to make the Plots pane bigger so that the difference is more apparent on my monitor (1920 x 1080 pixels). This corresponds on what would happen in the default layout if your monitor is 2560 x 1440 or thereabouts. The picture is shown with a zoom factor of 213% which makes it not sharp.

![plot-default](https://github.com/spyder-ide/spyder/assets/7941918/4d04eb18-a858-49d2-9bc5-3c3226a35db0)

This is what is looks like in the IPython console. The console does not zoom so the picture is always sharp, but small.

![console-default](https://github.com/spyder-ide/spyder/assets/7941918/8273bd15-1e06-4efc-8526-7cf60119465e)

If we double the resolution of the plots to 144 dpi, it is displayed with a zoom factor of 107% and looks quite sharp.

![plot-double](https://github.com/spyder-ide/spyder/assets/7941918/bb077c4e-f3ef-4478-aa55-3a6c544edc1c)

The IPython console does not zoom so the image is twice as big:

![console-double](https://github.com/spyder-ide/spyder/assets/7941918/f83556a0-4797-43e2-8d08-2359648f8338)

Increasing the DPI is not free, it makes the pictures bigger and thus that costs more memory. The PNG of the plot with 144 dpi is 29 kB, so if you have 100 plots that makes 2.9 MB, which by itself seems quite okay. There is probably more overhead, which might be substantial and thus cause problems, but I don't know.

If we don't care about memory and we want most users to get sharp images, I propose to increase the default DPI in Spyder so that plots on a fairly typical but high-end monitor are displayed with a zoom factor of around 100%. If we use 4K monitors as our reference, their resolution is 3840 x 2160. I estimate that the area in the Plots display for showing the actual plot is about 1700 x 900. With a default plot height of 4 inches, that suggests a resolution of 225 dpi. For comparison, the current default in Spyder is 72 dpi and in Matplotlib it is 100 dpi.

An alternative is to do what the IPython console does and not zoom in, this corresponds to having the option "Fit plots to window" unchecked by default (it is now checked by default). This should help with users complaining that the plot is not sharp, but instead we will get users complaining that the plot is too small.

Final note, I don't have a true hi-dpi monitor so I can't test that.


Resolving patch:
diff --git a/spyder/config/main.py b/spyder/config/main.py
--- a/spyder/config/main.py
+++ b/spyder/config/main.py
@@ -149,7 +149,7 @@
               'pylab/autoload': False,
               'pylab/backend': 'inline',
               'pylab/inline/figure_format': 'png',
-              'pylab/inline/resolution': 72,
+              'pylab/inline/resolution': 144,
               'pylab/inline/width': 6,
               'pylab/inline/height': 4,
               'pylab/inline/fontsize': 10.0,
@@ -670,4 +670,4 @@
 #    or if you want to *rename* options, then you need to do a MAJOR update in
 #    version, e.g. from 3.0.0 to 4.0.0
 # 3. You don't need to touch this value if you're just adding a new option
-CONF_VERSION = '82.1.0'
+CONF_VERSION = '82.2.0'


Test patch:
diff --git a/spyder/plugins/variableexplorer/widgets/tests/test_namespacebrowser.py b/spyder/plugins/variableexplorer/widgets/tests/test_namespacebrowser.py
--- a/spyder/plugins/variableexplorer/widgets/tests/test_namespacebrowser.py
+++ b/spyder/plugins/variableexplorer/widgets/tests/test_namespacebrowser.py
@@ -226,7 +226,7 @@ def test_namespacebrowser_plot_with_mute_inline_plotting_true(
 
     mock_axis.plot.assert_called_once_with(my_list)
     mock_print_figure.assert_called_once_with(
-        mock_figure, fmt='png', bbox_inches='tight', dpi=72)
+        mock_figure, fmt='png', bbox_inches='tight', dpi=144)
     expected_args = [mock_png, 'image/png', namespacebrowser.shellwidget]
     assert blocker.args == expected_args