Both ipython widgets and interactive objects have observe() methods. (See the results of the print statements.)
With the following example, I can confirm the actions of the observe() method on a slider widget but not on the interactive (ie) object.
Q: Is there any way to use the interactive's observe method or I have to call separate observe() method on it's all widget components ? If so, why ?
Expected behavior: Printing 'ie change observed' after changing inp1,
from IPython.display import display
import ipywidgets as widgets
int_range0_slider = widgets.IntSlider()
int_range1_slider = widgets.IntSlider()
output = widgets.Output()
def interactive_function(inp0,inp1):
with output:
print('ie changed. int_range0_slider: '+str(inp0)+' int_range1_slider: '+str(inp1))
return
def report_int_range0_change(change):
with output:
print('int_range0 change observed'+str(change))
return
def report_ie_change(change):
with output:
print('ie change observed'+str(change))
return
ie = widgets.interactive(interactive_function, inp0=int_range0_slider,inp1=int_range1_slider)
print(int_range0_slider.observe)
print(ie.observe)
int_range0_slider.observe(report_int_range0_change, names='value')
ie.observe(report_ie_change)
display(int_range0_slider,int_range1_slider,output)
I'm a newbie, any help on the correct usage would be appreciated.
Yeah, no you can't do that. The interactive object's observe will be for changes in children. So, no change there.
What you could do instead is put an observe on the children. Like this.
for child in ie.children:
child.observe(report_ie_change)
so, your code will look something like this:
from IPython.display import display
import ipywidgets as widgets
int_range0_slider = widgets.IntSlider()
int_range1_slider = widgets.IntSlider()
output = widgets.Output()
def interactive_function(inp0,inp1):
with output:
print('ie changed. int_range0_slider: '+str(inp0)+' int_range1_slider: '+str(inp1))
return
def report_int_range0_change(change):
with output:
print('int_range0 change observed'+str(change))
return
def report_ie_change(change):
with output:
print('ie change observed'+str(change))
return
ie = widgets.interactive(interactive_function, inp0=int_range0_slider,inp1=int_range1_slider)
# print(int_range0_slider.observe)
# print(ie.observe)
# int_range0_slider.observe(report_int_range0_change, names='value')
for child in ie.children:
child.observe(report_ie_change)
display(int_range0_slider,int_range1_slider,output)
Related
I am currently struggling trying to use the panel library in Python, in order to build an interactive dashboard to analyze and display CSV data. My current goal is to let the user enter an initial and a final date, which will be used to filter a DataFrame once a button is pressed. However, whenever I press the button, the on_click function is not completely executed before the script stops running. The code snippet is the following:
import panel as pn
pn.extension()
def acquire_data(dateBeginning, dateEnd):
eventDF = pd.read_csv('multi.csv')
eventDF['Date']= pd.to_datetime(eventDF['Date'])
dateDF = eventDF[eventDF.upvotes > 8]
print(eventDF)
def register_dates(event, save=True):
dateBeginning = date1Picker.value
dateEnd = date2Picker.value
if dateBeginning < dateEnd:
text = pn.widgets.StaticText(name='Static Text', value='A string')
spinner = pn.indicators.LoadingSpinner(width=50, height=50, value=True, color='info', bgcolor='light')
layout = pn.Column(text, spinner, align='center')
layout.app()
print('getting in')
acquire_data(dateBeginning, dateEnd)
print('getting out')
spinner.value = False
else:
print('Not working')
#pn.pane.Alert('## Alert\nThis is a warning!')
return save
date1Picker = pn.widgets.DatePicker(name='Date Initiale', margin=25)
date2Picker = pn.widgets.DatePicker(name='Date Finale', margin=25)
button = pn.widgets.Button(name="Analyse", button_type='primary', margin=(25, 0, 20, 200), width=200)
button.on_click(register_dates)
dateLayout = pn.Row(date1Picker, date2Picker)
layout = pn.Column(dateLayout, button, width=200, align='center')
layout.app()
I was also aiming at having the first layout be replaced by the one with the spinner and the text once the button is pressed, but I haven't found anything in the doc mentioning how to do so. If anyone could give me a hint regarding these issues, that would really help me!
In def acquire_data(dateBeginning, dateEnd):
pd.read_csv('multi.csv'), pd.to_datetime(eventDF['Date'])
For start, in this function I think you forgot to import panda and your app just crash.
add: import pandas as pd
Ex:
import panel as pn
import pandas as pd
I am just studying widget interaction in Python and Jupyter. My task is:
t=pd.DataFrame({'string':['i live here','you live in eltham','machine learning','learning english','go home','go back'],
'number':[1,3,2,3,1,2],
'word':['a','haha','runing over there','abcdefg','aaa','bye']})
import ipywidgets as widgets
from IPython.display import display
widgets.Text(
value='Hello World',
placeholder='Type something',
description='keyword:',
disabled=False
)
I need to type in some word, for example 'live', then the code will automatically search the data frame t and display all the rows with live in it.
I am seeking some hints, because I do not know where to start.
finally figure out a simple example. just put it here for someone who might need it.
t=pd.DataFrame({'string':['i live here','you live in eltham','machine learning','learning english','go home','go back','live home'],
'number':[1,3,2,3,1,2,4],
'word':['a','haha','runing over there','abcdefg','aaa','bye','hou']})
def myFUN_searchString(value,string):
s=string.split(' ')
return value in s
def myFUN_search(value):
t.loc[:,'Flag']=''
t.loc[:,'Flag']=[myFUN_searchString(value,x) for x in t.loc[:,'string']]
return t.loc[:,'Flag']
import ipywidgets as widgets
from IPython.display import display
keyword=widgets.Text(
value='electricity',
placeholder='Type something',
description='keyword:',
disabled=False
)
display(keyword)
button = widgets.Button(description="search")
display(button)
output = widgets.Output()
#output.capture()
def on_button_clicked(b):
t.loc[:,'Flag']=myFUN_search(keyword.value)
t1=t.loc[(t['Flag'])]
t1.drop(['Flag'],axis=1,inplace=True)
t1.reset_index(drop=True,inplace=True)
if t1.shape[0]>30:
t1=t1.loc[0:30]
display(t1)
button.on_click(on_button_clicked)
display(output)
I am trying to make an input widget for a module i have made (see this SO question).
The input widget should have a title bar and a variable number of input lines below. I had in mind to have an delete button at the end of each input line.
The delete button should ideally delete the container widget and all the children widgets, but hiding the container widget and children would also be acceptable.
I have not been able to find a useful recipe to this problem.
Currently i got this code, but i have no clue less as how to solve the problem.
import ipywidgets as w
def add_btn_clicked(b):
input_box.children = (input_box.children[0], line()) + input_box.children[1:]
def delete_btn_clicked(b):
# ???
with output:
print(b.keys)
return None
add = w.Button(icon="plus-circle")
add.on_click(add_btn_clicked)
title = w.HBox([w.Label(value=str(i)) for i in range(3)]+[add])
def line():
delete = w.Button(icon="trash")
delete.on_click(delete_btn_clicked)
return w.HBox([w.FloatText(value=i) for i in range(3)]+[delete])
input_box = w.VBox([title,line()])
output = w.Output()
display(input_box)
display(output)
Is there a way to tell what the parent element is from the button click or another way to achieve what I am attempting?
You can create the widgets and container separately, then define the .parent attribute on the children as the container, before assembling together. That way you can effectively hide the container when the button is clicked (with .parent.layout.display = 'none').
import ipywidgets as w
def add_btn_clicked(b):
input_box.children = (input_box.children[0], line()) + input_box.children[1:]
def delete_btn_clicked(b):
b.parent.layout.display = 'none'
add = w.Button(icon="plus-circle")
add.on_click(add_btn_clicked)
title = w.HBox([w.Label(value=str(i)) for i in range(3)]+[add])
def line():
delete = w.Button(icon="trash")
delete.on_click(delete_btn_clicked)
val_widgets = [w.FloatText(value=i) for i in range(3)]
container = w.HBox()
delete.parent = container
for widg in val_widgets:
widg.parent = container
children = val_widgets + [delete]
container.children = children
return container
input_box = w.VBox([title,line()])
output = w.Output()
display(input_box)
display(output)
The challenge:
How can you change the color for backround, font etc for widgets.SelectMultiple() and other widgets for that matter? Here's a simple setup for widgets.SelectMultiple()
Snippet / Cell 1:
# settings
%matplotlib inline
# imports
from ipywidgets import interactive, Layout
from IPython.display import clear_output
import ipywidgets as widgets
from IPython.display import display
# widget 1
wdg = widgets.SelectMultiple(
options=['Apples', 'Oranges', 'Pears'],
value=['Oranges'],
#rows=10,
description='Fruits',
disabled=False
)
display(wdg)
Widget 1:
What I've tried:
I thought i was onto something with Layout and style and was hoping the following setup with layout=Layout(width='75%', height='80px') would let me change colors somehow as well and not only width and height:
Snippet / Cell 2:
wdg2 = widgets.SelectMultiple(
options=['Apples', 'Oranges', 'Pears'],
value=['Oranges'],
description='Fruits',
layout=Layout(width='75%', height='80px'),
disabled=False
)
display(wdg2)
Widget2:
But to my huge disappointment it seems that you can't change colors in a similar way. According to the ipywidgets docs, properties of the style attribute are specific to each widget type. You can get a list of the style attributes for a widget with the keys property. And wdg2.style.keys returns this:
['_model_module',
'_model_module_version',
'_model_name',
'_view_count',
'_view_module',
'_view_module_version',
'_view_name',
'description_width']
And since there are noe color attributes there, is it impossible to change the colors for widgets.SelectMultiple()? For other widgets, like Button, you'll find an attribute button_color as well.
The short answer is: You can't do that without creating your own "custom widget".
Those attributes of style and layout objects are hard-coded in both the server-side and client-side libraries of ipywidgets.
There is a dirty way to get a similar effect though, by mixing the ButtonStyle with SelectMultiple.
# Tested on JupyterLab 0.35.3 with Python 3.6 kernel
import ipywidgets as widgets
from ipywidgets.widgets import widget_serialization, trait_types
from traitlets import Unicode, Instance, CaselessStrEnum
class MySelectMultiple(widgets.SelectMultiple):
style=trait_types.InstanceDict(widgets.ButtonStyle).tag(sync=True, **widget_serialization)
wdg2 = MySelectMultiple(
options=['Apples', 'Oranges', 'Pears'],
value=['Oranges'],
description='Fruits',
layout=widgets.Layout(width='75%', height='80px'),
style= {'button_color':'red'},
disabled=False
)
wdg2
wdg2.style.button_color = 'green'
Another dirty way is to inject a CSS rule into the notebook which affects all select widget.
%%html
<style>
.widget-select > select {background-color: red;}
</style>
Custom widget
The ultimate solution is to make your own custom widget.
Unfortunately you need to write both server- and client side codes for it.
For classical jupyter notebook, the client side code (JavaScript) can be put in a cell.
But this feature may be dropped in the "next-generation" of Jupyter, i.e. JupyterLab, for security reasons.
Cell 1
%%javascript
require.undef('myselectmultiple');
define('myselectmultiple', ["#jupyter-widgets/base"], function(widgets) {
class selectmultipleView extends widgets.SelectMultipleView {
render () {
super.render();
this.mycolor_changed();
this.model.on('change:mycolor', this.mycolor_changed, this);
}
mycolor_changed () {
var mycolor = this.model.get('mycolor')
this.el.childNodes[1].style.backgroundColor = mycolor;
}
}
return {
myselectmultipleview : selectmultipleView
};
});
Cell 2
class MySelectMultipleC(widgets.SelectMultiple):
_view_name = Unicode('myselectmultipleview').tag(sync=True)
_view_module = Unicode('myselectmultiple').tag(sync=True)
_view_module_version = Unicode('0.1.0').tag(sync=True)
mycolor = Unicode('white', help='background color').tag(sync=True)
wdg3 = MySelectMultipleC(
options=['Apples', 'Oranges', 'Pears'],
value=['Oranges'],
description='Fruits',
mycolor = 'green',
disabled=False
)
wdg3
Cell 3
wdg3.mycolor = 'red'
JupyterLab uses a completely different framework. To make the above custom widget working in the "Lab" interface, the client-side code should be translated to TypeScript, and then be compiled, built and installed on the Lab server.
Late to the party, but here is my simple solution, for the case where the color will be used to encode simple two (or a number of) states: use unicode!
sample:
code (in python 3... :) )
from ipywidgets import interactive, Layout
from IPython.display import clear_output
import ipywidgets as widgets
from IPython.display import display
c_base = int("1F534",base=16)
# widget 1
options=['Apples', 'Oranges', 'Pears']
state = [False,True,True]
colored_options = ['{} {}'.format(chr(c_base+s), o) for s,o in zip(state,options)]
wdg = widgets.SelectMultiple(
options=colored_options,
description='Fruits',
disabled=False
)
display(wdg)
Try searching with this code if you need more colours...:
for i in range (10):
ii = int('0x1f7e0',base=16)+i
print('{:>15}'.format('[{}: {}] '.format(hex(ii),chr(ii))),end='')
if i%7==6:
print()
jp_proxy_widget makes it easy to do anything you can do in HTML5/javascript in a widget. For example here is a colorized multiple select:
Find it here: https://github.com/AaronWatters/jp_proxy_widget
I have a module that creates a savedSettings.py file after the user used my tool this file is filled with variables to load into the gui next time the tool is used.
I have some checkboxes and a optionMenu. Reading and setting the variables for the checkboxes is as simple as:
# loadsettings into gui
if os.path.exists(userSettings):
sys.path.append(toolFolder)
import savedSettings
viewCBvalue = savedSettings.viewCheck
ornamentCBvalue = savedSettings.ornamentCheck
renderCBvalue = savedSettings.renderCheck
I thought the optionMenu would be the same and wrote:
encodingOMvalue = savedSettings.encodingCheck
When I now tell the GUI to use the variables:
cmds.checkBoxGrp( 'viewCB', label = 'View: ', value1 = viewCBvalue)
cmds.checkBoxGrp( 'ornamentCB', label = 'Show Ornaments: ', value1 = ornamentCBvalue)
cmds.checkBoxGrp( 'renderCB', label = 'Render offscreen: ', value1 = renderCBvalue)
cmds.optionMenuGrp( 'encodingOM', label = 'Encoding ', value = encodingOMvalue )
cmds.menuItem( 'tif', label = 'tif')
cmds.menuItem( 'jpg', label = 'jpg')
cmds.menuItem( 'png', label = 'png')
I get the follwing error:
RuntimeError: Item not found: tif #
My savedSettings.py looks like this:
# User Settings Savefile:
viewCheck = False
ornamentCheck = False
renderCheck = False
encodingCheck = "tif"
Would be great if someone explains me what I am doing wrong and how to set variables for the optionMenu.
Thanks for taking the time in advance and have a nice day coding!
Don't do this. instead use mayas internal mechanism, optionVar, for this.
But if you must do this then know that when you do:
import savedSettings
whatever is defined in savedSettings is stored inside savedSettings so if you have the var viewCBvalue then you call it with savedSettings.viewCBvalue. You could load this into main by calling import savedSettings as *, but you really, really do not want to do this!
Python import is not a normal function, results get cached.
Various other problems. Dont use import for this purpose
If you do not want to use optionVar, consider using pickle