From 25a0694b46079a241a9b8c0775cd6ed256921e15 Mon Sep 17 00:00:00 2001 From: Nikolay Akhmetov Date: Fri, 11 Aug 2023 10:41:46 -0400 Subject: [PATCH] NickAkhmetov/Add `allow_multiple_scopes_per_type` boolean arg to `use_coordination` and `link_views` (#274) * Add `allow_multiple_scopes_per_type` boolean arg to `use_coordination` and `link_views` * Remove lint-failing whitespace * Safely fetch existing value using `.get` * Use concatenation instead of appending --- vitessce/config.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/vitessce/config.py b/vitessce/config.py index 9b04d360..6e51b5a6 100644 --- a/vitessce/config.py +++ b/vitessce/config.py @@ -370,12 +370,13 @@ def get_coordination_scope(self, c_type): """ return self.view["coordinationScopes"].get(c_type) - def use_coordination(self, *c_scopes): + def use_coordination(self, *c_scopes, allow_multiple_scopes_per_type=False): """ Attach a coordination scope to this view instance. All views using the same coordination scope for a particular coordination type will effectively be linked together. :param \\*c_scopes: A variable number of coordination scope instances can be passed. :type \\*c_scopes: VitessceConfigCoordinationScope + :param bool allow_multiple_scopes_per_type: Whether to allow multiple coordination scopes per coordination type. If true, multiple values for the same coordination type are treated as a list. If false, latest value for same coordination type is used. Defaults to False. :returns: Self, to allow chaining. :rtype: VitessceConfigView @@ -402,7 +403,15 @@ def use_coordination(self, *c_scopes): """ for c_scope in c_scopes: assert isinstance(c_scope, VitessceConfigCoordinationScope) - self.view["coordinationScopes"][c_scope.c_type] = c_scope.c_scope + existing_value = self.view["coordinationScopes"].get(c_scope.c_type) + new_value = c_scope.c_scope + if (existing_value is not None and allow_multiple_scopes_per_type): + if (isinstance(existing_value, list)): + self.view["coordinationScopes"][c_scope.c_type] = existing_value + [new_value] + else: + self.view["coordinationScopes"][c_scope.c_type] = [existing_value, new_value] + else: + self.view["coordinationScopes"][c_scope.c_type] = new_value return self def set_xywh(self, x, y, w, h): @@ -813,7 +822,7 @@ def set_coordination_value(self, c_type, c_scope, c_value): self.config["coordinationSpace"][scope.c_type][scope.c_scope] = scope return scope - def link_views(self, views, c_types, c_values=None): + def link_views(self, views, c_types, c_values=None, allow_multiple_scopes_per_type=False): """ A convenience function for setting up new coordination scopes across a set of views. @@ -822,6 +831,7 @@ def link_views(self, views, c_types, c_values=None): :param c_types: The coordination types on which to coordinate the views. :type c_types: list of str or list of vitessce.constants.CoordinationType :param list c_values: Initial values corresponding to each coordination type. Should have the same length as the c_types array. Optional. + :param bool allow_multiple_scopes_per_type: Whether to allow multiple coordination scopes per coordination type. If true, multiple values for the same coordination type are treated as a list. If false, latest value for same coordination type is used. Defaults to False. :returns: Self, to allow chaining. :rtype: VitessceConfig @@ -829,7 +839,7 @@ def link_views(self, views, c_types, c_values=None): c_scopes = self.add_coordination(*c_types) for view in views: for c_scope in c_scopes: - view.use_coordination(c_scope) + view.use_coordination(c_scope, allow_multiple_scopes_per_type=allow_multiple_scopes_per_type) if c_values is not None and len(c_values) == len(c_types): for i, c_scope in enumerate(c_scopes):