Package topo :: Package plotting :: Module plotgroup
[hide private]
[frames] | no frames]

Source Code for Module topo.plotting.plotgroup

   1  """ 
   2  Hierarchy of PlotGroup classes, i.e. output-device-independent sets of 
   3  plots. 
   4   
   5  Includes PlotGroups for standard plots of anything in a Sheet 
   6  database, plus weight plots for one unit, and projections. 
   7   
   8  $Id: plotgroup.py 11315 2010-07-27 17:43:01Z ceball $ 
   9  """ 
  10  __version__='$Revision: 11315 $' 
  11   
  12  import copy 
  13  import Image 
  14   
  15  import param 
  16  from param.parameterized import ParamOverrides 
  17  from param import resolve_path 
  18   
  19  import topo 
  20   
  21  from topo.base.cf import CFSheet,CFProjection 
  22  from topo.base.projection import ProjectionSheet 
  23  from topo.sheet import GeneratorSheet,Sheet 
  24  from topo.misc.keyedlist import KeyedList 
  25   
  26  from plot import make_template_plot, Plot 
  27  from plotfilesaver import PlotGroupSaver,CFProjectionPlotGroupSaver 
  28   
  29  # General CEBALERTs for this file: 
  30  # * It is very difficult to understand what is happening in these 
  31  #   classes! 
  32  # * Improve hierarchy. E.g. GridPlotGroup should be a mixin? 
  33  # * Oh yeah, and don't forget about the TestPattern window. It has 
  34  #   some kind of PlotGroup in it too... 
  35  # * There are missing classes (e.g. currently only the CFProjections 
  36  #   can be plotted for CFSheets). 
  37  # * There are no unit tests 
  38   
  39   
40 -def _cmp_plot(plot1,plot2):
41 """ 42 Comparison function for sorting Plots. 43 It compares the precedence number first and then the src_name and name attributes. 44 """ 45 if plot1.precedence != plot2.precedence: 46 return cmp(plot1.precedence,plot2.precedence) 47 else: 48 return cmp((plot1.plot_src_name+plot1.name), 49 (plot2.plot_src_name+plot2.name))
50 51
52 -class PlotGroup(param.Parameterized):
53 """ 54 Container that has one or more Plots and also knows how to arrange 55 the plots and other special parameters. 56 """ 57 58 pre_plot_hooks = param.HookList(default=[],doc=""" 59 Commands to execute before updating this plot, e.g. to calculate sheet views. 60 61 The commands can be any callable Python objects, i.e. any x for 62 which x() is valid. The initial value is determined by the 63 template for this plot, but various arguments can be passed, a 64 modified version substituted, etc.""") 65 66 plot_hooks = param.HookList(default=[],doc=""" 67 Commands to execute when redrawing a plot rather than regenerating data. 68 69 E.g, for a plot with data measured once but displayed one 70 sheet or unit at at time, this command will be called whenever 71 the sheet or coordinate of unit to be plotted (or the 72 simulator time) has changed. 73 74 The commands can be any callable Python objects, i.e. any x for 75 which x() is valid. The initial value is determined by the 76 template for this plot, but various arguments can be passed, a 77 modified version substituted, etc.""") 78 79 # I guess the interface for users of the class (I just mean methods 80 # likely to be used) is: 81 # make_plots() 82 # scale_images() 83 # + parameters 84 85 # And the interface for subclasses (I mean methods likely to be 86 # overridden) is: 87 # _generate_plots() - return the list of plots 88 # _generate_labels() - return the list of labels 89 # _sort_plots() - sort the list of plots 90 # _exec_pre_plot_hooks() - run the pre_plot_hooks 91 # _exec_plot_hooks() - run the plot_hooks 92 93 94 ############################## 95 ########## interface for users 96
97 - def make_plots(self,update=True):
98 """ 99 Create and scale the plots, after first executing the PlotGroup's pre_plot_hooks 100 (if update is True) and plot_hooks. 101 """ 102 if update:self._exec_pre_plot_hooks() 103 self._exec_plot_hooks() 104 self._create_images(update) 105 self.scale_images()
106
107 - def scale_images(self,zoom_factor=None):
108 """Scale the images by the given zoom factor, if appropriate; default is to do nothing.""" 109 pass
110 111 112 ################################### 113 ########## interface for subclasses 114
115 - def _generate_plots(self):
116 """Return the list of Plots""" 117 # subclasses may have dynamically generated Plots to add 118 return self._static_plots[:]
119 120
121 - def _generate_labels(self):
122 return [plot.label() for plot in self.plots]
123 124
125 - def _sort_plots(self):
126 """Sort plots according to their precedence, then alphabetically.""" 127 self.plots.sort(_cmp_plot)
128 129
130 - def __init__(self,**params):
131 super(PlotGroup,self).__init__(**params) 132 self._static_plots = [] 133 self.plots = [] 134 self.labels = [] 135 self.time = None 136 self.filesaver = PlotGroupSaver(self)
137 138 # In the future, it might be good to be able to specify the 139 # plot rows and columns using tuples. For instance, if three 140 # columns are desired with the plots laid out from left to 141 # right, we could use (None, 3). If three rows are desired 142 # then (3, None). Another method that would work is [3,2,4] 143 # would have the first row with 3, the second row with 2, the 144 # third row with 4, etc. The default left-to-right ordering 145 # in one row could perhaps be represented as (None, Inf). 146 # 147 # Alternatively, we could add another precedence value, so 148 # that the usual precedence value controls where the plot 149 # appears left to right, while a rowprecedence value would 150 # control where it appears top to bottom. All plots with the 151 # same rowprecedence would go on the same row, and the actual 152 # value of the rowprecedence would determine which row goes 153 # first. 154 155
156 - def _exec_pre_plot_hooks(self,**kw):
157 for f in self.pre_plot_hooks: 158 f(**kw)
159 160
161 - def _exec_plot_hooks(self,**kw):
162 for f in self.plot_hooks: 163 f(**kw)
164 165 166 # unlikely to be overridden?
167 - def _create_images(self,update):
168 """ 169 Generate the sorted and scaled list of plots constituting the PlotGroup. 170 """ 171 self.plots = [plot for plot in self._generate_plots() if plot is not None] 172 173 # Suppress plots in the special case of plots not being updated 174 # and having no resizable images, to suppress plotgroups that 175 # have nothing but a color key 176 resizeable_plots = [plot for plot in self.plots if plot.resize] 177 if not update and not resizeable_plots: 178 self.plots=[] 179 180 # Take the timestamps from the underlying Plots 181 timestamps = [plot.timestamp for plot in self.plots 182 if plot.timestamp >= 0] 183 if len(timestamps)>0: 184 self.time = max(timestamps) 185 if max(timestamps) != min(timestamps): 186 self.warning("Combining Plots from different times (%s,%s)" % 187 (min(timestamps),max(timestamps))) 188 189 self._sort_plots() 190 self.labels = self._generate_labels()
191 192 193 194 ### In the rest of the file, whenever we do a loop through all the 195 ### simulation's sheets, seems like we should have set the sheet 196 ### Parameter's range instead. sheet's range is set by the GUI. 197 ### i.e. maybe there should be a sheet parameter here, with its range 198 ### set when the plotgroup is instantiated. Then if sheet=None, the 199 ### sheet's range can be used as the list of sheets. 200
201 -class SheetPlotGroup(PlotGroup):
202 203 # CEBALERT: shouldn't this be abstract? Currently it wouldn't do 204 # anything with its sheets if you asked it to make plots. 205 # 206 # (TemplatePlotGroup and TestPatternPlotGroup are the only two 207 # immediate children at the moment.) 208 209 sheet_coords = param.Boolean(default=False,doc=""" 210 Whether to scale plots based on their relative sizes in sheet 211 coordinates. If true, plots are scaled so that their sizes are 212 proportional to their area in sheet coordinates, so that one can 213 compare corresponding areas. If false, plots are scaled to have 214 similar sizes on screen, regardless of their corresponding 215 sheet areas, which maximizes the size of each plot.""") 216 217 # CEBALERT: in the GUI, this isn't getting the right help text. I 218 # suspect I put in a hack for these parameters in parametersframe, 219 # sp when used outside of a parametersframe, they don't get the 220 # help text. 221 normalize = param.ObjectSelector(default='None', 222 objects=['None','Individually'], 223 doc=""" 224 'Individually': scale each plot so that the peak value will be white 225 and the minimum value black. 226 227 'None': no scaling - 0.0 will be black and 1.0 will be white. 228 229 Normalization has the advantage of ensuring that any data that 230 is present will be visible, but the disadvantage that the 231 absolute scale will be obscured. Non-normalized plots are 232 guaranteed to be on a known scale, but only values between 0.0 233 and 1.0 will be visibly distinguishable.""") 234 235 integer_scaling = param.Boolean(default=False,doc=""" 236 When scaling bitmaps, whether to ensure that the scaled bitmap is an even 237 multiple of the original. If true, every unit will be represented by a 238 square of the same size. Typically false so that the overall area will 239 be correct, e.g. when using Sheet coordinates, which is often more 240 important.""") 241 242 auto_refresh = param.Boolean(default=False,doc=""" 243 If this plot is being displayed persistently (e.g. in a GUI), 244 whether to regenerate it automatically whenever the simulation 245 time advances. The default is False, because many plots are 246 slow to generate (including most preference map plots).""") 247 248 desired_maximum_plot_height = param.Number(default=0,bounds=(0,None),doc=""" 249 User-specified height of the tallest plot in this PlotGroup. 250 Other plots will generally be scaled as appropriate, either 251 to match this size (when sheet_coords is False), or to 252 have the appropriate relative size (when sheet_coords is True). 253 254 If enforce_minimum_plot_height is True, the actual maximum 255 plot height may be larger than this parameter's value. In 256 particular, with enforce_minimum_plot_height=True, the default 257 value of 0 gives plots that are the size of the underlying 258 matrix, which is the most efficient size for saving plots 259 directly to disk. Larger values (e.g. 150) are suitable when 260 displaying plots on screen.""") 261 262 enforce_minimum_plot_height = param.Boolean(default=True,doc=""" 263 If true, ensure that plots are never shown smaller than their 264 native size, i.e. with fewer than one pixel per matrix unit. 265 This option is normally left on for safety, so that no 266 visualization will be missing any units. However, it may be 267 acceptable to turn this check off when working with matrix 268 sizes much larger than your screen resolution.""") 269 270 # For users, adds: 271 # sheets() 272 # update_maximum_plot_height() 273 # scale_images() 274 275 ######################### 276 ########## adds for users 277
278 - def sheets(self):
279 return topo.sim.objects(Sheet).values()
280 281
282 - def update_maximum_plot_height(self,zoom_factor=None):
283 """ 284 Determine which plot will be the largest, based on the 285 settings for minimum plot heights, the specified zoom factor 286 (if not None), etc. 287 288 A minimum size (and potentially a maximum size) are enforced, 289 as described below. 290 291 If the scaled sizes would be outside of the allowed range, 292 False is returned. 293 294 For matrix coordinate plots (sheet_coords=False), the minimum 295 size is calculated as the native size of the largest bitmap to 296 be plotted. Other plots are then usually scaled up to (but 297 not greater than) this size, so that all plots are 298 approximately the same size, and no plot is missing any pixel. 299 300 For Sheet coordinate plots, the minimum plotting density that 301 will avoid losing pixels is determined by the maximum density 302 from any sheet. If all plots are then drawn at that density 303 (as they must be for them to be in Sheet coordinates), the 304 largest plot will then be the one with the largest sheet 305 bounds, and the size of that plot will be the maximum density 306 times the largest sheet bounds. 307 """ 308 # Apply optional scaling to the overall size 309 if zoom_factor: 310 self.desired_maximum_plot_height*=zoom_factor 311 312 self.maximum_plot_height=self.desired_maximum_plot_height 313 314 if (self.enforce_minimum_plot_height): 315 resizeable_plots = [p for p in self.plots if p.resize] 316 317 # CEBALERT: not sure how I should have handled this... 318 if not resizeable_plots: 319 return False 320 321 if not self.sheet_coords: 322 bitmap_heights = [p._orig_bitmap.height() for p in resizeable_plots] 323 minimum_height_of_tallest_plot = max(bitmap_heights) 324 325 else: 326 ### JABALERT: Should take the plot bounds instead of the sheet bounds 327 ### Specifically, a weights plot with situate=False 328 ### doesn't use the Sheet bounds, and so the 329 ### minimum_height is significantly overstated. 330 331 sheets = topo.sim.objects(Sheet) 332 max_sheet_height = max([(sheets[p.plot_src_name].bounds.lbrt()[3]- 333 sheets[p.plot_src_name].bounds.lbrt()[1]) 334 for p in resizeable_plots]) 335 max_sheet_density = max([sheets[p.plot_src_name].xdensity 336 for p in resizeable_plots]) 337 minimum_height_of_tallest_plot = max_sheet_height*max_sheet_density 338 self.max_sheet_height=max_sheet_height 339 340 if (self.maximum_plot_height < minimum_height_of_tallest_plot): 341 self.maximum_plot_height = minimum_height_of_tallest_plot 342 # Return failure if trying to reduce but hit the minimum 343 if zoom_factor: 344 return zoom_factor>1.0 345 346 # print zoom_factor, self.maximum_plot_height, self.desired_maximum_plot_height 347 return True
348 349 350 ### CEB: At least some of this scaling would be common to all 351 ### plotgroups, if some (e.g. featurecurve) didn't open new 352 ### windows. 353 ### 354 ### Could strip out the matrix-coord scaling and put it into 355 ### PlotGroup
356 - def scale_images(self,zoom_factor=None):
357 """ 358 Enlarge or reduce the bitmaps as needed for display. 359 360 The calculated sizes will be multiplied by the given 361 zoom_factor, if it is not None. 362 363 If the scaled sizes would be outside of the allowed range, no 364 scaling is done, and False is returned. (One might 365 conceivably instead want the scaling to reach the actual 366 minimum or maximum allowed, but if we did this, then repeated 367 enlargements and reductions would not be reversible, unless we 368 were very tricky about how we did it.) 369 """ 370 371 # No scaling to do if there are no scalable plots, or no desired size 372 resizeable_plots = [p for p in self.plots if p.resize] 373 if not resizeable_plots or not self.desired_maximum_plot_height: 374 return False 375 376 # Calculate maximum_plot_height, and abort if not feasible 377 if not self.update_maximum_plot_height(zoom_factor) and zoom_factor: 378 return False 379 380 # Scale the images so that each has a size up to the self.maximum_plot_height 381 for plot in resizeable_plots: 382 if self.sheet_coords: 383 s = topo.sim.objects(Sheet)[plot.plot_src_name] 384 scaling_factor=self.maximum_plot_height/float(s.xdensity)/self.max_sheet_height 385 else: 386 scaling_factor=self.maximum_plot_height/float(plot._orig_bitmap.height()) 387 388 if self.integer_scaling: 389 scaling_factor=max(1,int(scaling_factor)) 390 391 plot.set_scale(scaling_factor) 392 393 #print "Scaled %s %s: %d->%d (x %f)" % (plot.plot_src_name, plot.name, plot._orig_bitmap.height(), plot.bitmap.height(), scaling_factor) 394 395 return True
396 397 398
399 -def _get_value_range(plots):
400 """ 401 Helper function to return the (min,max) given by the value_ranges of 402 the given plots. 403 404 Return None if there were no plots with value_range. 405 """ 406 mins = [] 407 maxs = [] 408 for plot in plots: 409 if hasattr(plot,'value_range'): 410 mins.append(plot.value_range[0]) 411 maxs.append(plot.value_range[1]) 412 413 if len(mins)==0: 414 return None 415 else: 416 return (min(mins),max(maxs))
417 418
419 -class TemplatePlotGroup(SheetPlotGroup):
420 """ 421 Container that allows creation of different types of plots in a 422 way that is independent of particular models or Sheets. 423 424 A TemplatePlotGroup is constructed from a plot_templates list, an 425 optional command to run to generate the data, and other optional 426 parameters. 427 428 The plot_templates list should contain tuples (plot_name, 429 plot_template). Each plot_template is a list of (name, value) 430 pairs, where each name specifies a plotting channel (such as Hue 431 or Confidence), and the value is the name of a SheetView (such as 432 Activity or OrientationPreference). 433 434 Various types of plots support different channels. An SHC 435 plot supports Strength, Hue, and Confidence channels (with 436 Strength usually being visualized as luminance, Hue as a color 437 value, and Confidence as the saturation of the color). An RGB 438 plot supports Red, Green, and Blue channels. Other plot types 439 will be added eventually. 440 441 For instance, one could define an Orientation-colored Activity 442 plot as:: 443 444 plotgroups['Activity'] = 445 TemplatePlotGroup(name='Activity', category='Basic', 446 pre_plot_hooks=[measure_activity], 447 plot_templates=[('Activity', 448 {'Strength': 'Activity', 'Hue': 'OrientationPreference', 'Confidence': None})]) 449 450 This specifies that the final TemplatePlotGroup will contain up to 451 one Plot named Activity per Sheet, although there could be no 452 plots at all if no Sheet has a SheetView named Activity once 453 'measure_activity()' has been run. The Plot will be colored by 454 OrientationPreference if such a SheetView exists for that Sheet, 455 and the value (luminance) channel will be determined by the 456 SheetView Activity. This plot will be listed in the category 457 'Basic' anywhere such categories are relevant (e.g. in the GUI). 458 459 460 Here's a more complicated example specifying two different plots 461 in the same PlotGroup:: 462 463 TemplatePlotGroup(name='Orientation Preference', category='Basic' 464 pre_plot_hooks=[measure_or_pref.instance()], 465 plot_templates= 466 [('Orientation Preference', 467 {'Strength': None, 'Hue': 'OrientationPreference'}), 468 ('Orientation Selectivity', 469 {'Strength': 'OrientationSelectivity'})]) 470 471 Here the TemplatePlotGroup will contain up to two Plots per Sheet, 472 depending on which Sheets have OrientationPreference and 473 OrientationSelectivity SheetViews. 474 475 476 The function create_plotgroup provides a convenient way to define plots using 477 TemplatePlotGroups; search for create_plotgroup elsewhere in the code to see 478 examples. 479 """ 480 481 doc = param.String(default="",doc=""" 482 Documentation string describing this type of plot.""") 483 484 plot_immediately=param.Boolean(False,doc=""" 485 Whether to call the pre-plot hooks at once or only when the user asks for a refresh. 486 487 Should be set to true for quick plots, but false for those that take a long time 488 to calculate, so that the user can change the hooks if necessary.""") 489 490 prerequisites=param.List([],doc=""" 491 List of preference maps that must exist before this plot can be calculated.""") 492 493 category = param.String(default="User",doc=""" 494 Category to which this plot belongs, which will be created if necessary.""") 495 496 # CEBALERT: how to avoid repeating documentation? 497 # CB: also, documentation for normalization types needs cleaning up. 498 normalize = param.ObjectSelector(default='None', 499 objects=['None','Individually','AllTogether'],doc=""" 500 501 'Individually': scale each plot so that its maximum value is 502 white and its minimum value black. 503 504 'None': no scaling (0.0 will be black and 1.0 will be white). 505 506 'AllTogether': scale each plot so that the highest maximum value is 507 white, and the lowest minimum value is black. 508 509 510 Normalizing 'Individually' has the advantage of ensuring that 511 any data that is present will be visible, but the disadvantage 512 that the absolute scale will be obscured. Non-normalized 513 plots are guaranteed to be on a known scale, but only values 514 between 0.0 and 1.0 will be visibly distinguishable.""") 515 516 517 518 # Overrides: 519 # _generate_plots() - supports normalize=='AllTogether' 520 521 # For users, adds: 522 # add_template() 523 # add_static_image() 524 525 # For subclasses, adds: 526 # _template_plots() - 527 # _make_template_plot() - 528 # _kw_for_make_template_plots() - 529 530 531 ##################### 532 ########## overridden 533
534 - def _generate_plots(self):
535 if self.normalize=='AllTogether': 536 range_ = _get_value_range(self._template_plots(range_=None)) 537 else: 538 range_ = False 539 return self._static_plots[:]+self._template_plots(range_=range_)
540 541
542 - def __init__(self,plot_templates=None,static_images=None,**params):
543 super(TemplatePlotGroup,self).__init__(**params) 544 self.plot_templates = KeyedList(plot_templates or []) 545 # Add plots for the static images, if any 546 for image_name,file_path in static_images or []: 547 self.add_static_image(image_name,file_path)
548 549 550 ######################### 551 ########## adds for users 552 553 # JCALERT! We might eventually write these two functions 554 # 'Python-like' by using keyword argument to specify each 555 # channel and then get the dictionnary of all remaining 556 # arguments. 557 # 558 # JABALERT: We should also be able to store a documentation string 559 # describing each plot (for hovering help text) within each 560 # plot template. 561
562 - def add_template(self,name,specification_tuple_list):
563 dict_={} 564 for key,value in specification_tuple_list: 565 dict_[key]=value 566 self.plot_templates.append((name,dict_))
567 568 add_plot = add_template # CEBALERT: should be removed when callers updated 569 570
571 - def add_static_image(self,name,file_path):
572 """ 573 Construct a static image Plot (e.g. a color key for an Orientation Preference map). 574 """ 575 image = Image.open(resolve_path(file_path)) 576 plot = Plot(image,name=name) 577 self._static_plots.append(plot)
578 579 580 581 ############################## 582 ########## adds for subclasses 583
584 - def _template_plots(self,range_=False):
585 # calls make_template_plot for all plot_templates for all kw returned 586 # by _kw_for_make_template_plot!! 587 template_plots = [] 588 for plot_template_name,plot_template in self.plot_templates: 589 for kw in self._kw_for_make_template_plot(range_): 590 template_plots.append(self._make_template_plot(plot_template_name,plot_template,**kw)) 591 return template_plots
592 593
594 - def _kw_for_make_template_plot(self,range_):
595 # Return a list of dictionaries; for each dictionary, 596 # _make_template_plot() will be called with that dictionary 597 # as keyword arguments. 598 # 599 # Allows subclasses to control what range of things (sheets, 600 # projections) _make_template_plot() is called over, and to 601 # control what keyword arguments 602 # (sheet=,proj_=,range_=,bounds=,x=,...) are supplied. 603 return [dict(sheet=sheet,range_=range_) for sheet in self.sheets()]
604 605
606 - def _make_template_plot(self,plot_template_name,plot_template,**kw):
607 return make_template_plot(plot_template, 608 kw['sheet'].sheet_views, 609 kw['sheet'].xdensity, 610 kw['sheet'].bounds, 611 self.normalize, 612 # CEBALERT: after this class, p.t. name never used 613 name=plot_template_name, 614 range_=kw['range_'])
615 616 617
618 -def default_measureable_sheet():
619 """Returns the first sheet for which measure_maps is True (if any), or else the first sheet, for use as a default value.""" 620 621 sheets = [s for s in topo.sim.objects(Sheet).values() 622 if hasattr(s,'measure_maps') and s.measure_maps] 623 if len(sheets)<1: 624 sheets = [s for s in topo.sim.objects(Sheet).values()] 625 if len(sheets)<1: 626 raise ValueError("Unable to find a suitable measureable sheet.") 627 sht=sheets[0] 628 if len(sheets)>1: 629 param.Parameterized().message("Using sheet %s." % sht.name) 630 return sht
631 632 633
634 -class ProjectionSheetPlotGroup(TemplatePlotGroup):
635 """Abstract PlotGroup for visualizations of the Projections of one ProjectionSheet.""" 636 __abstract = True 637 638 # Class attribute; forms part of the key for sheet_views (see 639 # the _key() method). 640 keyname = "ProjectionSheet" 641 642 sheet = param.ObjectSelector(default=None, 643 compute_default_fn=default_measureable_sheet, 644 doc= 645 """The Sheet from which to produce plots.""") 646 647 normalize = param.ObjectSelector(default='None', 648 objects=['None','Individually','AllTogether','JointProjections'],doc=""" 649 'Individually': scale each plot so that the peak value will be white 650 and the minimum value black. 651 652 'None': no scaling - 0.0 will be black and 1.0 will be white. 653 654 'AllTogether': scale each plot so that the peak value of all the plots 655 is white, and the minimum value of all the plots will be 656 black. 657 658 'JointProjections': as 'Individually', except that plots produced from 659 projections whose weights are jointly normalized will be 660 jointly normalized. 661 662 Normalization has the advantage of ensuring that any data that 663 is present will be visible, but the disadvantage that the 664 absolute scale will be obscured. Non-normalized plots are 665 guaranteed to be on a known scale, but only values between 0.0 666 and 1.0 will be visibly distinguishable.""") 667 668 669 sheet_type = ProjectionSheet 670 671 # Overrides: 672 # _exec*plot_hooks() - passes sheet to hooks 673 # sheet() - returns only a single sheet 674 # _kw_for_make_template_plot() - adds projections (+ assumes one sheet) 675 # _template_plots() - support joint normalization 676 # _make_template_plot() - 677 # _generate_labels() - 678 679 # Adds for users: 680 # projections() - 681 682 # Adds for subclasses: 683 # _key() - 684 # _kw_for_one_proj() - 685 # _check_sheet_type() - 686 # _check_data_exist() - 687 688 689 ######################### 690 ########## adds for users 691
692 - def projections(self):
693 return self.sheet.projections().values()
694 695 696 ##################### 697 ########## overridden 698
699 - def sheets(self):
700 return [self.sheet]
701
702 - def _exec_pre_plot_hooks(self,**kw):
703 self.params('sheet').compute_default() 704 self._check_sheet_type() 705 super(ProjectionSheetPlotGroup,self)._exec_pre_plot_hooks(sheet=self.sheet,**kw)
706 707
708 - def _exec_plot_hooks(self,**kw):
710 711
712 - def _kw_for_make_template_plot(self,range_):
713 args = [] 714 for proj in self.projections(): 715 for d in self._kw_for_one_proj(proj): 716 d['range_']=range_[proj.name] 717 args.append(d) 718 return args
719 720
721 - def _template_plots(self,range_=False):
722 # all the extra processing is for normalize=='JointProjections' 723 724 # {name:range_} for the projections 725 named_ranges = dict.fromkeys([proj.name for proj in self.projections()],range_) 726 727 if self.normalize=='JointProjections': 728 ranges = {} 729 for group_key in self.sheet._grouped_in_projections('JointNormalize').keys(): 730 if group_key is None: 731 ranges[group_key]=False 732 else: 733 projlist = self.sheet._grouped_in_projections('JointNormalize')[group_key] 734 self._check_data_exist(projlist) 735 736 # hack: need a _kw_for_make_template() that works 737 # with a single range value and restricts 738 # projections to those with a specified key. 739 self._group_key = group_key 740 _orig = self._kw_for_make_template_plot 741 self._kw_for_make_template_plot = self._hack 742 743 plotlist = super(ProjectionSheetPlotGroup,self)._template_plots(range_=None) 744 self._kw_for_make_template_plot = _orig 745 ranges[group_key] = _get_value_range(plotlist) 746 747 for key,proj in self.__keyed_projections(): 748 named_ranges[proj.name] = ranges[key] 749 750 return super(ProjectionSheetPlotGroup,self)._template_plots(range_=named_ranges)
751 752
753 - def _make_template_plot(self,plot_template_name,plot_template,**kw):#sheet,proj
754 return make_template_plot(self._channels(plot_template,**kw), 755 kw['proj'].src.sheet_views, 756 kw['proj'].src.xdensity, 757 None, 758 self.normalize, 759 name=kw['proj'].name, 760 range_=kw['range_'])
761 762
763 - def _generate_labels(self):
764 return ["%s%s"%(plot.name, 765 (("\n(from %s)" % plot.proj_src_name) 766 if hasattr(plot,"proj_src_name") else "")) 767 for plot in self.plots]
768 769 770 ############################## 771 ########## adds for subclasses 772
773 - def _key(self,**kw):
774 # the key for sheet_views 775 return (self.keyname,self.sheet.name,kw['proj'].name)
776 777
778 - def _kw_for_one_proj(self,proj):
779 # Return a list of dictionaries; for each dictionary, 780 # _make_template_plot() will be called with that dictionary as 781 # keyword arguments. 782 return [dict(proj=proj)]
783 784 785 # CB: this isn't really necessary for these classes 786 # themselves. Right now we have it provide a useful error message