Skip to content

Commit 5c44d99

Browse files
committed
widgets: working on fully implementing clear output properly
1 parent 329f949 commit 5c44d99

File tree

2 files changed

+46
-12
lines changed

2 files changed

+46
-12
lines changed

src/packages/frontend/jupyter/widgets/manager2.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,6 @@ export class WidgetManager {
222222
};
223223

224224
private ipywidgets_state_BuffersChange = async (model_id: string) => {
225-
log("handleBuffersChange: ", model_id);
226225
/*
227226
The data structures currently don't store which buffers changed, so we're
228227
updating all of them before, which is of course inefficient.
@@ -237,6 +236,7 @@ export class WidgetManager {
237236
const model = await this.manager.get_model(model_id);
238237
const { buffer_paths, buffers } =
239238
await this.ipywidgets_state.get_model_buffers(model_id);
239+
log("handleBuffersChange: ", model_id, buffer_paths);
240240
const deserialized_state = model.get_state(true);
241241
const serializers = (model.constructor as any).serializers;
242242
const change: { [key: string]: any } = {};
@@ -245,10 +245,13 @@ export class WidgetManager {
245245
// Will that break something? We do set things properly later.
246246
const buffer = buffers[i];
247247
const key = buffer_paths[i][0];
248-
change[key] =
248+
const value =
249249
serializers[key]?.serialize({ buffer, ...deserialized_state[key] }) ??
250-
deserialized_state[key];
250+
deserialized_state[key] ??
251+
null; // must be null, not undefined, so can json.
252+
change[key] = value;
251253
}
254+
log("handleBuffersChange: ", model_id, change);
252255
model.set_state(change);
253256
};
254257

@@ -543,7 +546,9 @@ class Environment implements WidgetEnvironment {
543546
async renderOutput(outputItem: any, destination: Element): Promise<void> {
544547
// the gaussian plume notebook has example of this!
545548
log("renderOutput", { outputItem, destination });
546-
//$(destination).append($(`<pre>${JSON.stringify(outputItem)}</pre>`));
549+
if (outputItem == null) {
550+
return;
551+
}
547552
const message = fromJS(outputItem);
548553
const myDiv = document.createElement("div");
549554
destination.appendChild(myDiv);

src/packages/sync/editor/generic/ipywidgets-state.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -342,12 +342,13 @@ export class IpywidgetsState extends EventEmitter {
342342
type: "value" | "state" | "buffers" | "message",
343343
data: any,
344344
fire_change_event: boolean = true,
345+
merge?: "none" | "shallow" | "deep",
345346
): void => {
346347
const string_id = this.syncdoc.get_string_id();
347348
if (typeof data != "object") {
348349
throw Error("TypeError -- data must be a map");
349350
}
350-
let merge: "none" | "shallow" | "deep";
351+
let defaultMerge: "none" | "shallow" | "deep";
351352
if (type == "value") {
352353
// we manually do the shallow merge only on the data field.
353354
const data0 = this.get_model_value(model_id);
@@ -357,16 +358,19 @@ export class IpywidgetsState extends EventEmitter {
357358
}
358359
data = data0;
359360
}
360-
merge = "none";
361+
defaultMerge = "none";
361362
} else if (type == "buffers") {
362363
// we keep around the buffers that were
363364
// already set, but overwrite
364365
// when they change.
365-
merge = "deep";
366+
defaultMerge = "shallow";
366367
} else if (type == "message") {
367-
merge = "none";
368+
defaultMerge = "none";
368369
} else {
369-
merge = "deep";
370+
defaultMerge = "deep";
371+
}
372+
if (merge == null) {
373+
merge = defaultMerge;
370374
}
371375
this.table.set(
372376
{ string_id, type, model_id, data },
@@ -733,11 +737,12 @@ export class IpywidgetsState extends EventEmitter {
733737
if (model_id == null) return false; // should not happen.
734738

735739
if (mesg.header.msg_type == "clear_output") {
736-
if (mesg.content != null && mesg.content.wait) {
740+
if (mesg.content?.wait) {
737741
this.clear_output[model_id] = true;
738742
} else {
739743
delete this.clear_output[model_id];
740-
this.set_model_value(model_id, { outputs: [] });
744+
this.clearOutputBuffers();
745+
this.set_model_value(model_id, { outputs: null });
741746
}
742747
return true;
743748
}
@@ -747,9 +752,10 @@ export class IpywidgetsState extends EventEmitter {
747752
return false;
748753
}
749754

750-
let outputs: any[];
755+
let outputs: any;
751756
if (this.clear_output[model_id]) {
752757
delete this.clear_output[model_id];
758+
this.clearOutputBuffers();
753759
outputs = [];
754760
} else {
755761
outputs = this.get_model_value(model_id).outputs;
@@ -762,6 +768,29 @@ export class IpywidgetsState extends EventEmitter {
762768
return true;
763769
};
764770

771+
private clearOutputBuffers = () => {
772+
// TODO: need to clear all output buffers.
773+
/* Example where if you do not properly clear buffers, then broken output re-appears:
774+
775+
import ipywidgets as widgets
776+
from IPython.display import YouTubeVideo
777+
out = widgets.Output(layout={'border': '1px solid black'})
778+
out.append_stdout('Output appended with append_stdout')
779+
out.append_display_data(YouTubeVideo('eWzY2nGfkXk'))
780+
out
781+
782+
---
783+
784+
out.clear_output()
785+
786+
---
787+
788+
with out:
789+
print('hi')
790+
*/
791+
// TODO!!!!
792+
};
793+
765794
private sendCustomMessage = async (
766795
model_id: string,
767796
message: object,

0 commit comments

Comments
 (0)