예제 #1
0
class TemplatePlotGroup(SheetPlotGroup):
    """
    Container that allows creation of different types of plots in a
    way that is independent of particular models or Sheets.

    A TemplatePlotGroup is constructed from a plot_templates list, an
    optional command to run to generate the data, and other optional
    parameters.

    The plot_templates list should contain tuples (plot_name,
    plot_template).  Each plot_template is a list of (name, value)
    pairs, where each name specifies a plotting channel (such as Hue
    or Confidence), and the value is the name of a SheetView (such as
    Activity or OrientationPreference).

    Various types of plots support different channels.  An SHC
    plot supports Strength, Hue, and Confidence channels (with
    Strength usually being visualized as luminance, Hue as a color
    value, and Confidence as the saturation of the color).  An RGB
    plot supports Red, Green, and Blue channels.  Other plot types
    will be added eventually.

    For instance, one could define an Orientation-colored Activity
    plot as::

      plotgroups['Activity'] =
          TemplatePlotGroup(name='Activity', category='Basic',
              pre_plot_hooks=[measure_activity],
              plot_templates=[('Activity',
                  {'Strength': 'Activity', 'Hue': 'OrientationPreference', 'Confidence': None})])

    This specifies that the final TemplatePlotGroup will contain up to
    one Plot named Activity per Sheet, although there could be no
    plots at all if no Sheet has a SheetView named Activity once
    'measure_activity()' has been run.  The Plot will be colored by
    OrientationPreference if such a SheetView exists for that Sheet,
    and the value (luminance) channel will be determined by the
    SheetView Activity.  This plot will be listed in the category
    'Basic' anywhere such categories are relevant (e.g. in the GUI).


    Here's a more complicated example specifying two different plots
    in the same PlotGroup::

      TemplatePlotGroup(name='Orientation Preference', category='Basic'
          pre_plot_hooks=[measure_or_pref.instance()],
          plot_templates=
              [('Orientation Preference',
                  {'Strength': None, 'Hue': 'OrientationPreference'}),
               ('Orientation Selectivity',
                  {'Strength': 'OrientationSelectivity'})])

    Here the TemplatePlotGroup will contain up to two Plots per Sheet,
    depending on which Sheets have OrientationPreference and
    OrientationSelectivity SheetViews.


    The function create_plotgroup provides a convenient way to define plots using
    TemplatePlotGroups; search for create_plotgroup elsewhere in the code to see
    examples.
    """

    doc = param.String(default="",doc="""
        Documentation string describing this type of plot.""")

    plot_immediately=param.Boolean(False,doc="""
        Whether to call the pre-plot hooks at once or only when the user asks for a refresh.

        Should be set to true for quick plots, but false for those that take a long time
        to calculate, so that the user can change the hooks if necessary.""")

    prerequisites=param.List([],doc="""
        List of preference maps that must exist before this plot can be calculated.""")

    category = param.String(default="User",doc="""
        Category to which this plot belongs, which will be created if necessary.""")

    filterfn = param.Callable(default=alwaystrue,doc="""
        Boolean function allowing control over which items will be plotted.
        E.g.: filterfn=lambda x: x.name in ['Retina','V1'] for a plot ranging over
        Sheets, or filterfn=lambda x: x[0]==x[1] for a plot ranging over coordinates.""")

    # CEBALERT: how to avoid repeating documentation?
    # CB: also, documentation for normalization types needs cleaning up.
    normalize = param.ObjectSelector(default='None',
                                     objects=['None','Individually','AllTogether'],doc="""

        'Individually': scale each plot so that its maximum value is
        white and its minimum value black.

        'None': no scaling (0.0 will be black and 1.0 will be white).

        'AllTogether': scale each plot so that the highest maximum value is
        white, and the lowest minimum value is black.


        Normalizing 'Individually' has the advantage of ensuring that
        any data that is present will be visible, but the disadvantage
        that the absolute scale will be obscured.  Non-normalized
        plots are guaranteed to be on a known scale, but only values
        between 0.0 and 1.0 will be visibly distinguishable.""")



    # Overrides:
    # _generate_plots() - supports normalize=='AllTogether'

    # For users, adds:
    # add_template()
    # add_static_image()

    # For subclasses, adds:
    # _template_plots()             -
    # _make_template_plot()         -
    # _kw_for_make_template_plots() -


    #####################
    ########## overridden

    def _generate_plots(self):
        if self.normalize=='AllTogether':
            range_ = _get_value_range(self._template_plots(range_=None))
        else:
            range_ = False
        return self._static_plots[:]+self._template_plots(range_=range_)


    def __init__(self,plot_templates=None,static_images=None,**params):
        super(TemplatePlotGroup,self).__init__(**params)
        self.plot_templates = KeyedList(plot_templates or [])
        # Add plots for the static images, if any
        for image_name,file_path in static_images or []:
            self.add_static_image(image_name,file_path)


    #########################
    ########## adds for users

    # JCALERT! We might eventually write these two functions
    # 'Python-like' by using keyword argument to specify each
    # channel and then get the dictionnary of all remaining
    # arguments.
    #
    # JABALERT: We should also be able to store a documentation string
    # describing each plot (for hovering help text) within each
    # plot template.

    def add_template(self,name,specification_tuple_list):
        dict_={}
        for key,value in specification_tuple_list:
            dict_[key]=value
        self.plot_templates.append((name,dict_))

    add_plot = add_template # CEBALERT: should be removed when callers updated


    def add_static_image(self,name,file_path):
        """
        Construct a static image Plot (e.g. a color key for an Orientation Preference map).
        """
        image = Image.open(resolve_path(file_path))
        plot = Plot(image,name=name)
        self._static_plots.append(plot)



    ##############################
    ########## adds for subclasses

    def _template_plots(self,range_=False):
        # calls make_template_plot for all plot_templates for all kw returned
        # by _kw_for_make_template_plot!!
        template_plots = []
        for plot_template_name,plot_template in self.plot_templates:
            for kw in self._kw_for_make_template_plot(range_):
                template_plots.append(self._make_template_plot(plot_template_name,plot_template,**kw))
        return template_plots


    def _kw_for_make_template_plot(self,range_):
        # Return a list of dictionaries; for each dictionary,
        # _make_template_plot() will be called with that dictionary
        # as keyword arguments.
        #
        # Allows subclasses to control what range of things (sheets,
        # projections) _make_template_plot() is called over, and to
        # control what keyword arguments
        # (sheet=,proj_=,range_=,bounds=,x=,...) are supplied.
        return [dict(sheet=sheet,range_=range_) for sheet in filter(self.filterfn,self.sheets())]


    def _make_template_plot(self,plot_template_name,plot_template,**kw):
        return make_template_plot(plot_template,
                                  kw['sheet'].sheet_views,
                                  kw['sheet'].xdensity,
                                  kw['sheet'].bounds,
                                  self.normalize,
                                  # CEBALERT: after this class, p.t. name never used
                                  name=plot_template_name,
                                  range_=kw['range_'])