-
I embedded a matplotlib figure into my customtkinter application using FigureCanvasTkAgg and added a navigation toolbar ( navigationtoolbar2tk ). I can use: ctypes.windll.shcore.setprocessdpiawareness(1) to reverse the effects of HIGH DPI scaling, but it is not a stable fix. If I do ctypes.windll.shcore.setprocessdpiawareness(2) then it messes with the widgets...which is weird as that is what is turned on by default. Is there a known fix to this, or I should just live with it and change my program to accommodate this? |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 10 replies
-
Sample Reproducible Code with output might be helpful to resolve this problem. Regards. |
Beta Was this translation helpful? Give feedback.
-
If you want to resize icons and size of the toolbar and Canvas independent to DPI scaling, one way is to update button images and reset default scaling of the Here, in the following example, I modified actual icons. As size is increased, it may look pixelated, but if you want quality icons, create or find your own icons and use them by passing them in from PIL import ImageTk, Image
from tkinter import Checkbutton
from matplotlib.figure import Figure
from customtkinter import CTk, CTkFrame, CTkLabel
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk
class MatPlotCanvas(CTkFrame):
def __init__(self,
master: CTk,
figure: Figure,
width: int,
height: int,
toolbar_height: int = 100,
toolbar_icons_list: list[Image.Image] = None,
**kwargs
):
"""
The `matplotlib`.
~~~~~~~~~~~~~~~~
A frame widget containing `FigureCanvasTkAgg` of `matplotlib.backends` along with its toolbar allowing
manipulating their sizes.
Params:
- master: Any parent widget for the graph canvas window of the `matplotlib`.
- figure: A class object from `matplotlib.figure`
- width: Width of the graph canvas window of the `matplotlib`
- height: Height of the graph canvas window of the `matplotlib`
- toolbar_height: Height of the toolbar frame
- toolbar_icons_list: A list containg PIL Image objects of icons to be used.
- **kwargs: Other `CTkFrame` paramenters
"""
self.master = master
self.figure = figure
self.width = width
self.height = height
self.toolbar_height = toolbar_height
self.toolbar_icons_list = toolbar_icons_list
# Checking if Icons are provided
if self.toolbar_icons_list:
if len(list(filter(lambda x: isinstance(x, Image.Image), toolbar_icons_list))) != 7:
raise ValueError("There should be seven PIL images in the list.")
# Vital figure settings
self.figure.set_dpi(100)
self.figure.set_figwidth(self.width/100)
self.figure.set_figheight(self.height/100)
super().__init__(master, width=width, height=height, **kwargs)
# Canvas/Graph frame
self._gframe = CTkFrame(self, width=width, height=height)
self._gframe.pack(fill="x")
self.canvas_cls = FigureCanvasTkAgg(self.figure, master=self._gframe)
self.canvas_widget = self.canvas_cls.get_tk_widget()
self.canvas_widget.place(x=0, y=0, relwidth=1, relheight=1)
# Toolbar frame
self._tframe = CTkFrame(self, height=self.toolbar_height)
self._tframe.pack(fill="x")
self.toolbar_widget = NavigationToolbar2Tk(self.canvas_cls, window=self._tframe)
self.toolbar_widget.place(x=0, y=0, relwidth=1, relheight=1)
self._resize_toolbar_icons(self.toolbar_height)
def _resize_toolbar_icons(self, height: int):
"""Internal method to resize icons"""
size = height - 4
for i, button in enumerate(self.toolbar_widget._buttons.values()):
pil_img = ImageTk.getimage(getattr(button, "_ntimage")) if not self.toolbar_icons_list else self.toolbar_icons_list[i]
_ntimage = ImageTk.PhotoImage(pil_img.resize((size, size)))
if not isinstance(button, Checkbutton):
button.config(image = _ntimage, width=height, height=height)
button._ntimage = _ntimage
else:
button.config(image = _ntimage, width=height, height=height)
button.config(selectimage = _ntimage, width=height, height=height)
button._ntimage = _ntimage
if __name__ == "__main__":
app = CTk()
app.geometry("1000x600")
fig = Figure(constrained_layout=True)
ax = fig.add_subplot(111)
graph = MatPlotCanvas(app, figure=fig, width=400, height=300, toolbar_height=54)
graph.place(relx=0.25, anchor="center", rely=0.5)
frame = CTkFrame(app, width=400, height=(300 + graph.toolbar_height), fg_color="gray40") # toolbar height should be added
frame.place(relx=0.75, anchor="center", rely=0.5)
CTkLabel(frame, text="Same sized frame\nto check there is no more scaling.",
fg_color="black", corner_radius=20, height=70).place(relx=0.5, anchor="center", rely=0.5)
app.mainloop() Hope, this would be helpful to you. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
The size of toolbar buttons does not change as expected because the size of toolbar buttons depends on the size of toolbar. And, the size of toolbar is relative and fixed to the bounding box of the Canvas Figure.
As you can see in the following code block:
Also, the
matplotlib
sets scaling by its own independently of Process Dpi Awareness, so it may create conflicts in the size of other Tkinter widgets and the size of itself.As you can see in the following code block:
Note that the above images are screenshots of original
matplotlib
backend code scripts, you can inspect them by your own.Hope, the above explanation may help you in understanding why
matplotlib
's widgets do not scale wit…