Skip to content

Fix a bug with the Qt5 backend with mixed resolution displays - #8931

Merged
dstansby merged 5 commits into
matplotlib:masterfrom
astrofrog:qt5-fix-resolution-change
Jul 31, 2017
Merged

dstansby merged 5 commits into
matplotlib:masterfrom
astrofrog:qt5-fix-resolution-change

Conversation

@astrofrog

Copy link
Copy Markdown
Contributor

When mixed-resolution displays are present, the _dpi_ratio attribute on the canvas may change between paintEvents, which means that we need to change the size of the canvas. However, the underlying canvas is only resized if a resizeEvent is emitted, so we check whether _dpi_ratio has changed between paintEvents, and if so we emit a fake resizeEvent for the widget.

Fixes #8061

Note that while this fixes the issue described in #8061 which was that one could end up with weird visual issues such as:

screenshot

the plot for the lower DPI display still has too large fonts/linewidths, but I think that is a separate bug, which I'll try and fix in another PR.

cc @tacaswell @efiring

@astrofrog
astrofrog force-pushed the qt5-fix-resolution-change branch from e143f62 to 05bdbc4 Compare July 24, 2017 15:30
When mixed-resolution displays are present, the _dpi_ratio attribute on the canvas may change between paintEvents, which means that we need to change the size of the canvas. However, the underlying canvas is only resized if a resizeEvent is emitted, so we check whether _dpi_ratio has changed between paintEvents, and if so we emit a fake resizeEvent for the widget.
@astrofrog
astrofrog force-pushed the qt5-fix-resolution-change branch from 05bdbc4 to f8cb5c9 Compare July 24, 2017 15:30
# We use self.resizeEvent here instead of QApplication.postEvent
# since the latter doesn't guarantee that the event will be emitted
# straight away, and this causes visual delays in the changes.
self.resizeEvent(event)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this also trigger a paint event?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might do - I'm investigating

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently not - at least the paintEvent method does not get called again as a result.

@astrofrog

astrofrog commented Jul 24, 2017 •

Copy link
Copy Markdown
Contributor Author

@tacaswell @efiring - I think I've got things behaving as they should: with mixed resolution displays, the plots look the same on all displays!

There were fundamentally two issues here:

  • The figure.dpi should change when changing monitors
  • The canvas size and the figure dpi should be changed before the first paint event completes

My approach is to keep track in paintEvent of what the last dpi_ratio was when the canvas was drawn. If it has changed, the figure DPI is updated, then the canvas size.

However, note that changing the DPI normally causes the canvas to be resized, which we deal with ourselves already, so I've added a new forward argument to _set_dpi which is passed to set_size_inches.

One small worry I have is that testing this is difficult/impossible, so I'm not sure how we can prevent regressions? - update: regression test added

This is ready for review.

@astrofrog
astrofrog force-pushed the qt5-fix-resolution-change branch from 776c940 to f3c54a0 Compare July 24, 2017 18:42
Similarly to resizeEvent, we tell _set_dpi to not change the canvas size since we are doing it ourselves.
@astrofrog
astrofrog force-pushed the qt5-fix-resolution-change branch from b510f19 to 7f4a73a Compare July 24, 2017 19:28
@astrofrog

Copy link
Copy Markdown
Contributor Author

I think the AppVeyor failure is unrelated? (other PRs seem to suffer from it)

@tacaswell tacaswell added this to the 2.1 (next point release) milestone Jul 25, 2017
self.figure._original_dpi = self.figure.dpi
self.figure.dpi = self._dpi_ratio * self.figure._original_dpi

def _update_figure_dpi(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use this in the __init__ above as well (and get rid of the hasattr)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! :)

@astrofrog

Copy link
Copy Markdown
Contributor Author

@tacaswell - change implemented, and all tests are passing

@dstansby dstansby left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 can confirm the original bug, and that this fixes it (using a retina Macbook and non-retina external screen)

@dstansby
dstansby merged commit 51cfbd8 into matplotlib:master Jul 31, 2017
@anntzer anntzer mentioned this pull request Aug 2, 2017
5 of 6 tasks

@QuLogic QuLogic left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is not passing for me.

Plus a few minor tweaks.

# mixed resolution displays if dpi_ratio is changing between painting
# events.
if (self._dpi_ratio_prev is None or
self._dpi_ratio != self._dpi_ratio_prev):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under-indented.

assert size.width() == 200
assert size.height() == 80
assert_equal(qt_canvas.get_width_height(), (600, 240))
assert_equal(fig.get_size_inches(), (5, 2))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A regular assert would be fine here; you didn't need to import assert_equal.

qApp.processEvents()

# The DPI and the renderer width/height change
assert fig.dpi == 240

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't passing for me here; I think you need to do a little more to trigger an update.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, this seems to pass on the CI though - or does the test not get run on the CI? What platform and exact Qt version are you using?

@QuLogic QuLogic Aug 2, 2017 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any obvious skips in the CI, but you can confirm with @anntzer that the tests should be running. The odd thing is I don't see any Qt4 skips and one would normally preclude the other; maybe they just luckily ran on different processes?

I am running on Fedora+conda using the latest defaults Qt package (conda-forge's Qt seems to be broken) 5.6.2-5 with Python 3.6.

@anntzer anntzer Aug 2, 2017 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost certainly related to #8659 (causing some of the tests not being run).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scaling issues with PyQt5 when using mixed resolution displays

5 participants