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

[Bug]: compressed layout setting can be forgotten on second save · Issue #24954 · matplotlib/matplotlib · GitHub

Repository navigation

[Bug]: compressed layout setting can be forgotten on second save #24954

Closed

Description

Bug summary

I'm not sure whether this is really a bug or I'm just using an inconsistent combination of options. Under some specific circumstances (see below) compressed layout is not applied the second time a figure is saved.

Code for reproduction

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

arr = np.arange(100).reshape((10, 10))

matplotlib.rcParams['figure.constrained_layout.use'] = True

fig, ax_dict = plt.subplot_mosaic('AB;AC', figsize=(6, 9), width_ratios=[3, 2],
                                  layout='compressed')

for key in ["B", "C"]:
    ax_dict[key].imshow(arr)
    
fig.savefig("test1.png", bbox_inches="tight")
fig.savefig("test2.png", bbox_inches="tight")

Actual outcome

test1.png

test2.png

Expected outcome

Both images should look like the first.

Additional information

If I do not set the rcParams, all is well. If I do not set bbox_inches="tight" in my calls to savefig, the images are identical (but I have too much white space top and bottom). Maybe there is a better option than bbox_inches="tight" when using compressed layout?

For context, my real example is a script that makes several figures. For most of them I want constrained layout, so I set that once in the rcParams for convenience. Only one figure needs "compressed", and I am saving twice because I want both a png and a pdf. Fixed it in my current example by just reverting the rcParams setting for the one figure.

Operating system

RHEL7

Matplotlib Version

3.6.2 and main

Matplotlib Backend

QtAgg

Python version

3.9 and 3.11

Jupyter version

No response

Installation

conda

Activity

  1. jklymak commented on Jan 12, 2023

    Member

    Yeah we do some dancing around when we save with bbox inches - so this seems to get caught in that. I tried to track it down, but the figure-saving stack is full of context managers, and I can't see where the layout manager gets reset. Hopefully someone more cognizant of that part of the codebase can explain.

  2. rcomer commented on Jan 12, 2023

    MemberAuthor

    Thanks for looking @jklymak 🙂

  3. QuLogic commented on Jan 13, 2023

    Member

    I think it is set (temporarily) here;

    # we have already done layout above, so turn it off:
    stack.enter_context(self.figure._cm_set(layout_engine='none'))

  4. jklymak commented on Jan 13, 2023

    Member

    It is, but I don't understand what _cm_set does to reset the layout engine after this. Somehow it is dropping the old layout engine and making a new one, and the new one doesn't know that the old one was a 'compressed' engine.

  5. QuLogic commented on Jan 13, 2023

    Member

    It calls get_{kwarg} and after running calls set({kwarg}={old value}). So here it calls oldvalue = figure.get_layout_engine() and figure.set(layout_engine=oldvalue). Is figure.set_layout_engine(figure.get_layout_engine()) working correctly?

  6. rcomer commented on Jan 13, 2023

    MemberAuthor

    I am way out of my depth here but

    import matplotlib.pyplot as plt
    
    plt.rcParams['figure.constrained_layout.use'] = True
    fig = plt.figure(layout="compressed")
    
    print(fig.get_layout_engine()._compress)
    fig.set_layout_engine(fig.get_layout_engine())
    print(fig.get_layout_engine()._compress)
    
    fig.savefig('foo.png', bbox_inches='tight')
    print(fig.get_layout_engine()._compress)
    True
    True
    False
    

    Without the rcParams line, fig.get_layout_engine() returns None after the savefig.

  7. rcomer commented on Jan 13, 2023

    MemberAuthor

    I think the problem is the call to adjust_bbox

    restore_bbox = _tight_bbox.adjust_bbox(
    self.figure, bbox_inches, self.figure.canvas.fixed_dpi)

    which explicity calls

    fig.set_layout_engine(None)

    which will use the default constrained layout engine if the rcParams is set

    if layout is None:
    if mpl.rcParams['figure.autolayout']:
    layout = 'tight'
    elif mpl.rcParams['figure.constrained_layout.use']:
    layout = 'constrained'
    else:
    self._layout_engine = None
    return
    if layout == 'tight':
    new_layout_engine = TightLayoutEngine(**kwargs)
    elif layout == 'constrained':
    new_layout_engine = ConstrainedLayoutEngine(**kwargs)

  8. added this to the v3.7.0 milestone on Jan 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic: geometry managerLayoutEngine, Constrained layout, Tight layout

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL