diff --git a/owrx/property/__init__.py b/owrx/property/__init__.py index 7cc84f64..e6913bbc 100644 --- a/owrx/property/__init__.py +++ b/owrx/property/__init__.py @@ -402,9 +402,10 @@ class PropertyCarousel(PropertyDelegator): self.layers[key] = value def removeLayer(self, key): - if key in self.layers and self.layers[key] is self.pm: - self.switch() + need_switch = key in self.layers and self.layers[key] is self.pm del self.layers[key] + if need_switch: + self.switch() def switch(self, key=None): before = self.pm diff --git a/owrx/sdr.py b/owrx/sdr.py index 5b124d1d..b941b889 100644 --- a/owrx/sdr.py +++ b/owrx/sdr.py @@ -1,5 +1,5 @@ from owrx.config import Config -from owrx.source import SdrSourceEventClient +from owrx.source import SdrSourceEventClient, ProfileIsActiveFilter from owrx.feature import FeatureDetector, UnknownFeatureException from owrx.active.list import ActiveListTransformation, ActiveListFilter, ActiveListListener, ActiveList, ActiveListChange @@ -22,6 +22,7 @@ class ProfileNameMapper(ActiveListTransformation): def unmonitor(self, profile): self.subscriptions[id(profile)].cancel() + del self.subscriptions[id(profile)] class ProfileMapper(ActiveListTransformation): @@ -36,6 +37,7 @@ class ProfileMapper(ActiveListTransformation): def unmonitor(self, source): self.subscriptions[id(source)].cancel() + del self.subscriptions[id(source)] class ProfileChangeListener(ActiveListListener): @@ -49,16 +51,24 @@ class ProfileChangeListener(ActiveListListener): class HasProfilesFilter(ActiveListFilter): def __init__(self): self.monitors = {} + self.profiles = {} def predicate(self, device) -> bool: - return "profiles" in device and device["profiles"] and len(device["profiles"]) > 0 + if "profiles" not in device: + return False + if id(device) not in self.profiles: + self.profiles[id(device)] = device["profiles"].filter(ProfileIsActiveFilter()) + return len(self.profiles[id(device)]) > 0 def monitor(self, device, callback: callable): self.monitors[id(device)] = monitor = ProfileChangeListener(callback) - device["profiles"].addListener(monitor) + if id(device) not in self.profiles: + self.profiles[id(device)] = device["profiles"].filter(ProfileIsActiveFilter()) + self.profiles[id(device)].addListener(monitor) def unmonitor(self, device): - device["profiles"].removeListener(self.monitors[id(device)]) + self.profiles[id(device)].removeListener(self.monitors[id(device)]) + del self.monitors[id(device)] class SourceIsEnabledListener(SdrSourceEventClient): diff --git a/owrx/source/__init__.py b/owrx/source/__init__.py index af0cb9d7..cf43afcd 100644 --- a/owrx/source/__init__.py +++ b/owrx/source/__init__.py @@ -12,6 +12,7 @@ from owrx.command import CommandMapper from owrx.socket import getAvailablePort from owrx.property import PropertyStack, PropertyLayer, PropertyFilter, PropertyCarousel, PropertyDeleted from owrx.property.filter import ByLambda +from owrx.active.list import ActiveListFilter from owrx.active.list import ActiveList, ActiveListListener, ActiveListChange, ActiveListIndexAdded, ActiveListIndexDeleted, ActiveListIndexUpdated from owrx.form.input import Input, TextInput, NumberInput, CheckboxInput, ModesInput, ExponentialInput from owrx.form.input.converter import OptionalConverter @@ -98,12 +99,14 @@ class SdrProfileCarousel(PropertyCarousel): if "profiles" not in props: return - for profile in props["profiles"]: + profiles = props["profiles"].filter(ProfileIsActiveFilter()) + + for profile in profiles: self.addProfile(profile) # activate first available profile self.switch() - props["profiles"].addListener(SdrProfileCarouselListener(self)) + profiles.addListener(SdrProfileCarouselListener(self)) def addProfile(self, profile): profile_id = profile["id"] @@ -123,6 +126,21 @@ class SdrProfileCarousel(PropertyCarousel): return super()._getDefaultLayer() +class ProfileIsActiveFilter(ActiveListFilter): + def __init__(self): + self.subscriptions = {} + + def predicate(self, profile) -> bool: + return "enabled" not in profile or profile["enabled"] + + def monitor(self, profile, callback: callable): + self.subscriptions[id(profile)] = profile.filter("enabled").wire(lambda _: callback()) + + def unmonitor(self, profile): + self.subscriptions[id(profile)].cancel() + del self.subscriptions[id(profile)] + + class SdrSource(ABC): def __init__(self, props): self.id = props["id"] if "id" in props else None @@ -260,7 +278,7 @@ class SdrSource(ABC): return self.getProfile(self.props["profile_id"]) def getProfiles(self): - return self.props["profiles"] + return self.props["profiles"].filter(ProfileIsActiveFilter()) def getProfile(self, profile_id): try: @@ -599,20 +617,25 @@ class SdrDeviceDescription(object): def getDeviceInputs(self) -> List[Input]: keys = self.getDeviceMandatoryKeys() + self.getDeviceOptionalKeys() - return [TextInput("name", "Device name", validator=RequiredValidator())] + [ + return [ + TextInput("name", "Device name", validator=RequiredValidator()), + CheckboxInput("enabled", "Enable this device", converter=OptionalConverter(defaultFormValue=True)), + ] + [ i for i in self.getInputs() if i.id in keys ] def getProfileInputs(self) -> List[Input]: keys = self.getProfileMandatoryKeys() + self.getProfileOptionalKeys() - return [TextInput("name", "Profile name", validator=RequiredValidator())] + [ + return [ + TextInput("name", "Profile name", validator=RequiredValidator()), + CheckboxInput("enabled", "Enable this profile", converter=OptionalConverter(defaultFormValue=True)), + ] + [ i for i in self.getInputs() if i.id in keys ] def getInputs(self) -> List[Input]: return [ SdrDeviceTypeDisplay("type", "Device type"), - CheckboxInput("enabled", "Enable this device", converter=OptionalConverter(defaultFormValue=True)), GainInput("rf_gain", "Device gain", self.hasAgc()), NumberInput( "ppm", @@ -671,7 +694,7 @@ class SdrDeviceDescription(object): return keys def getProfileMandatoryKeys(self): - return ["name", "center_freq", "samp_rate", "start_freq", "start_mod"] + return ["name", "enabled", "center_freq", "samp_rate", "start_freq", "start_mod"] def getProfileOptionalKeys(self): return [