diff --git a/owrx/active/list/__init__.py b/owrx/active/list/__init__.py index ffbaf149..ca7ca39b 100644 --- a/owrx/active/list/__init__.py +++ b/owrx/active/list/__init__.py @@ -50,9 +50,6 @@ class ActiveListTransformation(ABC): def transform(self, value): pass - def __call__(self, *args, **kwargs): - return self.transform(*args, **kwargs) - def monitor(self, member, callback: callable): pass @@ -60,26 +57,38 @@ class ActiveListTransformation(ABC): pass +class BasicTransformation(ActiveListTransformation): + def __init__(self, transformation: callable): + self.transformation = transformation + + def transform(self, value): + return self.transformation(value) + + class ActiveListTransformationListener(ActiveListListener): - def __init__(self, transformation: callable, source: "ActiveList", target: "ActiveList"): + def __init__(self, transformation: ActiveListTransformation, source: "ActiveList", target: "ActiveList"): self.transformation = transformation self.source = source self.target = target - if isinstance(transformation, ActiveListTransformation): - for idx, v in enumerate(self.source): - transformation.monitor(v, partial(self._onMonitor, idx)) + for v in self.source: + transformation.monitor(v, partial(self._onMonitor, v)) def onListChange(self, source: "ActiveList", changes: list[ActiveListChange]): for change in changes: if isinstance(change, ActiveListIndexUpdated): - self.target[change.index] = self.transformation(change.newValue) + self.transformation.unmonitor(change.oldValue) + self.target[change.index] = self.transformation.transform(change.newValue) + self.transformation.monitor(change.newValue, partial(self._onMonitor, change.newValue)) elif isinstance(change, ActiveListIndexAdded): - self.target.insert(change.index, self.transformation(change.newValue)) + self.target.insert(change.index, self.transformation.transform(change.newValue)) + self.transformation.monitor(change.newValue, partial(self._onMonitor, change.newValue)) elif isinstance(change, ActiveListIndexDeleted): del self.target[change.index] + self.transformation.unmonitor(change.oldValue) - def _onMonitor(self, idx): - self.target[idx] = self.transformation(self.source[idx]) + def _onMonitor(self, value): + idx = self.source.index(value) + self.target[idx] = self.transformation.transform(self.source[idx]) class ActiveListFilterListener(ActiveListListener): @@ -133,12 +142,16 @@ class ActiveListFlattenListener(ActiveListListener): idx = self.getOffsetForIndex(change.index) for n, v in enumerate(change.newValue): self.target.insert(idx + n, v) + change.newValue.addListener(self) elif isinstance(change, ActiveListIndexUpdated): + change.oldValue.removeListener(self) idx = self.getOffsetForIndex(change.index) del self.target[idx, idx + len(change.oldValue)] for n, v in enumerate(change.newValue): self.target.insert(idx + n, v) + change.newValue.addListener(self) elif isinstance(change, ActiveListIndexDeleted): + change.oldValue.removeListener(self) idx = self.getOffsetForIndex(change.index) del self.target[idx, idx + len(change.oldValue)] else: @@ -186,9 +199,11 @@ class ActiveList: def index(self, value): return self.delegate.index(value) - def map(self, transform: Union[callable, ActiveListTransformation]): - res = ActiveList([transform(v) for v in self]) - self.addListener(ActiveListTransformationListener(transform, self, res)) + def map(self, transformation: Union[callable, ActiveListTransformation]): + if not isinstance(transformation, ActiveListTransformation): + transformation = BasicTransformation(transformation) + res = ActiveList([transformation.transform(v) for v in self]) + self.addListener(ActiveListTransformationListener(transformation, self, res)) return res def filter(self, filter: callable): diff --git a/owrx/sdr.py b/owrx/sdr.py index ae4523ad..2477f4ec 100644 --- a/owrx/sdr.py +++ b/owrx/sdr.py @@ -1,12 +1,51 @@ from owrx.config import Config from owrx.source import SdrSource from owrx.feature import FeatureDetector, UnknownFeatureException +from owrx.active.list import ActiveListTransformation import logging logger = logging.getLogger(__name__) +class ProfileNameMapper(ActiveListTransformation): + def __init__(self, source_id, source_name): + self.source_id = source_id + self.source_name = source_name + self.subscriptions = [] + + def transform(self, profile): + return {"id": "{}|{}".format(self.source_id, profile["id"]), "name": "{} {}".format(self.source_name, profile["name"])} + + def monitor(self, profile, callback: callable): + self.subscriptions.append(profile.filter("name").wire(lambda _: callback())) + + def unmonitor(self, member): + affected = [sub for sub in self.subscriptions if sub.subscriptee is member] + logger.debug("removing %i affected subs", len(affected)) + for sub in affected: + sub.cancel() + self.subscriptions.remove(sub) + + +class ProfileMapper(ActiveListTransformation): + def __init__(self): + self.subscriptions = [] + + def transform(self, source): + return source.getProfiles().map(ProfileNameMapper(source.getId(), source.getName())) + + def monitor(self, source, callback: callable): + self.subscriptions.append(source.getProps().filter("name").wire(lambda _: callback())) + + def unmonitor(self, member): + affected = [sub for sub in self.subscriptions if sub.subscriptee is member] + logger.debug("removing %i affected subs", len(affected)) + for sub in affected: + sub.cancel() + self.subscriptions.remove(sub) + + class SdrService(object): sources = None activeSources = None @@ -84,11 +123,8 @@ class SdrService(object): @staticmethod def getAvailableProfiles(): - def buildProfiles(source): - return source.getProfiles().map(lambda profile: {"id": "{}|{}".format(source.getId(), profile["id"]), "name": "{} {}".format(source.getName(), profile["name"])}) - if SdrService.availableProfiles is None: - SdrService.availableProfiles = SdrService.getActiveSources().map(buildProfiles).flatten() + SdrService.availableProfiles = SdrService.getActiveSources().map(ProfileMapper()).flatten() return SdrService.availableProfiles @staticmethod