##// END OF EJS Templates
Added Widget Tester example
Jonathan Frederic -
Show More
@@ -0,0 +1,279 b''
1 {
2 "metadata": {
3 "name": ""
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
8 {
9 "cells": [
10 {
11 "cell_type": "code",
12 "collapsed": false,
13 "input": [
14 "from IPython.html import widgets\n",
15 "from IPython.display import display"
16 ],
17 "language": "python",
18 "metadata": {},
19 "outputs": [],
20 "prompt_number": 1
21 },
22 {
23 "cell_type": "markdown",
24 "metadata": {},
25 "source": [
26 "This notebook shows how widgets can be used to manipulate the properties of other widgets."
27 ]
28 },
29 {
30 "cell_type": "markdown",
31 "metadata": {},
32 "source": [
33 "To list the properties of a widget and allow the user to modify them, a function that keeps widgets in sync with traits is required. This function will syncronize a traitlet of one widget with a traitlet of another widget. The method also supports one way syncronization of types that have string representations with string traits."
34 ]
35 },
36 {
37 "cell_type": "code",
38 "collapsed": false,
39 "input": [
40 "def sync_widgets_properties(widget_a, property_a, widget_b, property_b, str_rep=False):\n",
41 " def set_property_a(name, old, new):\n",
42 " if old != new:\n",
43 " if str_rep:\n",
44 " setattr(widget_a, property_a, str(new))\n",
45 " else:\n",
46 " setattr(widget_a, property_a, new)\n",
47 " def set_property_b(name, old, new):\n",
48 " if old != new and not str_rep:\n",
49 " setattr(widget_b, property_b, new)\n",
50 " widget_a.on_trait_change(set_property_b, property_a)\n",
51 " widget_b.on_trait_change(set_property_a, property_b)\n",
52 " if str_rep:\n",
53 " setattr(widget_a, property_a, str(getattr(widget_b, property_b)))\n",
54 " else:\n",
55 " setattr(widget_a, property_a, getattr(widget_b, property_b))\n",
56 " \n",
57 " return widget_a"
58 ],
59 "language": "python",
60 "metadata": {},
61 "outputs": [],
62 "prompt_number": 2
63 },
64 {
65 "cell_type": "markdown",
66 "metadata": {},
67 "source": [
68 "This function will create a best match widget to represent a traitlet of another widget. The function will then bind the two widgets using the `sync_widget_properties` method above."
69 ]
70 },
71 {
72 "cell_type": "code",
73 "collapsed": false,
74 "input": [
75 "def create_hooked_widget(control, key):\n",
76 " property_type = type(getattr(control, key))\n",
77 " \n",
78 " if key == \"orientation\":\n",
79 " return sync_widgets_properties(widgets.SelectionWidget(values=['vertical', 'horizontal'], default_view_name='ToggleButtonsView'), 'value', control, key)\n",
80 " elif property_type is int:\n",
81 " return sync_widgets_properties(widgets.IntWidget(), 'value', control, key)\n",
82 " elif property_type is float:\n",
83 " return sync_widgets_properties(widgets.FloatWidget(), 'value', control, key)\n",
84 " elif property_type is bool:\n",
85 " return sync_widgets_properties(widgets.BoolWidget(), 'value', control, key)\n",
86 " elif property_type is str or property_type is unicode:\n",
87 " return sync_widgets_properties(widgets.StringWidget(), 'value', control, key)\n",
88 " else:\n",
89 " return sync_widgets_properties(widgets.StringWidget(disabled=True), 'value', control, key, str_rep=True)\n",
90 " "
91 ],
92 "language": "python",
93 "metadata": {},
94 "outputs": [],
95 "prompt_number": 3
96 },
97 {
98 "cell_type": "markdown",
99 "metadata": {},
100 "source": [
101 "This function creates a modal that allows the user to read the doc string associated with a callable. The user can then invoke the callbable from the same modal."
102 ]
103 },
104 {
105 "cell_type": "code",
106 "collapsed": false,
107 "input": [
108 "modals = {}\n",
109 "def get_method_modal(control, method_name, parent=None):\n",
110 " if not method_name in dir(control):\n",
111 " return None\n",
112 " if not control in modals:\n",
113 " modals[control] = {}\n",
114 " if not method_name in modals[control]:\n",
115 " new_modal = widgets.ContainerWidget(default_view_name=\"ModalView\")\n",
116 " new_modal.description=\"Invoke \" + method_name\n",
117 " new_modal.button_text = method_name + \"(...)\"\n",
118 " \n",
119 " doc_str = 'No doc string'\n",
120 " try:\n",
121 " doc_str = '<pre>' + getattr(control, method_name).__doc__ + '</pre>'\n",
122 " except:\n",
123 " pass\n",
124 " \n",
125 " doc_label = widgets.StringWidget(parent=new_modal,default_view_name='LabelView', value=doc_str)\n",
126 " doc_label.set_css('color', 'blue')\n",
127 " exec_box = widgets.ContainerWidget(parent=new_modal)\n",
128 " exec_box.hbox()\n",
129 " exec_box.pack_center()\n",
130 " exec_box.align_center()\n",
131 " open_label = widgets.StringWidget(parent=exec_box,default_view_name='LabelView', value=method_name+'(&nbsp;&nbsp;')\n",
132 " exec_str = widgets.StringWidget(parent=exec_box)\n",
133 " close_label = widgets.StringWidget(parent=exec_box,default_view_name='LabelView', value='&nbsp;&nbsp;)')\n",
134 " button_row = widgets.ContainerWidget(parent=new_modal)\n",
135 " button_row.hbox()\n",
136 " button_row.pack_end()\n",
137 " button_box = widgets.ContainerWidget(parent=button_row)\n",
138 " button_box.flex0()\n",
139 " exec_button = widgets.ButtonWidget(parent=button_box, description=\"Execute\")\n",
140 " \n",
141 " def handle_method_exec():\n",
142 " my_control = control\n",
143 " exec \"my_control.\" + method_name.replace('\\n', '') + \"(\" + exec_str.value + \")\" in globals(), locals()\n",
144 " exec_button.on_click(handle_method_exec)\n",
145 " exec_str.on_submit(handle_method_exec)\n",
146 " \n",
147 " if parent is not None:\n",
148 " new_modal.parent = parent\n",
149 " \n",
150 " modals[control][method_name] = new_modal\n",
151 " display(new_modal)\n",
152 " return modals[control][method_name]"
153 ],
154 "language": "python",
155 "metadata": {},
156 "outputs": [],
157 "prompt_number": 4
158 },
159 {
160 "cell_type": "markdown",
161 "metadata": {},
162 "source": [
163 "This function renders the control panel."
164 ]
165 },
166 {
167 "cell_type": "code",
168 "collapsed": false,
169 "input": [
170 "def hook_control(control, parent=None):\n",
171 " explorer = widgets.ContainerWidget()\n",
172 " if parent is not None:\n",
173 " explorer.parent = parent\n",
174 " explorer.hbox()\n",
175 " \n",
176 " viewer = widgets.ContainerWidget(parent=explorer)\n",
177 " viewer.set_css({\n",
178 " 'background': 'white',\n",
179 " 'border': '1px solid #000',\n",
180 " 'overflow': 'hidden',\n",
181 " })\n",
182 " viewer.flex2()\n",
183 " \n",
184 " control.parent = viewer\n",
185 " \n",
186 " side = widgets.ContainerWidget(parent=explorer)\n",
187 " side.flex1()\n",
188 " \n",
189 " side_tab = widgets.MulticontainerWidget(parent=side)\n",
190 " side_tab.set_css('margin', '5px')\n",
191 " \n",
192 " properties = widgets.ContainerWidget(parent=side_tab)\n",
193 " methods = widgets.ContainerWidget(parent=side_tab)\n",
194 " side_tab.set_title(0, 'Properties')\n",
195 " side_tab.set_title(1, 'Methods')\n",
196 " \n",
197 " for key in sorted(control.keys):\n",
198 " property_control = create_hooked_widget(control, key)\n",
199 " property_control.parent = properties\n",
200 " property_control.description = key + ':'\n",
201 " \n",
202 " methods.vbox()\n",
203 " methods.set_css('overflow', 'hidden')\n",
204 " \n",
205 " methods_container = widgets.ContainerWidget(parent=methods)\n",
206 " methods_container.flex1()\n",
207 " methods_buttons = widgets.ContainerWidget(parent=methods)\n",
208 " methods_buttons.flex0()\n",
209 " methods_buttons.hbox()\n",
210 " methods_buttons.pack_end()\n",
211 " methods_buttons.set_css({\n",
212 " 'padding-top': '5px',\n",
213 " 'padding-right': '50px',\n",
214 " })\n",
215 " \n",
216 " execute_button = widgets.ButtonWidget(parent=methods_buttons, description=\"Invoke\")\n",
217 " \n",
218 " method_list = widgets.SelectionWidget(parent=methods_container, default_view_name=\"ListBoxView\")\n",
219 " method_list.description = \"Names:\"\n",
220 " method_list.set_css('height', '400px')\n",
221 " method_list.values = [attr_name for attr_name in dir(control) if callable(getattr(control, attr_name)) and not attr_name.startswith('_')]\n",
222 " \n",
223 " def handle_execute_method():\n",
224 " get_method_modal(control, method_list.value, parent=parent)\n",
225 " execute_button.on_click(handle_execute_method)\n",
226 " \n",
227 " display(explorer) \n",
228 " explorer.add_class('well')\n",
229 " return explorer"
230 ],
231 "language": "python",
232 "metadata": {},
233 "outputs": [],
234 "prompt_number": 5
235 },
236 {
237 "cell_type": "markdown",
238 "metadata": {},
239 "source": [
240 "This final bit of code allows the user to select what control and view he/she wants to manipulate."
241 ]
242 },
243 {
244 "cell_type": "code",
245 "collapsed": false,
246 "input": [
247 "control_tester = widgets.ContainerWidget()\n",
248 "widget_names = [widget_name for widget_name in dir(widgets) if widget_name.endswith('Widget') and widget_name != \"Widget\"]\n",
249 "widget_selector = widgets.SelectionWidget(parent=control_tester, description=\"Widget type:\", values=widget_names)\n",
250 "view_name = widgets.StringWidget(parent=control_tester, description=\"View name (optional):\")\n",
251 "display_button_container = widgets.ContainerWidget(parent=control_tester)\n",
252 "display_button_container.hbox()\n",
253 "display_button_container.pack_end()\n",
254 "display_button_container.set_css(\"padding\", \"5px\")\n",
255 "display_button = widgets.ButtonWidget(parent=display_button_container, description=\"Display\")\n",
256 "display(control_tester)\n",
257 "\n",
258 "last_displayed = [None]\n",
259 "def handle_display():\n",
260 " if last_displayed[0] is not None:\n",
261 " last_displayed[0].close()\n",
262 " widget_type = getattr(widgets, widget_selector.value)\n",
263 " widget = widget_type()\n",
264 " if len(view_name.value) > 0:\n",
265 " widget.default_view_name = view_name.value\n",
266 " last_displayed[0] = hook_control(widget)\n",
267 "display_button.on_click(handle_display)\n",
268 "view_name.on_submit(handle_display)\n"
269 ],
270 "language": "python",
271 "metadata": {},
272 "outputs": [],
273 "prompt_number": 6
274 }
275 ],
276 "metadata": {}
277 }
278 ]
279 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now