Skip to content

WxAgg backend draws the wrong size when wxpython app is high DPI aware on Windows #18940

Closed

Description

@jbhopkins

Bug report

In wxpython 4.1.0 initial support was added for high DPI awareness on Windows. Using matplotlib with the wxAgg backend and wxpython 4.1.0 on Windows results in everything being drawn at the wrong size. This is shown below, where the same plot is made without (left) and with (right) high DPI awareness turned on. For reference, I'm doing this on a 5120x2880 display with scale factor set to 200%.

Left, without high DPI awareness, right with high DPI awareness:
low_and_high_dpi

Code for reproduction

import ctypes

import wx
import matplotlib as mpl
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg

class PlotFrame(wx.Frame):
    def __init__(self, parent, title, *args, **kwargs):
        wx.Frame.__init__(self, parent, title=title, *args, **kwargs)

        self.fig = mpl.figure.Figure((5,4))
        self.subplot1 = self.fig.add_subplot(111)
        self.canvas = FigureCanvasWxAgg(self, wx.ID_ANY, self.fig)

        self.toolbar = NavigationToolbar2WxAgg(self.canvas)
        self.toolbar.Realize()

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        sizer.Add(self.toolbar, 0, wx.GROW)

        self.subplot1.plot([0, 1, 2, 3, 4], [0, 2, 4, 9, 16], '-o')

        self.SetSizer(sizer)
        self.Layout()
        self.Fit()
        self.CenterOnScreen()
        self.Show(True)

if __name__ == '__main__':
    # Windows high DPI support
    try:
        ctypes.windll.shcore.SetProcessDpiAwareness(True)
    except:
        pass

    app = wx.App(False)
    frame = PlotFrame(None, 'My Plot Frame')
    app.MainLoop()

Alternatively, using pyplot the issue is also reproduced:

import ctypes

import matplotlib as mpl
import matplotlib.pyplot as plt

# Windows high DPI support
try:
    ctypes.windll.shcore.SetProcessDpiAwareness(True)
except:
    pass

mpl.use('wxAgg')

plt.plot(range(10), range(10)[::-1])
plt.show()

Actual and expected outcomes
Actual outcome is a window that is half size, with all fonts, line thicknesses, icons, etc at half size (presumably because it's not accounting for the 200% scale factor). The expected outcome is something identical to the plot with high DPI awareness turned off, except sharp (i.e. natively rendering fonts, lines, etc at 2x size, instead of rendering at 1x and then Windows doing the scaling).

This is shown in the image above, reproduced here:
low_and_high_dpi

Matplotlib version

  • Operating system: Windows 10
  • Matplotlib version: Latest from git, displays as 3.3.2+1628.gfa547835c
  • Matplotlib backend (print(matplotlib.get_backend())): WxAgg
  • Python version: 3.8.2
  • Other libraries: Wxpython 4.1.0
  • Additional info: Tested on a 5120x2880 display with scale factor set to 200%.

Python installed through conda, matplotlib installed from source, wxpython 4.1.0 installed via pip (4.1.0 not available on conda yet).

Activity

  1. jbhopkins commented on Nov 12, 2020

    @jbhopkins
    ContributorAuthor

    I believe that the solution is likely to lie in using the new FromDIP method in 4.1.0 to scale all the pixel sizes sent to the renderer appropriately (this ought to also work for backends where high DPI scaling isn't explicitly needed, such as MacOS). In my own wxpython GUI I've added the function below to provide back compatibility with previous versions of wx.

    def _FromDIP(self, size):
        # This is a hack to provide easy back compatibility with wxpython < 4.1
        try:
            return self.FromDIP(size)
        except Exception:
            return size

    I looked through the backend code a bit, but I found the calls to the renderer confusing, and wasn't sure where this could be added. If someone wants to guide me, I'm happy to make and test the changes.

    Also, the high res version of the toolbar icons should be loaded if needed.

  2. tohc1 commented on Feb 28, 2021

    @tohc1
    Contributor

    @jbhopkins I came across this while looking at a different issue in the wx backends and noticed in the code matplotlib/backend/backend_wx.py that the dpi is hard-coded.

    # the True dots per inch on the screen; should be display dependent; see
    # http://groups.google.com/groups?q=screen+dpi+x11&hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=7077.26e81ad5%40swift.cs.tcd.ie&rnum=5
    # for some info about screen dpi
    PIXELS_PER_INCH = 75
    

    I think just changing to the dpi as read from wx.GetDisplayPPI() will cause the scaling to change. A quick test on Linux suggests that this function detects a dpi scaling change when changing the user interface scaling from x1 dpi=96 to x2 dpi=192.

  3. jbhopkins commented on Mar 1, 2021

    @jbhopkins
    ContributorAuthor

    @tohc1 Thanks! I'll test that out when I have the chance. If that does it, then the only other thing that has to be done is load in the high DPI icons when appropriate.

  4. github-actions commented on Sep 13, 2023

    @github-actions

    This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

  5. QuLogic commented on Sep 13, 2023

    @QuLogic
    Member

    #26710 should fix this.

  6. added this to the v3.9.0 milestone on Mar 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions