Skip to content

Commit aebec04

Browse files
committed
Add initial support for HiDPI in TkAgg on Windows.
At the moment, Tk does not support updating the 'scaling' value when the monitor pixel ratio changes, or the window is moved to a different one.
1 parent f9ec6f8 commit aebec04

File tree

3 files changed

+31
-3
lines changed

3 files changed

+31
-3
lines changed

lib/matplotlib/backends/_backend_tk.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class FigureCanvasTk(FigureCanvasBase):
166166
def __init__(self, figure=None, master=None, resize_callback=None):
167167
super().__init__(figure)
168168
self._idle_draw_id = None
169-
w, h = self.figure.bbox.size.astype(int)
169+
w, h = self.get_width_height(physical=True)
170170
self._tkcanvas = tk.Canvas(
171171
master=master, background="white",
172172
width=w, height=h, borderwidth=0, highlightthickness=0)
@@ -175,6 +175,7 @@ def __init__(self, figure=None, master=None, resize_callback=None):
175175
self._tkcanvas.create_image(w//2, h//2, image=self._tkphoto)
176176
self._resize_callback = resize_callback
177177
self._tkcanvas.bind("<Configure>", self.resize)
178+
self._tkcanvas.bind("<Map>", self._update_device_pixel_ratio)
178179
self._tkcanvas.bind("<Key>", self.key_press)
179180
self._tkcanvas.bind("<Motion>", self.motion_notify_event)
180181
self._tkcanvas.bind("<Enter>", self.enter_notify_event)
@@ -209,6 +210,18 @@ def filter_destroy(event):
209210
self._master = master
210211
self._tkcanvas.focus_set()
211212

213+
def _update_device_pixel_ratio(self, event=None):
214+
# Tk gives scaling with respect to 72 DPI, but most (all?) screens are
215+
# scaled vs 96 dpi, and pixel ratio settings are given in whole
216+
# percentages, so round to 2 digits.
217+
ratio = round(self._master.call('tk', 'scaling') / (96 / 72), 2)
218+
if self._set_device_pixel_ratio(ratio):
219+
# The easiest way to resize the canvas is to resize the canvas
220+
# widget itself, since we implement all the logic for resizing the
221+
# canvas backing store on that event.
222+
w, h = self.get_width_height(physical=True)
223+
self._tkcanvas.configure(width=w, height=h)
224+
212225
def resize(self, event):
213226
width, height = event.width, event.height
214227
if self._resize_callback is not None:
@@ -833,6 +846,7 @@ def new_figure_manager_given_figure(cls, num, figure):
833846
with _restore_foreground_window_at_end():
834847
if cbook._get_running_interactive_framework() is None:
835848
cbook._setup_new_guiapp()
849+
_c_internal_utils.Win32_SetDpiAwareness()
836850
window = tk.Tk(className="matplotlib")
837851
window.withdraw()
838852

setupext.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,8 @@ def get_extensions(self):
444444
],
445445
include_dirs=["src"],
446446
# psapi library needed for finding Tcl/Tk at run time.
447-
libraries=({"linux": ["dl"], "win32": ["psapi"],
448-
"cygwin": ["psapi"]}.get(sys.platform, [])),
447+
libraries=({"linux": ["dl"], "win32": ["psapi", "user32"],
448+
"cygwin": ["psapi", "user32"]}.get(sys.platform, [])),
449449
extra_link_args={"win32": ["-mwindows"]}.get(sys.platform, []))
450450
add_numpy_flags(ext)
451451
add_libagg_flags(ext)

src/_c_internal_utils.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,15 @@ mpl_SetForegroundWindow(PyObject* module, PyObject *arg)
124124
#endif
125125
}
126126

127+
static PyObject*
128+
mpl_SetDpiAwareness(PyObject* module)
129+
{
130+
#ifdef _WIN32
131+
SetProcessDPIAware();
132+
#endif
133+
Py_RETURN_NONE;
134+
}
135+
127136
static PyMethodDef functions[] = {
128137
{"display_is_valid", (PyCFunction)mpl_display_is_valid, METH_NOARGS,
129138
"display_is_valid()\n--\n\n"
@@ -151,6 +160,11 @@ static PyMethodDef functions[] = {
151160
"Win32_SetForegroundWindow(hwnd, /)\n--\n\n"
152161
"Wrapper for Windows' SetForegroundWindow. On non-Windows platforms, \n"
153162
"a no-op."},
163+
{"Win32_SetDpiAwareness",
164+
(PyCFunction)mpl_SetDpiAwareness, METH_NOARGS,
165+
"Win32_SetDpiAwareness()\n--\n\n"
166+
"Set Windows' process DPI awareness to be enabled. On non-Windows\n"
167+
"platforms, does nothing."},
154168
{NULL, NULL}}; // sentinel.
155169
static PyModuleDef util_module = {
156170
PyModuleDef_HEAD_INIT, "_c_internal_utils", "", 0, functions, NULL, NULL, NULL, NULL};

0 commit comments

Comments
 (0)