2014-01-14 17:43:46 +08:00
""" Base Widget class. Allows user to create widgets in the back-end that render
in the IPython notebook front - end .
2013-10-26 02:31:48 +08:00
"""
#-----------------------------------------------------------------------------
# Copyright (c) 2013, the IPython Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
2014-01-08 20:59:48 +08:00
from contextlib import contextmanager
2014-08-13 01:29:41 +08:00
import collections
2013-10-10 23:23:19 +08:00
2014-02-08 08:43:49 +08:00
from IPython . core . getipython import get_ipython
2013-10-16 01:14:04 +08:00
from IPython . kernel . comm import Comm
from IPython . config import LoggingConfigurable
2014-10-09 11:08:34 +08:00
from IPython . utils . importstring import import_item
2014-08-26 04:00:29 +08:00
from IPython . utils . traitlets import Unicode , Dict , Instance , Bool , List , \
CaselessStrEnum , Tuple , CUnicode , Int , Set
2013-10-17 14:09:20 +08:00
from IPython . utils . py3compat import string_types
2013-10-26 02:31:48 +08:00
#-----------------------------------------------------------------------------
# Classes
#-----------------------------------------------------------------------------
2014-01-22 07:14:27 +08:00
class CallbackDispatcher ( LoggingConfigurable ) :
2014-01-27 07:46:06 +08:00
""" A structure for registering and running callbacks """
callbacks = List ( )
def __call__ ( self , * args , * * kwargs ) :
""" Call all of the registered callbacks. """
2014-01-24 02:52:33 +08:00
value = None
2014-01-27 07:46:06 +08:00
for callback in self . callbacks :
try :
local_value = callback ( * args , * * kwargs )
except Exception as e :
2014-02-08 08:43:49 +08:00
ip = get_ipython ( )
if ip is None :
self . log . warn ( " Exception in callback %s : %s " , callback , e , exc_info = True )
else :
ip . showtraceback ( )
2014-01-27 07:46:06 +08:00
else :
2014-01-24 02:52:33 +08:00
value = local_value if local_value is not None else value
return value
2014-01-22 07:14:27 +08:00
def register_callback ( self , callback , remove = False ) :
""" (Un)Register a callback
Parameters
- - - - - - - - - -
callback : method handle
2014-01-27 07:46:06 +08:00
Method to be registered or unregistered .
2014-01-22 07:14:27 +08:00
remove = False : bool
2014-01-27 07:46:06 +08:00
Whether to unregister the callback . """
2014-01-22 07:14:27 +08:00
# (Un)Register the callback.
2014-01-27 07:46:06 +08:00
if remove and callback in self . callbacks :
self . callbacks . remove ( callback )
elif not remove and callback not in self . callbacks :
self . callbacks . append ( callback )
2013-10-10 23:23:19 +08:00
2014-02-08 08:43:49 +08:00
def _show_traceback ( method ) :
""" decorator for showing tracebacks in IPython """
def m ( self , * args , * * kwargs ) :
try :
return ( method ( self , * args , * * kwargs ) )
except Exception as e :
ip = get_ipython ( )
if ip is None :
self . log . warn ( " Exception in widget method %s : %s " , method , e , exc_info = True )
else :
ip . showtraceback ( )
return m
2014-01-22 07:14:27 +08:00
2014-09-24 11:37:17 +08:00
def register ( key = None ) :
2014-09-24 11:16:19 +08:00
""" Returns a decorator registering a widget class in the widget registry.
If no key is provided , the class name is used as a key . A key is
2014-10-14 09:45:16 +08:00
provided for each core IPython widget so that the frontend can use
2014-09-24 11:16:19 +08:00
this key regardless of the language of the kernel """
2014-09-24 11:37:17 +08:00
def wrap ( widget ) :
2014-09-24 11:16:19 +08:00
l = key if key is not None else widget . __module__ + widget . __name__
2014-09-24 11:37:17 +08:00
Widget . widget_types [ l ] = widget
return widget
return wrap
2014-01-22 07:14:27 +08:00
class Widget ( LoggingConfigurable ) :
2014-01-22 06:13:33 +08:00
#-------------------------------------------------------------------------
# Class attributes
#-------------------------------------------------------------------------
2014-01-27 07:46:06 +08:00
_widget_construction_callback = None
2014-01-14 17:43:46 +08:00
widgets = { }
2014-09-24 11:37:17 +08:00
widget_types = { }
2013-12-21 07:24:14 +08:00
2014-01-27 07:46:06 +08:00
@staticmethod
2013-12-21 07:24:14 +08:00
def on_widget_constructed ( callback ) :
2014-01-27 07:46:06 +08:00
""" Registers a callback to be called when a widget is constructed.
2014-01-16 22:20:04 +08:00
The callback must have the following signature :
2013-12-21 07:24:14 +08:00
callback ( widget ) """
2014-01-27 07:46:06 +08:00
Widget . _widget_construction_callback = callback
2013-12-21 07:24:14 +08:00
2014-01-27 07:46:06 +08:00
@staticmethod
2014-01-07 20:09:41 +08:00
def _call_widget_constructed ( widget ) :
2014-01-27 07:46:06 +08:00
""" Static method, called when a widget is constructed. """
if Widget . _widget_construction_callback is not None and callable ( Widget . _widget_construction_callback ) :
Widget . _widget_construction_callback ( widget )
2013-12-21 07:24:14 +08:00
2014-10-09 11:08:34 +08:00
@staticmethod
def handle_comm_opened ( comm , msg ) :
""" Static method, called when a widget is constructed. """
2014-10-28 08:57:02 +08:00
widget_class = import_item ( msg [ ' content ' ] [ ' data ' ] [ ' widget_class ' ] )
2014-10-28 01:56:37 +08:00
widget = widget_class ( comm = comm )
2014-10-09 11:08:34 +08:00
2014-01-22 06:13:33 +08:00
#-------------------------------------------------------------------------
# Traits
#-------------------------------------------------------------------------
2014-10-22 01:57:10 +08:00
_model_module = Unicode ( None , allow_none = True , help = """ A requirejs module name
in which to find _model_name . If empty , look in the global registry . """ )
2014-01-30 04:37:30 +08:00
_model_name = Unicode ( ' WidgetModel ' , help = """ Name of the backbone model
2014-01-14 17:43:46 +08:00
registered in the front - end to create and sync this widget with . """ )
2014-09-19 02:57:37 +08:00
_view_module = Unicode ( help = """ A requirejs module in which to find _view_name.
2014-09-18 06:51:09 +08:00
If empty , look in the global registry . """ , sync=True)
2014-09-25 11:10:41 +08:00
_view_name = Unicode ( None , allow_none = True , help = """ Default view registered in the front-end
2014-01-14 23:15:19 +08:00
to use to represent the widget . """ , sync=True)
2014-07-24 02:00:32 +08:00
comm = Instance ( ' IPython.kernel.comm.Comm ' )
2014-01-27 07:46:06 +08:00
2014-02-21 00:41:05 +08:00
msg_throttle = Int ( 3 , sync = True , help = """ Maximum number of msgs the
front - end can send before receiving an idle msg from the back - end . """ )
2014-01-27 07:46:06 +08:00
2014-10-09 12:17:11 +08:00
version = Int ( 0 , sync = True , help = """ Widget ' s version """ )
2014-01-27 07:46:06 +08:00
keys = List ( )
def _keys_default ( self ) :
return [ name for name in self . traits ( sync = True ) ]
_property_lock = Tuple ( ( None , None ) )
2014-08-13 01:29:41 +08:00
_send_state_lock = Int ( 0 )
_states_to_send = Set ( allow_none = False )
2014-01-27 07:46:06 +08:00
_display_callbacks = Instance ( CallbackDispatcher , ( ) )
_msg_callbacks = Instance ( CallbackDispatcher , ( ) )
2014-01-22 06:13:33 +08:00
#-------------------------------------------------------------------------
# (Con/de)structor
2014-01-27 07:46:06 +08:00
#-------------------------------------------------------------------------
2014-10-29 08:18:37 +08:00
def __init__ ( self , * * kwargs ) :
2014-01-16 22:20:04 +08:00
""" Public constructor """
2014-08-02 11:16:02 +08:00
self . _model_id = kwargs . pop ( ' model_id ' , None )
2014-01-07 20:04:24 +08:00
super ( Widget , self ) . __init__ ( * * kwargs )
2013-12-21 07:24:14 +08:00
2014-01-07 20:09:41 +08:00
Widget . _call_widget_constructed ( self )
2014-10-28 01:56:37 +08:00
self . open ( )
2013-12-29 13:46:06 +08:00
2013-10-10 23:23:19 +08:00
def __del__ ( self ) :
2013-10-26 02:31:48 +08:00
""" Object disposal """
2013-10-10 23:23:19 +08:00
self . close ( )
2013-12-29 13:46:06 +08:00
2014-01-22 06:13:33 +08:00
#-------------------------------------------------------------------------
# Properties
2014-01-27 07:46:06 +08:00
#-------------------------------------------------------------------------
2014-01-14 17:43:46 +08:00
2014-07-18 03:09:50 +08:00
def open ( self ) :
""" Open a comm to the frontend if one isn ' t already open. """
2014-07-24 02:00:32 +08:00
if self . comm is None :
2014-10-22 01:57:10 +08:00
args = dict ( target_name = ' ipython.widget ' ,
data = { ' model_name ' : self . _model_name ,
' model_module ' : self . _model_module } )
2014-10-09 08:56:46 +08:00
if self . _model_id is not None :
args [ ' comm_id ' ] = self . _model_id
2014-10-22 04:32:15 +08:00
self . comm = Comm ( * * args )
2014-07-18 03:09:50 +08:00
2014-10-22 04:32:15 +08:00
def _comm_changed ( self , name , new ) :
""" Called when the comm is changed. """
2014-11-13 03:29:10 +08:00
if new is None :
return
2014-10-10 03:39:10 +08:00
self . _model_id = self . model_id
self . comm . on_msg ( self . _handle_msg )
Widget . widgets [ self . model_id ] = self
2014-10-09 11:59:56 +08:00
# first update
self . send_state ( )
2014-01-03 06:34:42 +08:00
@property
def model_id ( self ) :
2014-01-22 06:16:24 +08:00
""" Gets the model id of this widget.
If a Comm doesn ' t exist yet, a Comm will be created automagically. " " "
2014-01-04 05:53:35 +08:00
return self . comm . comm_id
2013-12-29 13:46:06 +08:00
2014-01-22 06:13:33 +08:00
#-------------------------------------------------------------------------
# Methods
2014-01-27 07:46:06 +08:00
#-------------------------------------------------------------------------
2014-01-22 06:13:33 +08:00
def close ( self ) :
2014-01-27 07:46:06 +08:00
""" Close method.
2013-10-17 14:09:20 +08:00
2014-07-27 00:36:21 +08:00
Closes the underlying comm .
2014-01-22 06:13:33 +08:00
When the comm is closed , all of the widget views are automatically
removed from the front - end . """
2014-07-24 02:00:32 +08:00
if self . comm is not None :
2014-08-01 03:22:47 +08:00
Widget . widgets . pop ( self . model_id , None )
2014-07-24 02:00:32 +08:00
self . comm . close ( )
self . comm = None
2014-07-30 08:28:30 +08:00
2013-10-26 02:31:48 +08:00
def send_state ( self , key = None ) :
2014-01-14 17:43:46 +08:00
""" Sends the widget state, or a piece of it, to the front-end.
2013-10-26 02:31:48 +08:00
Parameters
- - - - - - - - - -
2014-08-13 01:29:41 +08:00
key : unicode , or iterable ( optional )
A single property ' s name or iterable of property names to sync with the front-end.
2013-10-26 02:31:48 +08:00
"""
2014-01-17 01:17:00 +08:00
self . _send ( {
" method " : " update " ,
2014-08-13 01:29:41 +08:00
" state " : self . get_state ( key = key )
2014-01-17 01:17:00 +08:00
} )
2013-10-26 02:31:48 +08:00
2014-01-01 00:26:07 +08:00
def get_state ( self , key = None ) :
2013-12-29 13:46:06 +08:00
""" Gets the widget state, or a piece of it.
2013-10-26 02:31:48 +08:00
Parameters
- - - - - - - - - -
2014-08-13 01:29:41 +08:00
key : unicode or iterable ( optional )
A single property ' s name or iterable of property names to get.
2013-10-26 02:31:48 +08:00
"""
2014-08-13 01:29:41 +08:00
if key is None :
keys = self . keys
elif isinstance ( key , string_types ) :
keys = [ key ]
elif isinstance ( key , collections . Iterable ) :
keys = key
else :
raise ValueError ( " key must be a string, an iterable of keys, or None " )
2014-07-13 12:32:34 +08:00
state = { }
for k in keys :
2014-08-19 01:28:07 +08:00
f = self . trait_metadata ( k , ' to_json ' , self . _trait_to_json )
2014-07-22 23:21:28 +08:00
value = getattr ( self , k )
state [ k ] = f ( value )
2014-07-13 12:32:34 +08:00
return state
2014-10-01 03:55:31 +08:00
def set_state ( self , sync_data ) :
""" Called when a state is received from the front-end. """
for name in self . keys :
if name in sync_data :
json_value = sync_data [ name ]
from_json = self . trait_metadata ( name , ' from_json ' , self . _trait_from_json )
with self . _lock_property ( name , json_value ) :
setattr ( self , name , from_json ( json_value ) )
2014-07-13 12:32:34 +08:00
2013-11-19 05:07:32 +08:00
def send ( self , content ) :
""" Sends a custom msg to the widget model in the front-end.
Parameters
- - - - - - - - - -
content : dict
Content of the message to send .
"""
2014-01-22 06:17:57 +08:00
self . _send ( { " method " : " custom " , " content " : content } )
2013-11-19 05:07:32 +08:00
2014-01-14 17:43:46 +08:00
def on_msg ( self , callback , remove = False ) :
2014-01-27 07:46:06 +08:00
""" (Un)Register a custom msg receive callback.
2013-11-19 05:07:32 +08:00
Parameters
- - - - - - - - - -
2014-01-27 07:46:06 +08:00
callback : callable
2014-01-30 06:55:12 +08:00
callback will be passed two arguments when a message arrives : :
2014-01-27 07:46:06 +08:00
callback ( widget , content )
2014-01-30 06:55:12 +08:00
2013-11-19 05:07:32 +08:00
remove : bool
True if the callback should be unregistered . """
2014-01-22 07:14:27 +08:00
self . _msg_callbacks . register_callback ( callback , remove = remove )
2013-11-19 05:07:32 +08:00
2013-11-07 01:41:26 +08:00
def on_displayed ( self , callback , remove = False ) :
2014-01-16 22:20:04 +08:00
""" (Un)Register a widget displayed callback.
2013-11-07 01:41:26 +08:00
2013-11-07 01:48:22 +08:00
Parameters
- - - - - - - - - -
2013-11-07 01:41:26 +08:00
callback : method handler
2014-01-30 06:55:12 +08:00
Must have a signature of : :
2014-01-27 07:46:06 +08:00
callback ( widget , * * kwargs )
2014-01-30 06:55:12 +08:00
2014-01-27 07:46:06 +08:00
kwargs from display are passed through without modification .
2013-11-07 01:41:26 +08:00
remove : bool
True if the callback should be unregistered . """
2014-01-22 07:14:27 +08:00
self . _display_callbacks . register_callback ( callback , remove = remove )
2013-11-07 01:41:26 +08:00
2014-01-22 06:13:33 +08:00
#-------------------------------------------------------------------------
2013-10-26 02:31:48 +08:00
# Support methods
2014-01-22 06:13:33 +08:00
#-------------------------------------------------------------------------
@contextmanager
2014-01-22 07:18:49 +08:00
def _lock_property ( self , key , value ) :
2014-01-22 06:13:33 +08:00
""" Lock a property-value pair.
2014-08-19 01:28:07 +08:00
The value should be the JSON state of the property .
2014-01-22 06:13:33 +08:00
NOTE : This , in addition to the single lock for all state changes , is
flawed . In the future we may want to look into buffering state changes
back to the front - end . """
self . _property_lock = ( key , value )
try :
yield
finally :
self . _property_lock = ( None , None )
2014-08-13 01:29:41 +08:00
@contextmanager
def hold_sync ( self ) :
""" Hold syncing any state until the context manager is released """
# We increment a value so that this can be nested. Syncing will happen when
# all levels have been released.
self . _send_state_lock + = 1
try :
yield
finally :
self . _send_state_lock - = 1
if self . _send_state_lock == 0 :
self . send_state ( self . _states_to_send )
self . _states_to_send . clear ( )
2014-01-22 06:13:33 +08:00
def _should_send_property ( self , key , value ) :
""" Check the property lock (property_lock) """
2014-08-19 01:28:07 +08:00
to_json = self . trait_metadata ( key , ' to_json ' , self . _trait_to_json )
if ( key == self . _property_lock [ 0 ]
and to_json ( value ) == self . _property_lock [ 1 ] ) :
2014-08-13 02:23:57 +08:00
return False
elif self . _send_state_lock > 0 :
2014-08-13 01:29:41 +08:00
self . _states_to_send . add ( key )
return False
2014-08-13 02:23:57 +08:00
else :
return True
2014-01-22 06:13:33 +08:00
# Event handlers
2014-02-08 08:43:49 +08:00
@_show_traceback
2014-01-22 06:13:33 +08:00
def _handle_msg ( self , msg ) :
""" Called when a msg is received from the front-end """
data = msg [ ' content ' ] [ ' data ' ]
method = data [ ' method ' ]
# Handle backbone sync methods CREATE, PATCH, and UPDATE all in one.
2014-10-09 11:08:34 +08:00
if method == ' backbone ' :
if ' sync_data ' in data :
sync_data = data [ ' sync_data ' ]
self . set_state ( sync_data ) # handles all methods
# Handle a state request.
elif method == ' request_state ' :
self . send_state ( )
2014-01-22 06:13:33 +08:00
2014-10-09 11:08:34 +08:00
# Handle a custom msg from the front-end.
2014-01-22 06:13:33 +08:00
elif method == ' custom ' :
2014-01-22 06:17:57 +08:00
if ' content ' in data :
self . _handle_custom_msg ( data [ ' content ' ] )
2014-01-22 06:13:33 +08:00
2014-10-09 11:08:34 +08:00
# Catch remainder.
else :
self . log . error ( ' Unknown front-end to back-end widget msg with method " %s " ' % method )
2014-01-22 06:13:33 +08:00
def _handle_custom_msg ( self , content ) :
""" Called when a custom msg is received. """
2014-01-27 07:46:06 +08:00
self . _msg_callbacks ( self , content )
2014-10-09 11:08:34 +08:00
2014-09-24 03:25:02 +08:00
def _notify_trait ( self , name , old_value , new_value ) :
2014-01-22 06:13:33 +08:00
""" Called when a property has been changed. """
2014-09-24 03:25:02 +08:00
# Trigger default traitlet callback machinery. This allows any user
# registered validation to be processed prior to allowing the widget
# machinery to handle the state.
2014-09-26 05:51:38 +08:00
LoggingConfigurable . _notify_trait ( self , name , old_value , new_value )
2014-09-24 03:25:02 +08:00
# Send the state after the user registered callbacks for trait changes
# have all fired (allows for user to validate values).
2014-09-26 05:51:38 +08:00
if self . comm is not None and name in self . keys :
2014-10-09 11:08:34 +08:00
# Make sure this isn't information that the front-end just sent us.
2014-09-24 03:25:02 +08:00
if self . _should_send_property ( name , new_value ) :
2014-10-10 03:39:10 +08:00
# Send new state to front-end
self . send_state ( key = name )
2014-01-22 06:13:33 +08:00
def _handle_displayed ( self , * * kwargs ) :
""" Called when a view has been displayed for this widget instance """
2014-01-27 07:46:06 +08:00
self . _display_callbacks ( self , * * kwargs )
2014-01-22 06:13:33 +08:00
2014-07-24 04:22:50 +08:00
def _trait_to_json ( self , x ) :
""" Convert a trait value to json
2014-01-22 06:13:33 +08:00
2014-07-13 12:32:34 +08:00
Traverse lists / tuples and dicts and serialize their values as well .
Replace any widgets with their model_id
"""
2014-01-22 06:23:44 +08:00
if isinstance ( x , dict ) :
2014-07-24 04:22:50 +08:00
return { k : self . _trait_to_json ( v ) for k , v in x . items ( ) }
2014-02-26 01:46:27 +08:00
elif isinstance ( x , ( list , tuple ) ) :
2014-07-24 04:22:50 +08:00
return [ self . _trait_to_json ( v ) for v in x ]
2014-01-22 06:23:44 +08:00
elif isinstance ( x , Widget ) :
2014-07-18 04:55:27 +08:00
return " IPY_MODEL_ " + x . model_id
2014-01-22 06:13:33 +08:00
else :
2014-01-22 06:25:08 +08:00
return x # Value must be JSON-able
2014-01-22 06:13:33 +08:00
2014-07-24 04:22:50 +08:00
def _trait_from_json ( self , x ) :
2014-07-13 12:32:34 +08:00
""" Convert json values to objects
2014-01-22 06:13:33 +08:00
2014-07-24 04:22:50 +08:00
Replace any strings representing valid model id values to Widget references .
2014-07-13 12:32:34 +08:00
"""
2014-01-22 06:23:44 +08:00
if isinstance ( x , dict ) :
2014-07-24 04:22:50 +08:00
return { k : self . _trait_from_json ( v ) for k , v in x . items ( ) }
2014-02-26 01:46:27 +08:00
elif isinstance ( x , ( list , tuple ) ) :
2014-07-24 04:22:50 +08:00
return [ self . _trait_from_json ( v ) for v in x ]
2014-07-18 04:55:27 +08:00
elif isinstance ( x , string_types ) and x . startswith ( ' IPY_MODEL_ ' ) and x [ 10 : ] in Widget . widgets :
2014-07-13 12:32:34 +08:00
# we want to support having child widgets at any level in a hierarchy
# trusting that a widget UUID will not appear out in the wild
2014-08-29 03:52:20 +08:00
return Widget . widgets [ x [ 10 : ] ]
2014-01-22 06:13:33 +08:00
else :
2014-01-22 06:23:44 +08:00
return x
2014-01-22 06:13:33 +08:00
2014-01-14 17:43:46 +08:00
def _ipython_display_ ( self , * * kwargs ) :
2014-01-16 22:20:04 +08:00
""" Called when `IPython.display.display` is called on the widget. """
2014-09-25 11:10:41 +08:00
# Show view.
if self . _view_name is not None :
self . _send ( { " method " : " display " } )
self . _handle_displayed ( * * kwargs )
2013-12-21 09:05:48 +08:00
def _send ( self , msg ) :
2014-01-16 22:20:04 +08:00
""" Sends a message to the model in the front-end. """
2014-01-08 07:13:07 +08:00
self . comm . send ( msg )
2014-01-03 07:36:24 +08:00
2014-01-07 20:04:24 +08:00
class DOMWidget ( Widget ) :
2014-12-04 08:27:59 +08:00
visible = Bool ( True , allow_none = True , help = " Whether the widget is visible. False collapses the empty space, while None preserves the empty space. " , sync = True )
2014-08-26 04:00:29 +08:00
_css = Tuple ( sync = True , help = " CSS property list: (selector, key, value) " )
_dom_classes = Tuple ( sync = True , help = " DOM classes applied to widget.$el. " )
2014-08-23 06:21:41 +08:00
width = CUnicode ( sync = True )
height = CUnicode ( sync = True )
2014-12-10 02:35:02 +08:00
# A default padding of 2.5 px makes the widgets look nice when displayed inline.
padding = CUnicode ( " 2.5px " , sync = True )
2014-08-26 12:28:45 +08:00
margin = CUnicode ( sync = True )
2014-08-23 06:21:41 +08:00
2014-09-17 01:17:59 +08:00
color = Unicode ( sync = True )
background_color = Unicode ( sync = True )
2014-08-23 06:21:41 +08:00
border_color = Unicode ( sync = True )
border_width = CUnicode ( sync = True )
2014-09-24 06:18:00 +08:00
border_radius = CUnicode ( sync = True )
2014-08-23 06:21:41 +08:00
border_style = CaselessStrEnum ( values = [ # http://www.w3schools.com/cssref/pr_border-style.asp
' none ' ,
' hidden ' ,
' dotted ' ,
' dashed ' ,
' solid ' ,
' double ' ,
' groove ' ,
' ridge ' ,
' inset ' ,
' outset ' ,
' initial ' ,
' inherit ' , ' ' ] ,
default_value = ' ' , sync = True )
font_style = CaselessStrEnum ( values = [ # http://www.w3schools.com/cssref/pr_font_font-style.asp
' normal ' ,
' italic ' ,
' oblique ' ,
' initial ' ,
' inherit ' , ' ' ] ,
default_value = ' ' , sync = True )
font_weight = CaselessStrEnum ( values = [ # http://www.w3schools.com/cssref/pr_font_weight.asp
' normal ' ,
' bold ' ,
' bolder ' ,
' lighter ' ,
' initial ' ,
2015-01-09 02:43:38 +08:00
' inherit ' , ' ' ] + list ( map ( str , range ( 100 , 1000 , 100 ) ) ) ,
2014-08-23 06:21:41 +08:00
default_value = ' ' , sync = True )
font_size = CUnicode ( sync = True )
font_family = Unicode ( sync = True )
2014-09-24 06:18:00 +08:00
def __init__ ( self , * pargs , * * kwargs ) :
super ( DOMWidget , self ) . __init__ ( * pargs , * * kwargs )
def _validate_border ( name , old , new ) :
if new is not None and new != ' ' :
if name != ' border_width ' and not self . border_width :
self . border_width = 1
if name != ' border_style ' and self . border_style == ' ' :
self . border_style = ' solid '
self . on_trait_change ( _validate_border , [ ' border_width ' , ' border_style ' , ' border_color ' ] )