Disable constant position reset of Matplotlib chart - python-3.x

I have a python code that by using Matplotlib displays a candlestick chart real time, so the last bar updates each second, the problem Is that the program doesn't let me scroll back/zoom in ... (interacting with the chart) because It keeps reset the position. How can I solve this?
Thanks, have a good day.
import pandas as pd
import mplfinance as mpf
import matplotlib.animation as animation
# Class to simulate getting more data from API:
class RealTimeAPI():
def __init__(self):
self.data_pointer = 0
self.data_frame = pd.read_csv('SP500_NOV2019_IDay.csv',
index_col=0, parse_dates=True)
# self.data_frame = self.data_frame.iloc[0:120,:]
self.df_len = len(self.data_frame)
def fetch_next(self):
r1 = self.data_pointer
self.data_pointer += 1
if self.data_pointer >= self.df_len:
return None
return self.data_frame.iloc[r1:self.data_pointer, :]
def initial_fetch(self):
if self.data_pointer > 0:
return
r1 = self.data_pointer
self.data_pointer += int(0.2*self.df_len)
return self.data_frame.iloc[r1:self.data_pointer, :]
rtapi = RealTimeAPI()
resample_map = {'Open': 'first',
'High': 'max',
'Low': 'min',
'Close': 'last'}
resample_period = '15T'
df = rtapi.initial_fetch()
rs = df.resample(resample_period).agg(resample_map).dropna()
fig, axes = mpf.plot(rs, returnfig=True, figsize=(11, 8),
type='candle', title='\n\nGrowing Candle')
ax = axes[0]
def animate(ival):
global df
global rs
nxt = rtapi.fetch_next()
if nxt is None:
print('no more data to plot')
ani.event_source.interval *= 3
if ani.event_source.interval > 12000:
exit()
return
df = df.append(nxt)
rs = df.resample(resample_period).agg(resample_map).dropna()
ax.clear()
mpf.plot(rs, ax=ax, type='candle')
ani = animation.FuncAnimation(fig, animate, interval=250)
mpf.show()

The code you have posted is the "growing candle animation" example from the mplfinance repository. Mplfinance does not use Matlab but rather MatPlotLib.
(It is temping to edit your question and change all references of "Matlab" to "Mplfinance/Matplotlib" but I will leave that to you to do, just in case I'm missing something or you are actually doing something with Matlab. Furthermore, just to be clear, the code is plotting a "candlestick chart," not a "bar chart." By the way, MatPlotLib was originally written to somewhat resemble the Matlab plotting interface, so some confusion is understandable, but matplotlib has since grown in many ways).
The comment by #CrisLuengo is correct in that the mplfinance code is re-drawing the plot more-or-less from scratch with each animation frame, so any interactive changes (such as zooming in) will be re-set with each animation frame. The mplfinance animation examples do this to keep the code simpler.
I believe it is possible to accomplish what you want (animation with interactivity) however I've never done it myself, so without being explicit as to how to do it, if I were going to do so, I would probably do something along the lines of the following posts:
https://stackoverflow.com/a/46327978/1639359
https://stackoverflow.com/a/46328223/1639359
https://github.com/matplotlib/mplfinance/issues/262#issuecomment-692316347
Full disclosure: I am the maintainer of the mplfinance package. Perhaps someday we will enhance mplfinance to make this easier; but for now such an enhancement is not a priority; therefore one of the above more complex solutions is necessary.

Related

How to enable matplotlib blitting to create fast realtime graph in jupyterlab?

I have the following code in jupyterlab, which updates a graph in realtime when I move a slider. If I disable blitting it updates, although at a very slow framerate (1 fps). If I enable blitting, it doesn't update at all - any idea why? The code uses the ipympl library to allow realtime update of matplotlib graphs in jupyterlab.
import pandas as pd, numpy as np
import time
import matplotlib.pyplot as plt
import ipywidgets as widgets
from itertools import count
from ipywidgets import Button, Layout
# next line enables ipympl
%matplotlib widget
blit = True # 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=Layout(width="500px", height="20px"))
int_slider.observe(on_value_changed, names="value")
sliders = widgets.VBox([int_slider, fig.canvas, output])
display(sliders)
The issue seems to originate from the implementation of matplotlib backend "webagg" used by default in jupyter lab (e.g. when using the magic command %matplotlib widget). Meaning the blitting could work (I haven't checked) if you were to use another backend (qt or tkinter).
=> See this Github Issue for the whole story : Support blitting in webagg backend #19059
Until recently (3/12/2020 & matplotlib 3.3), the blitting was not implemented in the file "backend_webagg_core.py" of the matplotlib module.
Less than a week ago, matplotlib developpers have proposed a patch for this functionnality.
If you want to do it on your own because you don't want to wait for a patch that would come in 2021, you need to modify a few lines in the matplotlib module.
I don't know it this modification would survive a pip update (hopefully not) and I wouldn't recommend it if you don't know what you are doing, since the matplotlib dev team runs several tests to make sure the whole code doesn't break when modifying the module source code.
For this you would need to do modify the class FigureCanvasWebAggCore from backend_webagg_core.py:
class FigureCanvasWebAggCore(backend_agg.FigureCanvasAgg):
supports_blit = True # instead of False
then inside the same class, add the following after the function "draw"
def blit(self, bbox=None):
self._png_is_old = True
self.manager.refresh_all()
you should also modify the following lines ( - means delete / + means add / no symbol means leave as it is):
- last_buffer = (np.frombuffer(self._last_renderer.buffer_rgba(),
- dtype=np.uint32)
- .reshape((renderer.height, renderer.width)))
- diff = buff != last_buffer
+ diff = buff != self._last_buff
same here
- # Swap the renderer frames
- self._renderer, self._last_renderer = (
- self._last_renderer, renderer)
+ # store the current buffer so we can compute the next diff
+ np.copyto(self._last_buff, buff)
and there
- self._last_renderer = backend_agg.RendererAgg(
- w, h, self.figure.dpi)
self._lastKey = key
+ self._last_buff = np.copy(np.frombuffer(
+ self._renderer.buffer_rgba(), dtype=np.uint32
+ ).reshape((self._renderer.height, self._renderer.width)))
Have a look this Github Issue for more details:
Support blitting in webagg backend #19059
A thought about the FPS
Before changing the core code of matplotlib, I would suggest a few modifications of your example.
I would change the way you compute FPS. The formula for computing the time spent to redraw should be imbedded fully within the function that does the drawing.
Creating a panda data frame to update y seems a bit slow to me. A numpty array would be sufficient. Then, this array should live outside the for loop.
def on_value_changed(change):
# monitor framerate
t_start = time.time()
with output:
global x,y
x.append(next_x)
y.append([change.new]*3)
ytmp = np.array(y)
...
print(f"FPS: {round(1/(time.time() - t_start),2)}", end=", ")
By performing those modifications, I get FPS output ranging from 4 to 8.
If I use a for loop to update the matplotlib drawing, the FPS goes between 6 and 11 (mostly around 10, 11):
class stuff:
def __init__(self, x):
self.new = x
s = stuff(0)
for i in range(40):
s.new = np.random.rand()
on_value_changed(s)
Now, to improve this score, we can probably go into the matplotlib code. But it is my beyond my knowledge.

How to link axes of all plots in a Bokeh layout?

I am designing a Bokeh layout using the Bokeh server. I am defining two main columns (see attached image), and I am attempting to link the x-axis of all plots on the right column. The problems are that:
I am trying to make this app as dynamic as possible, which mean that depending on the case-study, not all the plots will be available, and each individual plot is set from a separate function
Each plot object is stored in a list, and I don't know how to access their properties
The reference plot is not known a priori so I don't see how I can implement the example in the Bokeh doc - in other words, I need to first plot all the subplots to then get the relevant x_range
So I was wondering if it is possible to set the linking behaviour a posteriori once all plots in the column are defined (i.e. the output of plotDataset below). My intuition is to loop through the objects, get the children and set the x_range to the first plot, but I don't know how to do that.
Below is a simplified version of what I am trying to achieve. Ideally, I would get the x_range of the first plot of fCol and apply it to all other plots just before return column(fCol)
Any idea is greatly appreciated! And also, I am fairly beginner with Python so please shout if you see anything else horrible!
Thank you
def plotTS(data, col):
tTmp = []
# A loop that defines each tab of the plot
for i in range(len(col)):
fTmp = figure()
fTmp.circle(data[:]['time'], data[:][col[i]], color=color)
# Append tab
tTmp.append(Panel(child=fTmp))
# Return the tabs
return Tabs(tabs=tTmp)
def plotDataset(data):
col = ['NDVI', 'EVI'] # Name of the tabs
fCol = []
fCol.append(plotTS(data, col))
# NOTE: I use an append approach because in reality plotTS is called more than once
return column(fCol)
# General layout - I did not include the code for the left column
layout = row(leftColumn, plotDataset(data))
Link to image
See code below (Bokeh v1.1.0).
from bokeh.models import Panel, Tabs, Column, Row
from bokeh.plotting import figure
from tornado.ioloop import IOLoop
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
def modify_doc(doc):
leftColumn = Column(figure())
def plotTS(data, col):
tTmp = []
for i in col:
fTmp = figure()
fTmp.circle(data['x'], data['y'], color='black')
tTmp.append(Panel(child=fTmp, title = i))
return Tabs(tabs=tTmp)
def plotDataset(data):
col = ['NDVI', 'EVI']
fCol = plotTS(data, col)
shared_range = None
for panel in fCol.tabs:
fig = panel.child
if shared_range is None:
shared_range = fig.x_range
else:
fig.x_range = shared_range
return Column(fCol)
layout = Row(leftColumn, plotDataset(data = dict(x = [1, 2, 3], y = [1, 2, 3])))
doc.add_root(layout)
io_loop = IOLoop.current()
server = Server(applications = {'/app': Application(FunctionHandler(modify_doc))}, io_loop = io_loop, port = 5002)
server.start()
server.show('/app')
io_loop.start()

Fastest Way to Update and Plot Lists? [duplicate]

For years, I've been struggling to get efficient live plotting in matplotlib, and to this day I remain unsatisfied.
I want a redraw_figure function that updates the figure "live" (as the code runs), and will display the latest plots if I stop at a breakpoint.
Here is some demo code:
import time
from matplotlib import pyplot as plt
import numpy as np
def live_update_demo():
plt.subplot(2, 1, 1)
h1 = plt.imshow(np.random.randn(30, 30))
redraw_figure()
plt.subplot(2, 1, 2)
h2, = plt.plot(np.random.randn(50))
redraw_figure()
t_start = time.time()
for i in xrange(1000):
h1.set_data(np.random.randn(30, 30))
redraw_figure()
h2.set_ydata(np.random.randn(50))
redraw_figure()
print 'Mean Frame Rate: %.3gFPS' % ((i+1) / (time.time() - t_start))
def redraw_figure():
plt.draw()
plt.pause(0.00001)
live_update_demo()
Plots should update live when the code is run, and we should see the latest data when stopping at any breakpoint after redraw_figure(). The question is how to best implement redraw_figure()
In the implementation above (plt.draw(); plt.pause(0.00001)), it works, but is very slow (~3.7FPS)
I can implement it as:
def redraw_figure():
plt.gcf().canvas.flush_events()
plt.show(block=False)
And it runs faster (~11FPS), but plots are not up-to date when you stop at breakpoints (eg if I put a breakpoint on the t_start = ... line, the second plot does not appear).
Strangely enough, what does actually work is calling the show twice:
def redraw_figure():
plt.gcf().canvas.flush_events()
plt.show(block=False)
plt.show(block=False)
Which gives ~11FPS and does keep plots up-to-data if your break on any line.
Now I've heard it said that the "block" keyword is deprecated. And calling the same function twice seems like a weird, probably-non-portable hack anyway.
So what can I put in this function that will plot at a reasonable frame rate, isn't a giant kludge, and preferably will work across backends and systems?
Some notes:
I'm on OSX, and using TkAgg backend, but solutions on any backend/system are welcome
Interactive mode "On" will not work, because it does not update live. It just updates when in the Python console when the interpreter waits for user input.
A blog suggested the implementation:
def redraw_figure():
fig = plt.gcf()
fig.canvas.draw()
fig.canvas.flush_events()
But at least on my system, that does not redraw the plots at all.
So, if anybody has an answer, you would directly make me and thousands of others very happy. Their happiness would probably trickle through to their friends and relatives, and their friends and relatives, and so on, so that you could potentially improve the lives of billions.
Conclusions
ImportanceOfBeingErnest shows how you can use blit for faster plotting, but it's not as simple as putting something different in the redraw_figure function (you need to keep track of what things to redraw).
First of all, the code that is posted in the question runs with 7 fps on my machine, with QT4Agg as backend.
Now, as has been suggested in many posts, like here or here, using blit might be an option. Although this article mentions that blit causes strong memory leakage, I could not observe that.
I have modified your code a bit and compared the frame rate with and without the use of blit. The code below gives
28 fps when run without blit
175 fps with blit
Code:
import time
from matplotlib import pyplot as plt
import numpy as np
def live_update_demo(blit = False):
x = np.linspace(0,50., num=100)
X,Y = np.meshgrid(x,x)
fig = plt.figure()
ax1 = fig.add_subplot(2, 1, 1)
ax2 = fig.add_subplot(2, 1, 2)
img = ax1.imshow(X, vmin=-1, vmax=1, interpolation="None", cmap="RdBu")
line, = ax2.plot([], lw=3)
text = ax2.text(0.8,0.5, "")
ax2.set_xlim(x.min(), x.max())
ax2.set_ylim([-1.1, 1.1])
fig.canvas.draw() # note that the first draw comes before setting data
if blit:
# cache the background
axbackground = fig.canvas.copy_from_bbox(ax1.bbox)
ax2background = fig.canvas.copy_from_bbox(ax2.bbox)
plt.show(block=False)
t_start = time.time()
k=0.
for i in np.arange(1000):
img.set_data(np.sin(X/3.+k)*np.cos(Y/3.+k))
line.set_data(x, np.sin(x/3.+k))
tx = 'Mean Frame Rate:\n {fps:.3f}FPS'.format(fps= ((i+1) / (time.time() - t_start)) )
text.set_text(tx)
#print tx
k+=0.11
if blit:
# restore background
fig.canvas.restore_region(axbackground)
fig.canvas.restore_region(ax2background)
# redraw just the points
ax1.draw_artist(img)
ax2.draw_artist(line)
ax2.draw_artist(text)
# fill in the axes rectangle
fig.canvas.blit(ax1.bbox)
fig.canvas.blit(ax2.bbox)
# in this post http://bastibe.de/2013-05-30-speeding-up-matplotlib.html
# it is mentionned that blit causes strong memory leakage.
# however, I did not observe that.
else:
# redraw everything
fig.canvas.draw()
fig.canvas.flush_events()
#alternatively you could use
#plt.pause(0.000000000001)
# however plt.pause calls canvas.draw(), as can be read here:
#http://bastibe.de/2013-05-30-speeding-up-matplotlib.html
live_update_demo(True) # 175 fps
#live_update_demo(False) # 28 fps
Update:
For faster plotting, one may consider using pyqtgraph.
As the pyqtgraph documentation puts it: "For plotting, pyqtgraph is not nearly as complete/mature as matplotlib, but runs much faster."
I ported the above example to pyqtgraph. And although it looks kind of ugly, it runs with 250 fps on my machine.
Summing that up,
matplotlib (without blitting): 28 fps
matplotlib (with blitting): 175 fps
pyqtgraph : 250 fps
pyqtgraph code:
import sys
import time
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
import pyqtgraph as pg
class App(QtGui.QMainWindow):
def __init__(self, parent=None):
super(App, self).__init__(parent)
#### Create Gui Elements ###########
self.mainbox = QtGui.QWidget()
self.setCentralWidget(self.mainbox)
self.mainbox.setLayout(QtGui.QVBoxLayout())
self.canvas = pg.GraphicsLayoutWidget()
self.mainbox.layout().addWidget(self.canvas)
self.label = QtGui.QLabel()
self.mainbox.layout().addWidget(self.label)
self.view = self.canvas.addViewBox()
self.view.setAspectLocked(True)
self.view.setRange(QtCore.QRectF(0,0, 100, 100))
# image plot
self.img = pg.ImageItem(border='w')
self.view.addItem(self.img)
self.canvas.nextRow()
# line plot
self.otherplot = self.canvas.addPlot()
self.h2 = self.otherplot.plot(pen='y')
#### Set Data #####################
self.x = np.linspace(0,50., num=100)
self.X,self.Y = np.meshgrid(self.x,self.x)
self.counter = 0
self.fps = 0.
self.lastupdate = time.time()
#### Start #####################
self._update()
def _update(self):
self.data = np.sin(self.X/3.+self.counter/9.)*np.cos(self.Y/3.+self.counter/9.)
self.ydata = np.sin(self.x/3.+ self.counter/9.)
self.img.setImage(self.data)
self.h2.setData(self.ydata)
now = time.time()
dt = (now-self.lastupdate)
if dt <= 0:
dt = 0.000000000001
fps2 = 1.0 / dt
self.lastupdate = now
self.fps = self.fps * 0.9 + fps2 * 0.1
tx = 'Mean Frame Rate: {fps:.3f} FPS'.format(fps=self.fps )
self.label.setText(tx)
QtCore.QTimer.singleShot(1, self._update)
self.counter += 1
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
thisapp = App()
thisapp.show()
sys.exit(app.exec_())
Here's one way to do live plotting: get the plot as an image array then draw the image to a multithreaded screen.
Example using a pyformulas screen (~30 FPS):
import pyformulas as pf
import matplotlib.pyplot as plt
import numpy as np
import time
fig = plt.figure()
screen = pf.screen(title='Plot')
start = time.time()
for i in range(10000):
t = time.time() - start
x = np.linspace(t-3, t, 100)
y = np.sin(2*np.pi*x) + np.sin(3*np.pi*x)
plt.xlim(t-3,t)
plt.ylim(-3,3)
plt.plot(x, y, c='black')
# If we haven't already shown or saved the plot, then we need to draw the figure first...
fig.canvas.draw()
image = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))
screen.update(image)
#screen.close()
Disclaimer: I'm the maintainer of pyformulas

How to animate multiple figures at the same time

I made an animation for sorting algorithms and it it works great for animating one sorting algorithm, but when I try to animate multiple at the same time both windows come up but none of them are moving. I was wondering how I could go around to fix this.
When I run the code the first figure is stuck on the first frame and the second figure jumps to the last frame
import matplotlib.pyplot as plt
from matplotlib import animation
import random
# my class for getting data from sorting algorithms
from animationSorters import *
def sort_anim(samp_size=100, types=['bubblesort', 'quicksort']):
rndList = random.sample(range(1, samp_size+1), samp_size)
anim = []
for k in range(0, len(types)):
sort_type = types[k]
animation_speed = 1
def barlist(x):
if sort_type == 'bubblesort':
l = bubblesort_swaps(x)#returns bubble sort data
elif sort_type == 'quicksort':
l = quicksort_swaps(x)#returns quick sort data
final = splitSwaps(l, len(x))
return final
fin = barlist(rndList)
fig = plt.figure(k+1)
plt.rcParams['axes.facecolor'] = 'black'
n= len(fin)#Number of frames
x=range(1,len(rndList)+1)
barcollection = plt.bar(x,fin[0], color='w')
anim_title = sort_type.title() + '\nSize: ' + str(samp_size)
plt.title(anim_title)
def animate(i):
y=fin[i]
for i, b in enumerate(barcollection):
b.set_height(y[i])
anim.append(animation.FuncAnimation(fig,animate, repeat=False,
blit=False, frames=n, interval=animation_speed))
plt.show()
sort_anim()
As explained in the documentation for the animation module:
it is critical to keep a reference to the instance object. The
animation is advanced by a timer (typically from the host GUI
framework) which the Animation object holds the only reference to. If
you do not hold a reference to the Animation object, it (and hence the
timers), will be garbage collected which will stop the animation.
Therefore you need to return the references to your animations from your function, otherwise those objects are destroyed when exiting the function.
Consider the following simplification of your code:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
def my_func(nfigs=2):
anims = []
for i in range(nfigs):
fig = plt.figure(num=i)
ax = fig.add_subplot(111)
col = ax.bar(x=range(10), height=np.zeros((10,)))
ax.set_ylim([0, 1])
def animate(k, bars):
new_data = np.random.random(size=(10,))
for j, b in enumerate(bars):
b.set_height(new_data[j])
return bars,
ani = animation.FuncAnimation(fig, animate, fargs=(col, ), frames=100)
anims.append(ani)
return anims
my_anims = my_func(3)
# calling simply my_func() here would not work, you need to keep the returned
# array in memory for the animations to stay alive
plt.show()

Use ipython widgets to manipulate plots, that are constantly updated from within an infinite loop

Within an ipython notebook, using Jupyter I try to do a calculation, thats running for an extended period of time in a while loop. I use pyplot to show the current status of my calculation and I would like to be able to communicate with the calculation and the plot via ipywidgets. For example this could be a stop button for the calculation, or a slider that adjusts the update rate of the plot.
Below I show a minimal example of a sin function, that is constantly plotted with different phase shifts. The start button starts the calculation, the stop button stops it and the slider should adjust the speed at which the whole thing is updated. The textbox shows the current phase shift.
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import *
import IPython.display as display
import _thread
import time
%matplotlib nbagg
# set up widgets
buttons = widgets.ToggleButtons(
options=['Stop', 'Start'],
description='',
disabled=False,
)
text = widgets.Text(
value='0',
placeholder='',
description='Shift [rad]',
)
speed_slider = widgets.IntSlider(
description = "Speed",
value = 100,
min = 10,
max = 500,
step = 10,
)
container = widgets.HBox(children = [buttons, speed_slider, text])
display.display(container)
# functions
def mySin(x, x0):
return np.sin(x-x0)
def run_app(x, dx0):
x0 = np.remainder(np.float(text.value), 2*np.pi)
while buttons.value == buttons.options[1]:
x0 = np.remainder(x0 + dx0, 2*np.pi)
line.set_ydata(mySin(x, x0))
text.value = str(x0)
time.sleep(speed_slider.value/1000)
fig.canvas.draw()
# setup plot
N = 1000
x = np.linspace(0, 10*np.pi, N)
dx0 = 0.05*2*np.pi
fig, ax = plt.subplots(ncols = 1, nrows = 1, figsize = (8,3))
line = ax.plot(x, mySin(x, np.float(text.value)))[0]
# act on button change
def buttons_on_changed(val):
if buttons.value == buttons.options[1]:
_thread.start_new_thread(run_app, (x, dx0))
buttons.observe(buttons_on_changed)
I try to run the function "run_app" in a new thread in order to make the interaction possible. I'm aware that _thread is deprecated, but I would like to know first, wether this is the right way to go. The example above works more or less, but the execution stops after a couple of seconds or when I do something else in the notebook (scrolling, clicking, ...). So my questions are the following:
Is the Thread closing or just losing priority and why is it doing that?
Is that a good approach at all, or can that be achieved in an easier way. Unfortunately, I haven't found anything browsing the web.
Thanks a lot,
Frank

Resources