Push_notebook and show not displaying the same thing

I don’t get the same result when pushing a previously displayed bokeh plot with push_notebook() and when I am using show()

I tried to check if I was still using the same handler and plot -> I am.

def update(self):
    if self.__handler == None:
        self.__handler = show(self.__plot, notebook_handle=True)
    else:
        show(self.__plot)  # used here for debug purpose
        push_notebook(handle=self.__handler)

before calling the upper function I change self.__plot and hide some of it’s glyph.

    if self.__plot is not None:  # if the plot has already been displayed
        toDelete = self.__plot.select({'name': 'toDelete'})
        for glyph in toDelete:
            if glyph.visible == True:
                glyph.visible = False

In the first function (update):

The result of show(self.__plot) is not displaying the hidden glyphs.So it’s doing what I want.

The result of push_notebook(handle=self.__handler) is displaying everything, and unfortunatly the things that are supposed to be hidden.
Running this on anaconda environment, bokeh 1.1.0, notebook 5.7.8, chrome, python 3.7

Recreate my problem :

Isn’t push_notebook and show supposed to display the same plot if used one right after the other ?

No, this is not correct. It’s purpose is not to show anything at all, itself. The point of push_notebook is to update a previously shown plot in place, which is typically (e.g. in all the official docs and examples) in a previous cell up above. This is why your usage of show and push_notebook together raises flags. I can’t think of any situation where that makes sense.

I think it would help if you explain the user interaction you are trying to achieve from an API agnostic point of view, i.e. without referencing show or push_notebook, what do you want users to see and have happen?

I just want to update a previously shown graph(in the same window).
When I update my graph and when I show it again the content of the “updated graph” and of the" new graph" are not the same.

Can you provide a simpler/minimal example? Alternatively can you provide a more realistic notebook? Your notebook has push_notebook and show called multiple times in the same cell. This is not realistic usage, or at least it is very much not intended usage. Nothing in the docs ever demonstrates anything like this. You can find several examples of what I would consider standard/prototypical usage here:

Perhaps there is one point worth stressing: the handle that is returned by show will only the exact plot that was shown. Any other calls to show will generate entirely distinct and unconnected/unrelated plots. that will not be affected by any pushes to that first, other handle returned by the earlier call to show. This, again, is why your usage of multiple show and push_notebook calls at the same time does not make sense to me.

And actually, if I remove the call to show in your update method, then your code behaved exactly as I would expect. You call this sequence three times:

radar.createRadarGraph(text, flist)
radar.update()

the first with a two element flist, then a one element list, then a three element list, with a sleep in between. When I run the code I see a plot with two glyphs, then one, then three. That is what is supposed to happen.

I will say though that the approach you are taking by simply hiding glyphs and making new ones over and over will leak resources over time. I would not recommend it, generally. The entire point of push_notebook is that you can make a plot with a fixed set of glyphs up front once, then use push_notebook to update the data source. That will be efficient and not leak resources over time. In your case you could use multi_patch once, and never hide that glyph, and use push_notebook to update the data source (as the examples linkes above generally demonstrate) in order to update what the plot shows.

flist has only 2 elements the third time.
I just want my handler to stay connected to self.__plot, which if I understood right it’s supposed to do.
I have done many other graph I never had this problem, maybe it’s because I am hidding some of the elements.

Yes, my oversight.

So, I only had time to take this so far, but here is partial code structured the way I would recommend, that updates the data, rather than hiding/creating entirely new things. It shows 2, then 1, then 2 radar patches, and does not leak resources. You would need to continue the work to handle the “axes”. If their positions do not change, you can do it once in create_plot. Otherwise they would need the same treatment to draw glyphs once with empy sources, then update the sources later. Note I have remove the double underscores on the instance vars. This is really dis-recommended outside of a few very special cases (this coming from CPython Core Devs I work with).

In general, my best advice to you is to separate out generating data, from using that data to draw things. That decoupling will lead to a much more flexible design.

# CELL 1
import numpy as np
form radar import RadarGraph
from bokeh.io import output_notebook
# CELL 2
output_notebook()
# CELL 3
text1 = [
    'lexical richness max :0.326', 
    'ratio filling pause max :0.039', 
    'ratio feedback max :0.305', 
    'taille moyenne Ipu non feedback max :8.699', 
    'familiar language max :5.677'
]

flist1 = [
    np.array([0.45669999, 1, 0.47506903, 0.96134016, 1]),
    np.array([1, 0.58196036, 1, 1, 0.37513551])
]

text2 = [
    'lexical richness max :0.326', 
    'ratio filling pause max :0.023', 
    'ratio feedback max :0.305', 
    'taille moyenne Ipu non feedback max :8.699', 
    'familiar language max :2.130'
]

flist2 = [
    np.array([1, 1, 1, 1, 1])
]

text3 = [
    'lexical richness max :0.326', 
    'ratio filling pause max :0.039', 
    'ratio feedback max :0.305', 
    'taille moyenne Ipu non feedback max :8.699', 
    'familiar language max :5.677'
]

flist3 = [
    np.array([0.45669999, 1, 0.47506903, 0.96134016, 1]), 
    np.array([1, 0.58196036, 1, 1, 0.37513551])
]
# CELL 4
radar = RadarGraph()

radar.show()

radar.update(text1, flist1)

time.sleep(2)

radar.update(text2, flist2)

time.sleep(2)

radar.update(text3, flist3)

time.sleep(2)

And here is a (partial) radar.py:

import time

from bokeh.io import push_notebook, show
from bokeh.models import ColumnDataSource, LabelSet
from bokeh.plotting import figure

DEFAULT_COLORS=["black", "blue", "red", "yellow", "green", "purple", "white", "brown", "orange", "pink", "black"]
DEFAULT_TITLE = "radar graph"

def unit_poly_verts(theta, center):
    x0, y0, r = [center] * 3
    verts = [(r * np.cos(t) + x0, r * np.sin(t) + y0) for t in theta]
    return verts

def radar_patch(r, theta, center):
    offset = 0
    yt = (r * center + offset) * np.sin(theta) + center
    xt = (r * center + offset) * np.cos(theta) + center
    return xt, yt

class RadarGraph(object):

    def __init__(self, colors=DEFAULT_COLORS, title=DEFAULT_TITLE):
        self.colors = colors
        self.title = title
        self.handle = None
        self.center = 0.5
        
        self.label_source = ColumnDataSource(data=dict(x=[], y=[], text=[]))
        self.radar_source = ColumnDataSource(data=dict(xs=[], ys=[], color=[]))
        self.segment_source = ColumnDataSource(data=dict(x1=[], y1=[]))
        
        self.plot = self.create_plot(title, colors)
        
    def show(self):
        self.handle = show(self.plot, notebook_handle=True)

    def update(self, text, flist):
        
        # update labels data source
        text = text + [""]
        nbVar = len(flist[0])
        
        theta = np.linspace(0, 2 * np.pi, nbVar, endpoint=False)
        theta += np.pi / 2
        
        verts = unit_poly_verts(theta, self.center)
        x = [v[0] for v in verts]
        y = [v[1] for v in verts]
        
        self.label_source.data = {'x': x+[self.center], 'y': y+[1], 'text': text}
        
        # update radar data source
        xs, ys = [], []
        for i in range(len(flist)):
            xt, yt = radar_patch(flist[i], theta, self.center)
            xs.append(xt)
            ys.append(yt)
        self.radar_source.data = dict(xs=xs, ys=ys, color=self.colors[:len(xs)])
        
        # push the changes
        push_notebook(handle=self.handle)

    def create_plot(self, title, colors):
        
        plot = figure(title=title, tools="", x_range=(0, 2), y_range=(0, 1.5))

        plot.segment(x0=0.5, y0=0.5, x1='x1', y1='y1', color="black", line_width=1, 
                     source=self.segment_source)

        labels = LabelSet(x="x", y="y", text="text", source=self.label_source)
        plot.add_layout(labels)
        
        plot.patches(xs='xs', ys='ys', fill_color='color', fill_alpha=0.15,
                     source=self.radar_source)
        
        return plot

# You will have to take over here:

#         drawRadar(x, y, self.__plot, 10)   

#         def drawRadar(x1, y1, p, nbOfRulerSegment):
#             origine = 0.5
#             ticSize = 0.01
#             for i in range(0, len(x1)):
#                 p.segment(x0=origine, y0=origine, x1=x1[i],
#                           y1=y1[i], color="black", line_width=1)
#                 if x1[i] == origine:
#                     x1[i] = origine + 0.001  # prevent deviding by 0
#                 if y1[i] == origine:
#                     y1[i] = origine + 0.001  # prevent deviding by 0
#                 leadingCoefficient = (y1[i]-origine)/(x1[i]-origine)
#                 leadingCoefficientRuler = -(1/leadingCoefficient)
#                 deltaX = x1[i] - origine
#                 deltaY = y1[i] - origine
#                 rulerX0 = []
#                 rulerX1 = []
#                 rulerY0 = []
#                 rulerY1 = []

#                 # some quick maths: solving (a*x)^2 +x^2 - tickSize^2 = 0  -> pythagore theorem
#                 a = (leadingCoefficientRuler**2)+1
#                 c = -(ticSize**2)
#                 # b is equal to 0
#                 delta = - (4 * a * c)  # b^2 - 4*a*c
#                 if delta == 0:  # prevent sqrt(0)
#                     delta = 0.001
#                 solution = math.sqrt(delta)/(2*a)
#                 for i in range(1, nbOfRulerSegment):
#                     centreX = origine + ((float(i)/float(nbOfRulerSegment)) * deltaX)
#                     centreY = origine + ((float(i)/float(nbOfRulerSegment)) * deltaY)

#                     rulerX0.append(centreX + solution)
#                     rulerX1.append(centreX - solution)
#                     rulerY0.append(centreY + solution * leadingCoefficientRuler)
#                     rulerY1.append(centreY - solution * leadingCoefficientRuler)

#                 p.segment(x0=rulerX0, y0=rulerY0, x1=rulerX1, y1=rulerY1, color="black", line_width=0.5)



Screen cap of the above (you’ll need to tweek the ranges to your needs as well):