Dynamically change the shape of bokeh Figure - python-2.7

I am building a web app that will display images as part of a data analysis pipeline. For this, I need to dynamically change the width and height of a Figure object in bokeh.
With the following code, the shape of the Figure is changed, but the change only takes effect after I resize my browser window, even if the browser window resize is ever so small.
import bokeh.plotting
import bokeh.models
import bokeh.layouts
# set up the interface
fig1 = bokeh.plotting.figure()
button = bokeh.models.Button(label='scramble')
# define a callback and connect it
def callback():
fig1.width = int(fig1.width * .8)
button.on_click(callback)
# add everything to the document
bokeh.plotting.curdoc().add_root(bokeh.layouts.column(button, fig1))
Is there some update method which I need to run? I have read about "next tick callbacks" but I don't understand if that is relevant.
The above behavior occurs both with firefox and chromium on my gnome system.

The reason this is happening is because the layout is not getting updated. Although your code changes the figure's property value you have to recompute all values in the Document solver for an actual resize to happen.
Here is the line in BokehJS where the resize hook happens:
https://github.com/bokeh/bokeh/blob/master/bokehjs/src/coffee/document.coffee#L92
After resize is called at the document level, resize objects re-render:
https://github.com/bokeh/bokeh/blob/master/bokehjs/src/coffee/models/layouts/layout_dom.coffee#L61
The problem is that there's not currently, to the best of my knowledge, an exposed way to re-trigger the document resize event.
However you can do it client side. Here's working code using CustomJS:
test.py
from bokeh.io import show
from bokeh.layouts import column
from bokeh.models import Button, CustomJS
from bokeh.plotting import figure
fig = figure()
button = Button(label='scramble')
button.callback = CustomJS(args=dict(fig=fig), code="""
var old_width = fig.width;
var doc = fig.document;
fig.width = old_width * 0.8;
doc.resize();
""")
col = column(button, fig)
show(col)
This can be run with python test.py.
Note you could also do this with bokeh server replacing the last line show(col) with curdoc().add_root(col), but I didn't do that to emphasize that this is a client-side solution.

There is a way to dynamically resize bokeh charts with built in functionality. For example,
fig = plotting.figure(width=1200, height=900, title="Dynamic plot".format(chartType), sizing_mode='scale_width')
The key option being sizing_mode='scale_width'
The width and height commands serve as initial values. There are other options for sizing_mode so I would look into that.

Related

Bokeh + Holoviews + Datashader on Django

We are trying to build a web app--Dashboard-- to show different interactive(including click callback, fetch new data etc) charts with Bokeh + Holoviews + Datashader on DJango.
Since data is very large and could have 10+ million points we are using datashader. We can have a static html from backend from Bokeh + Holoviews + Datashader from Backend and pass it to front end using Django REST api as :
views.py
import numpy as np
import holoviews as hv
import datashader as ds
from dask import dataframe as dd
from bokeh.io import show, curdoc
from bokeh.layouts import layout
from bokeh.models import Slider, Button
from holoviews.operation.datashader import datashade
renderer = hv.renderer('bokeh').instance(mode='server')
def home(request):
def plot_info(y_col):
from vaex import dataframe as datafm
df_dask = dd.read_parquet(r"C:\Dropbox\1mln.parquet", engine='pyarrow',
columns=['nest11', 'nest21', 'first_element', 'second_element', 'timestamp'])
df_dask['timestamp'] = dd.to_datetime(df_dask.timestamp, unit='ns')
return hv.Curve((df_dask['timestamp'], df_dask[y_col]))
def bearer():
stream = hv.streams.Stream.define('y-axis', y_col="nest11")()
dmap = hv.DynamicMap(plot_info, streams=[stream])
vmap = datashade(dmap).opts(width=1200, height=600, responsive=True)
html = renderer.static_html(vmap)
return html
context = {
'seq_num': bearer(),
}
return render(request, 'home/welcome.html', context)
Works fine. However Since we used Datashader, data is aggregated and converted in static html when we zoom in we would not get the data which we are looking for at from end side. For that, my guess is we need Bokeh server.
My doubts are :(since use of Datashader is must for large dataset)
How can i use Bokeh server along with Django REST apis ? Also i want to have a customized html page at front end so i am using Django template.
Is there an alternative to Django for REST apis development with Bokeh + Datashader ?
Does Bokeh support REST APIs ? how ? pls share some examples of REST APIs and callbacks ? for example I've a Dashboard and when i click one chart, I should get more details about the chart and play around those charts in dashboard ? dropdown etc
I would strongly suggest using Panel which is built on top of Bokeh and supports HoloViews. For Django integration have a look at these docs.
/ 3. The Bokeh server is built on Tornado, which means it can be easily extended, e.g. in the next release of Panel (0.10) you will be able to easily register custom REST APIs to be served alongside your app. There aren't any examples yet since it's not released but I'll be working on a few examples in time for the next release which is due in about two weeks.

Tkinter: Text in Frame in Nootbook tab not painted

OS is Win7 64bit, Python is 2.7.16 64bit. I have a simple Tkinter GUI: Root containing a Notebook with two tabs. First tab contains a Frame which contains a Button. Second tab contains a Frame which contains a Text. The command bound to the Button spawns a thread with sets the content of the Text.
import Tkinter
import ttk
import threading
r = Tkinter.Tk()
n = ttk.Notebook(r)
n.pack(expand=1, fill="both")
control = ttk.Frame(n)
info = ttk.Frame(n)
tInfo = Tkinter.Text(info)
tInfo.pack(expand=1, fill="both")
n.add(control, text='Control')
n.add(info, text='Info')
infoMutex = threading.Lock()
def doGuiTest():
try:
infoMutex.acquire()
tInfo.config(state='normal')
tInfo.delete('1.0', Tkinter.END)
tInfo.insert(Tkinter.END, 'Test')
tInfo.config(state='disabled')
finally:
infoMutex.release()
def workerThread():
doGuiTest()
def execute():
worker=threading.Thread(target=workerThread)
worker.start()
bExecute=Tkinter.Button(control, text='Execute', command=execute)
bExecute.pack()
r.mainloop()
Expected result: The Text is reliably visible with the set content after the Button is clicked.
Actual result: The Text is only visible when the tab containing the Text has been manually brought to the foreground before the Button is clicked.
When I set the content of the Text directly from the Button's command everything works as expected. Sadly, in the real application I am working on the functionality triggered by the Button will be running for several minutes so using another thread is a must.
What am I missing to achieve a consistent behavior?

Tkinter - Multiple windows opening

Like the title says I'm having a problem with multiple windows being able to open. New window (same window) every time I click the button. I haven't been able to find an answer to this specific scenario. I've even tried disabling the button after opening window (which led to the problem of re-enabling).
Below is enough code to work with, you can see the problem by clicking the button a few times. I am hoping for a somewhat easy solution as I am fairly new to Tkinter. Also, the smaller window needs to resize (expand) with the main window, so I dont think a Toplevel window would work.
There may be others having this same problem, and thanks in advance!
Note: I'm using Python 2.7 (Tkinter)
#!/usr/bin/python
import os
import sys
import Tkinter as tk
from Tkinter import *
from ScrolledText import *
import tkFileDialog
import tkMessageBox
# Main
root = tk.Tk(className = "tex")
root.geometry("500x300")
root.title("tex")
tex = ScrolledText(root, padx=2, pady=2, undo=True, font=('Arial 11'))
def note_area():
btn_frame = Frame()
note = LabelFrame(tex, bd=1, relief='ridge')
tx = Text(note, width=18, relief='flat', padx=2, pady=2)
tx.insert('1.0', "Notes..")
tx.pack(side='top', fill=BOTH, expand=True)
note.pack(side='right', fill=Y)
btn_frame.pack(side='bottom', fill=Y)
# ToolBar Button (should only open one instance of note_area)
toolbar = Frame(root, bd=2, relief='groove')
b4 = Button(toolbar, text="Notes", width=4, command=note_area)
b4.pack(side=RIGHT, padx=4, pady=2)
toolbar.pack(side=TOP, fill=X)
tex.pack(fill="both", expand=True)
root.mainloop()
You just need make up a variable to keep track of if you have a note window open or not.
tex.notes_open = False
def note_area():
if tex.notes_open:
return # abort the function, notes already open
else:
tex.notes_open = True # set the flag for the next time
# rest of your code

which widget is used to sort column-wise data in tkinter GUI Python 2.7

I have written a GUI application in Python 2.7 in which many data are there to show in a column-wise. I just wanna to know that which widget should i use for shorting data column-wise so that the sorting widget will look good :-D or Which widget is used to sort column-wise data in tkinter GUI Python 2.7 ?
The below script demonstrates that you can get a callback from a Label widget if you wanted to use that instead.
from tkinter import *
root = Tk()
label = Label(root, text="Click me")
label.pack()
def callback(event):
print("You did it!")
label.bind("<Button-1>", callback)
root.mainloop()
The same logic can be applied to any widget, if you wanted to use a Listbox as a "button" (for some ungodly reason) for example, that's also possible with the same logic.

Can't remove matplotlib's padding around imshow() figure

I'm embedding matplotlib into my PyQt4 GUI and I'm having a heck of a time. I can get the image to display but it adds a very thick padding around the content that I'd like to remove. Here's what I'm doing:
from PyQt4.QtCore import *
from PyQt.QtGui import *
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4Agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.image as mpImage
import matplotlib.pyplot as plt
class MatPlotLibImage(FigureCanvas):
def __init__(self):
super(MatPlotLibImage, self).__init__(self.fig)
self.axes = self.fig.add_subplot(111)
def LoadImage():
image = mpImage.imread("myImage.png")
imgplot = self.axes.imshow(image, interpolation="nearest")
# plt.axis("Off") -> Doesn't do anything as far as I can tell
imgplot.axes.set_axis_off() # Gets rid of frame
imgplot.axes.get_xaxis().set_visible(False) # Turn off x-axis
imgplot.axes.get_yaxis().set_visible(False) # Turn off y-axis
If I add this widget to a QDockWidget I get the following result:
As you can see it renders with a large white padding around the content. I cannot seem to remove this and everything I'm turning up online is focused on removing the padding when saving the image, not displaying. Does anyone know how to remove this padding at display time? Thanks in advance.
You may use subplots_adjust to get rid of the margins. I.e.
self.fig.subplots_adjust(bottom=0, top=1, left=0, right=1)
This will tell the figure not to use any margins around its child axes. You may then still get some white space to one direction, which is due to the canvas aspect ratio not being the same as the image aspect. However, I think that you don't want to change the image aspect and so this remaining margin would acutally be desired.