Here are the examples of the python api bokeh.models.BoxAnnotation taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.
9 Examples
0
View Source File : visual_midi.py
License : MIT License
Project Creator : dubreuia
License : MIT License
Project Creator : dubreuia
def plot(self, pm: PrettyMIDI):
"""
Plots the pretty midi object as a plot object.
:param pm: the PrettyMIDI instance to plot
:return: the bokeh plot layout
"""
preset = self._preset
# Calculates the QPM from the MIDI file, might raise exception if confused
qpm = self._get_qpm(pm)
# Initialize the tools, those are present on the right hand side
plot = bokeh.plotting.figure(
tools="reset,hover,save,wheel_zoom,pan",
toolbar_location=preset.toolbar_location)
# Setup the hover and the data dict for bokeh,
# each property must match a property in the data dict
plot.select(dict(type=bokeh.models.HoverTool)).tooltips = ({
"program": "@program",
"pitch": "@top",
"velocity": "@velocity",
"duration": "@duration",
"start_time": "@left",
"end_time": "@right"})
data = dict(
program=[],
top=[],
bottom=[],
left=[],
right=[],
duration=[],
velocity=[],
color=[])
# Puts the notes in the dict for bokeh and saves first
# and last note time, bigger and smaller pitch
pitch_min = None
pitch_max = None
first_note_start = None
last_note_end = None
index_instrument = 0
for instrument in pm.instruments:
for note in instrument.notes:
pitch_min = min(pitch_min or self._MAX_PITCH, note.pitch)
pitch_max = max(pitch_max or self._MIN_PITCH, note.pitch)
color = self._get_color(index_instrument, note)
note_start = note.start
note_end = note.start + (note.end - note.start)
data["program"].append(instrument.program)
data["top"].append(note.pitch)
if self._show_velocity:
data["bottom"].append(note.pitch + (note.velocity / 127))
else:
data["bottom"].append(note.pitch + 1)
data["left"].append(note_start)
data["right"].append(note_end)
data["duration"].append(note_end - note_start)
data["velocity"].append(note.velocity)
data["color"].append(color)
first_note_start = min(first_note_start or sys.maxsize, note_start)
last_note_end = max(last_note_end or 0, note_end)
index_instrument = index_instrument + 1
# Shows an empty plot even if there are no notes
if first_note_start is None or last_note_end is None or pitch_min is None or pitch_max is None:
pitch_min = self._MIN_PITCH
pitch_max = pitch_min + 5
first_note_start = 0
last_note_end = 0
# Gets the pitch range (min, max) from either the provided arguments
# or min and max values from the notes
if self._plot_pitch_range_start is not None:
pitch_min = self._plot_pitch_range_start
else:
pitch_min = min(self._MAX_PITCH, pitch_min)
if self._plot_pitch_range_stop is not None:
pitch_max = self._plot_pitch_range_stop
else:
pitch_max = max(self._MIN_PITCH, pitch_max)
pitch_range = pitch_max + 1 - pitch_min
# Draws the rectangles on the plot from the data
source = ColumnDataSource(data=data)
plot.quad(left="left",
right="right",
top="top",
bottom="bottom",
line_alpha=1,
line_color="black",
color="color",
source=source)
# Draws the y grid by hand, because the grid has label on the ticks, but
# for a plot like this, the labels needs to fit in between the ticks.
# Also useful to change the background of the grid each line
for pitch in range(pitch_min, pitch_max + 1):
# Draws the background box and contours, on the underlay layer, so
# that the rectangles and over the box annotations
fill_alpha = (0.15 if pitch % 2 == 0 else 0.00)
box = BoxAnnotation(bottom=pitch,
top=pitch + 1,
fill_color="gray",
fill_alpha=fill_alpha,
line_color="black",
line_alpha=0.3,
line_width=1,
level="underlay")
plot.add_layout(box)
label = Label(
x=preset.label_y_axis_offset_x,
y=pitch + preset.label_y_axis_offset_y,
x_units="screen",
text=str(pitch),
render_mode="css",
text_font_size=preset.label_text_font_size,
text_font_style=preset.label_text_font_style)
plot.add_layout(label)
# Gets the time signature from pretty midi, or 4/4 if none
if self._midi_time_signature:
numerator, denominator = self._midi_time_signature.split("/")
time_signature = TimeSignature(int(numerator), int(denominator), 0)
else:
if pm.time_signature_changes:
if len(pm.time_signature_changes) > 1:
raise Exception("Multiple time signatures are not supported")
time_signature = pm.time_signature_changes[0]
else:
time_signature = TimeSignature(4, 4, 0)
# Gets seconds per bar and seconds per beat
if len(pm.get_beats()) >= 2:
seconds_per_beat = pm.get_beats()[1] - pm.get_beats()[0]
else:
seconds_per_beat = 0.5
if len(pm.get_downbeats()) >= 2:
seconds_per_bar = pm.get_downbeats()[1] - pm.get_downbeats()[0]
else:
seconds_per_bar = 2.0
# Defines the end time of the plot in seconds
if self._plot_bar_range_stop is not None:
plot_end_time = self._plot_bar_range_stop * seconds_per_bar
else:
# Calculates the plot start and end time, the start time can start after
# notes or truncate notes if the plot is too long (we left truncate the
# plot with the bounds)
# The plot start and plot end are a multiple of seconds per bar (closest
# smaller value for the start time, closest higher value for the end time)
plot_end_time = int((last_note_end) / seconds_per_bar) * seconds_per_bar
# If the last note end is exactly on a multiple of seconds per bar,
# we don't start a new one
is_on_bar = math.isclose(last_note_end % seconds_per_bar, seconds_per_bar)
is_on_bar_exact = math.isclose(last_note_end % seconds_per_bar, 0.0)
if not is_on_bar and not is_on_bar_exact:
plot_end_time += seconds_per_bar
# Defines the start time of the plot in seconds
if self._plot_bar_range_start is not None:
plot_start_time = self._plot_bar_range_start * seconds_per_bar
else:
start_time = int(first_note_start / seconds_per_bar) * seconds_per_bar
plot_max_length_time = self._plot_max_length_bar * seconds_per_bar
plot_start_time = max(plot_end_time - plot_max_length_time, start_time)
# Draws the vertical bar grid, with a different background color
# for each bar
if preset.show_bar:
bar_count = 0
for bar_time in pm.get_downbeats():
fill_alpha_index = bar_count % len(self._bar_fill_alphas)
fill_alpha = self._bar_fill_alphas[fill_alpha_index]
box = BoxAnnotation(left=bar_time,
right=bar_time + seconds_per_bar,
fill_color="gray",
fill_alpha=fill_alpha,
line_color="black",
line_width=2,
line_alpha=0.5,
level="underlay")
plot.add_layout(box)
bar_count += 1
# Draws the vertical beat grid, those are only grid lines
if preset.show_beat:
for beat_time in pm.get_beats():
box = BoxAnnotation(left=beat_time,
right=beat_time + seconds_per_beat,
fill_color=None,
line_color="black",
line_width=1,
line_alpha=0.4,
level="underlay")
plot.add_layout(box)
# Configure x axis
plot.xaxis.bounds = (plot_start_time, plot_end_time)
plot.xaxis.axis_label = "time (SEC)"
plot.xaxis.axis_label_text_font_size = preset.axis_label_text_font_size
plot.xaxis.ticker = bokeh.models.SingleIntervalTicker(interval=1)
plot.xaxis.major_tick_line_alpha = 0.9
plot.xaxis.major_tick_line_width = 1
plot.xaxis.major_tick_out = preset.axis_x_major_tick_out
plot.xaxis.minor_tick_line_alpha = 0
plot.xaxis.major_label_text_font_size = preset.label_text_font_size
plot.xaxis.major_label_text_font_style = preset.label_text_font_style
# Configure y axis
plot.yaxis.bounds = (pitch_min, pitch_max + 1)
plot.yaxis.axis_label = "pitch (MIDI)"
plot.yaxis.axis_label_text_font_size = preset.axis_label_text_font_size
plot.yaxis.ticker = bokeh.models.SingleIntervalTicker(interval=1)
plot.yaxis.major_label_text_alpha = 0
plot.yaxis.major_tick_line_alpha = 0.9
plot.yaxis.major_tick_line_width = 1
plot.yaxis.major_tick_out = preset.axis_y_major_tick_out
plot.yaxis.minor_tick_line_alpha = 0
plot.yaxis.axis_label_standoff = preset.axis_y_label_standoff
plot.outline_line_width = 1
plot.outline_line_alpha = 1
plot.outline_line_color = "black"
# The x grid is deactivated because is draw by hand (see x grid code)
plot.xgrid.grid_line_color = None
# The y grid is deactivated because is draw by hand (see y grid code)
plot.ygrid.grid_line_color = None
# Configure the plot size and range
if self._plot_title is None:
plot_title_text = "Visual MIDI (%s QPM, %s/%s)" % (
str(int(qpm)), time_signature.numerator, time_signature.denominator)
else:
plot_title_text = self._plot_title
plot.title = Title(text=plot_title_text,
text_font_size=preset.title_text_font_size)
plot.plot_width = preset.plot_width
if preset.row_height:
plot.plot_height = pitch_range * preset.row_height
else:
plot.plot_height = preset.plot_height
plot.x_range = Range1d(plot_start_time, plot_end_time)
plot.y_range = Range1d(pitch_min, pitch_max + 1)
plot.min_border_right = 50
if self._live_reload and preset.stop_live_reload_button:
callback = CustomJS(code="clearInterval(liveReloadInterval)")
button = Button(label="stop live reload")
button.js_on_click(callback)
layout = column(button, plot)
else:
layout = column(plot)
return layout
def save(self, pm: PrettyMIDI, filepath: str):
0
View Source File : genome_protein_plots.py
License : GNU General Public License v3.0
Project Creator : michellejlin
License : GNU General Public License v3.0
Project Creator : michellejlin
def protein_annotation(first):
protein_locs = []
protein_names = []
protein_lengths = []
# Shades in every other protein region. Provides warning if proteins overlap.
for i in range(0, proteins.shape[0]):
if(i==0):
x1 = 0
elif(proteins.iloc[i,1] < proteins.iloc[i-1,2]) and first:
print('WARNING: Protein-' + str(proteins.iloc[i,0]) + ' is overlapping with Protein-' + str(proteins.iloc[i-1,0]))
print('Analysis will continue but the visualization for these two proteins will look a little funny. Often the fix for this is simply deleting the small ancilary proteins that overlapping from the gff file and using the -f and -g flags. For more help see the readme.')
x1 = proteins.iloc[i,1]
else:
x1 = proteins.iloc[i,1]
if(i==proteins.shape[0]-1):
x2 = proteins.iloc[i,2]
else:
x2 = proteins.iloc[(i+1),1]
if(i%2==0):
genome_plot.add_layout(BoxAnnotation(left=x1, right=x2, fill_alpha=0.1, fill_color='green'))
protein_locs.append((x1+x2)/2)
protein_lengths.append(x2-x1)
protein_names.append(proteins.iloc[i,0])
if(os.stat("mat_peptides_additions.txt").st_size!=0):
# Makes arrows for mature peptides.
for i in range(0, mat_peptides_list.shape[0]):
x1 = mat_peptides_list.iloc[i,1]
x2 = mat_peptides_list.iloc[i,2]
genome_plot.add_layout(Arrow(end = VeeHead(size=20, fill_color = "cadetblue", fill_alpha = 0.3, line_alpha = 0),
line_color = "cadetblue", line_width = 20, x_start = x1, x_end = x2,
y_start = 5, y_end = 5, line_alpha = 0.3))
# Adds protein labels as tick marks.
genome_plot.xaxis.ticker = protein_locs
protein_locs2 = []
for protein_loc in protein_locs:
str_protein_loc = str(protein_loc)
## print("old str_protein_loc" + str_protein_loc)
if ".0" in str_protein_loc:
str_protein_loc = str_protein_loc.split('.')[0]
## print("split" + str_protein_loc)
protein_locs2.append(int(str_protein_loc))
else:
protein_locs2.append(float(str_protein_loc))
genome_plot.xaxis.major_label_overrides = dict(zip(protein_locs2, protein_names))
return protein_names,protein_lengths
# Creates the legend and configures some of the toolbar stuff.
def configurePlot(g):
0
View Source File : visualization.py
License : MIT License
Project Creator : pedromartins4
License : MIT License
Project Creator : pedromartins4
def plot_rsi(stock):
p = figure(x_axis_type="datetime", plot_width=WIDTH_PLOT, plot_height=200, title="RSI 15 days",
tools=TOOLS, toolbar_location='above')
p.line(x='date', y='rsi_15', line_width=2, color=BLUE, source=stock)
low_box = BoxAnnotation(top=30, fill_alpha=0.1, fill_color=RED)
p.add_layout(low_box)
high_box = BoxAnnotation(bottom=70, fill_alpha=0.1, fill_color=GREEN)
p.add_layout(high_box)
# Horizontal line
hline = Span(location=50, dimension='width', line_color='black', line_width=0.5)
p.renderers.extend([hline])
p.y_range = Range1d(0, 100)
p.yaxis.ticker = [30, 50, 70]
p.yaxis.formatter = PrintfTickFormatter(format="%f%%")
p.grid.grid_line_alpha = 0.3
return p
#### On-Balance Volume (OBV)
def plot_obv(stock):
0
View Source File : widgets.py
License : MIT License
Project Creator : smartyal
License : MIT License
Project Creator : smartyal
def stream_update_backgrounds(self):
""" we update the background by following this algo:
- take the last existing entry in the backgrounds
- do we have a new one which starts inside the last existing?
NO: find the
"""
#make current backgrounds from the latest data and check against the existing backgrounds, put those which we need to append
newBackgrounds = self.make_background_entries(self.streamingUpdateData)
addBackgrounds = [] # the backgrounds to be created new
self.logger.debug("stream_update_backgrounds")
if self.backgrounds == []:
#we don't have backgrounds yet, make them
self.hide_backgrounds()
else:
# we have backgrounds
# now see if we have to adjust the last background
for entry in newBackgrounds:
if entry["start"] < = self.backgrounds[-1]["end"] and entry["end"] > self.backgrounds[-1]["end"]:
# this is the first to show, an overlapping or extending one, we cant' extend the existing easily, so
# we put the new just right of the old
addEntry = {"start": self.backgrounds[-1]["end"], "end": entry["end"], "value":entry["value"], "color": entry["color"]}
addBackgrounds.append(addEntry)
if entry["start"] > self.backgrounds[-1]["end"] and entry["end"]> self.backgrounds[-1]["end"]:
#these are on the right side of the old ones, just add them
addBackgrounds.append(entry)
boxes =[]
for back in addBackgrounds:
name = "__background"+str('%8x'%random.randrange(16**8))
newBack = BoxAnnotation(left=back["start"], right=back["end"],
fill_color=back["color"],
fill_alpha=globalBackgroundsAlpha,
level = globalBackgroundsLevel,
name=name) # +"_annotaion
boxes.append(newBack)
back["rendererName"]=name
self.backgrounds.append(back) # put it in the list of backgrounds for later use
self.plot.renderers.extend(boxes)
#remove renderes out of sight
deleteList = []
for r in self.plot.renderers:
if r.name and "__background" in r.name:
#self.logger.debug(f"check {r.name}, is is {r.right} vs starttime {self.plot.x_range.start}")
#this is a background, so let's see if it is out of sight
if r.right < self.plot.x_range.start:
#this one can go, we can't see it anymore
deleteList.append(r.name)
self.logger.debug(f"remove background renderes out of sight{deleteList}")
if deleteList:
self.remove_renderers(deleteList=deleteList)
#newBackgrounds = self.make_background_entries(self.streamingUpdateData)
#self.hide_backgrounds()
#self.show_backgrounds()
return
def stream_update_new(self,data):
0
View Source File : widgets.py
License : MIT License
Project Creator : smartyal
License : MIT License
Project Creator : smartyal
def draw_annotation__old(self, anno, visible=False):
"""
draw one time annotation on the plot
Args:
anno: the annotation
visible: true/false
"""
try:
#self.logger.debug(f"draw_annotation {anno['name']} visible {visible}")
tag = anno["tags"][0]
mirror = self.server.get_mirror()
myColors = mirror["hasAnnotation"]["colors"][".properties"]["value"]
myTags = mirror["hasAnnotation"]["tags"][".properties"]["value"]
try: # to set color and pattern
if type(myColors) is list:
tagIndex = myTags.index(tag)
pattern = None
color = myColors[tagIndex]
elif type(myColors) is dict:
color = myColors[tag]["color"]
pattern = myColors[tag]["pattern"]
if not pattern is None:
if pattern not in [" ",".","o","-","|","+",":","@","/","\\","x",",","`","v",">","*"]:
pattern = 'x'
except:
color = None
pattern = None
if not color:
self.logger.error("did not find color for boxannotation")
color = "red"
start = anno["startTime"]
end = anno["endTime"]
infinity=globalInfinity
# we must use varea, as this is the only one glyph that supports hatches and does not create a blue box when zooming out
#self.logger.debug(f"have pattern with hatch {pattern}, tag {tag}, color{color} ")
if not pattern is None:
"""
source = ColumnDataSource(dict(x=[start, end], y1=[-infinity, -infinity], y2=[infinity, infinity]))
area = VArea(x="x",y1="y1",y2="y2",
fill_color=color,
name=anno["id"],
fill_alpha=globalAnnotationsAlpha,
hatch_color="black",
hatch_pattern=pattern,
hatch_alpha=1.0)
rendererType = "VArea"
"""
source = ColumnDataSource(dict(x=[start + (end - start) / 2], t=[infinity], b=[-infinity], w=[end - start]))
area = VBar(x="x", top="t", bottom="b", width="w",
fill_color=color,
name=anno["id"],
fill_alpha=globalAnnotationsAlpha,
hatch_color="black",
hatch_pattern=pattern,
hatch_alpha=1.0)
myrenderer = GlyphRenderer(data_source=source, glyph=area, name=anno['id'])
myrenderer.level = globalThresholdsLevel
rendererType = "VBar"
else:
#we use a Boxannotation as this is a lot more efficient in bokeh
"""
area = VArea(x="x", y1="y1", y2="y2",
fill_color=color,
name=anno["id"],
fill_alpha=globalAlpha)
"""
if any([True for tag in anno["tags"] if "anomaly" in tag]):
# if we have an anomaly to draw, we put it on top
level = globalThresholdsLevel
else:
level = globalAnnotationLevel
if BOX_ANNO:
source = None
myrenderer = BoxAnnotation(left=start,right=end,fill_color=color,fill_alpha=globalAnnotationsAlpha,name=anno['id'],level=level)
rendererType = "BoxAnnotation"
else:
#use rect
source = ColumnDataSource({"l": [start+(end-start)/2],"w": [end-start],"y": [-infinity],"height": [3 * infinity]})
recta = Rect(x="l", y="y", width="w", height="height", fill_color=color, fill_alpha=globalAnnotationsAlpha)
myrenderer = GlyphRenderer(data_source=source, glyph=recta, name=anno['id'],level=level)
rendererType = "Rect"
if myrenderer not in self.annoHovers:
self.annoHovers.append(myrenderer)
# bokeh hack to avoid adding the renderers directly: we create a renderer from the glyph and store it for later bulk assing to the plot
# which is a lot faster than one by one
if 0: #this was a trial for an extra object to hover the annotations
dic = {"y": [0],
"x": [start+(end-start)/20],
"w":[end-start],
"h":[infinity],
"l":[start],
"r":[end-start],
"t":[infinity],
"b":[-infinity],
"f":[0.9]}
col = ColumnDataSource(dic)
# the only glyph that worked for hovering was the circle, rect, quad did not work
#annoHover = self.plot.circle(x="x", y="f",size=15, fill_color="white",fill_alpha=0.5,source=col,name="annohover",y_range_name="y2",line_color="white",line_width=2) #works
#self.annoHovers.append(annoHover)
if visible:
self.add_renderers([myrenderer])
self.renderers[anno["id"]] = {"renderer": myrenderer, "info": copy.deepcopy(anno),"source": source,"rendererType":rendererType} # we keep this renderer to speed up later
self.__make_tooltips(force=True) #xxxkna
except Exception as ex:
self.logger.error(f"error draw annotation {anno}"+str(ex))
return None
def create_annotations_glyph(self, tag):
0
View Source File : widgets.py
License : MIT License
Project Creator : smartyal
License : MIT License
Project Creator : smartyal
def draw_threshold(self, annoDict):#, linePath=None):
""" draw the boxannotation for a threshold
Args:
modelPath(string): the path to the annotation, the modelPath-node must contain children startTime, endTime, colors, tags
"""
self.logger.debug(f"draw thresholds {annoDict}")
try:
#if the box is there already, then we skip
if annoDict["id"] in self.renderers:
self.logger.warning(f"have this already {annoDict['id']}")
return
#foundRenderer = self.find_renderer(annoDict["id"])
#if foundRenderer:
# #nothing to do
# return
#annotations = self.server.get_annotations()
# now get the first tag, we only use the first
#tag = annoDict["tags"][0]
color = self.lines[annoDict["variable"]].glyph.line_color
min = annoDict["min"]
max = annoDict["max"]
if min>max:
max,min = min,max # swap them
# print("draw new anno",color,start,end,modelPath)
if self.server.is_y2_variable(annoDict["variable"]):
newAnno = BoxAnnotation(top=max, bottom=min,
fill_color=color,
fill_alpha=globalThresholdsAlpha,
level=globalThresholdsLevel,
name=annoDict["id"],y_range_name="y2") # +"_annotaion
else:
newAnno = BoxAnnotation(top=max, bottom=min,
fill_color=color,
fill_alpha=globalThresholdsAlpha,
level = globalThresholdsLevel,
name=annoDict["id"]) # +"_annotaion
self.add_renderers([newAnno])
self.renderers[annoDict["id"]] = {"renderer": newAnno, "info": copy.deepcopy(annoDict)} # we keep this renderer to speed up later
except Exception as ex:
self.logger.error("error draw threshold "+str(annoDict["id"])+str(ex))
def draw_threshold2(self, anno,visible=False):
0
View Source File : widgets.py
License : MIT License
Project Creator : smartyal
License : MIT License
Project Creator : smartyal
def draw_threshold2(self, anno,visible=False):
""" draw the boxannotation for a threshold
Args:
anno
"""
try:
tag = anno["tags"][0]
if linePath:
color = self.lines[linePath].glyph.line_color
else:
color ="blue"
min = annotations[modelPath]["min"]
max = annotations[modelPath]["max"]
if min>max:
max,min = min,max # swap them
# print("draw new anno",color,start,end,modelPath)
newAnno = BoxAnnotation(top=max, bottom=min,
fill_color=color,
fill_alpha=globalAlpha,
name=modelPath) # +"_annotaion
self.add_renderers([newAnno])
except Exception as ex:
self.logger.error("error draw threshold "+str(modelPath)+ " "+linePath+" "+str(ex))
def make_background_entries(self, data, roundValues = True):
0
View Source File : widgets.py
License : MIT License
Project Creator : smartyal
License : MIT License
Project Creator : smartyal
def show_backgrounds(self,data=None):
"""
show the current backgrounds
Args:
data(dict): contains a dict holding the nodeid with of the background and the __time as keys and the lists of data
if te data is not given, we get the backgrounds fresh from the data server
"""
self.showBackgrounds=True
try:
self.logger.debug("show_backgrounds()")
backGroundNodeId = self.server.get_settings()["background"]["background"]
if not data:
#we have to pick up the background data first
self.logger.debug("get fresh background data from the model server %s",backGroundNodeId)
bins = self.server.get_settings()["bins"]
getData = self.server.get_data([backGroundNodeId], start=self.rangeStart, end=self.rangeEnd,
bins=bins) # for debug
data = getData
#now make the new backgrounds
backgrounds = self.make_background_entries(data)
#now we have a list of backgrounds
self.logger.info("have %i background entries",len(backgrounds))
#now plot them
boxes =[]
self.backgrounds=[]
for back in backgrounds:
name = "__background"+str('%8x'%random.randrange(16**8))
newBack = BoxAnnotation(left=back["start"], right=back["end"],
fill_color=back["color"],
fill_alpha=globalBackgroundsAlpha,
level = globalBackgroundsLevel,
name=name) # +"_annotaion
boxes.append(newBack)
back["rendererName"] = name
self.backgrounds.append(back) # put it in the list of backgrounds for later look up for streaming
self.plot.renderers.extend(boxes)
except Exception as ex:
self.logger.error(f"problem duringshow_backgrounds {ex} ")
def hide_backgrounds(self):
0
View Source File : callout.py
License : Apache License 2.0
Project Creator : spotify
License : Apache License 2.0
Project Creator : spotify
def box(self,
top=None,
bottom=None,
left=None,
right=None,
alpha=.2,
color='red'):
"""Add box callout to the chart.
Args:
top (numeric, optional): Top edge of the box.
bottom (numeric, optional): Bottom edge of the box.
left (numeric, optional): Left edge of the box.
right (numeric, optional): Right edge of the box.
alpha (float, optional): 0.2
color (str): Color name or hex value.
See chartify.color_palettes.show() for available color names.
Note:
The box will extend to the edge if the corresponding position
argument is omitted.
Returns:
Current chart object
"""
# Convert datetime values to epoch if datetime axis.
if isinstance(self._chart.axes, DatetimeXNumericalYAxes):
if left is not None:
left = self._chart.axes._convert_timestamp_to_epoch_ms(left)
if right is not None:
right = self._chart.axes._convert_timestamp_to_epoch_ms(right)
color = colors.Color(color).get_hex_l()
box = bokeh.models.BoxAnnotation(
top=top,
bottom=bottom,
left=left,
right=right,
fill_alpha=alpha,
fill_color=color)
self._chart.figure.add_layout(box)
return self._chart
def text(self,