IPython notebook widgets using interactive - widget

I'm having trouble creating widgets in a Jupyter notebook that update when other widget values are changed. This is the code I've been playing around with:
from ipywidgets import interact, interactive, fixed
import ipywidgets as widgets
from IPython.display import display
def func(arg1,arg2):
print arg1
print arg2
choice = widgets.ToggleButtons(description='Choice:',options=['A','B'])
display(choice)
metric = widgets.Dropdown(options=['mercury','venus','earth'],description='Planets:')
text = widgets.Text(description='Text:')
a = interactive(func,
arg1=metric,
arg2=text,
__manual=True)
def update(*args):
if choice.value == 'A':
metric = widgets.Dropdown(options=['mercury','venus','earth'],description='Planets:')
text = widgets.Text(description='Text:')
a.children = (metric,text)
else:
metric = widgets.Dropdown(options=['monday','tuesday','wednesday'],description='Days:')
text2 = widgets.Textarea(description='Text2:')
a.children = (metric,text2)
choice.observe(update,'value')
display(a)
The resulting widgets metric and text do change based whether A or B is selected, but the problem is that the "Run func" button goes away as soon as I change to B. I've tried adding the __manual attribute immediately before display(a), adding it within update, and several other places. How do I change the children of the widget box without overwriting the fact that I want to manually run the function?

Related

How do I repeat a function in a for loop (with multiple entries and listboxes)?

I've created multiple entry boxes and listboxes with one code, so I have three entry boxes and three listboxes next to eachother (used a for loop for this).
But I also made a function that the listbox will be automatically updated to show words that look like what I'm typing. So if I start typing 'horse' in the entry box, I only get sentences with 'horse' in the listbox. However, the function only works on one listbox. So although I have three entry boxes and three listboxes, the functions in my code will only be executed once (in one entry/listbox).
The functions are 'check' and 'fillout'.
I think I need to work with lamdba? Or with .bind(<'Return'>)? I tried both ways, but it just doesn't work out (and I don't understand lambda). How can I repeat not only the entry- and listbox, but also the define function in it?
My code:
import tkinter as tk
from tkinter import Listbox
from tkinter import *
interface = tk.Tk()
def update(data):
my_list.delete(0, END)
for item in data:
my_list.insert(END, item)
def check(e):
typed = entry.get()
if typed == '':
data = alist
else:
data = []
for item in alist:
if typed.lower() in item.lower():
data.append(item)
update(data)
def fillout(e):
entry.delete(0, END)
entry.insert(0, my_list.get(ACTIVE))
for x in range(3):
entry = Entry(interface, width=53)
entry.bind('<KeyRelease>', check)
#?entry.bind('<Return>', check)
entry.grid(row=0, column=x, pady=20, padx=5)
my_list: Listbox = Listbox(interface, height=20, width=50)
my_list.bind("<<ListboxSelect>>", fillout)
my_list.grid(row=3, column=x, pady=20, padx=5)
update(alist)
interface.mainloop()

In Gtk, how to make a window smaller when creating

I am trying to display both an image and a box with an Entry widget. I can do that, but the window is so large that the widget at the bottom is mostly out of view. I have tried several calls to set the window's size or unmaximize it, but they seem to have no effect. I determined that the problem only occurs when the image is large, but still wonder how to display a large image in a resizable window or, for that matter, to make any changes to the window's geometry from code. All the function call I tried seem to have no effect.
Here is my code:
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import GdkPixbuf
from urllib.request import urlopen
class Display(object):
def __init__(self):
self.window = Gtk.Window()
self.window.connect('destroy', self.destroy)
self.window.set_border_width(10)
# a box underneath would be added every time you do
# vbox.pack_start(new_widget)
vbox = Gtk.VBox()
self.image = Gtk.Image()
response = urlopen('http://1.bp.blogspot.com/-e-rzcjuCpk8/T3H-mSry7PI/AAAAAAAAOrc/Z3XrqSQNrSA/s1600/rubberDuck.jpg').read()
pbuf = GdkPixbuf.PixbufLoader()
pbuf.write(response)
pbuf.close()
self.image.set_from_pixbuf(pbuf.get_pixbuf())
self.window.add(vbox)
vbox.pack_start(self.image, False, False, 0)
self.entry = Gtk.Entry()
vbox.pack_start(self.entry, True,True, 0)
self.image.show()
self.window.show_all()
def main(self):
Gtk.main()
def destroy(self, widget, data=None):
Gtk.main_quit()
a=Display()
a.main()
Most of the posted information seems to pertain to Gtk2 rather than Gtk3, but there is a solution: to use a pix buf loader and set the size:
from gi.repository import Gtk, Gdk, GdkPixbuf
#more stuff
path = config['DEFAULT']['datasets']+'working.png'
with open(path,'rb') as f:
pixels = f.read()
loader = GdkPixbuf.PixbufLoader()
loader.set_size(400,400)
loader.write(pixels)
pb = GdkPixbuf.Pixbuf.new_from_file(path)
self.main_image.set_from_pixbuf(loader.get_pixbuf())
loader.close()

Bokeh: Link Hover tooltips geometrically to subplots

I have multiple categorical heatmap plots that are in a single display that have identical shapes and x,y coordinates. When hovering on any of the subplots I would like the inspection on one plot to trigger a new inspection on all other plots in the grid and display multiple tooltips simultaneously.
I have researched this topic and found similar posts such as:
Bokeh: Synchronizing hover tooltips in linked plots
Takeaway from link above: There are 2 suggested answers to this question, which attempt to mimic hover tooltips with text glyphs, however these implementations are not successful when I copy and run the code on my own computer (the graphs display correctly but the hover text glyphs don't appear). I assume this could be because of Bokeh API updates, but I am unsure. My reputation doesn't allow comments or I'd address this issue there.
Coordinate tooltips across multiple plots #1547
Takeaway from link above: There is no reproducible data so I am not able to recreate the plot listed here, however bryevdv summarizes what I am trying to do quite efficiently which I'll quote below:
Link on geometry. You might want the geometry of the inspection on one plot to trigger a completely new inspection (using that same geometry) on another plot. So if the cursor is at (10.5, 7) on one plot, then the additional plots do a hit test at (10.5, 7) and if there are glyphs that have any hovers a that point, then a hover gets drawn there.
I have created some generalized data to illustrate my problem:
from bokeh.io import show, output_notebook
from bokeh.layouts import gridplot
from bokeh.models import LinearColorMapper, HoverTool
from bokeh.plotting import figure, show, output_file
from bokeh.transform import transform
import numpy as np
import pandas as pd
data1 = [['A','A',100], ['A','B',175], ['B','A',75], ['B','B',200]]
data2 = [['A','A',25], ['A','B',100], ['B','A',50], ['B','B',75]]
data3 = [['A','A',150], ['A','B',75], ['B','A',25], ['B','B',125]]
df1 = pd.DataFrame(data1, columns = ['Left','Right','Value'])
df2 = pd.DataFrame(data2, columns = ['Left','Right','Value'])
df3 = pd.DataFrame(data3, columns = ['Left','Right','Value'])
def heatmap(df, title):
letters = ['A','B']
mapper = LinearColorMapper(palette=['#225ea8', '#41b6c4', '#a1dab4', '#ffffcc'], low=0, high=200)
TOOLS = 'reset'
p = figure(plot_width=255, plot_height=250, title=title,
x_range=letters,
y_range=list(reversed(letters)), x_axis_location='above',
tools=TOOLS, toolbar_location='below')
p.grid.grid_line_color = None
p.grid.grid_line_width = 0.5
p.axis.axis_line_color = None
p.axis.major_tick_line_color = None
p.axis.major_label_text_font_size = '9pt'
p.axis.major_label_standoff = 0
p.xaxis.major_label_orientation = 0
hover = HoverTool()
p.rect(x='Right', y='Left', width=1, height=1, line_color=None, source=df,
fill_color={'field': 'Value', 'transform': mapper})
hover.tooltips = [('Group','#Left #Right'), ('Value','#Value')]
p.tools.append(hover)
return p
output_notebook()
p1 = heatmap(df1, 'Plot 1')
p2 = heatmap(df2, 'Plot 2')
p3 = heatmap(df3, 'Plot 3')
grid = gridplot([[p1,p2,p3]])
show(grid)
Output:
My goal is to be able to observe the values across multiple plots at one time without having to be directed to another page or source, so I am open to alternative ways of doing this that doesn't involve hover tooltips. Thanks!

Change button name on ipywidgets

Is there a way to easily change the button name in the ipywidgets module? I am using the decorator, but cannot find in the documentation how to change the name to something other than "Run Interact". I believe I need to use the decorator since my function needs to be run on demand and depends on multiple inputs from different widgets, but I'm open to other ways of doing so as well.
import ipywidgets as widgets
from IPython.display import display
#widgets.interact_manual(number1 = widgets.Dropdown(
options=[1,2],
description='select a number'),
number2 = widgets.Dropdown(
options=[3,4],
description='select another number'))
def add_numbers(number1,number2):
return number1+number2
A bit quick and dirty but you can set
widgets.interact_manual.opts['manual_name'] = 'Your text here'
before defining your function and this should change the label name. If you have multiple interact_manual calls that need different labels you will need to change it each time.
import ipywidgets as widgets
from IPython.display import display
widgets.interact_manual.opts['manual_name'] = 'Your text here'
#widgets.interact_manual(number1 = widgets.Dropdown(
options=[1,2],
description='select a number'),
number2 = widgets.Dropdown(
options=[3,4],
description='select another number'),)
def add_numbers(number1,number2):
return number1+number2

How to left align widget label in iPython 3?

How can I left align, rather than right align, the label in an iPython 3 widget, such as IntSlider? My ultimate goal is to left align a set of labeled widgets. This entails left aligning their labels, since the label is the leftmost element of each widget.
I've read the discussion in
Aligning TextBox Widgets in IPython Notebooks, but (a) it focuses on making more space for a right-aligned label, and (b) the proposed solution does not seem to affect the label width. (As an aside, I would be interested in finding cell-executable code that can reset the minimum label width.)
I've also read the discussion in Change the size of the label in an IPython notebook widget, but it doesn't seem to offer a simple solution.
Thanks for your help.
Addendum (2015-06-02):
Looks like widget.interactive() does not play nicely with the solution suggested by Jakob. Example:
from IPython.html import widgets
from IPython.display import display
def mySlider(text='', twidth=100, min=0, max=10, value=5):
c1 = widgets.HBox()
ints = widgets.IntSlider(min=min, max=max, value=value)
text = widgets.HTML(text, width=twidth)
c1.children = (text, ints)
return c1
s1 = mySlider('Test')
s2 = mySlider('TestTest')
s3 = mySlider('TestTestTest')
def process(a, b, c):
print([a, b, c])
widgets.interactive(
process,
a=s1.children[1].value,
b=s2.children[1].value,
c=s3.children[1].value
)
yields slider labels a, b, c with the usual alignment.
You can simply combine an IntSlider with an Html widget to create your custom widget like
from IPython.html import widgets
from IPython.display import display
def mySlider(text='', twidth=100):
c1 = widgets.HBox()
ints = widgets.IntSlider()
text = widgets.HTML(text, width=twidth)
c1.children = (text, ints)
return c1
Using this method, some widgets could look like
s1 = mySlider('Test')
s2 = mySlider('TestTest')
s3 = mySlider('TestTestTest')
display(s1,s2,s3)
Update to work with interact
To use these custom widgets with interact it is necessary to add some properties and callbacks. The interact method requires the widget.description and the widget.value arguments to setup the interactive widgets. As our container widget does not have these arguments, they are added manually. Moreover it is necessary to link the container.value with the IntSlider.value. This is once realized by a simple assignment, and more important via the on_trait_change method.
Finally, the interact methods calls the process function on widget.on_trait_change callbacks, thus the container.on_trait_change method is replaced by the IntSlider.on_trait_change call.
The updated code looks like:
def mySlider2(text='', twidth=100, min=0, max=10, value=5):
c1 = widgets.HBox()
ints = widgets.IntSlider(min=min, max=max, value=value)
text = widgets.HTML(text, width=twidth)
c1.children = (text, ints)
c1.description = text
c1.value = ints.value
def update(name, value):
c1.value = value
ints.on_trait_change(update,'value')
c1.on_trait_change = ints.on_trait_change
return c1
s1a = mySlider2('Test')
s2a = mySlider2('TestTest')
s3a = mySlider2('TestTestTest')
widgets.interactive(
process,
a=s1a,
b=s2a,
c=s3a
)