Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jupyter: How to change color for widgets like SelectMultiple()?

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:

enter image description here

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:

enter image description here

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.

like image 665
vestland Avatar asked Feb 12 '19 08:02

vestland


2 Answers

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_red

wdg2.style.button_color = 'green'

wdg2_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>

enter image description here

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

enter image description here

Cell 3

wdg3.mycolor = 'red'

enter image description here

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.

like image 87
gdlmx Avatar answered Oct 01 '22 09:10

gdlmx


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:

enter image description here

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()

enter image description here

like image 28
ntg Avatar answered Oct 01 '22 09:10

ntg