0
# IPython Integration
1
2
Cell magic support for creating virtual files and managing widget development workflows within Jupyter notebooks. This integration provides seamless development experiences with inline content management and dynamic file creation.
3
4
## Capabilities
5
6
### Extension Loading
7
8
Function to load the anywidget IPython extension, enabling cell magic functionality.
9
10
```python { .api }
11
def load_ipython_extension(ipython):
12
"""
13
Load the IPython extension for anywidget magics.
14
15
Registers the AnyWidgetMagics class with the IPython shell,
16
enabling cell magic commands for virtual file management.
17
18
Parameters:
19
ipython: IPython.core.interactiveshell.InteractiveShell instance
20
21
Usage:
22
%load_ext anywidget
23
"""
24
```
25
26
### AnyWidgetMagics Class
27
28
IPython magics class providing cell magic commands for virtual file management and widget development.
29
30
```python { .api }
31
class AnyWidgetMagics(Magics):
32
"""
33
A set of IPython magics for working with virtual files.
34
35
Provides cell magic commands that enable inline definition
36
of ES modules and CSS for widget development within notebooks.
37
"""
38
39
def __init__(self, shell):
40
"""
41
Initialize the magics with IPython shell reference.
42
43
Parameters:
44
shell: InteractiveShell instance
45
"""
46
```
47
48
### Virtual File Cell Magic
49
50
Cell magic for creating virtual files from notebook cell contents.
51
52
```python { .api }
53
def vfile(self, line: str, cell: str):
54
"""
55
Create a virtual file with the contents of the cell.
56
57
Cell magic that captures cell content and creates a virtual file
58
that can be referenced by widgets using the vfile: protocol.
59
60
Parameters:
61
line (str): Magic line containing file name argument
62
cell (str): Cell contents to store as virtual file
63
64
Usage:
65
%%vfile my-widget.js
66
function render({ model, el }) {
67
el.innerHTML = "<h1>Hello from virtual file!</h1>";
68
}
69
export default { render };
70
"""
71
```
72
73
### Virtual File Management
74
75
Line magic for clearing virtual file registry.
76
77
```python { .api }
78
def clear_vfiles(self, line: str):
79
"""
80
Clear all virtual files from the registry.
81
82
Line magic that removes all virtual files created with %%vfile,
83
useful for cleaning up development environment.
84
85
Parameters:
86
line (str): Magic line (unused)
87
88
Usage:
89
%clear_vfiles
90
"""
91
```
92
93
## Usage Examples
94
95
### Loading the Extension
96
97
```python
98
# Load anywidget IPython extension
99
%load_ext anywidget
100
101
# Alternative: Load programmatically
102
import anywidget
103
anywidget.load_ipython_extension(get_ipython())
104
```
105
106
### Basic Virtual File Creation
107
108
```python
109
# Create virtual JavaScript file
110
%%vfile counter.js
111
function render({ model, el }) {
112
let count = () => model.get("value");
113
let btn = document.createElement("button");
114
btn.innerHTML = `Count: ${count()}`;
115
116
btn.addEventListener("click", () => {
117
model.set("value", count() + 1);
118
});
119
120
model.on("change:value", () => {
121
btn.innerHTML = `Count: ${count()}`;
122
});
123
124
el.appendChild(btn);
125
}
126
export default { render };
127
```
128
129
```python
130
# Create virtual CSS file
131
%%vfile counter.css
132
button {
133
background: #007cba;
134
color: white;
135
border: none;
136
padding: 10px 20px;
137
border-radius: 5px;
138
cursor: pointer;
139
font-size: 16px;
140
transition: background-color 0.2s;
141
}
142
143
button:hover {
144
background: #005a8b;
145
}
146
```
147
148
```python
149
# Use virtual files in widget
150
import anywidget
151
import traitlets as t
152
153
class CounterWidget(anywidget.AnyWidget):
154
_esm = "vfile:counter.js" # Reference virtual file
155
_css = "vfile:counter.css" # Reference virtual file
156
157
value = t.Int(0).tag(sync=True)
158
159
widget = CounterWidget()
160
widget # Displays counter with styling from virtual files
161
```
162
163
### Dynamic Widget Development
164
165
```python
166
# Define widget template
167
%%vfile template.js
168
function render({ model, el }) {
169
let template = model.get("template");
170
let data = model.get("data");
171
172
// Simple template rendering
173
let html = template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
174
return data[key] || match;
175
});
176
177
el.innerHTML = html;
178
179
model.on("change", () => {
180
let newTemplate = model.get("template");
181
let newData = model.get("data");
182
let newHtml = newTemplate.replace(/\{\{(\w+)\}\}/g, (match, key) => {
183
return newData[key] || match;
184
});
185
el.innerHTML = newHtml;
186
});
187
}
188
export default { render };
189
```
190
191
```python
192
import anywidget
193
import traitlets as t
194
195
class TemplateWidget(anywidget.AnyWidget):
196
_esm = "vfile:template.js"
197
198
template = t.Unicode("<h1>{{title}}</h1><p>{{message}}</p>").tag(sync=True)
199
data = t.Dict({"title": "Hello", "message": "World"}).tag(sync=True)
200
201
widget = TemplateWidget()
202
widget.data = {"title": "Dynamic Title", "message": "This content updates!"}
203
```
204
205
### Interactive Development Workflow
206
207
```python
208
# Create interactive chart widget
209
%%vfile chart.js
210
function render({ model, el }) {
211
// Simple bar chart implementation
212
let data = model.get("data");
213
let options = model.get("options");
214
215
let renderChart = () => {
216
el.innerHTML = "";
217
218
let container = document.createElement("div");
219
container.style.display = "flex";
220
container.style.alignItems = "end";
221
container.style.height = "200px";
222
container.style.padding = "20px";
223
container.style.background = options.background || "#f9f9f9";
224
225
data.forEach((value, index) => {
226
let bar = document.createElement("div");
227
bar.style.width = "30px";
228
bar.style.height = `${value * 2}px`;
229
bar.style.background = options.barColor || "#007cba";
230
bar.style.margin = "0 2px";
231
bar.style.display = "flex";
232
bar.style.alignItems = "end";
233
bar.style.justifyContent = "center";
234
bar.style.color = "white";
235
bar.style.fontSize = "12px";
236
bar.textContent = value;
237
238
container.appendChild(bar);
239
});
240
241
el.appendChild(container);
242
};
243
244
model.on("change", renderChart);
245
renderChart();
246
}
247
export default { render };
248
```
249
250
```python
251
import anywidget
252
import traitlets as t
253
254
class ChartWidget(anywidget.AnyWidget):
255
_esm = "vfile:chart.js"
256
257
data = t.List([]).tag(sync=True)
258
options = t.Dict({}).tag(sync=True)
259
260
# Create chart
261
chart = ChartWidget(
262
data=[10, 25, 15, 30, 45, 20],
263
options={"background": "#f0f0f0", "barColor": "#e74c3c"}
264
)
265
chart
266
267
# Update data interactively
268
chart.data = [5, 35, 25, 40, 15, 50]
269
chart.options = {"background": "#ffffff", "barColor": "#2ecc71"}
270
```
271
272
### Prototyping Complex Widgets
273
274
```python
275
# Create data table widget
276
%%vfile datatable.js
277
function render({ model, el }) {
278
let columns = model.get("columns");
279
let rows = model.get("rows");
280
281
let renderTable = () => {
282
let table = document.createElement("table");
283
table.style.width = "100%";
284
table.style.borderCollapse = "collapse";
285
table.style.fontFamily = "Arial, sans-serif";
286
287
// Header
288
let thead = document.createElement("thead");
289
let headerRow = document.createElement("tr");
290
headerRow.style.background = "#f8f9fa";
291
292
columns.forEach(col => {
293
let th = document.createElement("th");
294
th.textContent = col;
295
th.style.padding = "12px";
296
th.style.textAlign = "left";
297
th.style.borderBottom = "2px solid #dee2e6";
298
headerRow.appendChild(th);
299
});
300
301
thead.appendChild(headerRow);
302
table.appendChild(thead);
303
304
// Body
305
let tbody = document.createElement("tbody");
306
307
rows.forEach((row, index) => {
308
let tr = document.createElement("tr");
309
tr.style.background = index % 2 === 0 ? "#ffffff" : "#f8f9fa";
310
311
columns.forEach(col => {
312
let td = document.createElement("td");
313
td.textContent = row[col] || "";
314
td.style.padding = "12px";
315
td.style.borderBottom = "1px solid #dee2e6";
316
tr.appendChild(td);
317
});
318
319
tbody.appendChild(tr);
320
});
321
322
table.appendChild(tbody);
323
324
el.innerHTML = "";
325
el.appendChild(table);
326
};
327
328
model.on("change", renderTable);
329
renderTable();
330
}
331
export default { render };
332
```
333
334
```python
335
import anywidget
336
import traitlets as t
337
338
class DataTableWidget(anywidget.AnyWidget):
339
_esm = "vfile:datatable.js"
340
341
columns = t.List([]).tag(sync=True)
342
rows = t.List([]).tag(sync=True)
343
344
# Sample data
345
data_table = DataTableWidget(
346
columns=["Name", "Age", "City", "Score"],
347
rows=[
348
{"Name": "Alice", "Age": 30, "City": "New York", "Score": 85},
349
{"Name": "Bob", "Age": 25, "City": "San Francisco", "Score": 92},
350
{"Name": "Charlie", "Age": 35, "City": "Chicago", "Score": 78},
351
{"Name": "Diana", "Age": 28, "City": "Austin", "Score": 88}
352
]
353
)
354
data_table
355
```
356
357
### Virtual File Management
358
359
```python
360
# Clear all virtual files
361
%clear_vfiles
362
363
# Verify files are cleared
364
print("Virtual files cleared")
365
366
# Re-create files as needed
367
%%vfile simple.js
368
function render({ model, el }) {
369
el.innerHTML = "<p>Simple widget after clearing files</p>";
370
}
371
export default { render };
372
```
373
374
### Advanced Virtual File Patterns
375
376
```python
377
# Create reusable component library
378
%%vfile components.js
379
// Reusable UI components
380
export function createButton(text, onClick) {
381
let btn = document.createElement("button");
382
btn.textContent = text;
383
btn.style.cssText = `
384
background: #007cba;
385
color: white;
386
border: none;
387
padding: 8px 16px;
388
border-radius: 4px;
389
cursor: pointer;
390
margin: 4px;
391
`;
392
btn.addEventListener("click", onClick);
393
return btn;
394
}
395
396
export function createInput(placeholder, onChange) {
397
let input = document.createElement("input");
398
input.placeholder = placeholder;
399
input.style.cssText = `
400
padding: 8px;
401
border: 1px solid #ccc;
402
border-radius: 4px;
403
margin: 4px;
404
`;
405
input.addEventListener("input", (e) => onChange(e.target.value));
406
return input;
407
}
408
```
409
410
```python
411
# Use component library in widget
412
%%vfile app.js
413
import { createButton, createInput } from "vfile:components.js";
414
415
function render({ model, el }) {
416
let value = model.get("value");
417
418
let input = createInput("Enter text", (newValue) => {
419
model.set("value", newValue);
420
});
421
422
let button = createButton("Clear", () => {
423
model.set("value", "");
424
input.value = "";
425
});
426
427
let display = document.createElement("div");
428
display.style.cssText = "margin: 10px; padding: 10px; background: #f0f0f0;";
429
430
let updateDisplay = () => {
431
display.textContent = `Current value: ${model.get("value")}`;
432
};
433
434
model.on("change:value", updateDisplay);
435
updateDisplay();
436
437
el.appendChild(input);
438
el.appendChild(button);
439
el.appendChild(display);
440
}
441
export default { render };
442
```
443
444
```python
445
import anywidget
446
import traitlets as t
447
448
class ComponentWidget(anywidget.AnyWidget):
449
_esm = "vfile:app.js"
450
451
value = t.Unicode("").tag(sync=True)
452
453
widget = ComponentWidget()
454
widget
455
```