Bokeh: dashboard not updating axis range based on widget value - python-2.7

I am trying to implement an interactive dashboard in Bokeh with a "play" function that loops through all value pairs for two indicators selected by widgets.
Screen cap of dashboard
While the loop works, the dashboard resets the axis values for each step of the loop. So what I need is to set axis values based on the widget.value selected. To this end, I have built a data frame "ranges" that has the name of the indicator as index and the min/max value for each indicator as columns.
The updates for controls work thusly (x_axis,etc. are the names of the widgets):
controls = [x_axis, y_axis, start_yr, end_yr, years]
for control in controls:
control.on_change('value', lambda attr, old, new: update())
The update function is supposed to update the ranges upon change in the controls like this:
def update():
p.x_range = Range1d(start = ranges.loc[x_axis.value,"Min"],
end = ranges.loc[x_axis.value,"Max"])
p.y_range = Range1d(start = ranges.loc[y_axis.value,"Min"],
end = ranges.loc[y_axis.value,"Max"])
What should happen: Whenever I change the value of the widget, the ranges should update, but other than that, they should remain constant
What does happen: The ranges are set based on the value of the widget initially set and don't change on update.
I've tried to find examples trying to achieve something similar but no luck.

This is a working example:
import numpy as np
from bokeh.plotting import figure
from bokeh.models import Range1d
from bokeh.io import curdoc
x = np.linspace(0, 100, 1000)
y = np.sin(x)
p = figure(x_range=(0, 100))
p.circle(x, y)
def cb():
# this works:
p.x_range.start += 1
p.x_range.end += 1
# this also works:
#p.x_range = Range1d(p.x_range.start+1, p.x_range.end+1)
curdoc().add_periodic_callback(cb, 200)
curdoc().add_root(p)

Related

Bokeh tool tip displaying all the values for the line

I am trying to plot a line chart which includes tooltip, but the code below results in displaying all the values of the line in a tooltip instead displaying a single value for those co ordinates
#Import the library
import pandas
import itertools
import bokeh
import MySQLdb
from bokeh.plotting import figure, output_file, show
from bokeh.models import HoverTool
TOOLS='hover'
wells=['F1','F2','F3','F4','F5','F6','F7','F8','F9','F10','F11','F12','G1','G2','G3','G4','G5','G6','G7','G8','G9','G10','G11','G12']
p = figure(plot_width=800, plot_height=640,x_axis_type="datetime", tools=TOOLS)
p.title.text = 'Click on legend entries to hide the corresponding lines'
# Open database connection
db = MySQLdb.connect("localhost","user","password","db" )
#pallete for the lines
my_palette=bokeh.palettes.inferno(len(wells))
#create a statement to get the data
for name, color in zip(wells,my_palette):
stmnt='select date_time,col1,wells,test_value from db where wells="%s"'%(name)
#creating dataframe
df=pandas.read_sql(stmnt,con=db)
p.scatter(df['date_time'], df['test_value'], line_width=2, color=color, alpha=0.8, legend=name,)
#Inserting tool tip
hover = p.select(dict(type=HoverTool))
hover.tooltips = [("Wells","#wells"),("Date","#%s"%(df['date_time'])),("Values","#%s"%(df['test_value']))]
hover.mode = 'mouse'
#Adding a legend
p.legend.location = "top_right"
output_file("interactive_legend.html", title="interactive_legend.py example")
show(p)
Given below is the resultant screenshot
I am trying to get only one well,Date_time,Test_value at given mouse over instance
This code:
hover.tooltips = [
("Wells","#wells"),
("Date","#%s"%(df['date_time'])),
("Values","#%s"%(df['test_value']))
]
Does not do what you think. Let's suppose df['date_time'] has the value [10, 20, 30, 40]. Then after your string substitution, your tooltip looks like:
("Date", "#[10, 20, 30, 40]")
Which exactly explains what you are seeing. The #[10 part looks for a column named "[10" in your ColumnDataSource (because of the # in front). There isn't a column with that name, so the tooltip prints ??? to indicate it can't find data to look up. The rest 20, 30, 40 is just plain text, so it gets printed as-is. In your code, you are actually passing a Pandas series and not a list, so the string substitution also prints the Name and dtype info in the tooltip text as well.
Since you are passing sequence literals to scatter, it creates a Column Data Source for you, and the default names in the CDS it are 'x' and 'y'. My best guess, is that you actually want:
hover.tooltips = [
("Wells","#wells"),
("Date","#x"),
("Values","#y")
]
But note that you would want to do this outside the loop. As it is you are simply modifying the same hover tool over and over.

Get widget name of label in frame using mouse

Win7 SP1, Python 2.7,Tkinter.
I make a frame, and I place an array of labels within. The array is 10w x 24h.
I want to click on one of these labels and return the name of the label so I may alter it's variable.
I know how to do using a listbox, but how to do using just 'label in a frame'?
Thanks, Mark.
You don't need the label name, and besides labels don't have useful names. When the event fires you are given a reference to the widget, which you can use to query or modify the widget attributes.
Here's an example of how you can change a label by clicking on it. Run the program, and then click on any label as often as you want.
import Tkinter as tk
import time
def on_click(event):
now = time.strftime("%H:%M:%S")
event.widget.configure(text="you clicked me at %s" % now)
root = tk.Tk()
for row in range(4):
for col in range(4):
label = tk.Label(root, width=25, borderwidth=1, relief="sunken")
label.grid(row=row, column=col, padx=2, pady=2)
label.bind("<1>", on_click)
root.mainloop()

Updating several text objects in matplotlib simultaneously

I'm trying to animate several text objects in matplotlib at the same time. I have a 3-D numpy array (arrow_data) of data which stores the index of a unicode arrow which I need to plot (text for all unicode arrows are stored in a list arrows). The first and second dimension of this np.array indicate the location on a grid where this arrow needs to be plotted and the 3rd dimension is the 'time' dimension over which I need to update the plot (ie the arrows change through time over the 3d dimension of the array).
Below is the code I have for animating these arrows through 'time' using a loop, but I don't see how I can make a collection of text objects, as I can with other matplotlib functions like scatter, and then collectively (and efficiently) update the set_text property of each of them. The loop over the 'time' variable is fine but I'd prefer to not use a loop over the grid of text objects if possible.
Any thoughts on how to do this without the double nested loop?
Thanks.
from matplotlib import pyplot as plt
import numpy as np
import time
import matplotlib
matplotlib.interactive(True)
arrows = [u'\u2190', u'\u2196', u'\u2191', u'\u2197', u'\u2192', u'\u2198', u'\u2193', u'\u2199']
rest_time = 0.25
steps = 20
fig, ax = plt.subplots(1, 1)
ax.plot([0,4],[0,4], c = 'white')
objs = [[[] for i in range(4)] for j in range(4)]
for i in range(4):
for j in range(4):
objs[i][j] = ax.text(i+0.5,j+0.5, arrows[i], ha = 'center', va = 'center')
fig.canvas.draw()
arrow_data = np.random.randint(0,len(arrows), (4,4,steps))
for t in range(arrow_data.shape[2]):
for i in range(4):
for j in range(4):
objs[i][j].set_text(arrows[arrow_data[i,j,t]])
fig.canvas.draw()
time.sleep(rest_time)

wxPython Reload a grid in a panel

I've got several classes so I prefer not to paste any code here (if that's possible :P)
The problem:
I've created a class which creates a frame, and this frame contains a panel.
In another class I've stored all my settings.
On the panel are several sizers and among other attributes, it has a grid.
This grid is build from sequence 1 on the x axis and sequence y on the y axis.
To create my panel, I've divided my code into sections (like buildLeft() buildRight() buildTopRight() and so on which are linked to a main sizer in the buildFrame() method).
My grid is created in the buildTopRight() section of this class. It creates the grid by retrieving the values for sequence1 and sequence2 from the settings object and creates a grid of the length of this sequence accordingly. After this is done, the grid is bound to the sizer for the topRight section.
I also have a dropdown list (wx.Choice). If i select another option from this list, I want to remove an item from my sequence 1 and sequence 2.
The code to do this already works, and the data in my settings object changes accordingly.
However, I'm not able to reload the matrix, since if i call the buildTopRight() method again, the matrix is recreated and cropped to the topleft side of my screen, while leaving the old matrix in place.
Please help.
On request, this is the code for building my panel:
# import modules
import wx
import wx.grid
import matrixSettings as ms
# Panel class
class ResultatenPanel(wx.Panel):
def __init__(self, parent, id):
wx.Panel.__init__(self, parent, id, style=wx.BORDER_SUNKEN)
# link settings object
self.matSet = self.GetGrandParent().ms
# build the main sizers
self.sizerMain = wx.BoxSizer(wx.HORIZONTAL)
self.sizerMenu = wx.BoxSizer(wx.VERTICAL)
self.sizerRight = wx.BoxSizer(wx.VERTICAL)
self.sizerTopRight = wx.BoxSizer(wx.HORIZONTAL)
self.sizerBotRight = wx.BoxSizer(wx.HORIZONTAL)
# make individual parts
self.buildMenu()
self.buildTopRight()
self.buildBotRight()
self.buildRight()
# build total frame
self.buildFrame()
build right code (includes top and bottom right bits):
def buildRight(self):
self.sizerRight.Add(self.sizerTopRight, 5)
self.sizerRight.Add(self.sizerBotRight, 2)
the code to build the frame:
def buildFrame(self):
self.sizerMain.Add(self.sizerMenu, 1)
self.sizerMain.Add(self.sizerRight, 5, wx.EXPAND)
self.SetSizer(self.sizerMain)
top right code:
def buildTopRight(self):
self.grid = wx.grid.Grid(self)
print "buildTopRight called"
if self.matSet.getAlgoritme() == "Needleman-Wunsch":
self.matSet.setSeq1("-" + self.matSet.getSeq1())
self.matSet.setSeq2("-" + self.matSet.getSeq2())
# set grid
self.grid.CreateGrid(len(self.matSet.getSeq2()), len(self.matSet.getSeq1()))
self.grid.SetRowLabelSize(25)
self.grid.DisableDragColSize()
self.grid.DisableDragRowSize()
# set the grid proportions accurately
for x in range(0, len(self.matSet.getSeq1())):
# set the grid proportions accurately
for y in range(0, len(self.matSet.getSeq2())):
self.grid.SetRowSize(y, 25)
self.grid.SetRowLabelValue(y, self.matSet.getSeq2()[y].upper())
self.grid.SetCellValue(y, x, "0")
self.grid.SetReadOnly(y, x, True)
self.grid.SetColSize(x, 25)
self.grid.SetColLabelValue(x, self.matSet.getSeq1()[x].upper())
newly added:
self.sizerTopRight.Clear()
self.sizerTopRight.Add(self.grid, 1)
self.Update()
self.Layout()
I know this is a not very memory friendly solution.
You should hide the current grid and create a new one:
self.sizerTopRight.Hide(self.grid)
self.sizerTopRight.Add(self.new_grid, 1)
self.sizerTopRight.Show(self.new_grid)
self.Layout()

How to use grid_forget to eliminate a specific ordering of buttons after they have been instantiated

I am building the GUI for a boardgame for my software engineering class. I am using the TKinter toolkit on Python 2.7 (windows). I am stuck right now because I cant seem to find a way to ignore/forget a certain ordering of buttons. Essentially, I am trying to create a grid of buttons that would represent my game board. And right now, I have a game board that has a total of 49 buttons on a 7x7 grid.
So far this is what I have been able to do:
Instantiate all my button objects where columns = x and rows = y. This easily build a grid of x*y
I then place each button into a list (lets call this list1)
I want to use my list of button objects to ignore/forget/delete (for lack of a better description) certain buttons. I am thinking I can create a 2nd list (list2) of the indexes of the button objects that I want to use grid_forget on and then compare my two lists and only keep the ones that are not in list2. Unfortunately, this doesnt work out the way I want it to. Here is the code:
gameboard = ttk.Labelframe(root, padding = (8,8,8,8), text = "Gameboard",
relief = "sunken")
#forgetButtons will not be displayed on the game board b/c they do not have a
#label (they are not a: room, hallway, starting space)
forgetButtons = [0,1,3,5,6,7,13,14,16,18,21,30,32,41,42,43,45,46,47,48]
#this list tracks all the buttons on the gameboard
myButtons=[]
count = 0
for x in range(7): #build a 7x7 grid of buttons (49 buttons total)
for y in range(7):
btn = Button(gameboard, width=7, height=4)
myButtons.append(btn)
btn.grid(column=x, row=y, padx = 3, pady = 3)
#do some comparison here between the two lists
#to weed out the buttons found in forgetButtons
#**or maybe it should not be done here?**
btn.config(text="Room%d\none\ntwo\nfour\nfive" % x)
You don't need grid_forget these widgets if you just don't create them.
import itertools
import Tkinter as tk
root = tk.Tk()
forgetButtons = [0,1,3,5,6,7,13,14,16,18,21,30,32,41,42,43,45,46,47,48]
myButtons = []
for x, y in itertools.product(range(7), repeat=2):
if not x*7 + y in forgetButtons:
btn = tk.Button(root, width=7, height=4, text="Room%d\none\ntwo\nfour\nfive" % x)
btn.grid(column=x, row=y, padx=3, pady=3)
myButtons.append(btn)
root.mainloop()
I don't know the order to calculate the position for forgetButtons (usually the first index represents the row and the second one the column), but you can easily switch it.