Skip to content

Commit 1886509

Browse files
committed
widgets: mainly implement k3d object ref locking; also, add ipywidgets state help function
- this k3d locking is needed to workaround k3d implementing their own internal custom references, and just assuming everything is created in exactly the order it is in jupyterlab. With RTC the order can be more random, so we manually enforce it.
1 parent 9f0f132 commit 1886509

File tree

2 files changed

+53
-6
lines changed

2 files changed

+53
-6
lines changed

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

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import React from "react";
2424
import ReactDOM from "react-dom/client";
2525
import type { JupyterActions } from "@cocalc/frontend/jupyter/browser-actions";
2626
import { FileContext } from "@cocalc/frontend/lib/file-context";
27+
import { delay } from "awaiting";
28+
29+
const K3D_DELAY_MS = 25;
2730

2831
type DeserializedModelState = { [key: string]: any };
2932

@@ -39,6 +42,7 @@ export class WidgetManager {
3942
private last_changed: { [model_id: string]: { [key: string]: any } } = {};
4043
private state_lock: Set<string> = new Set();
4144
private watching: Set<string> = new Set();
45+
public k3dObjectIds = new Set<number>();
4246

4347
constructor({
4448
ipywidgets_state,
@@ -147,12 +151,19 @@ VBox([s1, s2])
147151
changed: SerializedModelState,
148152
merge: boolean,
149153
): Promise<void> => {
150-
const model: base.DOMWidgetModel | undefined =
151-
await this.manager.get_model(model_id);
154+
const model = await this.manager.get_model(model_id);
152155
log("updateModel", { model_id, merge, changed });
153-
if (model == null) {
154-
return;
156+
if (model.module == "k3d") {
157+
// k3d invents its own ad hoc inter-model reference scheme, so we have
158+
// to deal with that.
159+
if (changed.object_ids != null) {
160+
while (!isSubset(changed.object_ids, this.k3dObjectIds)) {
161+
log("k3d -- waiting for object_ids", changed.object_ids);
162+
await delay(K3D_DELAY_MS);
163+
}
164+
}
155165
}
166+
156167
//log(`setting state of model "${model_id}" to `, change);
157168
if (changed.last_changed != null) {
158169
this.last_changed[model_id] = changed;
@@ -598,6 +609,7 @@ VBox([s1, s2])
598609

599610
class Environment implements WidgetEnvironment {
600611
private manager: WidgetManager;
612+
601613
constructor(manager) {
602614
this.manager = manager;
603615
}
@@ -626,12 +638,17 @@ class Environment implements WidgetEnvironment {
626638
if (state == null) {
627639
throw Error("bug");
628640
}
641+
629642
if (state._model_module == "k3d" && state.type != null) {
630643
while (!state?.type || !state?.id) {
631644
log(
632-
"getSerializedModelState",
645+
"getSerializedModelState -- k3d case",
633646
model_id,
634-
"k3d: waiting for state.type to be defined",
647+
"k3d: waiting for state.type and state.id to be defined",
648+
{
649+
type: state?.type,
650+
id: state?.id,
651+
},
635652
);
636653

637654
await once(this.manager.ipywidgets_state, "change");
@@ -660,6 +677,19 @@ class Environment implements WidgetEnvironment {
660677

661678
log("getSerializedModelState", { model_id, state });
662679
setTimeout(() => this.manager.watchModel(model_id), 1);
680+
681+
if (state._model_module == "k3d") {
682+
if (state.object_ids != null) {
683+
while (!isSubset(state.object_ids, this.manager.k3dObjectIds)) {
684+
log("k3d -- waiting for object_ids", state.object_ids);
685+
await delay(K3D_DELAY_MS);
686+
}
687+
}
688+
if (state.id != null) {
689+
this.manager.k3dObjectIds.add(state.id);
690+
}
691+
}
692+
663693
return {
664694
modelName: state._model_name,
665695
modelModule: state._model_module,
@@ -751,3 +781,12 @@ const IPY_MODEL = "IPY_MODEL_";
751781
function isModelReference(value): boolean {
752782
return typeof value == "string" && value.startsWith(IPY_MODEL);
753783
}
784+
785+
function isSubset(X, Y) {
786+
for (const a of X) {
787+
if (!Y.has(a)) {
788+
return false;
789+
}
790+
}
791+
return true;
792+
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,14 @@ scat.x, scat.y = np.random.rand(2, 50)
524524
await this.table.save();
525525
};
526526

527+
values = () => {
528+
const x = this.table.get();
529+
if (x == null) {
530+
return [];
531+
}
532+
return Object.values(x.toJS()).filter((obj) => obj.data);
533+
};
534+
527535
// Clean up all data in the table about models that are not
528536
// referenced (directly or indirectly) in any cell in the notebook.
529537
// There is also a comm:close event/message somewhere, which

0 commit comments

Comments
 (0)