8

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.

vestland
  • 55,229
  • 37
  • 187
  • 305

3 Answers3

7

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

ntg
  • 12,950
  • 7
  • 74
  • 95
  • 1
    I like your ingenious solution, although it doesn't really answer the original question, it inspired me to use Unicode emojis to color-code individual lines. Note that in Windows 10, you can use the `Win+.(dot)` hotkey to open an emoji menu to search for the colors ⚪⚫ :) – TomsonTom May 19 '21 at 10:02
6

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.

gdlmx
  • 6,479
  • 1
  • 21
  • 39
  • Thanks for you answer! This is great! It might only be my lack of googling expertise, but this seemed impossible to find out! Would it be OK to invite you to chat for some minor follow-up questions? – vestland Feb 27 '19 at 08:11
  • I've already accepted your answer, but do you know how to change font, fontsize, and color of the selection indicator? – vestland Feb 28 '19 at 18:56
  • 1
    You can add more lines to the `render()` function in the javascript, such as `this.el.childNodes[1].style.fontSize = '12px'`. – gdlmx Feb 28 '19 at 23:30
  • 2
    It's better to include some CSS rules in your notebook and set the corresponding class to the ` – gdlmx Feb 28 '19 at 23:46
  • Cool! Thank you! – vestland Mar 01 '19 at 02:44
1

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:

colorized multiple select jupyter widget

Find it here: https://github.com/AaronWatters/jp_proxy_widget

Aaron Watters
  • 2,784
  • 3
  • 23
  • 37