implement optional device fields
This commit is contained in:
parent
f8beae5f46
commit
54a34b2084
@ -86,3 +86,20 @@ table.bookmarks .frequency {
|
|||||||
.sdr-profile-list .list-group-item {
|
.sdr-profile-list .list-group-item {
|
||||||
background: initial;
|
background: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.removable-group.removable {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.removable-group.removable .removable-item {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.removable-group.removable .option-remove-button {
|
||||||
|
flex: 0 0 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-add-button, .option-remove-button {
|
||||||
|
width: 70px;
|
||||||
|
}
|
26
htdocs/lib/settings/OptionalSection.js
Normal file
26
htdocs/lib/settings/OptionalSection.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
$.fn.optionalSection = function(){
|
||||||
|
this.each(function() {
|
||||||
|
var $section = $(this);
|
||||||
|
var $select = $section.find('.optional-select');
|
||||||
|
var $optionalInputs = $section.find('.optional-inputs');
|
||||||
|
$section.on('click', '.option-add-button', function(e){
|
||||||
|
var field = $select.val();
|
||||||
|
var group = $optionalInputs.find(".form-group[data-field='" + field + "']");
|
||||||
|
group.find('input, select').prop('disabled', false);
|
||||||
|
$select.parents('.form-group').before(group);
|
||||||
|
$select.find('option[value=\'' + field + '\']').remove();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$section.on('click', '.option-remove-button', function(e) {
|
||||||
|
var group = $(e.target).parents('.form-group')
|
||||||
|
group.find('input, select').prop('disabled', true);
|
||||||
|
$optionalInputs.append(group);
|
||||||
|
var $label = group.find('> label');
|
||||||
|
var $option = $('<option value="' + group.data('field') + '">' + $label.text() + '</option>');
|
||||||
|
$select.append($option);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
@ -5,4 +5,5 @@ $(function(){
|
|||||||
$('.wsjt-decoding-depths').wsjtDecodingDepthsInput();
|
$('.wsjt-decoding-depths').wsjtDecodingDepthsInput();
|
||||||
$('#waterfall_scheme').waterfallDropdown();
|
$('#waterfall_scheme').waterfallDropdown();
|
||||||
$('#rf_gain').gainInput();
|
$('#rf_gain').gainInput();
|
||||||
|
$('.optional-section').optionalSection();
|
||||||
});
|
});
|
@ -151,6 +151,7 @@ class CompiledAssetsController(GzipMixin, ModificationAwareController):
|
|||||||
"lib/settings/WsjtDecodingDepthsInput.js",
|
"lib/settings/WsjtDecodingDepthsInput.js",
|
||||||
"lib/settings/WaterfallDropdown.js",
|
"lib/settings/WaterfallDropdown.js",
|
||||||
"lib/settings/GainInput.js",
|
"lib/settings/GainInput.js",
|
||||||
|
"lib/settings/OptionalSection.js",
|
||||||
"settings.js",
|
"settings.js",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
@ -10,19 +10,25 @@ class Section(object):
|
|||||||
self.title = title
|
self.title = title
|
||||||
self.inputs = inputs
|
self.inputs = inputs
|
||||||
|
|
||||||
|
def render_input(self, input, data):
|
||||||
|
return input.render(data)
|
||||||
|
|
||||||
def render_inputs(self, data):
|
def render_inputs(self, data):
|
||||||
return "".join([i.render(data) for i in self.inputs])
|
return "".join([self.render_input(i, data) for i in self.inputs])
|
||||||
|
|
||||||
|
def classes(self):
|
||||||
|
return ["col-12", "settings-section"]
|
||||||
|
|
||||||
def render(self, data):
|
def render(self, data):
|
||||||
return """
|
return """
|
||||||
<div class="col-12 settings-section">
|
<div class="{classes}">
|
||||||
<h3 class="settings-header">
|
<h3 class="settings-header">
|
||||||
{title}
|
{title}
|
||||||
</h3>
|
</h3>
|
||||||
{inputs}
|
{inputs}
|
||||||
</div>
|
</div>
|
||||||
""".format(
|
""".format(
|
||||||
title=self.title, inputs=self.render_inputs(data)
|
classes=" ".join(self.classes()), title=self.title, inputs=self.render_inputs(data)
|
||||||
)
|
)
|
||||||
|
|
||||||
def parse(self, data):
|
def parse(self, data):
|
||||||
|
@ -71,7 +71,7 @@ class SdrDeviceController(SettingsFormController):
|
|||||||
def getSections(self):
|
def getSections(self):
|
||||||
try:
|
try:
|
||||||
description = SdrDeviceDescription.getByType(self.device["type"])
|
description = SdrDeviceDescription.getByType(self.device["type"])
|
||||||
return [description.getSection(self.device)]
|
return [description.getSection()]
|
||||||
except SdrDeviceDescriptionMissing:
|
except SdrDeviceDescriptionMissing:
|
||||||
# TODO provide a generic interface that allows to switch the type
|
# TODO provide a generic interface that allows to switch the type
|
||||||
return []
|
return []
|
||||||
|
@ -6,11 +6,19 @@ from enum import Enum
|
|||||||
|
|
||||||
|
|
||||||
class Input(ABC):
|
class Input(ABC):
|
||||||
def __init__(self, id, label, infotext=None, converter: Converter = None):
|
def __init__(self, id, label, infotext=None, converter: Converter = None, disabled=False, removable=False):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.label = label
|
self.label = label
|
||||||
self.infotext = infotext
|
self.infotext = infotext
|
||||||
self.converter = self.defaultConverter() if converter is None else converter
|
self.converter = self.defaultConverter() if converter is None else converter
|
||||||
|
self.disabled = disabled
|
||||||
|
self.removable = removable
|
||||||
|
|
||||||
|
def setDisabled(self, disabled=True):
|
||||||
|
self.disabled = disabled
|
||||||
|
|
||||||
|
def setRemovable(self, removable=True):
|
||||||
|
self.removable = removable
|
||||||
|
|
||||||
def defaultConverter(self):
|
def defaultConverter(self):
|
||||||
return NullConverter()
|
return NullConverter()
|
||||||
@ -18,23 +26,47 @@ class Input(ABC):
|
|||||||
def bootstrap_decorate(self, input):
|
def bootstrap_decorate(self, input):
|
||||||
infotext = "<small>{text}</small>".format(text=self.infotext) if self.infotext else ""
|
infotext = "<small>{text}</small>".format(text=self.infotext) if self.infotext else ""
|
||||||
return """
|
return """
|
||||||
<div class="form-group row">
|
<div class="form-group row" data-field="{id}">
|
||||||
<label class="col-form-label col-form-label-sm col-3" for="{id}">{label}</label>
|
<label class="col-form-label col-form-label-sm col-3" for="{id}">{label}</label>
|
||||||
<div class="col-9 p-0">
|
<div class="col-9 p-0 removable-group {removable}">
|
||||||
|
<div class="removeable-item">
|
||||||
{input}
|
{input}
|
||||||
{infotext}
|
{infotext}
|
||||||
</div>
|
</div>
|
||||||
|
{removebutton}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
""".format(
|
""".format(
|
||||||
id=self.id, label=self.label, input=input, infotext=infotext
|
id=self.id,
|
||||||
|
label=self.label,
|
||||||
|
input=input,
|
||||||
|
infotext=infotext,
|
||||||
|
removable="removable" if self.removable else "",
|
||||||
|
removebutton='<button class="btn btn-sm btn-danger option-remove-button">Remove</button>'
|
||||||
|
if self.removable
|
||||||
|
else "",
|
||||||
)
|
)
|
||||||
|
|
||||||
def input_classes(self):
|
def input_classes(self):
|
||||||
return " ".join(["form-control", "form-control-sm"])
|
return " ".join(["form-control", "form-control-sm"])
|
||||||
|
|
||||||
@abstractmethod
|
def input_properties(self, value):
|
||||||
|
props = {
|
||||||
|
"class": self.input_classes(),
|
||||||
|
"id": self.id,
|
||||||
|
"name": self.id,
|
||||||
|
"placeholder": self.label,
|
||||||
|
"value": value,
|
||||||
|
}
|
||||||
|
if self.disabled:
|
||||||
|
props["disabled"] = "disabled"
|
||||||
|
return props
|
||||||
|
|
||||||
|
def render_input_properties(self, value):
|
||||||
|
return " ".join('{}="{}"'.format(prop, value) for prop, value in self.input_properties(value).items())
|
||||||
|
|
||||||
def render_input(self, value):
|
def render_input(self, value):
|
||||||
pass
|
return "<input {properties} />".format(properties=self.render_input_properties(value))
|
||||||
|
|
||||||
def render(self, config):
|
def render(self, config):
|
||||||
value = config[self.id] if self.id in config else None
|
value = config[self.id] if self.id in config else None
|
||||||
@ -43,14 +75,15 @@ class Input(ABC):
|
|||||||
def parse(self, data):
|
def parse(self, data):
|
||||||
return {self.id: self.converter.convert_from_form(data[self.id][0])} if self.id in data else {}
|
return {self.id: self.converter.convert_from_form(data[self.id][0])} if self.id in data else {}
|
||||||
|
|
||||||
|
def getLabel(self):
|
||||||
|
return self.label
|
||||||
|
|
||||||
|
|
||||||
class TextInput(Input):
|
class TextInput(Input):
|
||||||
def render_input(self, value):
|
def input_properties(self, value):
|
||||||
return """
|
props = super().input_properties(value)
|
||||||
<input type="text" class="{classes}" id="{id}" name="{id}" placeholder="{label}" value="{value}">
|
props["type"] = "text"
|
||||||
""".format(
|
return props
|
||||||
id=self.id, label=self.label, classes=self.input_classes(), value=value
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NumberInput(Input):
|
class NumberInput(Input):
|
||||||
@ -62,6 +95,13 @@ class NumberInput(Input):
|
|||||||
def defaultConverter(self):
|
def defaultConverter(self):
|
||||||
return IntConverter()
|
return IntConverter()
|
||||||
|
|
||||||
|
def input_properties(self, value):
|
||||||
|
props = super().input_properties(value)
|
||||||
|
props["type"] = "number"
|
||||||
|
if self.step:
|
||||||
|
props["step"] = self.step
|
||||||
|
return props
|
||||||
|
|
||||||
def render_input(self, value):
|
def render_input(self, value):
|
||||||
if self.append:
|
if self.append:
|
||||||
append = """
|
append = """
|
||||||
@ -76,15 +116,11 @@ class NumberInput(Input):
|
|||||||
|
|
||||||
return """
|
return """
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<input type="number" class="{classes}" id="{id}" name="{id}" placeholder="{label}" value="{value}" {step}>
|
{input}
|
||||||
{append}
|
{append}
|
||||||
</div>
|
</div>
|
||||||
""".format(
|
""".format(
|
||||||
id=self.id,
|
input=super().render_input(value),
|
||||||
label=self.label,
|
|
||||||
classes=self.input_classes(),
|
|
||||||
value=value,
|
|
||||||
step='step="{0}"'.format(self.step) if self.step else "",
|
|
||||||
append=append,
|
append=append,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -116,13 +152,15 @@ class LocationInput(Input):
|
|||||||
def render_sub_input(self, value, id):
|
def render_sub_input(self, value, id):
|
||||||
return """
|
return """
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<input type="number" class="{classes}" id="{id}" name="{id}" placeholder="{label}" value="{value}" step="any">
|
<input type="number" class="{classes}" id="{id}" name="{id}" placeholder="{label}" value="{value}"
|
||||||
|
step="any" {disabled}>
|
||||||
</div>
|
</div>
|
||||||
""".format(
|
""".format(
|
||||||
id="{0}-{1}".format(self.id, id),
|
id="{0}-{1}".format(self.id, id),
|
||||||
label=self.label,
|
label=self.label,
|
||||||
classes=self.input_classes(),
|
classes=self.input_classes(),
|
||||||
value=value[id],
|
value=value[id],
|
||||||
|
disabled="disabled" if self.disabled else "",
|
||||||
)
|
)
|
||||||
|
|
||||||
def parse(self, data):
|
def parse(self, data):
|
||||||
@ -132,9 +170,9 @@ class LocationInput(Input):
|
|||||||
class TextAreaInput(Input):
|
class TextAreaInput(Input):
|
||||||
def render_input(self, value):
|
def render_input(self, value):
|
||||||
return """
|
return """
|
||||||
<textarea class="{classes}" id="{id}" name="{id}" style="height:200px;">{value}</textarea>
|
<textarea class="{classes}" id="{id}" name="{id}" style="height:200px;" {disabled}>{value}</textarea>
|
||||||
""".format(
|
""".format(
|
||||||
id=self.id, classes=self.input_classes(), value=value
|
id=self.id, classes=self.input_classes(), value=value, disabled="disabled" if self.disabled else ""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -146,7 +184,7 @@ class CheckboxInput(Input):
|
|||||||
def render_input(self, value):
|
def render_input(self, value):
|
||||||
return """
|
return """
|
||||||
<div class="{classes}">
|
<div class="{classes}">
|
||||||
<input class="form-check-input" type="checkbox" id="{id}" name="{id}" {checked}>
|
<input class="form-check-input" type="checkbox" id="{id}" name="{id}" {checked} {disabled}>
|
||||||
<label class="form-check-label" for="{id}">
|
<label class="form-check-label" for="{id}">
|
||||||
{checkboxText}
|
{checkboxText}
|
||||||
</label>
|
</label>
|
||||||
@ -155,6 +193,7 @@ class CheckboxInput(Input):
|
|||||||
id=self.id,
|
id=self.id,
|
||||||
classes=self.input_classes(),
|
classes=self.input_classes(),
|
||||||
checked="checked" if value else "",
|
checked="checked" if value else "",
|
||||||
|
disabled="disabled" if self.disabled else "",
|
||||||
checkboxText=self.checkboxText,
|
checkboxText=self.checkboxText,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -164,6 +203,9 @@ class CheckboxInput(Input):
|
|||||||
def parse(self, data):
|
def parse(self, data):
|
||||||
return {self.id: self.converter.convert_from_form(self.id in data and data[self.id][0] == "on")}
|
return {self.id: self.converter.convert_from_form(self.id in data and data[self.id][0] == "on")}
|
||||||
|
|
||||||
|
def getLabel(self):
|
||||||
|
return self.checkboxText
|
||||||
|
|
||||||
|
|
||||||
class Option(object):
|
class Option(object):
|
||||||
# used for both MultiCheckboxInput and DropdownInput
|
# used for both MultiCheckboxInput and DropdownInput
|
||||||
@ -186,7 +228,7 @@ class MultiCheckboxInput(Input):
|
|||||||
def render_checkbox(self, option, value):
|
def render_checkbox(self, option, value):
|
||||||
return """
|
return """
|
||||||
<div class="{classes}">
|
<div class="{classes}">
|
||||||
<input class="form-check-input" type="checkbox" id="{id}" name="{id}" {checked}>
|
<input class="form-check-input" type="checkbox" id="{id}" name="{id}" {checked} {disabled}>
|
||||||
<label class="form-check-label" for="{id}">
|
<label class="form-check-label" for="{id}">
|
||||||
{checkboxText}
|
{checkboxText}
|
||||||
</label>
|
</label>
|
||||||
@ -196,6 +238,7 @@ class MultiCheckboxInput(Input):
|
|||||||
classes=self.input_classes(),
|
classes=self.input_classes(),
|
||||||
checked="checked" if option.value in value else "",
|
checked="checked" if option.value in value else "",
|
||||||
checkboxText=option.text,
|
checkboxText=option.text,
|
||||||
|
disabled="disabled" if self.disabled else "",
|
||||||
)
|
)
|
||||||
|
|
||||||
def parse(self, data):
|
def parse(self, data):
|
||||||
@ -242,9 +285,12 @@ class DropdownInput(Input):
|
|||||||
|
|
||||||
def render_input(self, value):
|
def render_input(self, value):
|
||||||
return """
|
return """
|
||||||
<select class="{classes}" id="{id}" name="{id}">{options}</select>
|
<select class="{classes}" id="{id}" name="{id}" {disabled}>{options}</select>
|
||||||
""".format(
|
""".format(
|
||||||
classes=self.input_classes(), id=self.id, options=self.render_options(value)
|
classes=self.input_classes(),
|
||||||
|
id=self.id,
|
||||||
|
options=self.render_options(value),
|
||||||
|
disabled="disabled" if self.disabled else "",
|
||||||
)
|
)
|
||||||
|
|
||||||
def render_options(self, value):
|
def render_options(self, value):
|
||||||
|
@ -16,12 +16,12 @@ class GainInput(Input):
|
|||||||
|
|
||||||
return """
|
return """
|
||||||
<div id="{id}">
|
<div id="{id}">
|
||||||
<select class="{classes}" id="{id}-select" name="{id}-select">
|
<select class="{classes}" id="{id}-select" name="{id}-select" {disabled}>
|
||||||
{options}
|
{options}
|
||||||
</select>
|
</select>
|
||||||
<div class="option manual" style="display: none;">
|
<div class="option manual" style="display: none;">
|
||||||
<input type="number" id="{id}-manual" name="{id}-manual" value="{value}" class="{classes}"
|
<input type="number" id="{id}-manual" name="{id}-manual" value="{value}" class="{classes}"
|
||||||
placeholder="Manual device gain" step="any">
|
placeholder="Manual device gain" step="any" {disabled}>
|
||||||
</div>
|
</div>
|
||||||
{stageoption}
|
{stageoption}
|
||||||
</div>
|
</div>
|
||||||
@ -32,6 +32,7 @@ class GainInput(Input):
|
|||||||
label=self.label,
|
label=self.label,
|
||||||
options=self.render_options(value),
|
options=self.render_options(value),
|
||||||
stageoption="" if self.gain_stages is None else self.render_stage_option(value),
|
stageoption="" if self.gain_stages is None else self.render_stage_option(value),
|
||||||
|
disabled="disabled" if self.disabled else ""
|
||||||
)
|
)
|
||||||
|
|
||||||
def render_options(self, value):
|
def render_options(self, value):
|
||||||
@ -79,15 +80,16 @@ class GainInput(Input):
|
|||||||
inputs="".join(
|
inputs="".join(
|
||||||
"""
|
"""
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-3">{stage}</div>
|
<label class="col-form-label col-form-label-sm col-3">{stage}</label>
|
||||||
<input type="number" id="{id}-{stage}" name="{id}-{stage}" value="{value}"
|
<input type="number" id="{id}-{stage}" name="{id}-{stage}" value="{value}"
|
||||||
class="col-9 {classes}" placeholder="{stage}" step="any">
|
class="col-9 {classes}" placeholder="{stage}" step="any" {disabled}>
|
||||||
</div>
|
</div>
|
||||||
""".format(
|
""".format(
|
||||||
id=self.id,
|
id=self.id,
|
||||||
stage=stage,
|
stage=stage,
|
||||||
value=value_dict[stage] if stage in value_dict else "",
|
value=value_dict[stage] if stage in value_dict else "",
|
||||||
classes=self.input_classes(),
|
classes=self.input_classes(),
|
||||||
|
disabled="disabled" if self.disabled else "",
|
||||||
)
|
)
|
||||||
for stage in self.gain_stages
|
for stage in self.gain_stages
|
||||||
)
|
)
|
||||||
|
@ -22,7 +22,7 @@ class Q65ModeMatrix(Input):
|
|||||||
id=self.checkbox_id(mode, interval),
|
id=self.checkbox_id(mode, interval),
|
||||||
checked="checked" if "{}{}".format(mode.name, interval.value) in value else "",
|
checked="checked" if "{}{}".format(mode.name, interval.value) in value else "",
|
||||||
checkboxText="Mode {} interval {}s".format(mode.name, interval.value),
|
checkboxText="Mode {} interval {}s".format(mode.name, interval.value),
|
||||||
disabled="" if interval.is_available(mode) else "disabled",
|
disabled="" if interval.is_available(mode) and not self.disabled else "disabled",
|
||||||
)
|
)
|
||||||
|
|
||||||
def render_input(self, value):
|
def render_input(self, value):
|
||||||
@ -69,7 +69,7 @@ class WsjtDecodingDepthsInput(Input):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return """
|
return """
|
||||||
<input type="hidden" class="{classes}" id="{id}" name="{id}" value="{value}">
|
<input type="hidden" class="{classes}" id="{id}" name="{id}" value="{value}" {disabled}>
|
||||||
<div class="inputs" style="display:none;">
|
<div class="inputs" style="display:none;">
|
||||||
<select class="form-control form-control-sm">{options}</select>
|
<select class="form-control form-control-sm">{options}</select>
|
||||||
<input class="form-control form-control-sm" type="number" step="1">
|
<input class="form-control form-control-sm" type="number" step="1">
|
||||||
@ -79,6 +79,7 @@ class WsjtDecodingDepthsInput(Input):
|
|||||||
classes=self.input_classes(),
|
classes=self.input_classes(),
|
||||||
value=html.escape(value),
|
value=html.escape(value),
|
||||||
options="".join(render_mode(m) for m in Modes.getAvailableModes() if isinstance(m, WsjtMode)),
|
options="".join(render_mode(m) for m in Modes.getAvailableModes() if isinstance(m, WsjtMode)),
|
||||||
|
disabled="disabled" if self.disabled else ""
|
||||||
)
|
)
|
||||||
|
|
||||||
def input_classes(self):
|
def input_classes(self):
|
||||||
|
@ -375,10 +375,76 @@ class SdrDeviceDescriptionMissing(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SdrDeviceDescription(object):
|
class OptionalSection(Section):
|
||||||
def __init__(self):
|
def __init__(self, title, inputs: List[Input], mandatory, optional):
|
||||||
self.indexedInputs = {input.id: input for input in self.getInputs()}
|
super().__init__(title, *inputs)
|
||||||
|
self.mandatory = mandatory
|
||||||
|
self.optional = optional
|
||||||
|
self.optional_inputs = []
|
||||||
|
|
||||||
|
def classes(self):
|
||||||
|
classes = super().classes()
|
||||||
|
classes.append("optional-section")
|
||||||
|
return classes
|
||||||
|
|
||||||
|
def _is_optional(self, input):
|
||||||
|
return input.id in self.optional
|
||||||
|
|
||||||
|
def render_optional_select(self):
|
||||||
|
return """
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-form-label col-form-label-sm col-3">
|
||||||
|
Additional optional settings
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-group-sm col-9 p-0">
|
||||||
|
<select class="form-control from-control-sm optional-select">
|
||||||
|
{options}
|
||||||
|
</select>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-success option-add-button">Add</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
""".format(
|
||||||
|
options="".join(
|
||||||
|
"""
|
||||||
|
<option value="{value}">{name}</option>
|
||||||
|
""".format(
|
||||||
|
value=input.id,
|
||||||
|
name=input.getLabel(),
|
||||||
|
)
|
||||||
|
for input in self.optional_inputs
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def render_optional_inputs(self, data):
|
||||||
|
return """
|
||||||
|
<div class="optional-inputs" style="display: none;">
|
||||||
|
{inputs}
|
||||||
|
</div>
|
||||||
|
""".format(
|
||||||
|
inputs="".join(self.render_input(input, data) for input in self.optional_inputs)
|
||||||
|
)
|
||||||
|
|
||||||
|
def render_inputs(self, data):
|
||||||
|
return super().render_inputs(data) + self.render_optional_select() + self.render_optional_inputs(data)
|
||||||
|
|
||||||
|
def render(self, data):
|
||||||
|
indexed_inputs = {input.id: input for input in self.inputs}
|
||||||
|
visible_keys = set(self.mandatory + [k for k in self.optional if k in data])
|
||||||
|
optional_keys = set(k for k in self.optional if k not in data)
|
||||||
|
self.inputs = [input for k, input in indexed_inputs.items() if k in visible_keys]
|
||||||
|
for input in self.inputs:
|
||||||
|
if self._is_optional(input):
|
||||||
|
input.setRemovable()
|
||||||
|
self.optional_inputs = [input for k, input in indexed_inputs.items() if k in optional_keys]
|
||||||
|
for input in self.optional_inputs:
|
||||||
|
input.setRemovable()
|
||||||
|
input.setDisabled()
|
||||||
|
return super().render(data)
|
||||||
|
|
||||||
|
|
||||||
|
class SdrDeviceDescription(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getByType(sdr_type: str) -> "SdrDeviceDescription":
|
def getByType(sdr_type: str) -> "SdrDeviceDescription":
|
||||||
try:
|
try:
|
||||||
@ -407,7 +473,6 @@ class SdrDeviceDescription(object):
|
|||||||
),
|
),
|
||||||
CheckboxInput(
|
CheckboxInput(
|
||||||
"services",
|
"services",
|
||||||
"",
|
|
||||||
"Run background services on this device",
|
"Run background services on this device",
|
||||||
converter=OptionalConverter(defaultFormValue=True),
|
converter=OptionalConverter(defaultFormValue=True),
|
||||||
),
|
),
|
||||||
@ -431,8 +496,5 @@ class SdrDeviceDescription(object):
|
|||||||
def getOptionalKeys(self):
|
def getOptionalKeys(self):
|
||||||
return ["ppm", "always-on", "services", "rf_gain", "lfo_offset", "waterfall_min_level", "waterfall_max_level"]
|
return ["ppm", "always-on", "services", "rf_gain", "lfo_offset", "waterfall_min_level", "waterfall_max_level"]
|
||||||
|
|
||||||
def getSection(self, data):
|
def getSection(self):
|
||||||
visible_keys = set(self.getMandatoryKeys() + [k for k in self.getOptionalKeys() if k in data])
|
return OptionalSection("Device settings", self.getInputs(), self.getMandatoryKeys(), self.getOptionalKeys())
|
||||||
inputs = [input for k, input in self.indexedInputs.items() if k in visible_keys]
|
|
||||||
# TODO: render remaining keys in optional area
|
|
||||||
return Section("Device settings", *inputs)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user