##// END OF EJS Templates
Renamed *Widget to *,...
Jonathan Frederic -
Show More
@@ -1,188 +1,188 b''
1 1 var xor = function (a, b) {return !a ^ !b;};
2 2 var isArray = function (a) {
3 3 try {
4 4 return Object.toString.call(a) === "[object Array]" || Object.toString.call(a) === "[object RuntimeArray]";
5 5 } catch (e) {
6 6 return Array.isArray(a);
7 7 }
8 8 };
9 9 var recursive_compare = function(a, b) {
10 10 // Recursively compare two objects.
11 11 var same = true;
12 12 same = same && !xor(a instanceof Object || typeof a == 'object', b instanceof Object || typeof b == 'object');
13 13 same = same && !xor(isArray(a), isArray(b));
14 14
15 15 if (same) {
16 16 if (a instanceof Object) {
17 17 var key;
18 18 for (key in a) {
19 19 if (a.hasOwnProperty(key) && !recursive_compare(a[key], b[key])) {
20 20 same = false;
21 21 break;
22 22 }
23 23 }
24 24 for (key in b) {
25 25 if (b.hasOwnProperty(key) && !recursive_compare(a[key], b[key])) {
26 26 same = false;
27 27 break;
28 28 }
29 29 }
30 30 } else {
31 31 return a === b;
32 32 }
33 33 }
34 34
35 35 return same;
36 36 };
37 37
38 38 // Test the widget framework.
39 39 casper.notebook_test(function () {
40 40 var index;
41 41
42 42 this.then(function () {
43 43
44 44 // Check if the WidgetManager class is defined.
45 45 this.test.assert(this.evaluate(function() {
46 46 return IPython.WidgetManager !== undefined;
47 47 }), 'WidgetManager class is defined');
48 48 });
49 49
50 50 index = this.append_cell(
51 51 'from IPython.html import widgets\n' +
52 52 'from IPython.display import display, clear_output\n' +
53 53 'print("Success")');
54 54 this.execute_cell_then(index);
55 55
56 56 this.then(function () {
57 57 // Check if the widget manager has been instantiated.
58 58 this.test.assert(this.evaluate(function() {
59 59 return IPython.notebook.kernel.widget_manager !== undefined;
60 60 }), 'Notebook widget manager instantiated');
61 61
62 62 // Functions that can be used to test the packing and unpacking APIs
63 63 var that = this;
64 64 var test_pack = function (input) {
65 65 var output = that.evaluate(function(input) {
66 66 var model = new IPython.WidgetModel(IPython.notebook.kernel.widget_manager, undefined);
67 67 var results = model._pack_models(input);
68 68 return results;
69 69 }, {input: input});
70 70 that.test.assert(recursive_compare(input, output),
71 71 JSON.stringify(input) + ' passed through Model._pack_model unchanged');
72 72 };
73 73 var test_unpack = function (input) {
74 74 var output = that.evaluate(function(input) {
75 75 var model = new IPython.WidgetModel(IPython.notebook.kernel.widget_manager, undefined);
76 76 var results = model._unpack_models(input);
77 77 return results;
78 78 }, {input: input});
79 79 that.test.assert(recursive_compare(input, output),
80 80 JSON.stringify(input) + ' passed through Model._unpack_model unchanged');
81 81 };
82 82 var test_packing = function(input) {
83 83 test_pack(input);
84 84 test_unpack(input);
85 85 };
86 86
87 87 test_packing({0: 'hi', 1: 'bye'});
88 88 test_packing(['hi', 'bye']);
89 89 test_packing(['hi', 5]);
90 90 test_packing(['hi', '5']);
91 91 test_packing([1.0, 0]);
92 92 test_packing([1.0, false]);
93 93 test_packing([1, false]);
94 94 test_packing([1, false, {a: 'hi'}]);
95 95 test_packing([1, false, ['hi']]);
96 96
97 97 // Test multi-set, single touch code. First create a custom widget.
98 98 this.evaluate(function() {
99 99 var MultiSetView = IPython.DOMWidgetView.extend({
100 100 render: function(){
101 101 this.model.set('a', 1);
102 102 this.model.set('b', 2);
103 103 this.model.set('c', 3);
104 104 this.touch();
105 105 },
106 106 });
107 107 IPython.WidgetManager.register_widget_view('MultiSetView', MultiSetView);
108 108 }, {});
109 109 });
110 110
111 111 // Try creating the multiset widget, verify that sets the values correctly.
112 112 var multiset = {};
113 113 multiset.index = this.append_cell(
114 114 'from IPython.utils.traitlets import Unicode, CInt\n' +
115 115 'class MultiSetWidget(widgets.Widget):\n' +
116 116 ' _view_name = Unicode("MultiSetView", sync=True)\n' +
117 117 ' a = CInt(0, sync=True)\n' +
118 118 ' b = CInt(0, sync=True)\n' +
119 119 ' c = CInt(0, sync=True)\n' +
120 120 ' d = CInt(-1, sync=True)\n' + // See if it sends a full state.
121 121 ' def _handle_receive_state(self, sync_data):\n' +
122 122 ' widgets.Widget._handle_receive_state(self, sync_data)\n'+
123 123 ' self.d = len(sync_data)\n' +
124 124 'multiset = MultiSetWidget()\n' +
125 125 'display(multiset)\n' +
126 126 'print(multiset.model_id)');
127 127 this.execute_cell_then(multiset.index, function(index) {
128 128 multiset.model_id = this.get_output_cell(index).text.trim();
129 129 });
130 130
131 131 this.wait_for_widget(multiset);
132 132
133 133 index = this.append_cell(
134 134 'print("%d%d%d" % (multiset.a, multiset.b, multiset.c))');
135 135 this.execute_cell_then(index, function(index) {
136 136 this.test.assertEquals(this.get_output_cell(index).text.trim(), '123',
137 137 'Multiple model.set calls and one view.touch update state in back-end.');
138 138 });
139 139
140 140 index = this.append_cell(
141 141 'print("%d" % (multiset.d))');
142 142 this.execute_cell_then(index, function(index) {
143 143 this.test.assertEquals(this.get_output_cell(index).text.trim(), '3',
144 144 'Multiple model.set calls sent a partial state.');
145 145 });
146 146
147 147 var textbox = {};
148 148 throttle_index = this.append_cell(
149 149 'import time\n' +
150 'textbox = widgets.TextWidget()\n' +
150 'textbox = widgets.Text()\n' +
151 151 'display(textbox)\n' +
152 152 'textbox.add_class("my-throttle-textbox", selector="input")\n' +
153 153 'def handle_change(name, old, new):\n' +
154 154 ' display(len(new))\n' +
155 155 ' time.sleep(0.5)\n' +
156 156 'textbox.on_trait_change(handle_change, "value")\n' +
157 157 'print(textbox.model_id)');
158 158 this.execute_cell_then(throttle_index, function(index){
159 159 textbox.model_id = this.get_output_cell(index).text.trim();
160 160
161 161 this.test.assert(this.cell_element_exists(index,
162 162 '.widget-area .widget-subarea'),
163 163 'Widget subarea exists.');
164 164
165 165 this.test.assert(this.cell_element_exists(index,
166 166 '.my-throttle-textbox'), 'Textbox exists.');
167 167
168 168 // Send 20 characters
169 169 this.sendKeys('.my-throttle-textbox', '....................');
170 170 });
171 171
172 172 this.wait_for_widget(textbox);
173 173
174 174 this.then(function () {
175 175 var outputs = this.evaluate(function(i) {
176 176 return IPython.notebook.get_cell(i).output_area.outputs;
177 177 }, {i : throttle_index});
178 178
179 179 // Only 4 outputs should have printed, but because of timing, sometimes
180 180 // 5 outputs will print. All we need to do is verify num outputs <= 5
181 181 // because that is much less than 20.
182 182 this.test.assert(outputs.length <= 5, 'Messages throttled.');
183 183
184 184 // We also need to verify that the last state sent was correct.
185 185 var last_state = outputs[outputs.length-1]['text/plain'];
186 186 this.test.assertEquals(last_state, "20", "Last state sent when throttling.");
187 187 });
188 188 });
@@ -1,86 +1,86 b''
1 1 // Test widget bool class
2 2 casper.notebook_test(function () {
3 3 index = this.append_cell(
4 4 'from IPython.html import widgets\n' +
5 5 'from IPython.display import display, clear_output\n' +
6 6 'print("Success")');
7 7 this.execute_cell_then(index);
8 8
9 9 var bool_index = this.append_cell(
10 'bool_widgets = [widgets.CheckboxWidget(description="Title", value=True),\n' +
11 ' widgets.ToggleButtonWidget(description="Title", value=True)]\n' +
10 'bool_widgets = [widgets.Checkbox(description="Title", value=True),\n' +
11 ' widgets.ToggleButton(description="Title", value=True)]\n' +
12 12 'display(bool_widgets[0])\n' +
13 13 'display(bool_widgets[1])\n' +
14 14 'print("Success")');
15 15 this.execute_cell_then(bool_index, function(index){
16 16
17 17 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
18 18 'Create bool widget cell executed with correct output.');
19 19
20 20 this.test.assert(this.cell_element_exists(index,
21 21 '.widget-area .widget-subarea'),
22 22 'Widget subarea exists.');
23 23
24 24 this.test.assert(this.cell_element_exists(index,
25 25 '.widget-area .widget-subarea .widget-hbox-single input'),
26 26 'Checkbox exists.');
27 27
28 28 this.test.assert(this.cell_element_function(index,
29 29 '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
30 30 'Checkbox is checked.');
31 31
32 32 this.test.assert(this.cell_element_exists(index,
33 33 '.widget-area .widget-subarea .widget-hbox-single .widget-hlabel'),
34 34 'Checkbox label exists.');
35 35
36 36 this.test.assert(this.cell_element_function(index,
37 37 '.widget-area .widget-subarea .widget-hbox-single .widget-hlabel', 'html')=="Title",
38 38 'Checkbox labeled correctly.');
39 39
40 40 this.test.assert(this.cell_element_exists(index,
41 41 '.widget-area .widget-subarea button'),
42 42 'Toggle button exists.');
43 43
44 44 this.test.assert(this.cell_element_function(index,
45 45 '.widget-area .widget-subarea button', 'html')=="Title",
46 46 'Toggle button labeled correctly.');
47 47
48 48 this.test.assert(this.cell_element_function(index,
49 49 '.widget-area .widget-subarea button', 'hasClass', ['active']),
50 50 'Toggle button is toggled.');
51 51
52 52 });
53 53
54 54 index = this.append_cell(
55 55 'bool_widgets[0].value = False\n' +
56 56 'bool_widgets[1].value = False\n' +
57 57 'print("Success")');
58 58 this.execute_cell_then(index, function(index){
59 59
60 60 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
61 61 'Change bool widget value cell executed with correct output.');
62 62
63 63 this.test.assert(! this.cell_element_function(bool_index,
64 64 '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
65 65 'Checkbox is not checked. (1)');
66 66
67 67 this.test.assert(! this.cell_element_function(bool_index,
68 68 '.widget-area .widget-subarea button', 'hasClass', ['active']),
69 69 'Toggle button is not toggled. (1)');
70 70
71 71 // Try toggling the bool by clicking on the checkbox.
72 72 this.cell_element_function(bool_index, '.widget-area .widget-subarea .widget-hbox-single input', 'click');
73 73
74 74 this.test.assert(this.cell_element_function(bool_index,
75 75 '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
76 76 'Checkbox is checked. (2)');
77 77
78 78 // Try toggling the bool by clicking on the toggle button.
79 79 this.cell_element_function(bool_index, '.widget-area .widget-subarea button', 'click');
80 80
81 81 this.test.assert(this.cell_element_function(bool_index,
82 82 '.widget-area .widget-subarea button', 'hasClass', ['active']),
83 83 'Toggle button is toggled. (3)');
84 84
85 85 });
86 86 }); No newline at end of file
@@ -1,43 +1,43 b''
1 1 // Test widget button class
2 2 casper.notebook_test(function () {
3 3 index = this.append_cell(
4 4 'from IPython.html import widgets\n' +
5 5 'from IPython.display import display, clear_output\n' +
6 6 'print("Success")');
7 7 this.execute_cell_then(index);
8 8
9 9 var button_index = this.append_cell(
10 'button = widgets.ButtonWidget(description="Title")\n' +
10 'button = widgets.Button(description="Title")\n' +
11 11 'display(button)\n' +
12 12 'print("Success")\n' +
13 13 'def handle_click(sender):\n' +
14 14 ' display("Clicked")\n' +
15 15 'button.on_click(handle_click)');
16 16 this.execute_cell_then(button_index, function(index){
17 17
18 18 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
19 19 'Create button cell executed with correct output.');
20 20
21 21 this.test.assert(this.cell_element_exists(index,
22 22 '.widget-area .widget-subarea'),
23 23 'Widget subarea exists.');
24 24
25 25 this.test.assert(this.cell_element_exists(index,
26 26 '.widget-area .widget-subarea button'),
27 27 'Widget button exists.');
28 28
29 29 this.test.assert(this.cell_element_function(index,
30 30 '.widget-area .widget-subarea button', 'html')=='Title',
31 31 'Set button description.');
32 32
33 33 this.cell_element_function(index,
34 34 '.widget-area .widget-subarea button', 'click');
35 35 });
36 36
37 37 this.wait_for_output(button_index, 1);
38 38
39 39 this.then(function () {
40 40 this.test.assertEquals(this.get_output_cell(button_index, 1)['text/plain'], "'Clicked'",
41 41 'Button click event fires.');
42 42 });
43 43 }); No newline at end of file
@@ -1,80 +1,80 b''
1 1 // Test container class
2 2 casper.notebook_test(function () {
3 3 index = this.append_cell(
4 4 'from IPython.html import widgets\n' +
5 5 'from IPython.display import display, clear_output\n' +
6 6 'print("Success")');
7 7 this.execute_cell_then(index);
8 8
9 9 var container_index = this.append_cell(
10 'container = widgets.ContainerWidget()\n' +
11 'button = widgets.ButtonWidget()\n'+
10 'container = widgets.Container()\n' +
11 'button = widgets.Button()\n'+
12 12 'container.children = [button]\n'+
13 13 'display(container)\n'+
14 14 'container.add_class("my-test-class")\n'+
15 15 'print("Success")\n');
16 16 this.execute_cell_then(container_index, function(index){
17 17
18 18 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
19 19 'Create container cell executed with correct output.');
20 20
21 21 this.test.assert(this.cell_element_exists(index,
22 22 '.widget-area .widget-subarea'),
23 23 'Widget subarea exists.');
24 24
25 25 this.test.assert(this.cell_element_exists(index,
26 26 '.widget-area .widget-subarea .widget-container'),
27 27 'Widget container exists.');
28 28
29 29 this.test.assert(this.cell_element_exists(index,
30 30 '.widget-area .widget-subarea .my-test-class'),
31 31 'add_class works.');
32 32
33 33 this.test.assert(this.cell_element_exists(index,
34 34 '.widget-area .widget-subarea .my-test-class button'),
35 35 'Container parent/child relationship works.');
36 36 });
37 37
38 38 index = this.append_cell(
39 39 'container.set_css("float", "right")\n'+
40 40 'print("Success")\n');
41 41 this.execute_cell_then(index, function(index){
42 42
43 43 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
44 44 'Set container class CSS cell executed with correct output.');
45 45
46 46 this.test.assert(this.cell_element_function(container_index,
47 47 '.widget-area .widget-subarea .my-test-class', 'css', ['float'])=='right',
48 48 'set_css works.');
49 49 });
50 50
51 51 index = this.append_cell(
52 52 'container.remove_class("my-test-class")\n'+
53 53 'print("Success")\n');
54 54 this.execute_cell_then(index, function(index){
55 55
56 56 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
57 57 'Remove container class cell executed with correct output.');
58 58
59 59 this.test.assert(! this.cell_element_exists(container_index,
60 60 '.widget-area .widget-subarea .my-test-class'),
61 61 'remove_class works.');
62 62 });
63 63
64 64 index = this.append_cell(
65 65 'display(button)\n'+
66 66 'print("Success")\n');
67 67 this.execute_cell_then(index, function(index){
68 68
69 69 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
70 70 'Display container child executed with correct output.');
71 71
72 72 this.test.assert(! this.cell_element_exists(index,
73 73 '.widget-area .widget-subarea .widget-container'),
74 74 'Parent container not displayed.');
75 75
76 76 this.test.assert(this.cell_element_exists(index,
77 77 '.widget-area .widget-subarea button'),
78 78 'Child displayed.');
79 79 });
80 80 }); No newline at end of file
@@ -1,100 +1,100 b''
1 1 // Test widget float class
2 2 casper.notebook_test(function () {
3 3 index = this.append_cell(
4 4 'from IPython.html import widgets\n' +
5 5 'from IPython.display import display, clear_output\n' +
6 6 'print("Success")');
7 7 this.execute_cell_then(index);
8 8
9 9 var float_text = {};
10 10 float_text.query = '.widget-area .widget-subarea .widget-hbox-single .my-second-float-text';
11 11 float_text.index = this.append_cell(
12 'float_widget = widgets.FloatTextWidget()\n' +
12 'float_widget = widgets.FloatText()\n' +
13 13 'display(float_widget)\n' +
14 14 'float_widget.add_class("my-second-float-text", selector="input")\n' +
15 15 'print(float_widget.model_id)\n');
16 16 this.execute_cell_then(float_text.index, function(index){
17 17 float_text.model_id = this.get_output_cell(index).text.trim();
18 18
19 19 this.test.assert(this.cell_element_exists(index,
20 20 '.widget-area .widget-subarea'),
21 21 'Widget subarea exists.');
22 22
23 23 this.test.assert(this.cell_element_exists(index, float_text.query),
24 24 'Widget float textbox exists.');
25 25
26 26 this.cell_element_function(float_text.index, float_text.query, 'val', ['']);
27 27 this.sendKeys(float_text.query, '1.05');
28 28 });
29 29
30 30 this.wait_for_widget(float_text);
31 31
32 32 index = this.append_cell('print(float_widget.value)\n');
33 33 this.execute_cell_then(index, function(index){
34 34 this.test.assertEquals(this.get_output_cell(index).text, '1.05\n',
35 35 'Float textbox value set.');
36 36 this.cell_element_function(float_text.index, float_text.query, 'val', ['']);
37 37 this.sendKeys(float_text.query, '123456789.0');
38 38 });
39 39
40 40 this.wait_for_widget(float_text);
41 41
42 42 index = this.append_cell('print(float_widget.value)\n');
43 43 this.execute_cell_then(index, function(index){
44 44 this.test.assertEquals(this.get_output_cell(index).text, '123456789.0\n',
45 45 'Long float textbox value set (probably triggers throttling).');
46 46 this.cell_element_function(float_text.index, float_text.query, 'val', ['']);
47 47 this.sendKeys(float_text.query, '12hello');
48 48 });
49 49
50 50 this.wait_for_widget(float_text);
51 51
52 52 index = this.append_cell('print(float_widget.value)\n');
53 53 this.execute_cell_then(index, function(index){
54 54 this.test.assertEquals(this.get_output_cell(index).text, '12.0\n',
55 55 'Invald float textbox value caught and filtered.');
56 56 });
57 57
58 58 var float_text_query = '.widget-area .widget-subarea .widget-hbox-single .widget-numeric-text';
59 59 var slider = {};
60 60 slider.query = '.widget-area .widget-subarea .widget-hbox-single .slider';
61 61 slider.index = this.append_cell(
62 'floatrange = [widgets.BoundedFloatTextWidget(), \n' +
63 ' widgets.FloatSliderWidget()]\n' +
62 'floatrange = [widgets.BoundedFloatText(), \n' +
63 ' widgets.FloatSlider()]\n' +
64 64 '[display(floatrange[i]) for i in range(2)]\n' +
65 65 'print("Success")\n');
66 66 this.execute_cell_then(slider.index, function(index){
67 67
68 68 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
69 69 'Create float range cell executed with correct output.');
70 70
71 71 this.test.assert(this.cell_element_exists(index,
72 72 '.widget-area .widget-subarea'),
73 73 'Widget subarea exists.');
74 74
75 75 this.test.assert(this.cell_element_exists(index, slider.query),
76 76 'Widget slider exists.');
77 77
78 78 this.test.assert(this.cell_element_exists(index, float_text_query),
79 79 'Widget float textbox exists.');
80 80 });
81 81
82 82 index = this.append_cell(
83 83 'for widget in floatrange:\n' +
84 84 ' widget.max = 50.0\n' +
85 85 ' widget.min = -50.0\n' +
86 86 ' widget.value = 25.0\n' +
87 87 'print("Success")\n');
88 88 this.execute_cell_then(index, function(index){
89 89
90 90 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
91 91 'Float range properties cell executed with correct output.');
92 92
93 93 this.test.assert(this.cell_element_exists(slider.index, slider.query),
94 94 'Widget slider exists.');
95 95
96 96 this.test.assert(this.cell_element_function(slider.index, slider.query,
97 97 'slider', ['value']) == 25.0,
98 98 'Slider set to Python value.');
99 99 });
100 100 }); No newline at end of file
@@ -1,48 +1,48 b''
1 1 // Test image class
2 2 casper.notebook_test(function () {
3 3 index = this.append_cell(
4 4 'from IPython.html import widgets\n' +
5 5 'from IPython.display import display, clear_output\n' +
6 6 'print("Success")');
7 7 this.execute_cell_then(index);
8 8
9 9 // Get the temporary directory that the test server is running in.
10 10 var cwd = '';
11 11 index = this.append_cell('!echo $(pwd)');
12 12 this.execute_cell_then(index, function(index){
13 13 cwd = this.get_output_cell(index).text.trim();
14 14 });
15 15
16 16 var test_jpg = '/9j/4AAQSkZJRgABAQEASABIAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wgARCAABAAEDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAAA//EABUBAQEAAAAAAAAAAAAAAAAAAAME/9oADAMBAAIQAxAAAAECv//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAQUCf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Bf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Bf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEABj8Cf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8hf//aAAwDAQACAAMAAAAQn//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Qf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Qf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8Qf//Z';
17 17
18 18 var image_index = this.append_cell(
19 19 'import base64\n' +
20 20 'data = base64.b64decode("' + test_jpg + '")\n' +
21 'image = widgets.ImageWidget()\n' +
21 'image = widgets.Image()\n' +
22 22 'image.format = "jpeg"\n' +
23 23 'image.value = data\n' +
24 24 'image.width = "50px"\n' +
25 25 'image.height = "50px"\n' +
26 26 // Set css that will make the image render within the PhantomJS visible
27 27 // window. If we don't do this, the captured image will be black.
28 28 'image.set_css({"background": "blue", "z-index": "9999", "position": "fixed", "top": "0px", "left": "0px"})\n' +
29 29 'display(image)\n' +
30 30 'image.add_class("my-test-image")\n' +
31 31 'print("Success")\n');
32 32 this.execute_cell_then(image_index, function(index){
33 33
34 34 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
35 35 'Create image executed with correct output.');
36 36
37 37 this.test.assert(this.cell_element_exists(index,
38 38 '.widget-area .widget-subarea'),
39 39 'Widget subarea exists.');
40 40
41 41 var img_sel = '.widget-area .widget-subarea img';
42 42 this.test.assert(this.cell_element_exists(index, img_sel), 'Image exists.');
43 43
44 44 // Verify that the image's base64 data has made it into the DOM.
45 45 var img_src = this.cell_element_function(image_index, img_sel, 'attr', ['src']);
46 46 this.test.assert(img_src.indexOf(test_jpg) > -1, 'Image src data exists.');
47 47 });
48 48 }); No newline at end of file
@@ -1,157 +1,157 b''
1 1 // Test widget int class
2 2 casper.notebook_test(function () {
3 3 index = this.append_cell(
4 4 'from IPython.html import widgets\n' +
5 5 'from IPython.display import display, clear_output\n' +
6 6 'print("Success")');
7 7 this.execute_cell_then(index);
8 8
9 9 var int_text = {};
10 10 int_text.query = '.widget-area .widget-subarea .widget-hbox-single .my-second-int-text';
11 11 int_text.index = this.append_cell(
12 'int_widget = widgets.IntTextWidget()\n' +
12 'int_widget = widgets.IntText()\n' +
13 13 'display(int_widget)\n' +
14 14 'int_widget.add_class("my-second-int-text", selector="input")\n' +
15 15 'print(int_widget.model_id)\n');
16 16 this.execute_cell_then(int_text.index, function(index){
17 17 int_text.model_id = this.get_output_cell(index).text.trim();
18 18
19 19 this.test.assert(this.cell_element_exists(index,
20 20 '.widget-area .widget-subarea'),
21 21 'Widget subarea exists.');
22 22
23 23 this.test.assert(this.cell_element_exists(index, int_text.query),
24 24 'Widget int textbox exists.');
25 25
26 26 this.cell_element_function(int_text.index, int_text.query, 'val', ['']);
27 27 this.sendKeys(int_text.query, '1.05');
28 28 });
29 29
30 30 this.wait_for_widget(int_text);
31 31
32 32 index = this.append_cell('print(int_widget.value)\n');
33 33 this.execute_cell_then(index, function(index){
34 34 this.test.assertEquals(this.get_output_cell(index).text, '1\n',
35 35 'Int textbox value set.');
36 36 this.cell_element_function(int_text.index, int_text.query, 'val', ['']);
37 37 this.sendKeys(int_text.query, '123456789');
38 38 });
39 39
40 40 this.wait_for_widget(int_text);
41 41
42 42 index = this.append_cell('print(int_widget.value)\n');
43 43 this.execute_cell_then(index, function(index){
44 44 this.test.assertEquals(this.get_output_cell(index).text, '123456789\n',
45 45 'Long int textbox value set (probably triggers throttling).');
46 46 this.cell_element_function(int_text.index, int_text.query, 'val', ['']);
47 47 this.sendKeys(int_text.query, '12hello');
48 48 });
49 49
50 50 this.wait_for_widget(int_text);
51 51
52 52 index = this.append_cell('print(int_widget.value)\n');
53 53 this.execute_cell_then(index, function(index){
54 54 this.test.assertEquals(this.get_output_cell(index).text, '12\n',
55 55 'Invald int textbox value caught and filtered.');
56 56 });
57 57
58 58 index = this.append_cell(
59 59 'from IPython.html import widgets\n' +
60 60 'from IPython.display import display, clear_output\n' +
61 61 'print("Success")');
62 62 this.execute_cell_then(index);
63 63
64 64
65 65 var slider_query = '.widget-area .widget-subarea .widget-hbox-single .slider';
66 66 var int_text2 = {};
67 67 int_text2.query = '.widget-area .widget-subarea .widget-hbox-single .my-second-num-test-text';
68 68 int_text2.index = this.append_cell(
69 69 'intrange = [widgets.BoundedIntTextWidget(),\n' +
70 70 ' widgets.IntSliderWidget()]\n' +
71 71 '[display(intrange[i]) for i in range(2)]\n' +
72 72 'intrange[0].add_class("my-second-num-test-text", selector="input")\n' +
73 73 'print(intrange[0].model_id)\n');
74 74 this.execute_cell_then(int_text2.index, function(index){
75 75 int_text2.model_id = this.get_output_cell(index).text.trim();
76 76
77 77 this.test.assert(this.cell_element_exists(index,
78 78 '.widget-area .widget-subarea'),
79 79 'Widget subarea exists.');
80 80
81 81 this.test.assert(this.cell_element_exists(index, slider_query),
82 82 'Widget slider exists.');
83 83
84 84 this.test.assert(this.cell_element_exists(index, int_text2.query),
85 85 'Widget int textbox exists.');
86 86 });
87 87
88 88 index = this.append_cell(
89 89 'for widget in intrange:\n' +
90 90 ' widget.max = 50\n' +
91 91 ' widget.min = -50\n' +
92 92 ' widget.value = 25\n' +
93 93 'print("Success")\n');
94 94 this.execute_cell_then(index, function(index){
95 95
96 96 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
97 97 'Int range properties cell executed with correct output.');
98 98
99 99 this.test.assert(this.cell_element_exists(int_text2.index, slider_query),
100 100 'Widget slider exists.');
101 101
102 102 this.test.assert(this.cell_element_function(int_text2.index, slider_query,
103 103 'slider', ['value']) == 25,
104 104 'Slider set to Python value.');
105 105
106 106 this.test.assert(this.cell_element_function(int_text2.index, int_text2.query,
107 107 'val') == 25, 'Int textbox set to Python value.');
108 108
109 109 // Clear the int textbox value and then set it to 1 by emulating
110 110 // keyboard presses.
111 111 this.evaluate(function(q){
112 112 var textbox = IPython.notebook.element.find(q);
113 113 textbox.val('1');
114 114 textbox.trigger('keyup');
115 115 }, {q: int_text2.query});
116 116 });
117 117
118 118 this.wait_for_widget(int_text2);
119 119
120 120 index = this.append_cell('print(intrange[0].value)\n');
121 121 this.execute_cell_then(index, function(index){
122 122 this.test.assertEquals(this.get_output_cell(index).text, '1\n',
123 123 'Int textbox set int range value');
124 124
125 125 // Clear the int textbox value and then set it to 120 by emulating
126 126 // keyboard presses.
127 127 this.evaluate(function(q){
128 128 var textbox = IPython.notebook.element.find(q);
129 129 textbox.val('120');
130 130 textbox.trigger('keyup');
131 131 }, {q: int_text2.query});
132 132 });
133 133
134 134 this.wait_for_widget(int_text2);
135 135
136 136 index = this.append_cell('print(intrange[0].value)\n');
137 137 this.execute_cell_then(index, function(index){
138 138 this.test.assertEquals(this.get_output_cell(index).text, '50\n',
139 139 'Int textbox value bound');
140 140
141 141 // Clear the int textbox value and then set it to 'hello world' by
142 142 // emulating keyboard presses. 'hello world' should get filtered...
143 143 this.evaluate(function(q){
144 144 var textbox = IPython.notebook.element.find(q);
145 145 textbox.val('hello world');
146 146 textbox.trigger('keyup');
147 147 }, {q: int_text2.query});
148 148 });
149 149
150 150 this.wait_for_widget(int_text2);
151 151
152 152 index = this.append_cell('print(intrange[0].value)\n');
153 153 this.execute_cell_then(index, function(index){
154 154 this.test.assertEquals(this.get_output_cell(index).text, '50\n',
155 155 'Invalid int textbox characters ignored');
156 156 });
157 157 }); No newline at end of file
@@ -1,138 +1,138 b''
1 1 // Test selection class
2 2 casper.notebook_test(function () {
3 3 index = this.append_cell(
4 4 'from IPython.html import widgets\n' +
5 5 'from IPython.display import display, clear_output\n' +
6 6 'print("Success")');
7 7 this.execute_cell_then(index);
8 8
9 9 var combo_selector = '.widget-area .widget-subarea .widget-hbox-single .btn-group .widget-combo-btn';
10 10 var multibtn_selector = '.widget-area .widget-subarea .widget-hbox-single .btn-group[data-toggle="buttons-radio"]';
11 11 var radio_selector = '.widget-area .widget-subarea .widget-hbox .widget-radio-box';
12 12 var list_selector = '.widget-area .widget-subarea .widget-hbox .widget-listbox';
13 13
14 14 var selection_index;
15 15 var selection_values = 'abcd';
16 16 var check_state = function(context, index, state){
17 17 if (0 <= index && index < selection_values.length) {
18 18 var multibtn_state = context.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(' + (index + 1) + ')', 'hasClass', ['active']);
19 19 var radio_state = context.cell_element_function(selection_index, radio_selector + ' .radio:nth-child(' + (index + 1) + ') input', 'prop', ['checked']);
20 20 var list_val = context.cell_element_function(selection_index, list_selector, 'val');
21 21 var combo_val = context.cell_element_function(selection_index, combo_selector, 'html');
22 22
23 23 var val = selection_values.charAt(index);
24 24 var list_state = (val == list_val);
25 25 var combo_state = (val == combo_val);
26 26
27 27 return multibtn_state == state &&
28 28 radio_state == state &&
29 29 list_state == state &&
30 30 combo_state == state;
31 31 }
32 32 return true;
33 33 };
34 34
35 35 var verify_selection = function(context, index){
36 36 for (var i = 0; i < selection_values.length; i++) {
37 37 if (!check_state(context, i, i==index)) {
38 38 return false;
39 39 }
40 40 }
41 41 return true;
42 42 };
43 43
44 44 //values=["' + selection_values + '"[i] for i in range(4)]
45 45 selection_index = this.append_cell(
46 46 'values=["' + selection_values + '"[i] for i in range(4)]\n' +
47 'selection = [widgets.DropdownWidget(values=values),\n' +
48 ' widgets.ToggleButtonsWidget(values=values),\n' +
49 ' widgets.RadioButtonsWidget(values=values),\n' +
50 ' widgets.SelectWidget(values=values)]\n' +
47 'selection = [widgets.Dropdown(values=values),\n' +
48 ' widgets.ToggleButtons(values=values),\n' +
49 ' widgets.RadioButtons(values=values),\n' +
50 ' widgets.Select(values=values)]\n' +
51 51 '[display(selection[i]) for i in range(4)]\n' +
52 52 'for widget in selection:\n' +
53 53 ' def handle_change(name,old,new):\n' +
54 54 ' for other_widget in selection:\n' +
55 55 ' other_widget.value = new\n' +
56 56 ' widget.on_trait_change(handle_change, "value")\n' +
57 57 'print("Success")\n');
58 58 this.execute_cell_then(selection_index, function(index){
59 59 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
60 60 'Create selection cell executed with correct output.');
61 61
62 62 this.test.assert(this.cell_element_exists(index,
63 63 '.widget-area .widget-subarea'),
64 64 'Widget subarea exists.');
65 65
66 66 this.test.assert(this.cell_element_exists(index, combo_selector),
67 67 'Widget combobox exists.');
68 68
69 69 this.test.assert(this.cell_element_exists(index, multibtn_selector),
70 70 'Widget multibutton exists.');
71 71
72 72 this.test.assert(this.cell_element_exists(index, radio_selector),
73 73 'Widget radio buttons exists.');
74 74
75 75 this.test.assert(this.cell_element_exists(index, list_selector),
76 76 'Widget list exists.');
77 77
78 78 // Verify that no items are selected.
79 79 this.test.assert(verify_selection(this, 0), 'Default first item selected.');
80 80 });
81 81
82 82 index = this.append_cell(
83 83 'for widget in selection:\n' +
84 84 ' widget.value = "a"\n' +
85 85 'print("Success")\n');
86 86 this.execute_cell_then(index, function(index){
87 87 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
88 88 'Python select item executed with correct output.');
89 89
90 90 // Verify that the first item is selected.
91 91 this.test.assert(verify_selection(this, 0), 'Python selected');
92 92
93 93 // Verify that selecting a radio button updates all of the others.
94 94 this.cell_element_function(selection_index, radio_selector + ' .radio:nth-child(2) input', 'click');
95 95 });
96 96 this.wait_for_idle();
97 97 this.then(function () {
98 98 this.test.assert(verify_selection(this, 1), 'Radio button selection updated view states correctly.');
99 99
100 100 // Verify that selecting a list option updates all of the others.
101 101 this.cell_element_function(selection_index, list_selector + ' option:nth-child(3)', 'click');
102 102 });
103 103 this.wait_for_idle();
104 104 this.then(function () {
105 105 this.test.assert(verify_selection(this, 2), 'List selection updated view states correctly.');
106 106
107 107 // Verify that selecting a multibutton option updates all of the others.
108 108 // Bootstrap3 has changed the toggle button group behavior. Two clicks
109 109 // are required to actually select an item.
110 110 this.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(4)', 'click');
111 111 this.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(4)', 'click');
112 112 });
113 113 this.wait_for_idle();
114 114 this.then(function () {
115 115 this.test.assert(verify_selection(this, 3), 'Multibutton selection updated view states correctly.');
116 116
117 117 // Verify that selecting a combobox option updates all of the others.
118 118 this.cell_element_function(selection_index, '.widget-area .widget-subarea .widget-hbox-single .btn-group ul.dropdown-menu li:nth-child(3) a', 'click');
119 119 });
120 120 this.wait_for_idle();
121 121 this.then(function () {
122 122 this.test.assert(verify_selection(this, 2), 'Combobox selection updated view states correctly.');
123 123 });
124 124
125 125 this.wait_for_idle();
126 126
127 127 index = this.append_cell(
128 128 'for widget in selection:\n' +
129 129 ' d = widget.values.copy()\n' +
130 130 ' d["z"] = "z"\n' +
131 131 ' widget.values = d\n' +
132 132 'selection[0].value = "z"');
133 133 this.execute_cell_then(index, function(index){
134 134
135 135 // Verify that selecting a combobox option updates all of the others.
136 136 this.test.assert(verify_selection(this, 4), 'Item added to selection widget.');
137 137 });
138 138 }); No newline at end of file
@@ -1,113 +1,113 b''
1 1 // Test multicontainer class
2 2 casper.notebook_test(function () {
3 3 index = this.append_cell(
4 4 'from IPython.html import widgets\n' +
5 5 'from IPython.display import display, clear_output\n' +
6 6 'print("Success")');
7 7 this.execute_cell_then(index);
8 8
9 9 // Test tab view
10 10 var multicontainer1_query = '.widget-area .widget-subarea div div.nav-tabs';
11 11 var multicontainer1_index = this.append_cell(
12 'multicontainer = widgets.TabWidget()\n' +
13 'page1 = widgets.TextWidget()\n' +
14 'page2 = widgets.TextWidget()\n' +
15 'page3 = widgets.TextWidget()\n' +
12 'multicontainer = widgets.Tab()\n' +
13 'page1 = widgets.Text()\n' +
14 'page2 = widgets.Text()\n' +
15 'page3 = widgets.Text()\n' +
16 16 'multicontainer.children = [page1, page2, page3]\n' +
17 17 'display(multicontainer)\n' +
18 18 'multicontainer.selected_index = 0\n' +
19 19 'print("Success")\n');
20 20 this.execute_cell_then(multicontainer1_index, function(index){
21 21
22 22 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
23 23 'Create multicontainer cell executed with correct output. (1)');
24 24
25 25 this.test.assert(this.cell_element_exists(index,
26 26 '.widget-area .widget-subarea'),
27 27 'Widget subarea exists.');
28 28
29 29 this.test.assert(this.cell_element_exists(index, multicontainer1_query),
30 30 'Widget tab list exists.');
31 31
32 32 this.test.assert(this.cell_element_exists(index, multicontainer1_query),
33 33 'First widget tab list exists.');
34 34
35 35 // JQuery selector is 1 based
36 36 this.click(multicontainer1_query + ' li:nth-child(2) a');
37 37 });
38 38
39 39 this.wait_for_idle();
40 40
41 41 index = this.append_cell(
42 42 'print(multicontainer.selected_index)\n' +
43 43 'multicontainer.selected_index = 2'); // 0 based
44 44 this.execute_cell_then(index, function(index){
45 45 this.test.assertEquals(this.get_output_cell(index).text, '1\n', // 0 based
46 46 'selected_index property updated with tab change.');
47 47
48 48 // JQuery selector is 1 based
49 49 this.test.assert(!this.cell_element_function(multicontainer1_index, multicontainer1_query + ' li:nth-child(1)', 'hasClass', ['active']),
50 50 "Tab 1 is not selected.");
51 51 this.test.assert(!this.cell_element_function(multicontainer1_index, multicontainer1_query + ' li:nth-child(2)', 'hasClass', ['active']),
52 52 "Tab 2 is not selected.");
53 53 this.test.assert(this.cell_element_function(multicontainer1_index, multicontainer1_query + ' li:nth-child(3)', 'hasClass', ['active']),
54 54 "Tab 3 is selected.");
55 55 });
56 56
57 57 index = this.append_cell('multicontainer.set_title(1, "hello")\nprint("Success")'); // 0 based
58 58 this.execute_cell_then(index, function(index){
59 59 this.test.assert(this.cell_element_function(multicontainer1_index, multicontainer1_query +
60 60 ' li:nth-child(2) a', 'html') == 'hello',
61 61 'Tab page title set (after display).');
62 62 });
63 63
64 64 // Test accordion view
65 65 var multicontainer2_query = '.widget-area .widget-subarea .panel-group';
66 66 var multicontainer2_index = this.append_cell(
67 'multicontainer = widgets.AccordionWidget()\n' +
68 'page1 = widgets.TextWidget()\n' +
69 'page2 = widgets.TextWidget()\n' +
70 'page3 = widgets.TextWidget()\n' +
67 'multicontainer = widgets.Accordion()\n' +
68 'page1 = widgets.Text()\n' +
69 'page2 = widgets.Text()\n' +
70 'page3 = widgets.Text()\n' +
71 71 'multicontainer.children = [page1, page2, page3]\n' +
72 72 'multicontainer.set_title(2, "good")\n' +
73 73 'display(multicontainer)\n' +
74 74 'multicontainer.selected_index = 0\n' +
75 75 'print("Success")\n');
76 76 this.execute_cell_then(multicontainer2_index, function(index){
77 77
78 78 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
79 79 'Create multicontainer cell executed with correct output. (2)');
80 80
81 81 this.test.assert(this.cell_element_exists(index,
82 82 '.widget-area .widget-subarea'),
83 83 'Widget subarea exists.');
84 84
85 85 this.test.assert(this.cell_element_exists(index, multicontainer2_query),
86 86 'Widget accordion exists.');
87 87
88 88 this.test.assert(this.cell_element_exists(index, multicontainer2_query +
89 89 ' .panel:nth-child(1) .panel-collapse'),
90 90 'First accordion page exists.');
91 91
92 92 // JQuery selector is 1 based
93 93 this.test.assert(this.cell_element_function(index, multicontainer2_query +
94 94 ' .panel.panel-default:nth-child(3) .panel-heading .accordion-toggle',
95 95 'html')=='good', 'Accordion page title set (before display).');
96 96
97 97 // JQuery selector is 1 based
98 98 this.click(multicontainer2_query + ' .panel:nth-child(2) .panel-heading .accordion-toggle');
99 99 });
100 100
101 101 this.wait_for_idle();
102 102
103 103 index = this.append_cell('print(multicontainer.selected_index)'); // 0 based
104 104 this.execute_cell_then(index, function(index){
105 105 this.test.assertEquals(this.get_output_cell(index).text, '1\n', // 0 based
106 106 'selected_index property updated with tab change.');
107 107
108 108 var is_collapsed = this.evaluate(function(s){
109 109 return $(s + ' div.panel:nth-child(2) a').hasClass('collapsed'); // 1 based
110 110 }, {s: multicontainer2_query});
111 111 this.test.assertEquals(is_collapsed, false, 'Was tab actually opened?');
112 112 });
113 113 }); No newline at end of file
@@ -1,53 +1,53 b''
1 1 // Test widget string class
2 2 casper.notebook_test(function () {
3 3 index = this.append_cell(
4 4 'from IPython.html import widgets\n' +
5 5 'from IPython.display import display, clear_output\n' +
6 6 'print("Success")');
7 7 this.execute_cell_then(index);
8 8
9 9 var string_index = this.append_cell(
10 'string_widget = [widgets.TextWidget(value = "xyz", placeholder = "abc"),\n' +
11 ' widgets.TextareaWidget(value = "xyz", placeholder = "def"),\n' +
12 ' widgets.HTMLWidget(value = "xyz"),\n' +
13 ' widgets.LatexWidget(value = "$\\\\LaTeX{}$")]\n' +
10 'string_widget = [widgets.Text(value = "xyz", placeholder = "abc"),\n' +
11 ' widgets.Textarea(value = "xyz", placeholder = "def"),\n' +
12 ' widgets.HTML(value = "xyz"),\n' +
13 ' widgets.Latex(value = "$\\\\LaTeX{}$")]\n' +
14 14 '[display(widget) for widget in string_widget]\n'+
15 15 'print("Success")');
16 16 this.execute_cell_then(string_index, function(index){
17 17
18 18 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
19 19 'Create string widget cell executed with correct output.');
20 20
21 21 this.test.assert(this.cell_element_exists(index,
22 22 '.widget-area .widget-subarea'),
23 23 'Widget subarea exists.');
24 24
25 25 this.test.assert(this.cell_element_exists(index,
26 26 '.widget-area .widget-subarea .widget-hbox-single input[type=text]'),
27 27 'Textbox exists.');
28 28
29 29 this.test.assert(this.cell_element_exists(index,
30 30 '.widget-area .widget-subarea .widget-hbox textarea'),
31 31 'Textarea exists.');
32 32
33 33 this.test.assert(this.cell_element_function(index,
34 34 '.widget-area .widget-subarea .widget-hbox textarea', 'val')=='xyz',
35 35 'Python set textarea value.');
36 36
37 37 this.test.assert(this.cell_element_function(index,
38 38 '.widget-area .widget-subarea .widget-hbox-single input[type=text]', 'val')=='xyz',
39 39 'Python set textbox value.');
40 40
41 41 this.test.assert(this.cell_element_exists(string_index,
42 42 '.widget-area .widget-subarea div span.MathJax_Preview'),
43 43 'MathJax parsed the LaTeX successfully.');
44 44
45 45 this.test.assert(this.cell_element_function(index,
46 46 '.widget-area .widget-subarea .widget-hbox textarea', 'attr', ['placeholder'])=='def',
47 47 'Python set textarea placeholder.');
48 48
49 49 this.test.assert(this.cell_element_function(index,
50 50 '.widget-area .widget-subarea .widget-hbox-single input[type=text]', 'attr', ['placeholder'])=='abc',
51 51 'Python set textbox placehoder.');
52 52 });
53 53 });
@@ -1,12 +1,24 b''
1 1 from .widget import Widget, DOMWidget, CallbackDispatcher
2 2
3 from .widget_bool import Checkbox, ToggleButton
4 from .widget_button import Button
5 from .widget_container import Container, Popup, FlexContainer, HBox, VBox
6 from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress
7 from .widget_image import Image
8 from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress
9 from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select
10 from .widget_selectioncontainer import Tab, Accordion
11 from .widget_string import HTML, Latex, Text, Textarea
12 from .interaction import interact, interactive, fixed
13
14 # Deprecated classes
3 15 from .widget_bool import CheckboxWidget, ToggleButtonWidget
4 16 from .widget_button import ButtonWidget
5 from .widget_container import ContainerWidget, PopupWidget, FlexContainerWidget, HBoxWidget, VBoxWidget
17 from .widget_container import ContainerWidget, PopupWidget
6 18 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
7 19 from .widget_image import ImageWidget
8 20 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
9 21 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
10 22 from .widget_selectioncontainer import TabWidget, AccordionWidget
11 23 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
12 24 from .interaction import interact, interactive, fixed
@@ -1,257 +1,257 b''
1 1 """Interact with functions using widgets."""
2 2
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2013, the IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 from __future__ import print_function
16 16
17 17 try: # Python >= 3.3
18 18 from inspect import signature, Parameter
19 19 except ImportError:
20 20 from IPython.utils.signatures import signature, Parameter
21 21 from inspect import getcallargs
22 22
23 23 from IPython.core.getipython import get_ipython
24 from IPython.html.widgets import (Widget, TextWidget,
25 FloatSliderWidget, IntSliderWidget, CheckboxWidget, DropdownWidget,
26 ContainerWidget, DOMWidget)
24 from IPython.html.widgets import (Widget, Text,
25 FloatSlider, IntSlider, Checkbox, Dropdown,
26 Container, DOMWidget)
27 27 from IPython.display import display, clear_output
28 28 from IPython.utils.py3compat import string_types, unicode_type
29 29 from IPython.utils.traitlets import HasTraits, Any, Unicode
30 30
31 31 empty = Parameter.empty
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Classes and Functions
35 35 #-----------------------------------------------------------------------------
36 36
37 37
38 38 def _matches(o, pattern):
39 39 """Match a pattern of types in a sequence."""
40 40 if not len(o) == len(pattern):
41 41 return False
42 42 comps = zip(o,pattern)
43 43 return all(isinstance(obj,kind) for obj,kind in comps)
44 44
45 45
46 46 def _get_min_max_value(min, max, value=None, step=None):
47 47 """Return min, max, value given input values with possible None."""
48 48 if value is None:
49 49 if not max > min:
50 50 raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
51 51 value = min + abs(min-max)/2
52 52 value = type(min)(value)
53 53 elif min is None and max is None:
54 54 if value == 0.0:
55 55 min, max, value = 0.0, 1.0, 0.5
56 56 elif value == 0:
57 57 min, max, value = 0, 1, 0
58 58 elif isinstance(value, (int, float)):
59 59 min, max = (-value, 3*value) if value > 0 else (3*value, -value)
60 60 else:
61 61 raise TypeError('expected a number, got: %r' % value)
62 62 else:
63 63 raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
64 64 if step is not None:
65 65 # ensure value is on a step
66 66 r = (value - min) % step
67 67 value = value - r
68 68 return min, max, value
69 69
70 70 def _widget_abbrev_single_value(o):
71 71 """Make widgets from single values, which can be used as parameter defaults."""
72 72 if isinstance(o, string_types):
73 return TextWidget(value=unicode_type(o))
73 return Text(value=unicode_type(o))
74 74 elif isinstance(o, dict):
75 return DropdownWidget(values=o)
75 return Dropdown(values=o)
76 76 elif isinstance(o, bool):
77 return CheckboxWidget(value=o)
77 return Checkbox(value=o)
78 78 elif isinstance(o, float):
79 79 min, max, value = _get_min_max_value(None, None, o)
80 return FloatSliderWidget(value=o, min=min, max=max)
80 return FloatSlider(value=o, min=min, max=max)
81 81 elif isinstance(o, int):
82 82 min, max, value = _get_min_max_value(None, None, o)
83 return IntSliderWidget(value=o, min=min, max=max)
83 return IntSlider(value=o, min=min, max=max)
84 84 else:
85 85 return None
86 86
87 87 def _widget_abbrev(o):
88 88 """Make widgets from abbreviations: single values, lists or tuples."""
89 89 float_or_int = (float, int)
90 90 if isinstance(o, (list, tuple)):
91 91 if o and all(isinstance(x, string_types) for x in o):
92 return DropdownWidget(values=[unicode_type(k) for k in o])
92 return Dropdown(values=[unicode_type(k) for k in o])
93 93 elif _matches(o, (float_or_int, float_or_int)):
94 94 min, max, value = _get_min_max_value(o[0], o[1])
95 95 if all(isinstance(_, int) for _ in o):
96 cls = IntSliderWidget
96 cls = IntSlider
97 97 else:
98 cls = FloatSliderWidget
98 cls = FloatSlider
99 99 return cls(value=value, min=min, max=max)
100 100 elif _matches(o, (float_or_int, float_or_int, float_or_int)):
101 101 step = o[2]
102 102 if step <= 0:
103 103 raise ValueError("step must be >= 0, not %r" % step)
104 104 min, max, value = _get_min_max_value(o[0], o[1], step=step)
105 105 if all(isinstance(_, int) for _ in o):
106 cls = IntSliderWidget
106 cls = IntSlider
107 107 else:
108 cls = FloatSliderWidget
108 cls = FloatSlider
109 109 return cls(value=value, min=min, max=max, step=step)
110 110 else:
111 111 return _widget_abbrev_single_value(o)
112 112
113 113 def _widget_from_abbrev(abbrev, default=empty):
114 114 """Build a Widget instance given an abbreviation or Widget."""
115 115 if isinstance(abbrev, Widget) or isinstance(abbrev, fixed):
116 116 return abbrev
117 117
118 118 widget = _widget_abbrev(abbrev)
119 119 if default is not empty and isinstance(abbrev, (list, tuple, dict)):
120 120 # if it's not a single-value abbreviation,
121 121 # set the initial value from the default
122 122 try:
123 123 widget.value = default
124 124 except Exception:
125 125 # ignore failure to set default
126 126 pass
127 127 if widget is None:
128 128 raise ValueError("%r cannot be transformed to a Widget" % (abbrev,))
129 129 return widget
130 130
131 131 def _yield_abbreviations_for_parameter(param, kwargs):
132 132 """Get an abbreviation for a function parameter."""
133 133 name = param.name
134 134 kind = param.kind
135 135 ann = param.annotation
136 136 default = param.default
137 137 not_found = (name, empty, empty)
138 138 if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
139 139 if name in kwargs:
140 140 value = kwargs.pop(name)
141 141 elif ann is not empty:
142 142 value = ann
143 143 elif default is not empty:
144 144 value = default
145 145 else:
146 146 yield not_found
147 147 yield (name, value, default)
148 148 elif kind == Parameter.VAR_KEYWORD:
149 149 # In this case name=kwargs and we yield the items in kwargs with their keys.
150 150 for k, v in kwargs.copy().items():
151 151 kwargs.pop(k)
152 152 yield k, v, empty
153 153
154 154 def _find_abbreviations(f, kwargs):
155 155 """Find the abbreviations for a function and kwargs passed to interact."""
156 156 new_kwargs = []
157 157 for param in signature(f).parameters.values():
158 158 for name, value, default in _yield_abbreviations_for_parameter(param, kwargs):
159 159 if value is empty:
160 160 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
161 161 new_kwargs.append((name, value, default))
162 162 return new_kwargs
163 163
164 164 def _widgets_from_abbreviations(seq):
165 165 """Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
166 166 result = []
167 167 for name, abbrev, default in seq:
168 168 widget = _widget_from_abbrev(abbrev, default)
169 169 if not widget.description:
170 170 widget.description = name
171 171 result.append(widget)
172 172 return result
173 173
174 174 def interactive(__interact_f, **kwargs):
175 175 """Build a group of widgets to interact with a function."""
176 176 f = __interact_f
177 177 co = kwargs.pop('clear_output', True)
178 178 kwargs_widgets = []
179 container = ContainerWidget()
179 container = Container()
180 180 container.result = None
181 181 container.args = []
182 182 container.kwargs = dict()
183 183 kwargs = kwargs.copy()
184 184
185 185 new_kwargs = _find_abbreviations(f, kwargs)
186 186 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
187 187 # that will lead to a valid call of the function. This protects against unspecified
188 188 # and doubly-specified arguments.
189 189 getcallargs(f, **{n:v for n,v,_ in new_kwargs})
190 190 # Now build the widgets from the abbreviations.
191 191 kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
192 192
193 193 # This has to be done as an assignment, not using container.children.append,
194 194 # so that traitlets notices the update. We skip any objects (such as fixed) that
195 195 # are not DOMWidgets.
196 196 c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
197 197 container.children = c
198 198
199 199 # Build the callback
200 200 def call_f(name, old, new):
201 201 container.kwargs = {}
202 202 for widget in kwargs_widgets:
203 203 value = widget.value
204 204 container.kwargs[widget.description] = value
205 205 if co:
206 206 clear_output(wait=True)
207 207 try:
208 208 container.result = f(**container.kwargs)
209 209 except Exception as e:
210 210 ip = get_ipython()
211 211 if ip is None:
212 212 container.log.warn("Exception in interact callback: %s", e, exc_info=True)
213 213 else:
214 214 ip.showtraceback()
215 215
216 216 # Wire up the widgets
217 217 for widget in kwargs_widgets:
218 218 widget.on_trait_change(call_f, 'value')
219 219
220 220 container.on_displayed(lambda _: call_f(None, None, None))
221 221
222 222 return container
223 223
224 224 def interact(__interact_f=None, **kwargs):
225 225 """interact(f, **kwargs)
226 226
227 227 Interact with a function using widgets."""
228 228 # positional arg support in: https://gist.github.com/8851331
229 229 if __interact_f is not None:
230 230 # This branch handles the cases:
231 231 # 1. interact(f, **kwargs)
232 232 # 2. @interact
233 233 # def f(*args, **kwargs):
234 234 # ...
235 235 f = __interact_f
236 236 w = interactive(f, **kwargs)
237 237 f.widget = w
238 238 display(w)
239 239 return f
240 240 else:
241 241 # This branch handles the case:
242 242 # @interact(a=30, b=40)
243 243 # def f(*args, **kwargs):
244 244 # ...
245 245 def dec(f):
246 246 w = interactive(f, **kwargs)
247 247 f.widget = w
248 248 display(w)
249 249 return f
250 250 return dec
251 251
252 252 class fixed(HasTraits):
253 253 """A pseudo-widget whose value is fixed and never synced to the client."""
254 254 value = Any(help="Any Python object")
255 255 description = Unicode('', help="Any Python object")
256 256 def __init__(self, value, **kwargs):
257 257 super(fixed, self).__init__(value=value, **kwargs)
@@ -1,34 +1,37 b''
1 """BoolWidget class.
1 """Bool class.
2 2
3 3 Represents a boolean using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from .widget import DOMWidget
17 17 from IPython.utils.traitlets import Unicode, Bool
18 from IPython.utils.warn import DeprecatedClass
18 19
19 20 #-----------------------------------------------------------------------------
20 21 # Classes
21 22 #-----------------------------------------------------------------------------
22 class _BoolWidget(DOMWidget):
23 class _Bool(DOMWidget):
23 24 value = Bool(False, help="Bool value", sync=True)
24 25 description = Unicode('', help="Description of the boolean (label).", sync=True)
25 26 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
26 27
27 28
28 class CheckboxWidget(_BoolWidget):
29 class Checkbox(_Bool):
29 30 _view_name = Unicode('CheckboxView', sync=True)
30 31
31 32
32 class ToggleButtonWidget(_BoolWidget):
33 class ToggleButton(_Bool):
33 34 _view_name = Unicode('ToggleButtonView', sync=True)
34 35
36 CheckboxWidget = DeprecatedClass(Checkbox, 'CheckboxWidget')
37 ToggleButtonWidget = DeprecatedClass(ToggleButton, 'ToggleButtonWidget')
@@ -1,56 +1,60 b''
1 """ButtonWidget class.
1 """Button class.
2 2
3 3 Represents a button in the frontend using a widget. Allows user to listen for
4 4 click events on the button and trigger backend code when the clicks are fired.
5 5 """
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (c) 2013, the IPython Development Team.
8 8 #
9 9 # Distributed under the terms of the Modified BSD License.
10 10 #
11 11 # The full license is in the file COPYING.txt, distributed with this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17 from .widget import DOMWidget, CallbackDispatcher
18 18 from IPython.utils.traitlets import Unicode, Bool
19 from IPython.utils.warn import DeprecatedClass
19 20
20 21 #-----------------------------------------------------------------------------
21 22 # Classes
22 23 #-----------------------------------------------------------------------------
23 class ButtonWidget(DOMWidget):
24 class Button(DOMWidget):
24 25 _view_name = Unicode('ButtonView', sync=True)
25 26
26 27 # Keys
27 28 description = Unicode('', help="Description of the button (label).", sync=True)
28 29 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
29 30
30 31 def __init__(self, **kwargs):
31 32 """Constructor"""
32 super(ButtonWidget, self).__init__(**kwargs)
33 super(Button, self).__init__(**kwargs)
33 34 self._click_handlers = CallbackDispatcher()
34 35 self.on_msg(self._handle_button_msg)
35 36
36 37 def on_click(self, callback, remove=False):
37 38 """Register a callback to execute when the button is clicked.
38 39
39 40 The callback will be called with one argument,
40 41 the clicked button widget instance.
41 42
42 43 Parameters
43 44 ----------
44 45 remove : bool (optional)
45 46 Set to true to remove the callback from the list of callbacks."""
46 47 self._click_handlers.register_callback(callback, remove=remove)
47 48
48 49 def _handle_button_msg(self, _, content):
49 50 """Handle a msg from the front-end.
50 51
51 52 Parameters
52 53 ----------
53 54 content: dict
54 55 Content of the msg."""
55 56 if content.get('event', '') == 'click':
56 57 self._click_handlers(self)
58
59
60 ButtonWidget = DeprecatedClass(Button, 'ButtonWidget')
@@ -1,59 +1,64 b''
1 """ContainerWidget class.
1 """Container class.
2 2
3 3 Represents a container that can be used to group other widgets.
4 4 """
5 5
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 from .widget import DOMWidget
10 10 from IPython.utils.traitlets import Unicode, Tuple, TraitError, Int, CaselessStrEnum
11 from IPython.utils.warn import DeprecatedClass
11 12
12 class ContainerWidget(DOMWidget):
13 class Container(DOMWidget):
13 14 _view_name = Unicode('ContainerView', sync=True)
14 15
15 16 # Child widgets in the container.
16 17 # Using a tuple here to force reassignment to update the list.
17 18 # When a proper notifying-list trait exists, that is what should be used here.
18 19 children = Tuple(sync=True, allow_none=False)
19 20
20 21 def __init__(self, children = (), **kwargs):
21 22 kwargs['children'] = children
22 super(ContainerWidget, self).__init__(**kwargs)
23 self.on_displayed(ContainerWidget._fire_children_displayed)
23 super(Container, self).__init__(**kwargs)
24 self.on_displayed(Container._fire_children_displayed)
24 25
25 26 def _fire_children_displayed(self):
26 27 for child in self.children:
27 28 child._handle_displayed()
28 29
29 30
30 class PopupWidget(ContainerWidget):
31 class Popup(Container):
31 32 _view_name = Unicode('PopupView', sync=True)
32 33
33 34 description = Unicode(sync=True)
34 35 button_text = Unicode(sync=True)
35 36
36 37
37 class FlexContainerWidget(ContainerWidget):
38 class FlexContainer(Container):
38 39 _view_name = Unicode('FlexContainerView', sync=True)
39 40 flex = Int(0, sync=True, help="""Specify the flexible-ness of the model.""")
40 41 def _flex_changed(self, name, old, new):
41 42 new = min(max(0, new), 2)
42 43 if self.flex != new:
43 44 self.flex = new
44 45
45 46 _locations = ['start', 'center', 'end', 'baseline', 'stretch']
46 47 pack = CaselessStrEnum(
47 48 values=_locations,
48 49 default_value='start', allow_none=False, sync=True)
49 50 align = CaselessStrEnum(
50 51 values=_locations,
51 52 default_value='start', allow_none=False, sync=True)
52 53
53 54
54 class VBoxWidget(FlexContainerWidget):
55 class VBox(FlexContainer):
55 56 _view_name = Unicode('VBoxContainerView', sync=True)
56 57
57 58
58 class HBoxWidget(FlexContainerWidget):
59 class HBox(FlexContainer):
59 60 _view_name = Unicode('HBoxContainerView', sync=True)
61
62 ContainerWidget = DeprecatedClass(Container, 'ContainerWidget')
63 PopupWidget = DeprecatedClass(Popup, 'PopupWidget')
64
@@ -1,61 +1,69 b''
1 """FloatWidget class.
1 """Float class.
2 2
3 3 Represents an unbounded float using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from .widget import DOMWidget
17 17 from IPython.utils.traitlets import Unicode, CFloat, Bool, Enum
18 from IPython.utils.warn import DeprecatedClass
18 19
19 20 #-----------------------------------------------------------------------------
20 21 # Classes
21 22 #-----------------------------------------------------------------------------
22 class _FloatWidget(DOMWidget):
23 class _Float(DOMWidget):
23 24 value = CFloat(0.0, help="Float value", sync=True)
24 25 disabled = Bool(False, help="Enable or disable user changes", sync=True)
25 26 description = Unicode(help="Description of the value this widget represents", sync=True)
26 27
27 28
28 class _BoundedFloatWidget(_FloatWidget):
29 class _BoundedFloat(_Float):
29 30 max = CFloat(100.0, help="Max value", sync=True)
30 31 min = CFloat(0.0, help="Min value", sync=True)
31 32 step = CFloat(0.1, help="Minimum step that the value can take (ignored by some views)", sync=True)
32 33
33 34 def __init__(self, *pargs, **kwargs):
34 35 """Constructor"""
35 36 DOMWidget.__init__(self, *pargs, **kwargs)
36 37 self._validate('value', None, self.value)
37 38 self.on_trait_change(self._validate, ['value', 'min', 'max'])
38 39
39 40 def _validate(self, name, old, new):
40 41 """Validate value, max, min."""
41 42 if self.min > new or new > self.max:
42 43 self.value = min(max(new, self.min), self.max)
43 44
44 45
45 class FloatTextWidget(_FloatWidget):
46 class FloatText(_Float):
46 47 _view_name = Unicode('FloatTextView', sync=True)
47 48
48 49
49 class BoundedFloatTextWidget(_BoundedFloatWidget):
50 class BoundedFloatText(_BoundedFloat):
50 51 _view_name = Unicode('FloatTextView', sync=True)
51 52
52 53
53 class FloatSliderWidget(_BoundedFloatWidget):
54 class FloatSlider(_BoundedFloat):
54 55 _view_name = Unicode('FloatSliderView', sync=True)
55 56 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
56 57 help="Vertical or horizontal.", sync=True)
57 58 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
58 59
59 60
60 class FloatProgressWidget(_BoundedFloatWidget):
61 class FloatProgress(_BoundedFloat):
61 62 _view_name = Unicode('ProgressView', sync=True)
63
64 _FloatWidget = DeprecatedClass(_Float, '_FloatWidget')
65 _BoundedFloatWidget = DeprecatedClass(_BoundedFloat, '_BoundedFloatWidget')
66 FloatTextWidget = DeprecatedClass(FloatText, 'FloatTextWidget')
67 BoundedFloatTextWidget = DeprecatedClass(BoundedFloatText, 'BoundedFloatTextWidget')
68 FloatSliderWidget = DeprecatedClass(FloatSlider, 'FloatSliderWidget')
69 FloatProgressWidget = DeprecatedClass(FloatProgress, 'FloatProgressWidget')
@@ -1,35 +1,38 b''
1 """ImageWidget class.
1 """Image class.
2 2
3 3 Represents an image in the frontend using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 import base64
17 17
18 18 from .widget import DOMWidget
19 19 from IPython.utils.traitlets import Unicode, CUnicode, Bytes
20 from IPython.utils.warn import DeprecatedClass
20 21
21 22 #-----------------------------------------------------------------------------
22 23 # Classes
23 24 #-----------------------------------------------------------------------------
24 class ImageWidget(DOMWidget):
25 class Image(DOMWidget):
25 26 _view_name = Unicode('ImageView', sync=True)
26 27
27 28 # Define the custom state properties to sync with the front-end
28 29 format = Unicode('png', sync=True)
29 30 width = CUnicode(sync=True)
30 31 height = CUnicode(sync=True)
31 32 _b64value = Unicode(sync=True)
32 33
33 34 value = Bytes()
34 35 def _value_changed(self, name, old, new):
35 36 self._b64value = base64.b64encode(new)
37
38 ImageWidget = DeprecatedClass(Image, 'ImageWidget')
@@ -1,60 +1,68 b''
1 """IntWidget class.
1 """Int class.
2 2
3 3 Represents an unbounded int using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from .widget import DOMWidget
17 17 from IPython.utils.traitlets import Unicode, CInt, Bool, Enum
18 from IPython.utils.warn import DeprecatedClass
18 19
19 20 #-----------------------------------------------------------------------------
20 21 # Classes
21 22 #-----------------------------------------------------------------------------
22 class _IntWidget(DOMWidget):
23 class _Int(DOMWidget):
23 24 value = CInt(0, help="Int value", sync=True)
24 25 disabled = Bool(False, help="Enable or disable user changes", sync=True)
25 26 description = Unicode(help="Description of the value this widget represents", sync=True)
26 27
27 28
28 class _BoundedIntWidget(_IntWidget):
29 class _BoundedInt(_Int):
29 30 step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
30 31 max = CInt(100, help="Max value", sync=True)
31 32 min = CInt(0, help="Min value", sync=True)
32 33
33 34 def __init__(self, *pargs, **kwargs):
34 35 """Constructor"""
35 36 DOMWidget.__init__(self, *pargs, **kwargs)
36 37 self.on_trait_change(self._validate, ['value', 'min', 'max'])
37 38
38 39 def _validate(self, name, old, new):
39 40 """Validate value, max, min."""
40 41 if self.min > new or new > self.max:
41 42 self.value = min(max(new, self.min), self.max)
42 43
43 44
44 class IntTextWidget(_IntWidget):
45 class IntText(_Int):
45 46 _view_name = Unicode('IntTextView', sync=True)
46 47
47 48
48 class BoundedIntTextWidget(_BoundedIntWidget):
49 class BoundedIntText(_BoundedInt):
49 50 _view_name = Unicode('IntTextView', sync=True)
50 51
51 52
52 class IntSliderWidget(_BoundedIntWidget):
53 class IntSlider(_BoundedInt):
53 54 _view_name = Unicode('IntSliderView', sync=True)
54 55 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
55 56 help="Vertical or horizontal.", sync=True)
56 57 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
57 58
58 59
59 class IntProgressWidget(_BoundedIntWidget):
60 class IntProgress(_BoundedInt):
60 61 _view_name = Unicode('ProgressView', sync=True)
62
63 _IntWidget = DeprecatedClass(_Int, '_IntWidget')
64 _BoundedIntWidget = DeprecatedClass(_BoundedInt, '_BoundedIntWidget')
65 IntTextWidget = DeprecatedClass(IntText, 'IntTextWidget')
66 BoundedIntTextWidget = DeprecatedClass(BoundedIntText, 'BoundedIntTextWidget')
67 IntSliderWidget = DeprecatedClass(IntSlider, 'IntSliderWidget')
68 IntProgressWidget = DeprecatedClass(IntProgress, 'IntProgressWidget')
@@ -1,125 +1,132 b''
1 """SelectionWidget classes.
1 """Selection classes.
2 2
3 3 Represents an enumeration using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 from collections import OrderedDict
18 18 from threading import Lock
19 19
20 20 from .widget import DOMWidget
21 21 from IPython.utils.traitlets import Unicode, List, Bool, Any, Dict, TraitError
22 22 from IPython.utils.py3compat import unicode_type
23 from IPython.utils.warn import DeprecatedClass
23 24
24 25 #-----------------------------------------------------------------------------
25 26 # SelectionWidget
26 27 #-----------------------------------------------------------------------------
27 class _SelectionWidget(DOMWidget):
28 class _Selection(DOMWidget):
28 29 """Base class for Selection widgets
29 30
30 31 ``values`` can be specified as a list or dict. If given as a list,
31 32 it will be transformed to a dict of the form ``{str(value):value}``.
32 33 """
33 34
34 35 value = Any(help="Selected value")
35 36 values = Dict(help="""Dictionary of {name: value} the user can select.
36 37
37 38 The keys of this dictionary are the strings that will be displayed in the UI,
38 39 representing the actual Python choices.
39 40
40 41 The keys of this dictionary are also available as value_names.
41 42 """)
42 43 value_name = Unicode(help="The name of the selected value", sync=True)
43 44 value_names = List(Unicode, help="""Read-only list of names for each value.
44 45
45 46 If values is specified as a list, this is the string representation of each element.
46 47 Otherwise, it is the keys of the values dictionary.
47 48
48 49 These strings are used to display the choices in the front-end.""", sync=True)
49 50 disabled = Bool(False, help="Enable or disable user changes", sync=True)
50 51 description = Unicode(help="Description of the value this widget represents", sync=True)
51 52
52 53
53 54 def __init__(self, *args, **kwargs):
54 55 self.value_lock = Lock()
55 56 self._in_values_changed = False
56 57 if 'values' in kwargs:
57 58 values = kwargs['values']
58 59 # convert list values to an dict of {str(v):v}
59 60 if isinstance(values, list):
60 61 # preserve list order with an OrderedDict
61 62 kwargs['values'] = OrderedDict((unicode_type(v), v) for v in values)
62 63 # python3.3 turned on hash randomization by default - this means that sometimes, randomly
63 64 # we try to set value before setting values, due to dictionary ordering. To fix this, force
64 65 # the setting of self.values right now, before anything else runs
65 66 self.values = kwargs.pop('values')
66 67 DOMWidget.__init__(self, *args, **kwargs)
67 68
68 69 def _values_changed(self, name, old, new):
69 70 """Handles when the values dict has been changed.
70 71
71 72 Setting values implies setting value names from the keys of the dict.
72 73 """
73 74 self._in_values_changed = True
74 75 try:
75 76 self.value_names = list(new.keys())
76 77 finally:
77 78 self._in_values_changed = False
78 79
79 80 # ensure that the chosen value is one of the choices
80 81 if self.value not in new.values():
81 82 self.value = next(iter(new.values()))
82 83
83 84 def _value_names_changed(self, name, old, new):
84 85 if not self._in_values_changed:
85 86 raise TraitError("value_names is a read-only proxy to values.keys(). Use the values dict instead.")
86 87
87 88 def _value_changed(self, name, old, new):
88 89 """Called when value has been changed"""
89 90 if self.value_lock.acquire(False):
90 91 try:
91 92 # Reverse dictionary lookup for the value name
92 93 for k,v in self.values.items():
93 94 if new == v:
94 95 # set the selected value name
95 96 self.value_name = k
96 97 return
97 98 # undo the change, and raise KeyError
98 99 self.value = old
99 100 raise KeyError(new)
100 101 finally:
101 102 self.value_lock.release()
102 103
103 104 def _value_name_changed(self, name, old, new):
104 105 """Called when the value name has been changed (typically by the frontend)."""
105 106 if self.value_lock.acquire(False):
106 107 try:
107 108 self.value = self.values[new]
108 109 finally:
109 110 self.value_lock.release()
110 111
111 112
112 class ToggleButtonsWidget(_SelectionWidget):
113 class ToggleButtons(_Selection):
113 114 _view_name = Unicode('ToggleButtonsView', sync=True)
114 115
115 116
116 class DropdownWidget(_SelectionWidget):
117 class Dropdown(_Selection):
117 118 _view_name = Unicode('DropdownView', sync=True)
118 119
119 120
120 class RadioButtonsWidget(_SelectionWidget):
121 class RadioButtons(_Selection):
121 122 _view_name = Unicode('RadioButtonsView', sync=True)
122 123
123 124
124 class SelectWidget(_SelectionWidget):
125 class Select(_Selection):
125 126 _view_name = Unicode('SelectView', sync=True)
127
128 _SelectionWidget = DeprecatedClass(_Selection, '_SelectionWidget')
129 ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
130 DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
131 RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget')
132 SelectWidget = DeprecatedClass(Select, 'SelectWidget')
@@ -1,58 +1,63 b''
1 """SelectionContainerWidget class.
1 """SelectionContainer class.
2 2
3 3 Represents a multipage container that can be used to group other widgets into
4 4 pages.
5 5 """
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (c) 2013, the IPython Development Team.
8 8 #
9 9 # Distributed under the terms of the Modified BSD License.
10 10 #
11 11 # The full license is in the file COPYING.txt, distributed with this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17 from .widget_container import ContainerWidget
18 18 from IPython.utils.traitlets import Unicode, Dict, CInt
19 from IPython.utils.warn import DeprecatedClass
19 20
20 21 #-----------------------------------------------------------------------------
21 22 # Classes
22 23 #-----------------------------------------------------------------------------
23 class _SelectionContainerWidget(ContainerWidget):
24 class _SelectionContainer(ContainerWidget):
24 25 _titles = Dict(help="Titles of the pages", sync=True)
25 26 selected_index = CInt(0, sync=True)
26 27
27 28 # Public methods
28 29 def set_title(self, index, title):
29 30 """Sets the title of a container page.
30 31
31 32 Parameters
32 33 ----------
33 34 index : int
34 35 Index of the container page
35 36 title : unicode
36 37 New title"""
37 38 self._titles[index] = title
38 39 self.send_state('_titles')
39 40
40 41 def get_title(self, index):
41 42 """Gets the title of a container pages.
42 43
43 44 Parameters
44 45 ----------
45 46 index : int
46 47 Index of the container page"""
47 48 if index in self._titles:
48 49 return self._titles[index]
49 50 else:
50 51 return None
51 52
52 53
53 class AccordionWidget(_SelectionContainerWidget):
54 class Accordion(_SelectionContainer):
54 55 _view_name = Unicode('AccordionView', sync=True)
55 56
56 57
57 class TabWidget(_SelectionContainerWidget):
58 class Tab(_SelectionContainer):
58 59 _view_name = Unicode('TabView', sync=True)
60
61 _SelectionContainerWidget = DeprecatedClass(_SelectionContainer, '_SelectionContainerWidget')
62 AccordionWidget = DeprecatedClass(Accordion, 'AccordionWidget')
63 TabWidget = DeprecatedClass(Tab, 'TabWidget')
@@ -1,73 +1,80 b''
1 """StringWidget class.
1 """String class.
2 2
3 3 Represents a unicode string using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from .widget import DOMWidget, CallbackDispatcher
17 17 from IPython.utils.traitlets import Unicode, Bool
18 from IPython.utils.warn import DeprecatedClass
18 19
19 20 #-----------------------------------------------------------------------------
20 21 # Classes
21 22 #-----------------------------------------------------------------------------
22 class _StringWidget(DOMWidget):
23 class _String(DOMWidget):
23 24 value = Unicode(help="String value", sync=True)
24 25 disabled = Bool(False, help="Enable or disable user changes", sync=True)
25 26 description = Unicode(help="Description of the value this widget represents", sync=True)
26 27 placeholder = Unicode("", help="Placeholder text to display when nothing has been typed", sync=True)
27 28
28 29
29 class HTMLWidget(_StringWidget):
30 class HTML(_String):
30 31 _view_name = Unicode('HTMLView', sync=True)
31 32
32 33
33 class LatexWidget(_StringWidget):
34 class Latex(_String):
34 35 _view_name = Unicode('LatexView', sync=True)
35 36
36 37
37 class TextareaWidget(_StringWidget):
38 class Textarea(_String):
38 39 _view_name = Unicode('TextareaView', sync=True)
39 40
40 41 def scroll_to_bottom(self):
41 42 self.send({"method": "scroll_to_bottom"})
42 43
43 44
44 class TextWidget(_StringWidget):
45 class Text(_String):
45 46 _view_name = Unicode('TextView', sync=True)
46 47
47 48 def __init__(self, **kwargs):
48 super(TextWidget, self).__init__(**kwargs)
49 super(Text, self).__init__(**kwargs)
49 50 self._submission_callbacks = CallbackDispatcher()
50 51 self.on_msg(self._handle_string_msg)
51 52
52 53 def _handle_string_msg(self, _, content):
53 54 """Handle a msg from the front-end.
54 55
55 56 Parameters
56 57 ----------
57 58 content: dict
58 59 Content of the msg."""
59 60 if content.get('event', '') == 'submit':
60 61 self._submission_callbacks(self)
61 62
62 63 def on_submit(self, callback, remove=False):
63 64 """(Un)Register a callback to handle text submission.
64 65
65 66 Triggered when the user clicks enter.
66 67
67 68 Parameters
68 69 ----------
69 70 callback: callable
70 71 Will be called with exactly one argument: the Widget instance
71 72 remove: bool (optional)
72 73 Whether to unregister the callback"""
73 74 self._submission_callbacks.register_callback(callback, remove=remove)
75
76 _StringWidget = DeprecatedClass(_String, '_StringWidget')
77 HTMLWidget = DeprecatedClass(HTML, 'HTMLWidget')
78 LatexWidget = DeprecatedClass(Latex, 'LatexWidget')
79 TextareaWidget = DeprecatedClass(Textarea, 'TextareaWidget')
80 TextWidget = DeprecatedClass(Text, 'TextWidget')
@@ -1,67 +1,81 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for warnings. Shoudn't we just use the built in warnings module.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 import sys
19 import warnings
19 20
20 21 from IPython.utils import io
21 22
22 23 #-----------------------------------------------------------------------------
23 24 # Code
24 25 #-----------------------------------------------------------------------------
25 26
26 27 def warn(msg,level=2,exit_val=1):
27 28 """Standard warning printer. Gives formatting consistency.
28 29
29 30 Output is sent to io.stderr (sys.stderr by default).
30 31
31 32 Options:
32 33
33 34 -level(2): allows finer control:
34 35 0 -> Do nothing, dummy function.
35 36 1 -> Print message.
36 37 2 -> Print 'WARNING:' + message. (Default level).
37 38 3 -> Print 'ERROR:' + message.
38 39 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
39 40
40 41 -exit_val (1): exit value returned by sys.exit() for a level 4
41 42 warning. Ignored for all other levels."""
42 43
43 44 if level>0:
44 45 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
45 46 print(header[level], msg, sep='', file=io.stderr)
46 47 if level == 4:
47 48 print('Exiting.\n', file=io.stderr)
48 49 sys.exit(exit_val)
49 50
50 51
51 52 def info(msg):
52 53 """Equivalent to warn(msg,level=1)."""
53 54
54 55 warn(msg,level=1)
55 56
56 57
57 58 def error(msg):
58 59 """Equivalent to warn(msg,level=3)."""
59 60
60 61 warn(msg,level=3)
61 62
62 63
63 64 def fatal(msg,exit_val=1):
64 65 """Equivalent to warn(msg,exit_val=exit_val,level=4)."""
65 66
66 67 warn(msg,exit_val=exit_val,level=4)
67 68
69
70 def DeprecatedClass(base, class_name):
71 # Hook the init method of the base class.
72 def init_hook(self, *pargs, **kwargs):
73 base.__init__(self, *pargs, **kwargs)
74
75 # Warn once per class.
76 if base not in DeprecatedClass._warned_classes:
77 DeprecatedClass._warned_classes.append(base)
78 warn('"{}" is deprecated, please use "{}" instead.'.format(
79 class_name, base.__name__))
80 return type(class_name, (base,), {'__init__': init_hook})
81 DeprecatedClass._warned_classes = []
General Comments 0
You need to be logged in to leave comments. Login now