Skip to content

Commit b849265

Browse files
committed
page_mode and page_esm params
1 parent bc7b353 commit b849265

File tree

2 files changed

+80
-16
lines changed

2 files changed

+80
-16
lines changed

vitessce/config.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ class VitessceConfigView:
462462
A class to represent a view (i.e. visualization component) in the Vitessce view config layout.
463463
"""
464464

465-
def __init__(self, component, coordination_scopes, x, y, w, h):
465+
def __init__(self, component, coordination_scopes, x, y, w, h, uid=None):
466466
"""
467467
Not meant to be instantiated directly, but instead created and returned by the ``VitessceConfig.add_view()`` method.
468468
@@ -480,17 +480,20 @@ def __init__(self, component, coordination_scopes, x, y, w, h):
480480
"x": x,
481481
"y": y,
482482
"w": w,
483-
"h": h
483+
"h": h,
484484
}
485+
if uid is not None:
486+
self.view["uid"] = uid
485487

486488
def _to_py_params(self):
487489
params_dict = {
488490
"component": self.view["component"],
489491
"x": self.view["x"],
490492
"y": self.view["y"],
491493
"w": self.view["w"],
492-
"h": self.view["h"]
493494
}
495+
if "uid" in self.view:
496+
params_dict["uid"] = self.view["uid"]
494497
# Only include coordination_scopes if there are coordination scopes other than
495498
# the coorindation scope for the 'dataset' coordination type.
496499
non_dataset_coordination_scopes = {
@@ -1002,7 +1005,7 @@ def get_datasets(self):
10021005
"""
10031006
return self.config["datasets"]
10041007

1005-
def add_view(self, view_type, dataset=None, dataset_uid=None, x=0, y=0, w=1, h=1, mapping=None, coordination_scopes=None, props=None):
1008+
def add_view(self, view_type, dataset=None, dataset_uid=None, x=0, y=0, w=1, h=1, mapping=None, uid=None, coordination_scopes=None, props=None):
10061009
"""
10071010
Add a view to the config.
10081011
@@ -1070,7 +1073,7 @@ def add_view(self, view_type, dataset=None, dataset_uid=None, x=0, y=0, w=1, h=1
10701073
if coordination_scopes is not None:
10711074
internal_coordination_scopes.update(coordination_scopes)
10721075
vcv = VitessceConfigView(
1073-
component_str, internal_coordination_scopes, x, y, w, h)
1076+
component_str, internal_coordination_scopes, x, y, w, h, uid=uid)
10741077

10751078
# Use the mapping parameter if component is scatterplot and the mapping is not None
10761079
if mapping is not None:
@@ -1635,7 +1638,9 @@ def from_dict(config):
16351638
raise ValueError(
16361639
"Multiple datasets are present, so every view must have an explicit dataset coordination scope.")
16371640
new_view = VitessceConfigView(
1638-
c['component'], c_coord_scopes, c['x'], c['y'], c['w'], c['h'])
1641+
c['component'], c_coord_scopes, c['x'], c['y'], c['w'], c['h'],
1642+
uid=(c["uid"] if "uid" in c else None)
1643+
)
16391644
if 'props' in c.keys():
16401645
new_view.set_props(**c['props'])
16411646
vc.config['layout'].append(new_view)

vitessce/widget.py

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ def get_uid_str(uid):
203203
const remountOnUidChange = view.model.get('remount_on_uid_change');
204204
const storeUrls = view.model.get('store_urls');
205205
206+
const pageMode = view.model.get('page_mode');
207+
const pageEsm = view.model.get('page_esm');
208+
206209
const pkgName = (jsDevMode ? "@vitessce/dev" : "vitessce");
207210
208211
importMap.imports["vitessce"] = (customJsUrl.length > 0
@@ -216,14 +219,17 @@ def get_uid_str(uid):
216219
PluginViewType,
217220
PluginCoordinationType,
218221
PluginJointFileType,
222+
PluginAsyncFunction,
219223
z,
220224
useCoordination,
225+
usePageModeView,
221226
} = await importWithMap("vitessce", importMap);
222227
223228
let pluginViewTypes = [];
224229
let pluginCoordinationTypes = [];
225230
let pluginFileTypes = [];
226231
let pluginJointFileTypes = [];
232+
let pluginAsyncFunctions = [];
227233
228234
const stores = Object.fromEntries(
229235
storeUrls.map(storeUrl => ([
@@ -248,16 +254,18 @@ def get_uid_str(uid):
248254
const pluginModule = (await import(pluginEsmUrl)).default;
249255
URL.revokeObjectURL(pluginEsmUrl);
250256
251-
const pluginsObj = await pluginModule.createPlugins({
257+
const pluginDeps = {
252258
React,
253259
PluginFileType,
254260
PluginViewType,
255261
PluginCoordinationType,
256262
PluginJointFileType,
263+
PluginAsyncFunction,
257264
z,
258265
useCoordination,
259266
invokeCommand: invokePluginCommand,
260-
});
267+
};
268+
const pluginsObj = await pluginModule.createPlugins(pluginDeps);
261269
if(Array.isArray(pluginsObj.pluginViewTypes)) {
262270
pluginViewTypes = [...pluginViewTypes, ...pluginsObj.pluginViewTypes];
263271
}
@@ -270,11 +278,30 @@ def get_uid_str(uid):
270278
if(Array.isArray(pluginsObj.pluginJointFileTypes)) {
271279
pluginJointFileTypes = [...pluginJointFileTypes, ...pluginsObj.pluginJointFileTypes];
272280
}
281+
if(Array.isArray(pluginsObj.pluginAsyncFunctions)) {
282+
pluginAsyncFunctions = [...pluginAsyncFunctions, ...pluginsObj.pluginAsyncFunctions];
283+
}
273284
} catch(e) {
285+
console.error("Error loading plugin ESM or executing createPlugins function.");
274286
console.error(e);
275287
}
276288
}
277289
290+
let PageComponent;
291+
try {
292+
const pageEsmUrl = URL.createObjectURL(new Blob([pageEsm], { type: "text/javascript" }));
293+
const pageModule = (await import(pageEsmUrl)).default;
294+
URL.revokeObjectURL(pageEsmUrl);
295+
296+
const pageDeps = {
297+
usePageModeView,
298+
};
299+
PageComponent = await pageModule.createPage(pageDeps);
300+
} catch(e) {
301+
console.error("Error loading page ESM or executing createPage function.")
302+
console.error(e);
303+
}
304+
278305
function VitessceWidget(props) {
279306
const { model } = props;
280307
@@ -338,14 +365,17 @@ def get_uid_str(uid):
338365
339366
const vitessceProps = {
340367
height, theme, config, onConfigChange, validateConfig,
341-
pluginViewTypes, pluginCoordinationTypes, pluginFileTypes, pluginJointFileTypes,
342-
remountOnUidChange, stores,
368+
pluginViewTypes, pluginCoordinationTypes,
369+
pluginFileTypes,pluginJointFileTypes, pluginAsyncFunctions,
370+
remountOnUidChange, stores, pageMode,
343371
};
344372
345373
return e('div', { ref: divRef, style: { height: height + 'px' } },
346374
e(React.Suspense, { fallback: e('div', {}, 'Loading...') },
347375
e(React.StrictMode, {},
348-
e(Vitessce, vitessceProps)
376+
e(Vitessce, vitessceProps,
377+
(pageMode ? e(PageComponent, {}) : null)
378+
),
349379
),
350380
),
351381
);
@@ -379,6 +409,7 @@ def get_uid_str(uid):
379409
PluginViewType,
380410
PluginCoordinationType,
381411
PluginJointFileType,
412+
PluginAsyncFunction,
382413
z,
383414
useCoordination,
384415
invokeCommand,
@@ -388,11 +419,32 @@ def get_uid_str(uid):
388419
pluginFileTypes: undefined,
389420
pluginCoordinationTypes: undefined,
390421
pluginJointFileTypes: undefined,
422+
pluginAsyncFunctions: undefined,
391423
};
392424
}
393425
export default { createPlugins };
394426
"""
395427

428+
DEFAULT_PAGE_ESM = """
429+
function createPage(utilsForPage) {
430+
const {
431+
usePageModeView,
432+
} = utilsForPage;
433+
434+
function PageComponent(props) {
435+
const BiomarkerSelect = usePageModeView('biomarker-select');
436+
437+
return (
438+
<div>
439+
<BiomarkerSelect />
440+
</div>
441+
);
442+
}
443+
return PageComponent;
444+
}
445+
export default { createPage };
446+
"""
447+
396448

397449
class VitesscePlugin:
398450
"""
@@ -437,10 +489,13 @@ class VitessceWidget(anywidget.AnyWidget):
437489
custom_js_url = Unicode('').tag(sync=True)
438490
plugin_esm = List(trait=Unicode(''), default_value=[]).tag(sync=True)
439491
remount_on_uid_change = Bool(True).tag(sync=True)
492+
page_mode = Bool(False).tag(sync=True)
493+
page_esm = Unicode('').tag(sync=True)
494+
440495

441496
store_urls = List(trait=Unicode(''), default_value=[]).tag(sync=True)
442497

443-
def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=False, js_package_version='3.3.12', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True):
498+
def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=False, js_package_version='3.3.12', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, page_mode=False, page_esm=None):
444499
"""
445500
Construct a new Vitessce widget.
446501
@@ -453,8 +508,10 @@ def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=
453508
:param str js_package_version: The version of the NPM package ('vitessce' if not js_dev_mode else '@vitessce/dev').
454509
:param bool js_dev_mode: Should @vitessce/dev be used (typically for debugging purposes)? By default, False.
455510
:param str custom_js_url: A URL to a JavaScript file to use (instead of 'vitessce' or '@vitessce/dev' NPM package).
456-
:param list[WidgetPlugin] plugins: A list of subclasses of VitesscePlugin. Optional.
511+
:param list[VitesscePlugin] plugins: A list of subclasses of VitesscePlugin. Optional.
457512
:param bool remount_on_uid_change: Passed to the remountOnUidChange prop of the <Vitessce/> React component. By default, True.
513+
:param bool page_mode: Whether to render the <Vitessce/> component in grid-mode or page-mode. By default, False.
514+
:param str page_esm: The ES module string for the page component creation function. Optional.
458515
459516
.. code-block:: python
460517
:emphasize-lines: 4
@@ -487,6 +544,7 @@ def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=
487544
config=config_dict, height=height, theme=theme, proxy=proxy,
488545
js_package_version=js_package_version, js_dev_mode=js_dev_mode, custom_js_url=custom_js_url,
489546
plugin_esm=plugin_esm, remount_on_uid_change=remount_on_uid_change,
547+
page_mode=page_mode, page_esm=('' if page_esm is None else page_esm),
490548
uid=uid_str, store_urls=list(self._stores.keys())
491549
)
492550

@@ -550,9 +608,7 @@ def _plugin_command(self, params, buffers):
550608
return command_func(command_params, buffers)
551609

552610
# Launch Vitessce using plain HTML representation (no ipywidgets)
553-
554-
555-
def ipython_display(config, height=600, theme='auto', base_url=None, host_name=None, uid=None, port=None, proxy=False, js_package_version='3.3.12', js_dev_mode=False, custom_js_url='', plugin_esm=DEFAULT_PLUGIN_ESM, remount_on_uid_change=True):
611+
def ipython_display(config, height=600, theme='auto', base_url=None, host_name=None, uid=None, port=None, proxy=False, js_package_version='3.3.12', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True):
556612
from IPython.display import display, HTML
557613
uid_str = "vitessce" + get_uid_str(uid)
558614

@@ -562,6 +618,9 @@ def ipython_display(config, height=600, theme='auto', base_url=None, host_name=N
562618
routes = config.get_routes()
563619
serve_routes(config, routes, use_port)
564620

621+
plugins = plugins or []
622+
plugin_esm = [p.plugin_esm for p in plugins]
623+
565624
model_vals = {
566625
"uid": uid_str,
567626
"js_package_version": js_package_version,

0 commit comments

Comments
 (0)