-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WID-223: create global context as singleton and observer to hold shar…
…ed and reactive state between viewers and operations
- Loading branch information
1 parent
4c5a6d1
commit 2eba9ff
Showing
3 changed files
with
160 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# "TheVirtualBrain - Widgets" package | ||
# | ||
# (c) 2022-2023, TVB Widgets Team | ||
# | ||
from typing import Callable | ||
|
||
|
||
class SingletonMeta(type): | ||
_instances = {} | ||
|
||
def __call__(cls, *args, **kwargs): | ||
if cls not in cls._instances: | ||
cls._instances[cls] = super().__call__(*args, **kwargs) | ||
return cls._instances[cls] | ||
|
||
|
||
class GlobalContext(metaclass=SingletonMeta): | ||
MATRIX_OPTIONS = [('Tracts', 'tracts'), ('Weights', 'weights')] | ||
_observed_state = dict() | ||
|
||
def __init__(self): | ||
self._matrix = 'weights' | ||
|
||
@property | ||
def matrix(self): | ||
return self._matrix | ||
|
||
@matrix.setter | ||
def matrix(self, value): | ||
old_value = self._matrix | ||
self._matrix = value | ||
if old_value == value: | ||
return | ||
try: | ||
observers = self._observed_state['matrix'] | ||
for observer in observers: | ||
observer(value) | ||
except KeyError: | ||
pass | ||
|
||
def observe(self, observer_func, value_observed): | ||
# type: (Callable[[any], any], str) -> None | ||
""" | ||
Method to register an observer for the specified value. | ||
When the specified value changes (if the value is set to the same as previous, | ||
observers are not triggered), all registered observers ar called | ||
with the new value passed as param. | ||
""" | ||
try: | ||
observers_list = self._observed_state[value_observed] | ||
observers_list.append(observer_func) | ||
except KeyError: | ||
self._observed_state[value_observed] = [observer_func] | ||
|
||
def remove_observer(self, observer_func, value_observed): | ||
# type: (Callable[[any], any], str) -> None | ||
""" | ||
Unregister a registered observer. | ||
""" | ||
try: | ||
observers_list = self._observed_state[value_observed] | ||
observers_list.remove(observer_func) | ||
except KeyError: | ||
pass | ||
|
||
|
||
CONTEXT = GlobalContext() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# "TheVirtualBrain - Widgets" package | ||
# | ||
# (c) 2022-2023, TVB Widgets Team | ||
# | ||
import ipywidgets | ||
from tvbwidgets.ui.base_widget import TVBWidget | ||
from tvbwidgets.ui.connectivity_ipy.global_context import CONTEXT | ||
|
||
|
||
class ConnectivityOperations(ipywidgets.VBox, TVBWidget): | ||
def add_datatype(self, datatype): | ||
""" | ||
Currently not supported | ||
""" | ||
pass | ||
|
||
def __init__(self, connectivity, **kwargs): | ||
super().__init__(layout={**self.DEFAULT_BORDER, 'width': '50%'}, **kwargs) | ||
self.connectivity = connectivity | ||
selector = self.__get_node_selector() | ||
children = [ | ||
ipywidgets.HTML( | ||
value=f'<h3>Operations for Connectivity-{connectivity.number_of_regions}</h3>' | ||
), selector] | ||
self.children = children | ||
self.regions_checkboxes = [] | ||
|
||
def __get_node_selector(self): | ||
left_children = [] | ||
right_children = [] | ||
region_labels = self.connectivity.region_labels | ||
# print('region labels: ', region_labels) | ||
for region in region_labels: | ||
label = str(region) | ||
selector = ipywidgets.Checkbox(value=False, description=label, layout={'width': 'max-content'}, | ||
indent=False) | ||
if label.startswith('l'): | ||
left_children.append(selector) | ||
else: | ||
right_children.append(selector) | ||
|
||
self.regions_checkboxes = [*left_children, *right_children] | ||
|
||
left = ipywidgets.VBox(children=(ipywidgets.HTML('<p>Left hemisphere</p>'), | ||
ipywidgets.VBox(children=left_children, | ||
)), | ||
layout={'width': '50%', 'align-items': 'start'} | ||
) | ||
right = ipywidgets.VBox(children=(ipywidgets.HTML('<p>Right hemisphere</p>'), | ||
ipywidgets.VBox(children=right_children, | ||
)), | ||
layout={'width': '50%', 'align-items': 'start'} | ||
) | ||
matrix_dropdown = ipywidgets.Dropdown( | ||
options=CONTEXT.MATRIX_OPTIONS, | ||
value=CONTEXT.matrix, | ||
description='Matrix:' | ||
) | ||
|
||
def on_change(value): | ||
CONTEXT.matrix = value['new'] | ||
|
||
matrix_dropdown.observe(on_change, 'value') | ||
|
||
def on_ctx_change(value): | ||
matrix_dropdown.value = value | ||
|
||
CONTEXT.observe(on_ctx_change, 'matrix') | ||
|
||
container = ipywidgets.VBox(children=(matrix_dropdown, | ||
ipywidgets.HBox(children=(left, right)))) | ||
accordion = ipywidgets.Accordion(children=[container], selected_index=None, | ||
layout={'height': '50vh'}) | ||
accordion.set_title(0, 'Regions selector') | ||
return accordion | ||
|
||
@property | ||
def selected_regions(self): | ||
return map(lambda x: x.description, filter(lambda x: x.value, self.regions_checkboxes)) |