##// END OF EJS Templates
Added system that allows js to be required by widgets.
Jonathan Frederic -
Show More
@@ -1,157 +1,175 b''
1
1
2 from copy import copy
2 from copy import copy
3 from glob import glob
3 from glob import glob
4 import uuid
4 import uuid
5 import sys
5 import sys
6 import os
6 import os
7
7
8 import IPython
8 import IPython
9 from IPython.kernel.comm import Comm
9 from IPython.kernel.comm import Comm
10 from IPython.config import LoggingConfigurable
10 from IPython.config import LoggingConfigurable
11 from IPython.utils.traitlets import Unicode, Dict
11 from IPython.utils.traitlets import Unicode, Dict, List
12 from IPython.display import Javascript, display
12 from IPython.display import Javascript, display
13 from IPython.utils.py3compat import string_types
13 from IPython.utils.py3compat import string_types
14
14
15
15
16 class Widget(LoggingConfigurable):
16 class Widget(LoggingConfigurable):
17
17
18 ### Public declarations
18 ### Public declarations
19 target_name = Unicode('widget')
19 target_name = Unicode('widget')
20 default_view_name = Unicode()
20 default_view_name = Unicode()
21 js_requirements = List()
21
22
22
23
23 ### Private/protected declarations
24 ### Private/protected declarations
24 _keys = []
25 _keys = []
25 _property_lock = False
26 _property_lock = False
26 _parent = None
27 _parent = None
27 _children = []
28 _children = []
28 _css = Dict()
29 _css = Dict()
29
30
30
31
31 ### Public constructor
32 ### Public constructor
32 def __init__(self, parent=None, **kwargs):
33 def __init__(self, parent=None, **kwargs):
33 super(Widget, self).__init__(**kwargs)
34 super(Widget, self).__init__(**kwargs)
34
35
35 self._children = []
36 self._children = []
36 if parent is not None:
37 if parent is not None:
37 parent._children.append(self)
38 parent._children.append(self)
38 self._parent = parent
39 self._parent = parent
39 self.comm = None
40 self.comm = None
40
41
41 # Register after init to allow default values to be specified
42 # Register after init to allow default values to be specified
42 self.on_trait_change(self._handle_property_changed, self.keys)
43 self.on_trait_change(self._handle_property_changed, self.keys)
43
44
44
45
45 def __del__(self):
46 def __del__(self):
46 self.close()
47 self.close()
47
48
48 def close(self):
49 def close(self):
49 self.comm.close()
50 self.comm.close()
50 del self.comm
51 del self.comm
51
52
52
53
53 ### Properties
54 ### Properties
54 def _get_parent(self):
55 def _get_parent(self):
55 return self._parent
56 return self._parent
56 parent = property(_get_parent)
57 parent = property(_get_parent)
57
58
58
59
59 def _get_children(self):
60 def _get_children(self):
60 return copy(self._children)
61 return copy(self._children)
61 children = property(_get_children)
62 children = property(_get_children)
62
63
63
64
64 def _get_keys(self):
65 def _get_keys(self):
65 keys = ['_css']
66 keys = ['_css']
66 keys.extend(self._keys)
67 keys.extend(self._keys)
67 return keys
68 return keys
68 keys = property(_get_keys)
69 keys = property(_get_keys)
69
70
70 def _get_css(self, key, selector=""):
71 def _get_css(self, key, selector=""):
71 if selector in self._css and key in self._css[selector]:
72 if selector in self._css and key in self._css[selector]:
72 return self._css[selector][key]
73 return self._css[selector][key]
73 else:
74 else:
74 return None
75 return None
75 def _set_css(self, value, key, selector=""):
76 def _set_css(self, value, key, selector=""):
76 if selector not in self._css:
77 if selector not in self._css:
77 self._css[selector] = {}
78 self._css[selector] = {}
78
79
79 # Only update the property if it has changed.
80 # Only update the property if it has changed.
80 if not (key in self._css[selector] and value in self._css[selector][key]):
81 if not (key in self._css[selector] and value in self._css[selector][key]):
81 self._css[selector][key] = value
82 self._css[selector][key] = value
82 self.send_state() # Send new state to client.
83 self.send_state() # Send new state to client.
83
84
84 css = property(_get_css, _set_css)
85 css = property(_get_css, _set_css)
85
86
86
87
87 ### Event handlers
88 ### Event handlers
88 def _handle_msg(self, msg):
89 def _handle_msg(self, msg):
89
90
90 # Handle backbone sync methods
91 # Handle backbone sync methods
91 sync_method = msg['content']['data']['sync_method']
92 sync_method = msg['content']['data']['sync_method']
92 sync_data = msg['content']['data']['sync_data']
93 sync_data = msg['content']['data']['sync_data']
93 if sync_method.lower() in ['create', 'update']:
94 if sync_method.lower() in ['create', 'update']:
94 self._handle_recieve_state(sync_data)
95 self._handle_recieve_state(sync_data)
95
96
96
97
97 def _handle_recieve_state(self, sync_data):
98 def _handle_recieve_state(self, sync_data):
98 self._property_lock = True
99 self._property_lock = True
99 try:
100 try:
100
101
101 # Use _keys instead of keys - Don't get retrieve the css from the client side.
102 # Use _keys instead of keys - Don't get retrieve the css from the client side.
102 for name in self._keys:
103 for name in self._keys:
103 if name in sync_data:
104 if name in sync_data:
104 setattr(self, name, sync_data[name])
105 setattr(self, name, sync_data[name])
105 finally:
106 finally:
106 self._property_lock = False
107 self._property_lock = False
107
108
108
109
109 def _handle_property_changed(self, name, old, new):
110 def _handle_property_changed(self, name, old, new):
110 if not self._property_lock and self.comm is not None:
111 if not self._property_lock and self.comm is not None:
111 # TODO: Validate properties.
112 # TODO: Validate properties.
112 # Send new state to frontend
113 # Send new state to frontend
113 self.send_state()
114 self.send_state()
114
115
115
116
116 def _handle_close(self):
117 def _handle_close(self):
117 self.comm = None
118 self.comm = None
118
119
119
120
120 ### Public methods
121 ### Public methods
121 def _repr_widget_(self, view_name=None):
122 def _repr_widget_(self, view_name=None):
122 if not view_name:
123 if not view_name:
123 view_name = self.default_view_name
124 view_name = self.default_view_name
124
125
126 # Require traitlet specified widget js
127 for requirement in self.js_requirements:
128 self._require_js(requirement)
129
125 # Create a comm.
130 # Create a comm.
126 if self.comm is None:
131 if self.comm is None:
127 self.comm = Comm(target_name=self.target_name)
132 self.comm = Comm(target_name=self.target_name)
128 self.comm.on_msg(self._handle_msg)
133 self.comm.on_msg(self._handle_msg)
129 self.comm.on_close(self._handle_close)
134 self.comm.on_close(self._handle_close)
130
135
131 # Make sure model is syncronized
136 # Make sure model is syncronized
132 self.send_state()
137 self.send_state()
133
138
134 # Show view.
139 # Show view.
135 if self.parent is None:
140 if self.parent is None:
136 self.comm.send({"method": "show", "view_name": view_name})
141 self.comm.send({"method": "show", "view_name": view_name})
137 else:
142 else:
138 self.comm.send({"method": "show",
143 self.comm.send({"method": "show",
139 "view_name": view_name,
144 "view_name": view_name,
140 "parent": self.parent.comm.comm_id})
145 "parent": self.parent.comm.comm_id})
141
146
142 # Now show children if any.
147 # Now show children if any.
143 for child in self.children:
148 for child in self.children:
144 child._repr_widget_()
149 child._repr_widget_()
145 return None
150 return None
146
151
147
152
148 def send_state(self):
153 def send_state(self):
149 state = {}
154 state = {}
150 for key in self.keys:
155 for key in self.keys:
151 try:
156 try:
152 state[key] = getattr(self, key)
157 state[key] = getattr(self, key)
153 except Exception as e:
158 except Exception as e:
154 pass # Eat errors, nom nom nom
159 pass # Eat errors, nom nom nom
155 self.comm.send({"method": "update",
160 self.comm.send({"method": "update",
156 "state": state})
161 "state": state})
157 No newline at end of file
162
163 ### Private methods
164
165 def _require_js(self, js_path):
166 # Since we are loading requirements that must be loaded before this call
167 # returns, preform async js load.
168 display(Javascript(data="""
169 $.ajax({
170 url: '{0}',
171 async: false,
172 dataType: "script",
173 timeout: 1000, // Wait a maximum of one second for the script to load.
174 });
175 """.format(js_path))) No newline at end of file
@@ -1,11 +1,12 b''
1 import os
1 import os
2
2
3 from base import Widget
3 from base import Widget
4 from IPython.utils.traitlets import Unicode
4 from IPython.utils.traitlets import Unicode, List
5 from IPython.utils.javascript import display_all_js
5 from IPython.utils.javascript import display_all_js
6
6
7 class ContainerWidget(Widget):
7 class ContainerWidget(Widget):
8 target_name = Unicode('container_widget')
8 target_name = Unicode('container_widget')
9 default_view_name = Unicode('ContainerView')
9 default_view_name = Unicode('ContainerView')
10 js_requirements = List(["notebook/js/widgets/container.js"])
10
11
11 _keys = []
12 _keys = []
@@ -1,17 +1,18 b''
1 import os
1 import os
2
2
3 from base import Widget
3 from base import Widget
4 from IPython.utils.traitlets import Unicode, Float, Bool
4 from IPython.utils.traitlets import Unicode, Float, Bool, List
5 from IPython.utils.javascript import display_all_js
5 from IPython.utils.javascript import display_all_js
6
6
7 class FloatRangeWidget(Widget):
7 class FloatRangeWidget(Widget):
8 target_name = Unicode('FloatRangeWidgetModel')
8 target_name = Unicode('FloatRangeWidgetModel')
9 default_view_name = Unicode('FloatSliderView')
9 default_view_name = Unicode('FloatSliderView')
10 js_requirements = List(["notebook/js/widgets/float_range.js"])
10 _keys = ['value', 'step', 'max', 'min', 'disabled', 'orientation']
11 _keys = ['value', 'step', 'max', 'min', 'disabled', 'orientation']
11
12
12 value = Float(0.0)
13 value = Float(0.0)
13 max = Float(100.0) # Max value
14 max = Float(100.0) # Max value
14 min = Float(0.0) # Min value
15 min = Float(0.0) # Min value
15 disabled = Bool(False) # Enable or disable user changes
16 disabled = Bool(False) # Enable or disable user changes
16 step = Float(0.1) # Minimum step that the value can take (ignored by some views)
17 step = Float(0.1) # Minimum step that the value can take (ignored by some views)
17 orientation = Unicode(u'horizontal') # Vertical or horizontal (ignored by some views)
18 orientation = Unicode(u'horizontal') # Vertical or horizontal (ignored by some views)
@@ -1,17 +1,18 b''
1 import os
1 import os
2
2
3 from base import Widget
3 from base import Widget
4 from IPython.utils.traitlets import Unicode, Int, Bool
4 from IPython.utils.traitlets import Unicode, Int, Bool, List
5 from IPython.utils.javascript import display_all_js
5 from IPython.utils.javascript import display_all_js
6
6
7 class IntRangeWidget(Widget):
7 class IntRangeWidget(Widget):
8 target_name = Unicode('IntRangeWidgetModel')
8 target_name = Unicode('IntRangeWidgetModel')
9 default_view_name = Unicode('IntSliderView')
9 default_view_name = Unicode('IntSliderView')
10 js_requirements = List(["notebook/js/widgets/int_range.js"])
10 _keys = ['value', 'step', 'max', 'min', 'disabled', 'orientation']
11 _keys = ['value', 'step', 'max', 'min', 'disabled', 'orientation']
11
12
12 value = Int(0)
13 value = Int(0)
13 max = Int(100) # Max value
14 max = Int(100) # Max value
14 min = Int(0) # Min value
15 min = Int(0) # Min value
15 disabled = Bool(False) # Enable or disable user changes
16 disabled = Bool(False) # Enable or disable user changes
16 step = Int(1) # Minimum step that the value can take (ignored by some views)
17 step = Int(1) # Minimum step that the value can take (ignored by some views)
17 orientation = Unicode(u'horizontal') # Vertical or horizontal (ignored by some views)
18 orientation = Unicode(u'horizontal') # Vertical or horizontal (ignored by some views)
@@ -1,15 +1,16 b''
1 import os
1 import os
2
2
3 from base import Widget
3 from base import Widget
4 from IPython.utils.traitlets import Unicode, List, Bool
4 from IPython.utils.traitlets import Unicode, List, Bool
5 from IPython.utils.javascript import display_all_js
5 from IPython.utils.javascript import display_all_js
6
6
7 class SelectionWidget(Widget):
7 class SelectionWidget(Widget):
8 target_name = Unicode('SelectionWidgetModel')
8 target_name = Unicode('SelectionWidgetModel')
9 default_view_name = Unicode('DropdownView')
9 default_view_name = Unicode('DropdownView')
10 js_requirements = List(["notebook/js/widgets/selection.js"])
10 _keys = ['value', 'values', 'disabled']
11 _keys = ['value', 'values', 'disabled']
11
12
12 value = Unicode()
13 value = Unicode()
13 values = List() # List of values the user can select
14 values = List() # List of values the user can select
14 disabled = Bool(False) # Enable or disable user changes
15 disabled = Bool(False) # Enable or disable user changes
15 No newline at end of file
16
@@ -1,14 +1,15 b''
1 import os
1 import os
2
2
3 from base import Widget
3 from base import Widget
4 from IPython.utils.traitlets import Unicode, Bool
4 from IPython.utils.traitlets import Unicode, Bool, List
5 from IPython.utils.javascript import display_all_js
5 from IPython.utils.javascript import display_all_js
6
6
7 class StringWidget(Widget):
7 class StringWidget(Widget):
8 target_name = Unicode('StringWidgetModel')
8 target_name = Unicode('StringWidgetModel')
9 default_view_name = Unicode('TextboxView')
9 default_view_name = Unicode('TextboxView')
10 js_requirements = List(["notebook/js/widgets/string.js"])
10 _keys = ['value', 'row_count', 'disabled']
11 _keys = ['value', 'row_count', 'disabled']
11
12
12 value = Unicode()
13 value = Unicode()
13 disabled = Bool(False) # Enable or disable user changes
14 disabled = Bool(False) # Enable or disable user changes
14 No newline at end of file
15
General Comments 0
You need to be logged in to leave comments. Login now