Not sure if this is the correct place, as it may be more ‘param’/‘panel’ related.
I’m using PyQt to provide some cusomisation to a plot running via a Bokeh server, using param to update. I’m able to update a slider widget in the browser from python server side, but when trying to update a Bokeh plot, I get the error:
RuntimeError: _pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes
The same occurs with tkinter, using the example from https://github.com/pyviz/panel/issues/344 as a basis.
Any pointers as to what I’m doing wrong would be much appreciated!
Simple example below with tk and the sine wave from https://panel.pyviz.org/user_guide/Django_Apps.html:
import asyncio
import tkinter as tk
from functools import partial
from threading import Thread
import numpy as np
import panel as pn
import param
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from panel.io import get_server
class SineWave(param.Parameterized):
offset = param.Number(default=0.0, bounds=(-5.0,5.0))
amplitude = param.Number(default=1.0, bounds=(-5.0,5.0))
phase = param.Number(default=0.0,bounds=(0.0,2*np.pi))
frequency = param.Number(default=1.0, bounds=(0.1, 5.1))
N = param.Integer(default=200, bounds=(0,None))
x_range = param.Range(default=(0, 4*np.pi),bounds=(0,4*np.pi))
y_range = param.Range(default=(-2.5,2.5),bounds=(-10,10))
def __init__(self, **params):
super().__init__(**params)
x, y = self.sine()
self.cds = ColumnDataSource(data=dict(x=x, y=y))
self.plot = figure(
plot_height=400,
plot_width=400,
tools='pan,wheel_zoom,box_zoom,save,reset',
x_range=self.x_range,
y_range=self.y_range,
sizing_mode='stretch_both',
)
self.plot.line('x', 'y', source=self.cds, line_width=3, line_alpha=0.6)
@param.depends(
'N',
'frequency',
'amplitude',
'offset',
'phase',
'x_range',
'y_range',
watch=True
)
def update_plot(self):
x, y = self.sine()
self.cds.data = dict(x=x, y=y)
self.plot.x_range.start, self.plot.x_range.end = self.x_range
self.plot.y_range.start, self.plot.y_range.end = self.y_range
def sine(self):
x = np.linspace(0, 4*np.pi, self.N)
y = self.amplitude*np.sin(self.frequency*x + self.phase) + self.offset
return x, y
def set_offset(self, offset):
self.offset = offset
sinwave = SineWave()
row = pn.Row(sinwave.plot)
def get_server_in_thread(**kwargs):
asyncio.set_event_loop(asyncio.new_event_loop())
get_server(**kwargs)
class Display():
def __init__(self):
Thread(target=partial(get_server_in_thread,panel=row,start=True,show=True,port=5010)).start()
self.root = tk.Tk()
self.slider = tk.Scale(self.root, from_=0., to=1., resolution=0.1,
orient=tk.HORIZONTAL, command=self.on_slider_change)
self.slider.pack()
self.root.mainloop()
def on_slider_change(self, event):
sinwave.offset = float(event)
display = Display()