Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

blitting doesn't work - would be useful to speed up graph draw for faster realtime-graphs #228

Open
kaiaeberli opened this issue May 19, 2020 · 2 comments

Comments

@kaiaeberli
Copy link

@kaiaeberli kaiaeberli commented May 19, 2020

I have the following code in jupyterlab. This allows me to move a slider and update a graph in realtime. However the framerate is quite low (1fps) if I call fig.canvas.draw(). I therefore tried blitting, however it does not seem to affect the graph. Is this supposed to work with ipympl?

Many thanks for your help.

Environment:
Jupyterlab==1.2.6
ipympl==0.5.6
jupyter labextension list:

@ibqn/jupyterlab-codecellbtn v0.1.3 enabled ok
@jupyter-widgets/jupyterlab-manager v1.1.0 enabled ok
@jupyterlab/google-drive v1.0.0 enabled ok
@jupyterlab/toc v1.0.1 enabled ok
jupyter-matplotlib v0.7.2 enabled ok
jupyterlab-plotly v1.5.2 enabled ok
plotlywidget v1.5.2 enabled ok

import pandas as pd, numpy as np
import time
import matplotlib.pyplot as plt

from ipywidgets import interact, interactive, fixed, interact_manual, Layout, VBox, HBox
import ipywidgets as widgets

from itertools import count
%matplotlib widget


blit = False # False works, True doesn't.

plt.close('all')
plt.ioff()

output = widgets.Output(layout={'width': '700px', 'height': '300px'})
fig, axs= plt.subplots(3, 2, figsize=(10, 8), sharex=True)
fig.canvas.header_visible = False
fig.canvas.toolbar_visible = False


for i in range(3):    
    axs[i,0].set_ylim(-1.5,1.5)
    axs[i,0].set_xlim(0,20)
    
# index giver
x_value = count()

# expanding dataset
x, y = [], []

# initialise dummy data
[x.append(next(x_value)) for i in range(2)]
[y.append([1]*3) for i in range(2)]

# setup desired and actual angle plots
col_names = ['col1', 'col2', 'col3']
ax_df = pd.DataFrame(index=x,columns=col_names, data=y).plot(subplots=True, ax=axs[:,0])

if blit:
    bgs = []
    for ax in ax_df:
        # cache the background
        ax_background = fig.canvas.copy_from_bbox(ax.bbox)
        bgs.append(ax_background)
    
    fig.canvas.draw()  # initial draw required

# monitor framerate
t_start = time.time()   

# event handler
def on_value_changed(change):    
    with output:    
        next_x = next(x_value) # generate next x axis value
        x.append(next_x)
        y.append([change.new]*3) 

        for i in range(3):            
            if blit:              
                # update data
                line = ax_df[i].get_lines()[0]
                line.set_data(x, pd.DataFrame(y).iloc[:,i])
                
                # restore background
                fig.canvas.restore_region(bgs[i])
                
                # redraw just the points
                ax_df[i].draw_artist(line)

                # fill in the axes rectangle
                fig.canvas.blit(ax_df[i].bbox)
                
            else:
                # update data
                ax_df[i].get_lines()[0].set_data(x, pd.DataFrame(y).iloc[:,i])

                # rescale view
                ax_df[i].autoscale_view(None,'x',None)
                ax_df[i].relim()
            
        fig.canvas.flush_events()
        
        if not blit:
            fig.canvas.draw() # this slows down framerate, not required for blit

        print(f"FPS: {round(next_x/(time.time() - t_start),2)}", end=", ")

sliders = []
int_slider = widgets.FloatSlider(description="test", 
                                 min=-1, max=1, 
                                 value = 0, continuous_update=True,
                                 orientation="horizontal",                                      
                                 layout=widgets.Layout(width="500px", height="20px"))    
int_slider.observe(on_value_changed, names="value")
sliders = widgets.VBox([int_slider, fig.canvas, output])
display(sliders)


@kaiaeberli
Copy link
Author

@kaiaeberli kaiaeberli commented May 25, 2020

The issue seems to be related to send_binary method of Canvas class in ipympl. It does not send any blob's back when blitting and plt.ioff(), but does when drawing (it then sends the full graph). However, when blitting, and setting plt.ion(), it does send back something, but it is just the graph line on transparent background. The graph line looks somewhat broken (not solid):

image

I also noticed that as soon as I resize the graph, the line suddenly becomes solid (assuming it resends the full graph):

image

@kaiaeberli kaiaeberli changed the title enable blitting for faster realtime-graphs? blitting doesn't work - would be useful to speed up graph draw for faster realtime-graphs May 25, 2020
@ianhi
Copy link
Contributor

@ianhi ianhi commented Jul 1, 2020

I'd try using fig.canvas.draw_idle - I found that that was sometimes helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.