2

I'm trying to create a simple bokeh server application that allows a user to load a file from a <input type="file"> file selection button. The app will then plot the data from the file that the user selected. The code below is very simplistic, and I simply don't know how to pass the file information from the file selector to python. I need to use python to handle the file I/O and not html or javascript.

I can get it to work just fine when I run bokeh serve --show example.py path/to/input_file at the command line, but I don't want the user to specify this each time. I need them to be able to click a button to "upload" the file. This application is running locally, so there is no uploading to a server or anything like that.

Is there a better method than <input type="file"> ?

from bokeh.plotting import figure
from bokeh.layouts import layout
from bokeh.models import ColumnDataSource, Div
from bokeh.io import curdoc

desc = Div(text="""
<h1>A simple example</h1>
<input type="file">
<br />""", width=800)

# Create Column Data Source that will be used by the plot
source = ColumnDataSource(data=dict(x=[], y=[]))

p = figure(plot_height=550, plot_width=800, title="", toolbar_location='above')
p.line(x="x", y="y", source=source)

def update():
    x_data,y_data = read_file_data(input_file_name) # function to read specific file type
    source.data = dict(
        x=x_data,
        y=y_data,
    )

sizing_mode = 'fixed'  # 'scale_width' also looks nice with this example
l = layout([
    [desc],
    [p],
], sizing_mode=sizing_mode)

update()
curdoc().add_root(l)
curdoc().title = "Sample"
smillerc
  • 158
  • 5
  • 16

2 Answers2

6

Maintainer note: CoffeeScript support is deprecated in Bokeh and will be removed entirely in Bokeh 2.0. This example would need to be re-written in JavaScript or TypeScript

As of Bokeh 0.12.4, there's no built-in file chooser input widget. But it is possible to create new extensions to Bokeh that work as seamlessly as the built-in widgets to connect JS events to Python.

The code below is a super-rough implementation of a model that wraps an <input type="file"> to hook it up to Python code. This code should work with Bokeh 0.12.4 and newer.

from bokeh.core.properties import String
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Button, LayoutDOM

IMPL = """
import * as p from "core/properties"
import {LayoutDOM, LayoutDOMView} from "models/layouts/layout_dom"

export class FileInputView extends LayoutDOMView
  initialize: (options) ->
    super(options)
    input = document.createElement("input")
    input.type = "file"
    input.onchange = () =>
      @model.value = input.value
    @el.appendChild(input)

export class FileInput extends LayoutDOM
  default_view: FileInputView
  type: "FileInput"
  @define {
    value: [ p.String ]
  }
"""

class FileInput(LayoutDOM):
    __implementation__ = IMPL
    value = String()

input = FileInput()

def upload():
    print(input.value)
button = Button(label="Upload")
button.on_click(upload)

curdoc().add_root(column(input, button))

This results in the output below:

enter image description here

There are almost certainly improvements that could be made to this. SO is not really a good place for iterative and collaborative discussion, so if you have questions about improving this, I'd suggest the project Discourse list as the best place to continue.

bigreddot
  • 33,642
  • 5
  • 69
  • 122
0

I faced the same task (pass files to Bokeh widget) but with some other restrictions (Tornado with embedded Bokeh server). So the code below is not the exact solution but it can help:

Tornado HTTP web page with embedded Bokeh widget which communicates with other page of the same application https://gist.github.com/Sklavit/c378a18661f0d91918931eba5a1d7553

Sklavit
  • 2,225
  • 23
  • 29