class Plot(PlotObject): data_sources = List title = String("Bokeh Plot") x_range = Instance(DataRange1d, has_ref=True) y_range = Instance(DataRange1d, has_ref=True) # We shouldn't need to create mappers manually on the Python side #xmapper = Instance(LinearMapper) #ymapper = Instance(LinearMapper) #mapper = Instance(GridMapper) # A list of all renderers on this plot; this includes guides as well # as glyph renderers renderers = List(has_ref=True) tools = List(has_ref=True) # TODO: These don't appear in the CS source, but are created by mpl.py, so # I'm leaving them here for initial compatibility testing. axes = List(has_ref=True) # TODO: How do we want to handle syncing of the different layers? # image = List # underlay = List # glyph = List # overlay = List # annotation = List height = Int(400) width = Int(400) background_fill = Color("white") border_fill = Color("white") canvas_width = Int(400) canvas_height = Int(400) outer_width = Int(400) outer_height = Int(400) border_top = Int(50) border_bottom = Int(50) border_left = Int(50) border_right = Int(50)
class PointGlyph(XyGlyph): """A set of glyphs placed in x,y coordinates with the same attributes.""" fill_color = Color(default=DEFAULT_PALETTE[1]) fill_alpha = Override(default=0.7) marker = String(default='circle') size = Float(default=8) def __init__(self, x=None, y=None, line_color=None, fill_color=None, marker=None, size=None, **kwargs): kwargs['x'] = x kwargs['y'] = y if line_color is not None: kwargs['line_color'] = line_color if fill_color is not None: kwargs['fill_color'] = fill_color if marker is not None: kwargs['marker'] = marker if size is not None: kwargs['size'] = size super(PointGlyph, self).__init__(**kwargs) self.setup() def get_glyph(self): return marker_types[self.marker] def build_renderers(self): glyph_type = self.get_glyph() glyph = glyph_type(x='x_values', y='y_values', line_color=self.line_color, fill_color=self.fill_color, size=self.size, fill_alpha=self.fill_alpha, line_alpha=self.line_alpha) yield GlyphRenderer(glyph=glyph)
def test_Color(self): prop = Color() self.assertTrue(prop.is_valid(None)) self.assertFalse(prop.is_valid(False)) self.assertFalse(prop.is_valid(True)) self.assertFalse(prop.is_valid(0)) self.assertFalse(prop.is_valid(1)) self.assertFalse(prop.is_valid(0.0)) self.assertFalse(prop.is_valid(1.0)) self.assertFalse(prop.is_valid(1.0 + 1.0j)) self.assertFalse(prop.is_valid("")) self.assertFalse(prop.is_valid(())) self.assertFalse(prop.is_valid([])) self.assertFalse(prop.is_valid({})) self.assertFalse(prop.is_valid(Foo())) self.assertTrue(prop.is_valid((0, 127, 255))) self.assertFalse(prop.is_valid((0, -127, 255))) self.assertFalse(prop.is_valid((0, 127))) self.assertFalse(prop.is_valid((0, 127, 1.0))) self.assertFalse(prop.is_valid((0, 127, 255, 255))) self.assertTrue(prop.is_valid((0, 127, 255, 1.0))) self.assertTrue(prop.is_valid("#00aaff")) self.assertTrue(prop.is_valid("#00AAFF")) self.assertTrue(prop.is_valid("#00AaFf")) self.assertFalse(prop.is_valid("00aaff")) self.assertFalse(prop.is_valid("00AAFF")) self.assertFalse(prop.is_valid("00AaFf")) self.assertFalse(prop.is_valid("#00AaFg")) self.assertFalse(prop.is_valid("#00AaFff")) self.assertTrue(prop.is_valid("blue")) self.assertFalse(prop.is_valid("BLUE")) self.assertFalse(prop.is_valid("foobar"))
def test_Color(self): prop = Color() self.assertTrue(prop.is_valid(None)) self.assertFalse(prop.is_valid(False)) self.assertFalse(prop.is_valid(True)) self.assertFalse(prop.is_valid(0)) self.assertFalse(prop.is_valid(1)) self.assertFalse(prop.is_valid(0.0)) self.assertFalse(prop.is_valid(1.0)) self.assertFalse(prop.is_valid(1.0+1.0j)) self.assertFalse(prop.is_valid("")) self.assertFalse(prop.is_valid(())) self.assertFalse(prop.is_valid([])) self.assertFalse(prop.is_valid({})) self.assertFalse(prop.is_valid(Foo())) self.assertTrue(prop.is_valid((0, 127, 255))) self.assertFalse(prop.is_valid((0, -127, 255))) self.assertFalse(prop.is_valid((0, 127))) self.assertFalse(prop.is_valid((0, 127, 1.0))) self.assertFalse(prop.is_valid((0, 127, 255, 255))) self.assertTrue(prop.is_valid((0, 127, 255, 1.0))) self.assertTrue(prop.is_valid("#00aaff")) self.assertTrue(prop.is_valid("#00AAFF")) self.assertTrue(prop.is_valid("#00AaFf")) self.assertFalse(prop.is_valid("00aaff")) self.assertFalse(prop.is_valid("00AAFF")) self.assertFalse(prop.is_valid("00AaFf")) self.assertFalse(prop.is_valid("#00AaFg")) self.assertFalse(prop.is_valid("#00AaFff")) self.assertTrue(prop.is_valid("blue")) self.assertTrue(prop.is_valid("BLUE")) self.assertFalse(prop.is_valid("foobar"))
class GMapPlot(PlotObject): center_lat = Float center_lng = Float zoom_level = Int(12) data_sources = List title = String("Bokeh Plot") png = String('') title = String('') # A list of all renderers on this plot; this includes guides as well # as glyph renderers renderers = List(has_ref=True) tools = List(has_ref=True) # TODO: These don't appear in the CS source, but are created by mpl.py, so # I'm leaving them here for initial compatibility testing. axes = List(has_ref=True) x_range = Instance(Range1d, has_ref=True) y_range = Instance(Range1d, has_ref=True) # TODO: How do we want to handle syncing of the different layers? # image = List # underlay = List # glyph = List # # annotation = List height = Int(800) width = Int(800) border_fill = Color("white") canvas_width = Int(800) canvas_height = Int(800) outer_width = Int(800) outer_height = Int(800) min_border_top = Int(50) min_border_bottom = Int(50) min_border_left = Int(50) min_border_right = Int(50) min_border = Int(50) def vm_serialize(self): # GlyphRenderers need to serialize their state a little differently, # because the internal glyph instance is turned into a glyphspec data = super(GMapPlot, self).vm_serialize() data.pop('center_lat', None) data.pop('center_lng', None) data.pop('zoom_level', None) data["map_options"] = { 'lat': self.center_lat, 'lng': self.center_lng, 'zoom': self.zoom_level } self._session.raw_js_snippets(self) return data @classmethod def load_json(cls, attrs, instance=None): """Loads all json into a instance of cls, EXCEPT any references which are handled in finalize """ inst = super(GMapPlot, cls).load_json(attrs, instance=instance) if hasattr(inst, 'map_options'): mo = inst.map_options del inst.map_options inst.center_lat = mo['lat'] inst.center_lng = mo['lng'] inst.zoom_level = mo['zoom'] return inst def get_raw_js(self): return '<script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>' def script_inject(self): return script_inject(self._session, self._id, self.__view_model__) def script_inject_escaped(self): return script_inject(self._session, self._id, self.__view_model__) def vm_props(self, *args, **kw): # FIXME: We need to duplicate the height and width into canvas and # outer height/width. This is a quick fix for the gorpiness, but this # needs to be fixed more structurally on the JS side, and then this # should be revisited on the Python side. if "canvas_width" not in self._changed_vars: self.canvas_width = self.width if "outer_width" not in self._changed_vars: self.outer_width = self.width if "canvas_height" not in self._changed_vars: self.canvas_height = self.height if "outer_height" not in self._changed_vars: self.outer_height = self.height return super(GMapPlot, self).vm_props(*args, **kw)
class Plot(PlotObject): data_sources = List title = String("Bokeh Plot") x_range = Instance(DataRange1d, has_ref=True) y_range = Instance(DataRange1d, has_ref=True) png = String('') title = String('') # We shouldn't need to create mappers manually on the Python side #xmapper = Instance(LinearMapper) #ymapper = Instance(LinearMapper) #mapper = Instance(GridMapper) # A list of all renderers on this plot; this includes guides as well # as glyph renderers renderers = List(has_ref=True) tools = List(has_ref=True) # TODO: These don't appear in the CS source, but are created by mpl.py, so # I'm leaving them here for initial compatibility testing. axes = List(has_ref=True) # TODO: How do we want to handle syncing of the different layers? # image = List # underlay = List # glyph = List # # annotation = List height = Int(600) width = Int(600) background_fill = Color("white") border_fill = Color("white") canvas_width = Int(400) canvas_height = Int(400) outer_width = Int(400) outer_height = Int(400) min_border_top = Int(50) min_border_bottom = Int(50) min_border_left = Int(50) min_border_right = Int(50) min_border = Int(50) def script_inject(self): return script_inject(self._session, self._id, self.__view_model__) def script_inject_escaped(self): return script_inject(self._session, self._id, self.__view_model__) def vm_props(self, *args, **kw): # FIXME: We need to duplicate the height and width into canvas and # outer height/width. This is a quick fix for the gorpiness, but this # needs to be fixed more structurally on the JS side, and then this # should be revisited on the Python side. if "canvas_width" not in self._changed_vars: self.canvas_width = self.width if "outer_width" not in self._changed_vars: self.outer_width = self.width if "canvas_height" not in self._changed_vars: self.canvas_height = self.height if "outer_height" not in self._changed_vars: self.outer_height = self.height return super(Plot, self).vm_props(*args, **kw)
class CompositeGlyph(HasProps): """Represents a subset of data. A collection of hetero or homogeneous glyph renderers which represent a subset of data. The purpose of the composite glyph is to abstract away the details of constructing glyphs, based on the details of a subset of data, from the grouping operations that a generalized builder must implement. In general, the Builder operates at the full column oriented data source level, segmenting and assigning attributes from a large selection, while the composite glyphs will typically be passed an array-like structures with one or more singlular attributes to apply. Another way to explain the concept is that the Builder operates as the groupby, as in pandas, while the CompositeGlyph operates as the apply. What is the responsibility of the Composite Glyph? - Produce GlyphRenderers - Apply any aggregations - Tag the GlyphRenderers with the group label - Apply transforms due to chart operations - Operations require implementation of special methods """ label = String('All', help='Identifies the subset of data.') values = Either(Column(Float), Column(String), help='Array-like values.') color = Color(default='gray') fill_alpha = Float(default=0.8) source = Instance(ColumnDataSource) operations = List(Any) renderers = List(Instance(GlyphRenderer)) left_buffer = Float(default=0.0) right_buffer = Float(default=0.0) top_buffer = Float(default=0.0) bottom_buffer = Float(default=0.0) def __init__(self, **kwargs): label = kwargs.pop('label', None) if label is not None: if not isinstance(label, str): label = str(label) kwargs['label'] = label super(CompositeGlyph, self).__init__(**kwargs) self.setup() def setup(self): self.renderers = [renderer for renderer in self.build_renderers()] if self.renderers is not None: self.refresh() def refresh(self): if self.renderers is not None: self.source = self.build_source() self._set_sources() def build_renderers(self): raise NotImplementedError('You must return list of renderers.') def build_source(self): raise NotImplementedError('You must return ColumnDataSource.') def _set_sources(self): """Store reference to source in each glyph renderer.""" for renderer in self.renderers: renderer.data_source = self.source def __stack__(self, glyphs): pass def __jitter__(self, glyphs): pass def __dodge__(self, glyphs): pass def __overlay__(self, glyphs): pass def apply_operations(self): pass
class CompositeGlyph(HasProps): """Represents a subset of data. A collection of hetero or homogeneous glyph renderers which represent a subset of data. The purpose of the composite glyph is to abstract away the details of constructing glyphs, based on the details of a subset of data, from the grouping operations that a generalized builders must implement. In general, the Builder operates at the full column oriented data source level, segmenting and assigning attributes from a large selection, while the composite glyphs will typically be passed an array-like structures with one or more singlular attributes to apply. Another way to explain the concept is that the Builder operates as the groupby, as in pandas, while the CompositeGlyph operates as the function used in the apply. What is the responsibility of the Composite Glyph? - Produce GlyphRenderers - Apply any aggregations - Tag the GlyphRenderers with the group label - Apply transforms due to chart operations - Note: Operations require implementation of special methods """ # composite glyph inputs label = String('None', help='Identifies the subset of data.') values = Either(Column(Float), Column(String), help="""Array-like values, which are used as the input to the composite glyph.""") # derived from inputs source = Instance(ColumnDataSource, help="""The data source used for the contained glyph renderers. Simple glyphs part of the composite glyph might not use the column data source.""") renderers = List(Instance(GlyphRenderer)) operations = List(Any, help="""A list of chart operations that can be applied to manipulate their visual depiction.""") color = Color(default='gray', help="""A high level color. Some glyphs will implement more specific color attributes for parts or specific glyphs.""" ) line_color = Color(default='black', help="""A default outline color for contained glyphs.""") fill_alpha = Float(default=0.8) left_buffer = Float(default=0.0) right_buffer = Float(default=0.0) top_buffer = Float(default=0.0) bottom_buffer = Float(default=0.0) def __init__(self, **kwargs): label = kwargs.pop('label', None) if label is not None: if not isinstance(label, str): label = str(label) kwargs['label'] = label super(CompositeGlyph, self).__init__(**kwargs) def setup(self): """Build renderers and data source and set sources on renderers.""" self.renderers = [renderer for renderer in self.build_renderers()] if self.renderers is not None: self.refresh() def refresh(self): """Update the GlyphRenderers. .. note: this method would be called after data is added. """ if self.renderers is not None: self.source = self.build_source() self._set_sources() def build_renderers(self): raise NotImplementedError('You must return list of renderers.') def build_source(self): raise NotImplementedError('You must return ColumnDataSource.') def _set_sources(self): """Store reference to source in each GlyphRenderer. .. note:: if the glyphs that are part of the composite glyph differ, you may have to override this method and handle the sources manually. """ for renderer in self.renderers: renderer.data_source = self.source def __stack__(self, glyphs): """A special method the `stack` function applies to composite glyphs.""" pass def __jitter__(self, glyphs): """A special method the `jitter` function applies to composite glyphs.""" pass def __dodge__(self, glyphs): """A special method the `dodge` function applies to composite glyphs.""" pass def __overlay__(self, glyphs): """A special method the `overlay` function applies to composite glyphs.""" pass def apply_operations(self): pass