-->

Matplotlib setting `axes` object with `imshow` cau

2019-07-26 01:24发布

问题:

Description

I have began refactoring some code based on the future warning of matplotlib, to re-use the initially defined axes object. However, I noticed that whenever I was re-using my axes object, the image size would be variable. Since, I have managed to isolate the problem to the axes.imshow method as after using imshow, the y-axis of any subsequent drawing on that axes has a y-axis that seems to rescale.

The feeling I have is that the y-axis scale is retained from the initial image that is plotted using imshow (I thought that axes.clear should reset this). Specifically in the below examples, shuffling plots some data spanning ~ 9.90 to 10.10 but because the original image spanned form 0 to 50 the y-axis is barely visible.

Below are first two screenshots of the expected and then 'bugged' behaviour, followed by an MVCE that has two sections that can be toggled to get the expected or 'bugged' behaviour:

Images

  1. Splash without imshow:

  2. Screen after 'Foo -> Shuffle' (Expected behaviour):

  3. Splash with imshow:

  4. Screen after 'Foo -> Shuffle' (unexpected behaviour):

MVCE

from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg
)
import tkinter as tk
from matplotlib import image, figure
from numpy import random, linspace
from os import path, getcwd
from pylab import get_cmap

class Foo(object):
    @classmethod
    def run(cls):
        root = tk.Tk()
        Foo(root)
        root.mainloop()

    def __init__(self, master):
        # Figure & canvas
        self.fig = figure.Figure(figsize=(5,5))
        self.axes = self.fig.add_subplot(111)
        self.canvas = FigureCanvasTkAgg(self.fig, master=master)
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=tk.YES)

        # Splash image (This 'bugs')
        Z = random.random((50,50))
        self.axes.imshow(Z, cmap=get_cmap("Spectral"), interpolation='nearest')
        self.canvas.draw()

        # Dummy start data (This Works)
        #self.axes.plot(random.normal(10,0.05,100))
        #self.canvas.draw()

        # MENU
        menu = tk.Menu(master)
        master.config(menu=menu)
        test_menu = tk.Menu(menu, tearoff=0)
        menu.add_cascade(label="Foo", menu=test_menu)
        test_menu.add_command(label="Shuffle",
                              command=self.shuffle)
        test_menu.add_command(label="Add",
                              command=self.add)

    def add(self):
        x_data = linspace(0,10, 1000)
        y_data = random.normal(x_data)
        self.axes.plot(x_data, y_data)
        self.canvas.draw()

    def shuffle(self):
        self.axes.clear()
        self.axes.plot(random.normal(10,0.05,100))
        self.canvas.draw()


if __name__ == "__main__":
   Foo.run()

Question

What is going on here, specifically what is causing the image to appear so differently and what can be done about it?

回答1:

When no argument is given for aspect, it defaults to None. From the documentation:

If None, default to rc image.aspect value

Therefore if no argument is given to imshow, it will use whatever the rcParam for "image.aspect" is, which you can find by doing:

print (plt.rcParams["image.aspect"])  # default is "equal"

A fix to your problem would be to set it to "auto" in your shuffle function using axes.set_aspect():

def shuffle(self):
    self.axes.clear()
    self.axes.plot(random.normal(10,0.05,100))
    self.axes.set_aspect("auto")
    self.canvas.draw()

If you don't mind changing the aspect ratio of imshow, there is also an aspect= argument:

self.axes.imshow(Z, cmap=get_cmap("Spectral"), interpolation='nearest', aspect="auto")