From: John H. <jdh...@ac...> - 2004-07-06 20:16:24
|
>>>>> "danny" == danny shevitz <dan...@ya...> writes: danny> John, First things first. Thanks for all your help. I danny> wasn't aware of the figlegend command (even though it is danny> clearly in the documentation). I can do what I want with danny> figlegends althought it takes some fiddling to get the danny> positions right. I have to use explicit coordinates because danny> the legends are referenced to the figure not the axes so danny> the boxes don't default to lying inside the graphs and danny> cross over the axes boundaries. In addition the legends danny> are in separate boxes with figlegend, which is acceptable, danny> but not necessarily optimal. I did run into another problem danny> using figlegend, namely that I can't get a handle to the danny> legend text so I'm not able to set the fontsize. I'd danny> appreciate it if you could illuminate me with how to set danny> fontsize in a figlegend. The way to come to this knowledge is: 1) look at the return value of the figlegend command - in this case the docs don't say what they are, but you can always print type(ret) or simply print(ret) where ret is the return value of figlegend to get an idea. I've updated the docs to state A matplotlib.legend.Legend instance is returned 2) Go to the class documentation, either by doing help(ret) from the python shell or going to http://matplotlib.sourceforge.net/classdocs.html and clicking on the legend link http://matplotlib.sourceforge.net/matplotlib.legend.html. 3) Peruse the docs for Legend: in this case you'll see get_frame(self) return the Rectangle instance used to frame the legend get_lines(self) return a list of lines.Line2D instances in the legend get_patches(self) return a list of patch instances in the legend get_texts(self) return a list of text.Text instance in the legend So you can do leg = figlegend(blah, blah) texts = leg.get_texts() # get a sequence of legend texts lines = leg.get_lines() # get a sequence of legend lines patches = leg.get_patches() # get a sequence of legend patches I don't mind you asking - I'm just trying to give you some pointers for future reference. danny> In addition the legends are in separate boxes with danny> figlegend, which is acceptable, but not necessarily danny> optimal. Not sure exactly what you mean. You can put them in the same box by controlling the list of handles and text strings you pass figlegend, or you can turn the legend frames off.... leg.draw_frame(False) danny> So, on to other things. I cooked up an example that shows danny> what I was originally trying to do. I have two routines, danny> doPlots1 and doPlots2, that do exactly the same thing. The danny> only difference is in the default arguments, which I don't danny> actually set, I just use the default. Figure 1 has the danny> effect I was looking for, although I for the life of me danny> don't understand why the two routines give difference danny> answers, or for that matter, why figure 1 works the way it danny> does. danny> In any case. Figure 1 is what I was shooting for. If you danny> want to post some reliable code that does this, I'd danny> appreciate it. See my comments below: from matplotlib.matlab import * import Numeric as N # It is bad form to import Numeric or numarray after importing # matplotlib. This is what numerix is for. You should use import # matplotlib.numerix as N if you want to namespace Numeric. The # command from matplotlib.matlab import * already imports all the # numerix symbols. def doPlots1(const,color,legendList=[],legendTextList=[]): # In passing the empty list as a default argument, you are sharing the # list between function calls. This explains why doPlots1 *works* and # doPlots2 doesn't. This is a well know gotcha. The default # arguments are evaluated *only once* at module load time, so # legendList and legendText list are *the same* lists between function # calls y1=N.arange(0., 10, 1)+ const x = arange(len(y1)) ln1 = plot(x,y1,color+'+-') legendList.append(ln1) # ln1 is a length 1 list, not a line! You should do (note the comma) # # ln1, = plot(x,y1,color+'+-') # # to unpack the length one sequence. You only get a way with it # because legend flattens the list for you legendTextList.append('line1') y2 = y1 + 1 ln2 = plot(x,y2,color+'^-') legendList.append(ln2) legendTextList.append('line2') legend(legendList,legendTextList) show() # You should only call "show" once per script. I repeat, you should # only call "show" once per script. If you want to force a redraw use # get_current_fig_manager().canvas.draw(). The next matplotlib # release defines a "draw" function for this purpose. def doPlots2(const,color): legendList=[] legendTextList=[] # Here legendList and legendTextList are local to the function, not # shared between function calls y1=N.arange(0., 10, 1)+ const x = arange(len(y1)) ln1 = plot(x,y1,color+'+-') legendList.append(ln1) legendTextList.append('line1') y2 = y1 + 1 ln2 = plot(x,y2,color+'^-') legendList.append(ln2) legendTextList.append('line2') legend(legendList,legendTextList) show() figure(1) doPlots1(0,'r') doPlots1(5,'b') figure(2) doPlots2(0,'r') doPlots2(5,'b') danny> In any case. Figure 1 is what I was shooting for. If you danny> want to post some reliable code that does this, I'd danny> appreciate it. While I don't know exactly what you are shooting for, the code below replicates what Figure 1 looks like after your second call to doPlots1. from matplotlib.matlab import * handles = [] labels = [] for const, color in ( (0, 'r'), (5, 'b') ): y1 = arange(0., 10, 1)+ const x = arange(len(y1)) y2 = y1 + 1 lines = plot(x, y1, color+'+-', x, y2, color+'^-' ) handles.extend(lines) labels.extend(('line1', 'line2')) legend(handles, labels, 'upper left') show() Here's another approach using the label keyword arg from matplotlib.matlab import * handles = [] labels = [] for const, color in ( (0, 'r'), (5, 'b') ): y1 = arange(0., 10, 1)+ const x = arange(len(y1)) y2 = y1 + 1 plot(x, y1, color+'+-', label='line1') plot(x, y2, color+'^-', label='line2' ) legend() show() Hope this helps! JDH |