12

Consider (Assume code runs without error):

import matplotlib.figure as matfig
import numpy as np

ind = np.arange(N)
width = 0.50;
fig = matfig.Figure(figsize=(16.8, 8.0))
fig.subplots_adjust(left=0.06, right = 0.87)
ax1 = fig.add_subplot(111)
prev_val = None
fig.add_axes(ylabel = 'Percentage(%)', xlabel='Wafers', title=title, xticks=(ind+width/2.0, source_data_frame['WF_ID']))
fig.add_axes(ylim=(70, 100))

for key, value in bar_data.items():
    ax1.bar(ind, value, width, color='#40699C', bottom=prev_val)
    if prev_val:
        prev_val = [a+b for (a, b) in zip(prev_val, value)]
    else:
        prev_val = value

names = []
for i in range(0, len(col_data.columns)):
    names.append(col_data.columns[i])
ax1.legend(names, bbox_to_anchor=(1.15, 1.02))

I now want to save my figure with fig.savefig(outputPath, dpi=300), but I get AttributeError: 'NoneType' object has no attribute 'print_figure', because fig.canvas is None. The sub plots should be on the figures canvas, so it shouldn't be None. I think i'm missing a key concept about matplot figures canvas.How can I update fig.canvas to reflect the current Figure, so i can use fig.savefig(outputPath, dpi=300)? Thanks!

ah bon
  • 9,293
  • 12
  • 65
  • 148
TheoretiCAL
  • 19,461
  • 8
  • 43
  • 65

3 Answers3

16

One of the things that plt.figure does for you is wrangle the backend for you, and that includes setting up the canvas. The way the architecture of mpl is the Artist level objects know how to set themselves up, make sure everything is in the right place relative to each other etc and then when asked, draw them selves onto the canvas. Thus, even though you have set up subplots and lines, you have not actually used the canvas yet. When you try to save the figure you are asking the canvas to ask all the artists to draw them selves on to it. You have not created a canvas (which is specific to a given backend) so it complains.

Following the example here you need to create a canvas you can embed in your tk application (following on from your last question)

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
canvas = FigureCanvasTkAgg(f, master=root)

canvas is a Tk widget and can be added to a gui.

If you don't want to embed your figure in Tk you can use the pure OO methods shown here (code lifted directly from link):

from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure

fig = Figure()
canvas = FigureCanvas(fig)
ax = fig.add_subplot(111)
ax.plot([1,2,3])
ax.set_title('hi mom')
ax.grid(True)
ax.set_xlabel('time')
ax.set_ylabel('volts')
canvas.print_figure('test')
tacaswell
  • 84,579
  • 22
  • 210
  • 199
3

Matplotlib can be very confusing.

What I like to do is use the the figure() method and not the Figure() method. Careful with the capitalization. In the example code below, if you have figure = plt.Figure() you will get the error that is in the question. By using figure = plt.figure() the canvas is created for you.

Here's an example that also includes a little tidbit about re-sizing your image as you also wanted help on that as well.

#################################
# Importing Modules
#################################
import numpy
import matplotlib.pyplot as plt


#################################
# Defining Constants
#################################
x_triangle = [0.0, 6.0, 3.0]
y_triangle = [0.0, 0.0, 3.0 * numpy.sqrt(3.0)]
x_coords = [4.0]
y_coords = [1.0]
big_n = 5000
# file_obj = open('/Users/lego/Downloads/sierpinski_python.dat', 'w')
figure = plt.figure()
axes = plt.axes()



#################################
# Defining Functions
#################################
def interger_function():
    value = int(numpy.floor(1+3*numpy.random.rand(1)[0]))
    return value


def sierpinski(x_value, y_value, x_traingle_coords, y_triangle_coords):
    index_for_chosen_vertex = interger_function() - 1
    x_chosen_vertex = x_traingle_coords[index_for_chosen_vertex]
    y_chosen_vertex = y_triangle_coords[index_for_chosen_vertex]
    next_x_value = (x_value + x_chosen_vertex) / 2
    next_y_value = (y_value + y_chosen_vertex) / 2
    return next_x_value, next_y_value



#################################
# Performing Work
#################################

for i in range(0, big_n):
    result_from_sierpinski = sierpinski(x_coords[i], y_coords[i], x_triangle, y_triangle)
    x_coords.append(result_from_sierpinski[0])
    y_coords.append(result_from_sierpinski[1])

axes.plot(x_coords, y_coords, marker = 'o', color='darkcyan', linestyle='none')
plot_title_string = "Sierpinski Gasket with N = " + str(big_n)
plt.title(plot_title_string)
plt.xlabel('x coodinate')
plt.ylabel('y coordinate')
figure.set_figheight(10)
figure.set_figwidth(20)
file_path = '{0}.png'.format(plot_title_string)
figure.savefig(file_path, bbox_inches='tight')
plt.close()
# plt.show()
Russell Lego
  • 440
  • 5
  • 6
1

Your sample code wasn't complete enough for me to run it to verify my answer, and it might depend on how ‘matfig’ is defined, but I’m going to guess that what you want is:

fig = matfig.figure(figsize=(16.8, 8.0))

not:

fig = matfig.Figure(figsize=(16.8, 8.0))

figure is the module method that you should invoke/call.

Figure is the top level container for all the plot elements, though it's bit more complicated than this.

cloudpoint
  • 56
  • 4
  • If you look at his previous question, he is try to do this with out using `pyplot` – tacaswell Jul 09 '13 at 04:32
  • import matplotlib.figure as matfig, as tcaswell stated, I'm rewriting my pyplot code with figure and wasn't sure how to translate it completelty – TheoretiCAL Jul 09 '13 at 16:30
  • I didn't see TheoretiCAL's other question to provide the extra context about what is really wanted. I just answered how to get rid of the savefig 'NoneType' error message that he shows above based on his instruction to “Assume code runs without error”. My answer applies to this limited statement of the problem, one that I've encountered before. – cloudpoint Jul 10 '13 at 02:35