FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

[Bug]: unstable `tkagg` small plot size window · Issue #28330 · matplotlib/matplotlib · GitHub

Repository navigation

[Bug]: unstable tkagg small plot size window #28330

Closed

Description

Bug summary

Hi, if I reduce the plot size and use tkagg backend, the size of the plot window becomes unstable, and it changes by moving mouse (when the coordinates are shown); please see the below video:

2024-06-03-11-31-30.mp4

Code for reproduction

%matplotlib tk
import matplotlib.pyplot as plt
plt.figure( figsize=(2,2))
plt.plot(range(50))
plt.show()

Actual outcome

The plot size changes because of the (x,y) coordinates shown on the right bottom side.

Expected outcome

The plot size shouldn't change similar to qtagg backend.

Additional information

No response

Operating system

Windows 10 and 11

Matplotlib Version

3.9.0

Matplotlib Backend

tkagg

Python version

3.10.11

Jupyter version

iPython 8.24.0

Installation

pip

Activity

  1. timhoffm commented on Jun 3, 2024

    Member

    This is likely due to the cursor coordinates being displayed when one hovers over the Axes.

    We need to configure the toolbar not to expand for the coordinates.

  2. added this to the v3.10.0 milestone on Jun 3, 2024
  3. QuLogic commented on Jun 12, 2024

    Member

    I tried to look for this, but I'm not sure I could find how to cause a widget to 'overflow' from the window; maybe @richardsheridan knows?

  4. richardsheridan commented on Jun 13, 2024

    Contributor

    Unfortunately not that I've run into. I've "solved" this before by ensuring a widget has a sufficiently large minimum size.

  5. tacaswell commented on Jun 13, 2024

    Member

    Setting a minimum widget size also has the effect of putting a floor on the figure size. This seems practically harmless (if your figure is smaller than the toolbar you probably can not see it anyway!), but I ran into an issue where I was doing

    fig = plt.figure(figsize=small_exact_size_for_paper_figure)
    ...
    plt.show()
    fig.savefig('/tmp/now_wrong_size.pdf')

    where as the names suggest, I was setting a small figure size (so it was saved as the size I wanted in the output so that the fonts were ~correct and the file was the wrong size due to show putting it on the screen and it getting expanded to fit. I think you can also have the same problem the otherway where you try to make a poster-sized figure and then it get shrunk to fit the screen (@efiring notes we should add scroll bars for this case).

  6. sandyowlet commented on Feb 27, 2025

    Contributor

    Setting a minimum widget size also has the effect of putting a floor on the figure size. This seems practically harmless (if your figure is smaller than the toolbar you probably can not see it anyway!), but I ran into an issue where I was doing

    fig = plt.figure(figsize=small_exact_size_for_paper_figure)
    ...
    plt.show()
    fig.savefig('/tmp/now_wrong_size.pdf')
    where as the names suggest, I was setting a small figure size (so it was saved as the size I wanted in the output so that the fonts were ~correct and the file was the wrong size due to show putting it on the screen and it getting expanded to fit. I think you can also have the same problem the otherway where you try to make a poster-sized figure and then it get shrunk to fit the screen (@efiring notes we should add scroll bars for this case).

    Hi, I've had the same issuse lately.

    I believe tkinter offers a way to prevent child widgets from expanding their parent container.

    self.pack_propagate(False) # for pack layout(Currently in use).

    This is the effect of the windows after the modification.

  7. timhoffm commented on Apr 16, 2025

    Member

    @sandyowlet do you want to create a pull request with your proposed change?

  8. QuLogic commented on Apr 26, 2025

    Member

    I was going to open a PR, but it seems I can no longer reproduce the problem (with Tk 8.6 on Python 3.13.) So preferably someone who can would do so (unless it's already been fixed?)

  9. timhoffm commented on Apr 28, 2025

    Member

    As a datapoint: The problem exists here with Tk 8.6 Python 3.12.8).

    Where exactly should the fix be placed? I've tried here:

    diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py
    index 0bbff1379f..3b38006ee1 100644
    --- a/lib/matplotlib/backends/_backend_tk.py
    +++ b/lib/matplotlib/backends/_backend_tk.py
    @@ -673,6 +673,7 @@ class NavigationToolbar2Tk(NavigationToolbar2, tk.Frame):
             NavigationToolbar2.__init__(self, canvas)
             if pack_toolbar:
                 self.pack(side=tk.BOTTOM, fill=tk.X)
    +        self.pack_propagate(False)
     
         def _rescale(self):

    But this results in the initial figure being so small that the save button is cut off.

  10. QuLogic commented on Apr 28, 2025

    Member

    That seems to be the right spot; if the save button is cut off, then there's not much we can do, as that's the size that was requested.

    Qt does have a dropdown with additional buttons that automatically appears. We could emulate that, but it's more work since it's not built in to the toolkit.

  11. sandyowlet commented on Apr 30, 2025

    Contributor

    As a datapoint: The problem exists here with Tk 8.6 Python 3.12.8).

    Where exactly should the fix be placed? I've tried here:

    diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py
    index 0bbff1379f..3b38006ee1 100644
    --- a/lib/matplotlib/backends/_backend_tk.py
    +++ b/lib/matplotlib/backends/_backend_tk.py
    @@ -673,6 +673,7 @@ class NavigationToolbar2Tk(NavigationToolbar2, tk.Frame):
    NavigationToolbar2.init(self, canvas)
    if pack_toolbar:
    self.pack(side=tk.BOTTOM, fill=tk.X)

    •    self.pack_propagate(False)
      

      def _rescale(self):
      But this results in the initial figure being so small that the save button is cut off.

    Hi, I just tried it fix locally.

    (original left, fixed right)

    I found that the self.pack_propagate(False) successfully fixed the wrong intial figure size and toolbar size.

    • plt.figure( figsize=(2,2)) -> The right figure width should be 200 px(originally 238).
    • tk.Frame.__init__(self, ... width=int(canvas.figure.bbox.width), height=50) -> The right ToolBar height should be 50px(originally 44)

    Maybe I can try to develop an automatic folding toolbar like Qt.

  12. timhoffm commented on Apr 30, 2025

    Member

    I'm not sure this is the best/right solution. Deactivating pack_propagate also makes the toolbar margins larger.

    When I manually resize the window once, the size stays and the text (if present) is cut off

    Actually, I think this is the mode we would like to be in from the beginning.

  13. timhoffm commented on Apr 30, 2025

    Member

    This seems better in that the toolbar margins stay small (but it still enforces the wider initial window size)

    diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py
    index 0bbff1379f..77d600b5b6 100644
    --- a/lib/matplotlib/backends/_backend_tk.py
    +++ b/lib/matplotlib/backends/_backend_tk.py
    @@ -673,6 +673,9 @@ class NavigationToolbar2Tk(NavigationToolbar2, tk.Frame):
             NavigationToolbar2.__init__(self, canvas)
             if pack_toolbar:
                 self.pack(side=tk.BOTTOM, fill=tk.X)
    +        window.update_idletasks()  # ensure window picks up the initial size
    +        # changes of _message_label should not affect window size
    +        self.pack_propagate(False)
     
         def _rescale(self):
             """
  14. sandyowlet commented on May 1, 2025

    Contributor

    @timhoffm
    It seems I add the self.pack_propagate(False) after tk.Frame.__init__()

                 window = canvas.get_tk_widget().master
             tk.Frame.__init__(self, master=window, borderwidth=2,
                               width=int(canvas.figure.bbox.width), height=50)
    +        window.update_idletasks()
    +        self.pack_propagate(False)
    
             self._buttons = {}
             for text, tooltip_text, image_file, callback in self.toolitems:
    diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py
    index 0bbff1379f..77d600b5b6 100644
    --- a/lib/matplotlib/backends/_backend_tk.py
    +++ b/lib/matplotlib/backends/_backend_tk.py
    @@ -673,6 +673,9 @@ class NavigationToolbar2Tk(NavigationToolbar2, tk.Frame):
             NavigationToolbar2.__init__(self, canvas)
             if pack_toolbar:
                 self.pack(side=tk.BOTTOM, fill=tk.X)
    +        window.update_idletasks()  # ensure window picks up the initial size
    +        # changes of _message_label should not affect window size
    +        self.pack_propagate(False)
     
         def _rescale(self):
             """

    The former locks the Widget size before adding any buttons to the tkFrame, so window.update_idletasks() will not have any impact.

    The key point is that using window.update_idletasks() can cause size issues on Windows systems.
    Using the script in this issue as an example.

    %matplotlib tk
    import matplotlib.pyplot as plt
    plt.figure( figsize=(2,2))
    plt.plot(range(50))
    plt.show()

    Output:


    My local develop environment: Windows10, 2560x1440, Python3.12, matplotlib 3.11.0.dev745+g192e935ee0


    Codespcaces, Python3.12

    It also displays different size on my another Windows 11 development device.

    From what I konw, Tkinter produces inconsistent output on Windows and Linux systems, and this kind of issue is hard to catch.

    However, as it stands, neither of them is producing the target 2x2 (200x200) image size.

    If I only use self.pack_propagate(False), I get the exact 2x2 figure size on my local development environment.

    I think the priority is to output the right fig size asked by the user.

    Regarding the overly wide Toolbar margins we observed, I believe this is due to setting a fixed height of 50.

            tk.Frame.__init__(self, master=window, borderwidth=2,
                              width=int(canvas.figure.bbox.width), height=50)

    By default(without pack_propagate(true)), tkFrame will try to use the minimum height(about 45) instead of following the input parameters(50).
    Therefore, we can try modifying the default height of the ToolBar.

  15. richardsheridan commented on May 1, 2025

    Contributor

    I don't know if events need to be flushed or not to get the right behavior, but update_idletasks in libraries can break user code (see #17789), and should be avoided in favor of something like after_idle(pack_propagate, False).

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

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL