Skip to content

[Bug | Regression]: dpi of plot linked to screen scaling - plots expand off the screen #21994

Open
@rowlesmr

Description

@rowlesmr

Bug summary

Plotting data, and then replotting over the same plot and toolbar results in the plot expanding in size, but only if the screen scaling (as set in the Windows display settings) is set to greater than 100%.

Code for reproduction is a MWE based on my actual program structure.

Code for reproduction

import PySimpleGUI as sg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import matplotlib.pyplot as plt
import matplotlib as mpl


class PlotData:
    def __init__(self, m_canvas_x, m_canvas_y):
        self.canvas_x = m_canvas_x
        self.canvas_y = m_canvas_y

    def plot(self, x, y, fig):
        dpi = plt.gcf().get_dpi()
        print(f"{dpi=}")

        if fig:
            plt.close(fig)
        fig, ax = plt.subplots(1, 1)
        fig = plt.gcf()
        fig.set_size_inches(self.canvas_x / float(dpi), self.canvas_y / float(dpi))
        print(f"{fig.get_size_inches()=}")
        fig.set_tight_layout(True)
        plt.margins(x=0)

        ax.plot(x, y, label="a legend entry")
        ax.set_xlabel("X ordinate")
        ax.set_ylabel("Y ordinate")
        plt.title("Title", loc="left")

        return fig


# if this next line is there, and you're using matplotlib 3.5.1,
#  the plot grows when you press "plot" multiple times.
#  matplotlib v3.4.3 works fine.
#  What does this line do? If dpi_awarenes is not None, then the following is activated in PySimpleGUI
#         if running_windows():
#             if platform.release() == "7":
#                 ctypes.windll.user32.SetProcessDPIAware()
#             elif platform.release() == "8" or platform.release() == "10":
#                 ctypes.windll.shcore.SetProcessDpiAwareness(1)
sg.set_options(dpi_awareness=True)

canvas_x = 600
canvas_y = 300

plot = PlotData(canvas_x, canvas_y)
figure = None
figure_agg = None

x_data = [1, 2, 3, 4, 5]
y_data = [[2, 6, 4, 7, 9], [7, 3, 7, 3, 5]]


# https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_Embedded_Toolbar.py
# https://github.com/PySimpleGUI/PySimpleGUI/issues/3989#issuecomment-794005240
def draw_figure_w_toolbar(canvas, figure, canvas_toolbar):
    print(f"{canvas.winfo_width()=}, {canvas.winfo_height()=}")
    if canvas.children:
        for child in canvas.winfo_children():
            child.destroy()
    if canvas_toolbar.children:
        for child in canvas_toolbar.winfo_children():
            child.destroy()
    figure_canvas_agg = FigureCanvasTkAgg(figure, master=canvas)
    figure_canvas_agg.draw()
    toolbar = Toolbar(figure_canvas_agg, canvas_toolbar)
    toolbar.update()
    figure_canvas_agg.get_tk_widget().pack(side='right', fill='both', expand=1)
    return figure_canvas_agg


# https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_Embedded_Toolbar.py
class Toolbar(NavigationToolbar2Tk):
    def __init__(self, *args, **kwargs):
        super(Toolbar, self).__init__(*args, **kwargs)


layout = \
    [
        [sg.Button("Plot", key="plot_data"), sg.Button("Exit", key="exit")],
        [sg.Column(layout=[[sg.Canvas(size=(canvas_x, canvas_y), key="single_plot", expand_x=True, expand_y=True)]], pad=(0, 0), expand_x=True, expand_y=True)],
        [sg.Sizer(v_pixels=60), sg.Canvas(key="single_matplotlib_controls")]
    ]


def gui() -> None:
    global figure, figure_agg
    window = sg.Window("Data plotter", layout, finalize=True, resizable=True)
    print(f"{mpl.__version__=}")
    i = 0
    while True:
        event, values = window.read()
        i += 1
        print(f"{i=}")
        if event in (None, "exit"):
            break
        elif event == "plot_data":
            figure = plot.plot(x_data, y_data[i % 2], figure)
            figure_agg = draw_figure_w_toolbar(window["single_plot"].TKCanvas, figure,
                                               window["single_matplotlib_controls"].TKCanvas)


if __name__ == "__main__":
    gui()

Actual outcome

Actual and expected outcomes are both shown here:

https://youtu.be/7c_Rt_BDfnM

Expected outcome

Actual and expected outcomes are both shown here:

https://youtu.be/7c_Rt_BDfnM

Additional information

If the program is made DPI aware (ctypes.windll.shcore.SetProcessDpiAwareness(1), done by sg.set_options(dpi_awareness=True)) then the bad behaviour occurs.
If not, then the behaviour is not seen.

There is no issue seen at all if using 3.4.3

Operating system

Windows 10

Matplotlib Version

3.5.1

Matplotlib Backend

TKAgg

Python version

3.9.4

Jupyter version

No response

Installation

pip

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions