is there a way to customize the panel.pane.Audio element?
I've tried to pass arguments like width or height like:
pn_audio = pn.pane.Audio(audio_data, sample_rate=sps, width=800)
but with no effect after starting as bokeh app.
Here is some usable code:
import numpy as np
import holoviews as hv
import holoviews.plotting.bokeh
from holoviews import opts
import panel as pn
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.server.server import Server
import socket
hostname = socket.gethostname()
IPAddr = socket.gethostbyname(hostname)
def generate_audio():
sps = 25600 # Samples per second
duration = 10 # Duration in seconds
modulator_frequency = 2.0
carrier_frequency = 120.0
modulation_index = 2.0
time = np.arange(sps*duration) / sps
modulator = np.sin(2.0 * np.pi * modulator_frequency * time) * modulation_index
carrier = np.sin(2.0 * np.pi * carrier_frequency * time)
waveform = np.sin(2. * np.pi * (carrier_frequency * time + modulator))
waveform_quiet = waveform * 0.3
waveform_int = np.int16(waveform_quiet * 32767)
return waveform_int, sps
def modify_doc(doc):
audio_data, sps = generate_audio()
hv_curve = hv.Curve( audio_data).opts(width=800)
pn_curve = pn.panel(hv_curve)
pn_audio = pn.pane.Audio(audio_data, sample_rate=sps)
# pn_txt = pn.widgets.TextInput(name='A widget', value='A string')
plot = pn.Column(*[pn_curve,pn_audio])
doc.add_root(plot.get_root())
return doc
app = Application(FunctionHandler(modify_doc))
server = Server(app, address=IPAddr,
allow_websocket_origin=['{}:5006'.format(IPAddr)],port=5006)
server.start()
print('Opening Bokeh application on {}'.format(IPAddr))
server.io_loop.add_callback(server.show, "/")
server.io_loop.start()
I start the app from comandline
python start_bokeh_server.py
The goal is to get the audio element as wide as the above plot.
For now it look like that bokeh app
Any ideas on how to get this done, without diving deep into the javascript part of panel?
Thanks in advance.
Related
I'm writing code to update a matplotlib graph in real time while embedded into a PyQt5 application. Separately, the two pieces of code (for the graph and the embedding of a still graph. Putting the two together causes a blank window with a graph to open. Upon closing the window it opens the correct application window, with the embedded graph. However, the graph is not updating, but with the line data of what SHOULD be on the graph at said time.
Removing plt.show() only causes a blank graph to appear in the embedded window. It appears that the code runs on the bank graph that opens first, and then updates it to the hidden graph in the application window.
Is there any...simple fix, or is this going to be a much longer process?
# This aint it chief
import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from PyQt5.QtWidgets import QApplication, QWidget
import itertools as itrt
import matplotlib.animation as animation
class Canvas(FigureCanvas):
def __init__(self, parent):
fig, self.ax = plt.subplots()
super().__init__(fig)
self.setParent(parent)
# creates figure "ax" with a grid
self.ax.grid()
# creates time
t = []
# sets time as xdata
xdata = t
# ydata inputs
ydataps1 = []
ydataps2 = []
ydataps3 = []
ydataps4 = []
ydatads1 = []
# generates data for the graph
def datagen():
# creates timer on the x-axis
for cnt in itrt.count():
t = cnt / 10
# y value inputs
ps1y = np.log(np.pi * t)
ps2y = np.log(5 * np.pi * t)
ps3y = np.log(3 * np.pi * t)
ps4y = np.log(2 * np.pi * t)
ds1y = np.log(1.5 * np.pi * t)
# yields data to move it to run function
yield t, ps1y, ps2y, ps3y, ps4y, ds1y
# # creates lines with line-width two, at points t and y[]
lineps1, = self.ax.plot(t, [], lw=2)
lineps2, = self.ax.plot(t, [], lw=2)
lineps3, = self.ax.plot(t, [], lw=2)
lineps4, = self.ax.plot(t, [], lw=2)
lineds1, = self.ax.plot(t, [], lw=2)
# init function, clears line data and sets the line data to be t and y[]
def init():
# clear data
del xdata[:]
del ydataps1[:]
del ydataps2[:]
del ydataps3[:]
del ydataps4[:]
del ydatads1[:]
# set line data to cleared
lineps1.set_data(xdata, ydataps1)
lineps2.set_data(xdata, ydataps2)
lineps3.set_data(xdata, ydataps3)
lineps4.set_data(xdata, ydataps4)
lineds1.set_data(xdata, ydatads1)
# return updated values
return lineps1, lineps2, lineps3, lineps4, lineds1
# updates values for data
def run(data):
# time (t) and y = data
t, yps1, yps2, yps3, yps4, yds1 = data
# update x to be set to time
xdata.append(t)
# update ydatas to new y values
ydataps1.append(yps1)
ydataps2.append(yps2)
ydataps3.append(yps3)
ydataps4.append(yps4)
ydatads1.append(yds1)
# auto-scaling (kinda)
xmin, xmax = self.ax.get_xlim()
ymin, ymax = self.ax.get_ylim()
# compares all y data to ensure the graph scales on the highest value
ydata_list = [yps1, yps2, yps3, yps4, yds1]
max_value = max(ydata_list)
# y scale
if max_value >= ymax:
self.ax.set_ylim(ymin, 2*ymax)
self.ax.figure.canvas.draw()
# Time autoscale
if t >= xmax:
self.ax.set_xlim(xmin, 2*xmax)
self.ax.figure.canvas.draw()
# updates lines
lineps1.set_data(xdata, ydataps1)
lineps2.set_data(xdata, ydataps2)
lineps3.set_data(xdata, ydataps3)
lineps4.set_data(xdata, ydataps4)
lineds1.set_data(xdata, ydatads1)
# returns updated line values
return lineps1, lineps2, lineps3, lineps4, lineds1
# creates an animation function which runs all the functions in a loop
ani = animation.FuncAnimation(fig, run, datagen, interval=1, init_func=init)
# show graph, absolutely necessary
plt.show(block=True)
class AppDemo(QWidget) :
def __init__(self):
super().__init__()
self.resize(1600, 800)
chart = Canvas(self)
app = QApplication(sys.argv)
demo = AppDemo()
demo.show()
sys.exit(app.exec_())
Apologies if this is a stupid question, but I'm wondering if it would be possible to create a dash app and stream live data straight into the graph?
I've been working on a trading bot which has a number of elements. I am currently trying to stream data that updates every 2000ms from a websocket and feed that straight into the graph.
I'm relatively new to programming so could use some assistance and criticism where neccesary!
So, as this live graph will ultimately be incorporated as part of the GUI and bot functionality, I am trying to make this priceTicker class encompass both initialising the dash app and also live updating the graph with the websocket stream that is received.
I am able to stream the data without problem and could parse the data into json, or csv or anything really. I am also able to initialise the graph in the dash app, but it isnt updating the graph with new data.
I have looked at a few SO posts around the general area but not found anything that explains it for my case unfortunately - sorry if there is any posts out there that i've missed!
Below is my full code:
from binance.client import Client
from binance.websockets import BinanceSocketManager
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input
import plotly
import plotly.graph_objs as go
from collections import deque
import pandas as pd
import json
class priceTicker:
def __init__(self, api_key, secret_key):
self.app = dash.Dash(__name__)
self.ticker_time = deque(maxlen=200)
self.last_price = deque(maxlen=200)
self.app.layout = html.Div(
[
dcc.Graph(id = 'live-graph', animate = True),
dcc.Interval(
id = 'graph-update',
interval = 2000,
n_intervals = 0
)
]
)
client = Client(api_key, secret_key)
socket = BinanceSocketManager(client)
socket.start_kline_socket('BTCUSDT', self.on_message, '1m')
socket.start()
def on_message(self, message):
app = self.app
price = {'time':message['E'], 'price':message['k']['c']}
self.ticker_time.append(message['E'])
self.last_price.append(message['k']['c'])
ticker_time = self.ticker_time
last_price = self.last_price
#print(self.ticker_time, self.last_price)
#app.callback(
Output('live-graph', 'figure'),
[Input('graph-update', 'n_intervals')])
def update_graph(ticker_time,last_price):
data = go.Scatter(
x = list(ticker_time),
y = list(last_price),
name ='Scatter',
mode = 'lines+markers'
)
return {'data': [data],
'layout': go.Layout(xaxis=dict(range=[min(ticker_time), max(ticker_time)]),
yaxis=dict(range=[min(last_price), max(last_price)]),)}
def Main():
api_key = ''
secret_key = ''
ticker = priceTicker(api_key, secret_key)
ticker.app.run_server(debug=True, host='127.0.0.1', port=16552)
if __name__ == '__main__':
#app.run_server(debug=False, host='127.0.0.1', port=10010)
Main()
I'm not sure where I'm going wrong but it doesn't seem like the call back function is being called. Sorry if i've missed something obvious!
I'm running on 3.7.6 and macos Big Sur
If anyone has any advice would be greatly appreciated.
cheers
For anyone that's interested or trying to do a similar thing to me.
I managed to fix the problem by calling the app.callback within init and assigning the callback to a function within the class:
from binance.client import Client
from binance.websockets import BinanceSocketManager
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input
import plotly
import plotly.graph_objs as go
from collections import deque
import pandas as pd
import json
class priceTicker:
def __init__(self, api_key, secret_key):
self.ticker_time = deque(maxlen=200)
self.last_price = deque(maxlen=200)
client = Client(api_key, secret_key)
socket = BinanceSocketManager(client)
socket.start_kline_socket('BTCUSDT', self.on_message, '1m')
socket.start()
self.app = dash.Dash()
self.app.layout = html.Div(
[
dcc.Graph(id = 'live-graph', animate = True),
dcc.Interval(
id = 'graph-update',
interval = 2000,
n_intervals = 0
)
]
)
self.app.callback(
Output('live-graph', 'figure'),
[Input('graph-update', 'n_intervals')])(self.update_graph)
#app.run_server(debug=True, host='127.0.0.1', port=16452)
def on_message(self, message):
#price = {'time':message['E'], 'price':message['k']['c']}
self.ticker_time.append(message['E'])
self.last_price.append(message['k']['c'])
#print(self.ticker_time, self.last_price)
def update_graph(self, n):
data = go.Scatter(
x = list(self.ticker_time),
y = list(self.last_price),
name ='Scatter',
mode = 'lines+markers'
)
return {'data': [data],
'layout': go.Layout(xaxis=dict(range=[min(self.ticker_time), max(self.ticker_time)]),
yaxis=dict(range=[min(self.last_price), max(self.last_price)]),)}
def Main():
api_key = ''
secret_key = ''
ticker = priceTicker(api_key, secret_key)
ticker.app.run_server(debug=True, host='127.0.0.1', port=16452)
if __name__ == '__main__':
#app.run_server(debug=False, host='127.0.0.1', port=10010)
Main()
Output is as expected with the app updating the last price every 2 sec.
Cheers
My goal here was to create a Bokeh widgetbox which you could use to provide input to a form, however the only widgets that allow me to get my inputs back from Bokeh's javascript side into Python variables are RadioButtonGroups. Otherwise, Multiselect, AutocompleteInput, and others are ONLY able to return my input if it's in the form of an integer. This is being done in a Jupyter NB using Python and Bokeh 0.13.0, if you happen to have any questions or I've left anything out please don't hesitate to let me know.
Please see code below:
import os, time, sys
from bokeh.layouts import widgetbox
from bokeh.models.widgets import RadioButtonGroup, Button, TextInput
from bokeh.models.widgets.inputs import MultiSelect
from bokeh.models.widgets import AutocompleteInput
from bokeh.models.callbacks import CustomJS
from bokeh.io import output_notebook, show
from bokeh.models import ColumnDataSource
from bokeh.models.widgets.markups import Div
from bokeh.resources import INLINE
output_notebook(resources=INLINE)
ipies=[str(i.strip('.ipynb')) for i in os.listdir() if i.endswith('.ipynb')]
minutes=0
hours=0
days=0
months=0
daysmonth=0
days=0
minute_options=[str(i) for i in range(1,60)]
minute_options.append("*")
minute_callback = CustomJS(args=dict(a=minute_options), code="""
var options=a;
console.log(options[parseInt(cb_obj.active)]);
var ind=options[parseInt(cb_obj.active)];
IPython.notebook.kernel.execute('minutes='+ind+'');
""")
minute_rbg = RadioButtonGroup(
labels=minute_options, callback=minute_callback, active=len(minute_options)-1)
min_title=Div(text="Which minute on the hour, * for every minute")
hour_options=[str(i) for i in range(24)]
hour_options.append("*")
hour_callback = CustomJS(args=dict(a=hour_options), code="""
var options=a;
console.log(options[parseInt(cb_obj.active)]);
var ind=options[parseInt(cb_obj.active)];
IPython.notebook.kernel.execute('hours='+ind+'');
""")
hour_rbg = RadioButtonGroup(
labels=hour_options,callback=hour_callback,active=len(hour_options)-1)
hour_title=Div(text="Which hour in a day, * for every hour")
day_month_options=[str(i) for i in range(1,32)]
day_month_options.append("*")
day_month_callback = CustomJS(args=dict(a=day_month_options), code="""
var options=a;
console.log(options[parseInt(cb_obj.active)]);
var ind=options[parseInt(cb_obj.active)];
IPython.notebook.kernel.execute('daysmonth='+ind+'');
""")
day_month_rbg = RadioButtonGroup(
labels=day_month_options,callback=day_month_callback,active=len(day_month_options)-1)
day_month_title=Div(text="Which day in a month, * for every day of the month")
month_options=[str(i) for i in range(1,13)]
month_options.append("*")
month_callback = CustomJS(args=dict(a=month_options), code="""
var options=a;
console.log(options[parseInt(cb_obj.active)]);
var ind=options[parseInt(cb_obj.active)];
IPython.notebook.kernel.execute('months='+ind+'');
""")
month_rbg = RadioButtonGroup(
labels=month_options, callback=month_callback,active=len(month_options)-1)
month_title=Div(text="Which month in a year, * for every month")
day_week_options=[str(i) for i in range(7)]
day_week_options.append("*")
day_week_callback = CustomJS(args=dict(a=day_week_options), code="""
var options=a;
console.log(options[parseInt(cb_obj.active)]);
var ind=options[parseInt(cb_obj.active)];
IPython.notebook.kernel.execute('days='+ind+'');
""")
day_week_rbg = RadioButtonGroup(
labels=day_week_options, callback=day_week_callback, active=len(day_week_options)-1)
days_week_title=Div(text="Which day in a week, * for every day of the week")
import pandas as pd
ipies_pd = pd.DataFrame({"a":ipies})
#Selection source: IPIES CDS
cds = ColumnDataSource(data=ipies_pd)
#Selection source: autocompleteinput & multiselect widgets
selection_callback = CustomJS(args=dict(source=cds), code="""
// THIS CALLBACK ONLY BRINGS BACK SELECTIONS IF THEY ARE INTEGERS
var cbobj = cb_obj.value;
IPython.notebook.kernel.execute("auto_c_value = "\"+ cbobj + "\");
""")
ms = MultiSelect(title="power", callback=selection_callback, options=ipies)
auto_c=AutocompleteInput(completions=ipies, height=100, callback=selection_callback)
button_callback = CustomJS(code="""
var cur_index = IPython.notebook.get_selected_index()+1;
var end_index=IPython.notebook.ncells();
IPython.notebook.execute_cell_range(IPython.notebook.get_selected_index()+1, IPython.notebook.ncells());
""")
button = Button(label="button~!", button_type="primary", callback=button_callback)
w_box = widgetbox(min_title, minute_rbg, hour_title, hour_rbg, day_month_title, day_month_rbg,
month_title, month_rbg,days_week_title, day_week_rbg, button,
ms, auto_c, width=800, sizing_mode='scale_both')
show(w_box)
I am trying to render a simple app on bokeh server. I took the script straight from the bokeh website.
# myapp.py
from random import random
from bokeh.layouts import column
from bokeh.models import Button
from bokeh.palettes import RdYlBu3
from bokeh.plotting import figure, curdoc
# create a plot and style its properties
p = figure(x_range=(0, 100), y_range=(0, 100), toolbar_location=None)
p.border_fill_color = 'black'
p.background_fill_color = 'black'
p.outline_line_color = None
p.grid.grid_line_color = None
# add a text renderer to our plot (no data yet)
r = p.text(x=[], y=[], text=[], text_color=[], text_font_size="20pt",
text_baseline="middle", text_align="center")
i = 0
ds = r.data_source
# create a callback that will add a number in a random location
def callback():
global i
# BEST PRACTICE --- update .data in one step with a new dict
new_data = dict()
new_data['x'] = ds.data['x'] + [random()*70 + 15]
new_data['y'] = ds.data['y'] + [random()*70 + 15]
new_data['text_color'] = ds.data['text_color'] + [RdYlBu3[i%3]]
new_data['text'] = ds.data['text'] + [str(i)]
ds.data = new_data
i = i + 1
# add a button widget and configure with the call back
button = Button(label="Press Me")
button.on_click(callback)
# put the button and plot in a layout and add to the document
curdoc().add_root(column(button, p))
I run this in my Win Python Command Prompt.exe using the below:
python -m bokeh serve --show main.py
This runs the app, but I am left with a blank webpage. See the output in the command prompt below:
Can anyone help with how to get this to show the doc?
I want to click on the loaded models and move them around. I used the code from chess sample examples and panda 3d tutorial without any success. Can someone figure out whats wrong with the code.
Thanks
from math import pi, sin, cos
import sys
from direct.showbase.ShowBase import ShowBase
import direct.directbase.DirectStart
from direct.task import Task
from panda3d.core import TextNode
from direct.gui.OnscreenText import OnscreenText
from panda3d.core import CollisionTraverser, CollisionNode
from panda3d.core import CollisionHandlerQueue, CollisionRay
from panda3d.core import Point3, Vec3, Vec4, BitMask32
from direct.showbase.DirectObject import DirectObject
from panda3d.core import AmbientLight, DirectionalLight, LightAttrib
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# quit when esc is pressed
self.accept('escape', sys.exit)
#base.disableMouse()
# load the box model
self.box = self.loader.loadModel("models/xbox")
self.box.reparentTo(camera)
self.box.setScale(2.0, 2.0, 2.0)
self.box.setPos(8, 50, 0)
self.keyMap = {
"w" :False ,
"s" :False,
"a": False,
"d": False,
"mouse1": False,
"mouse3": False,
}
# CollisionTraverser and a Collision Handler is set up
self.picker = CollisionTraverser()
self.pq = CollisionHandlerQueue()
self.pickerNode = CollisionNode('mouseRay')
self.pickerNP = camera.attachNewNode(self.pickerNode)
self.pickerNode.setFromCollideMask(BitMask32.bit(1))
self.box.setCollideMask(BitMask32.bit(1))
self.pickerRay = CollisionRay()
self.pickerNode.addSolid(self.pickerRay)
self.picker.addCollider(self.pickerNP, self.pq)
self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask')
self.accept("mouse1", self.setKey, ["mouse1", True])
def mouseTask(self,task):
# check if we have access to the mouse
if base.mouseWatcherNode.hasMouse():
# get the mouse position
mpos = base.mouseWatcherNode.getMouse()
# set the position of the ray based on the mouse position
self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
self.picker.traverse(render)
# if we have hit something sort the hits so that the closest is first and highlight the node
if self.pq.getNumEntries() > 0:
self.pq.sortEntries()
pickedObj = self.picker.getEntry(0).getIntoNodePath()
def setKey(self,key,value):
self.keyMap[key] = value
app = MyApp()
app.run()
I was just trying to do the same thing, when I found your question.
Thanks for your code, it help me to start!
I've manage to get it working :)
Just a remark: you use a task, with no return, this make the task run once.
You should have used: return task.cont
Anyway, here my working code for panda3d devel (1.8.0+):
import sys
from direct.showbase.ShowBase import ShowBase
from pandac.PandaModules import *
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# quit when esc is pressed
self.accept('escape',sys.exit)
#base.disableMouse()
# load the box model
box = self.loader.loadModel("models/box")
box.reparentTo(render)
box.setScale(2.0, 2.0, 2.0)
box.setPos(8, 50, 0)
panda = base.loader.loadModel("models/panda")
panda.reparentTo(render)
panda.setPos(0, 10, 0)
panda.setScale(0.1, 0.1, 0.1)
cNodePanda = panda.attachNewNode(CollisionNode('cnode_panda'))
cNodePanda.node().addSolid(CollisionSphere(0,0,5,5))
cNodePanda.show()
# CollisionTraverser and a Collision Handler is set up
self.picker = CollisionTraverser()
self.picker.showCollisions(render)
self.pq = CollisionHandlerQueue()
self.pickerNode = CollisionNode('mouseRay')
self.pickerNP = camera.attachNewNode(self.pickerNode)
self.pickerNode.setFromCollideMask(BitMask32.bit(1))
box.setCollideMask(BitMask32.bit(1))
panda.setCollideMask(BitMask32.bit(1))
self.pickerRay = CollisionRay()
self.pickerNode.addSolid(self.pickerRay)
self.picker.addCollider(self.pickerNP,self.pq)
self.accept("mouse1",self.mouseClick)
def mouseClick(self):
print('mouse click')
# check if we have access to the mouse
if base.mouseWatcherNode.hasMouse():
# get the mouse position
mpos = base.mouseWatcherNode.getMouse()
# set the position of the ray based on the mouse position
self.pickerRay.setFromLens(base.camNode,mpos.getX(),mpos.getY())
self.picker.traverse(render)
# if we have hit something sort the hits so that the closest is first and highlight the node
if self.pq.getNumEntries() > 0:
self.pq.sortEntries()
pickedObj = self.pq.getEntry(0).getIntoNodePath()
print('click on ' + pickedObj.getName())
app = MyApp()
app.run()