implement optional device fields
This commit is contained in:
@ -6,11 +6,19 @@ from enum import Enum
|
||||
|
||||
|
||||
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.label = label
|
||||
self.infotext = infotext
|
||||
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):
|
||||
return NullConverter()
|
||||
@ -18,23 +26,47 @@ class Input(ABC):
|
||||
def bootstrap_decorate(self, input):
|
||||
infotext = "<small>{text}</small>".format(text=self.infotext) if self.infotext else ""
|
||||
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>
|
||||
<div class="col-9 p-0">
|
||||
{input}
|
||||
{infotext}
|
||||
<div class="col-9 p-0 removable-group {removable}">
|
||||
<div class="removeable-item">
|
||||
{input}
|
||||
{infotext}
|
||||
</div>
|
||||
{removebutton}
|
||||
</div>
|
||||
</div>
|
||||
""".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):
|
||||
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):
|
||||
pass
|
||||
return "<input {properties} />".format(properties=self.render_input_properties(value))
|
||||
|
||||
def render(self, config):
|
||||
value = config[self.id] if self.id in config else None
|
||||
@ -43,14 +75,15 @@ class Input(ABC):
|
||||
def parse(self, data):
|
||||
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):
|
||||
def render_input(self, value):
|
||||
return """
|
||||
<input type="text" class="{classes}" id="{id}" name="{id}" placeholder="{label}" value="{value}">
|
||||
""".format(
|
||||
id=self.id, label=self.label, classes=self.input_classes(), value=value
|
||||
)
|
||||
def input_properties(self, value):
|
||||
props = super().input_properties(value)
|
||||
props["type"] = "text"
|
||||
return props
|
||||
|
||||
|
||||
class NumberInput(Input):
|
||||
@ -62,6 +95,13 @@ class NumberInput(Input):
|
||||
def defaultConverter(self):
|
||||
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):
|
||||
if self.append:
|
||||
append = """
|
||||
@ -76,15 +116,11 @@ class NumberInput(Input):
|
||||
|
||||
return """
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="number" class="{classes}" id="{id}" name="{id}" placeholder="{label}" value="{value}" {step}>
|
||||
{input}
|
||||
{append}
|
||||
</div>
|
||||
""".format(
|
||||
id=self.id,
|
||||
label=self.label,
|
||||
classes=self.input_classes(),
|
||||
value=value,
|
||||
step='step="{0}"'.format(self.step) if self.step else "",
|
||||
input=super().render_input(value),
|
||||
append=append,
|
||||
)
|
||||
|
||||
@ -116,13 +152,15 @@ class LocationInput(Input):
|
||||
def render_sub_input(self, value, id):
|
||||
return """
|
||||
<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>
|
||||
""".format(
|
||||
id="{0}-{1}".format(self.id, id),
|
||||
label=self.label,
|
||||
classes=self.input_classes(),
|
||||
value=value[id],
|
||||
disabled="disabled" if self.disabled else "",
|
||||
)
|
||||
|
||||
def parse(self, data):
|
||||
@ -132,9 +170,9 @@ class LocationInput(Input):
|
||||
class TextAreaInput(Input):
|
||||
def render_input(self, value):
|
||||
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(
|
||||
id=self.id, classes=self.input_classes(), value=value
|
||||
id=self.id, classes=self.input_classes(), value=value, disabled="disabled" if self.disabled else ""
|
||||
)
|
||||
|
||||
|
||||
@ -145,16 +183,17 @@ class CheckboxInput(Input):
|
||||
|
||||
def render_input(self, value):
|
||||
return """
|
||||
<div class="{classes}">
|
||||
<input class="form-check-input" type="checkbox" id="{id}" name="{id}" {checked}>
|
||||
<label class="form-check-label" for="{id}">
|
||||
{checkboxText}
|
||||
</label>
|
||||
</div>
|
||||
<div class="{classes}">
|
||||
<input class="form-check-input" type="checkbox" id="{id}" name="{id}" {checked} {disabled}>
|
||||
<label class="form-check-label" for="{id}">
|
||||
{checkboxText}
|
||||
</label>
|
||||
</div>
|
||||
""".format(
|
||||
id=self.id,
|
||||
classes=self.input_classes(),
|
||||
checked="checked" if value else "",
|
||||
disabled="disabled" if self.disabled else "",
|
||||
checkboxText=self.checkboxText,
|
||||
)
|
||||
|
||||
@ -164,6 +203,9 @@ class CheckboxInput(Input):
|
||||
def parse(self, data):
|
||||
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):
|
||||
# used for both MultiCheckboxInput and DropdownInput
|
||||
@ -186,7 +228,7 @@ class MultiCheckboxInput(Input):
|
||||
def render_checkbox(self, option, value):
|
||||
return """
|
||||
<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}">
|
||||
{checkboxText}
|
||||
</label>
|
||||
@ -196,6 +238,7 @@ class MultiCheckboxInput(Input):
|
||||
classes=self.input_classes(),
|
||||
checked="checked" if option.value in value else "",
|
||||
checkboxText=option.text,
|
||||
disabled="disabled" if self.disabled else "",
|
||||
)
|
||||
|
||||
def parse(self, data):
|
||||
@ -242,9 +285,12 @@ class DropdownInput(Input):
|
||||
|
||||
def render_input(self, value):
|
||||
return """
|
||||
<select class="{classes}" id="{id}" name="{id}">{options}</select>
|
||||
<select class="{classes}" id="{id}" name="{id}" {disabled}>{options}</select>
|
||||
""".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):
|
||||
|
@ -16,12 +16,12 @@ class GainInput(Input):
|
||||
|
||||
return """
|
||||
<div id="{id}">
|
||||
<select class="{classes}" id="{id}-select" name="{id}-select">
|
||||
<select class="{classes}" id="{id}-select" name="{id}-select" {disabled}>
|
||||
{options}
|
||||
</select>
|
||||
<div class="option manual" style="display: none;">
|
||||
<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>
|
||||
{stageoption}
|
||||
</div>
|
||||
@ -32,6 +32,7 @@ class GainInput(Input):
|
||||
label=self.label,
|
||||
options=self.render_options(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):
|
||||
@ -79,15 +80,16 @@ class GainInput(Input):
|
||||
inputs="".join(
|
||||
"""
|
||||
<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}"
|
||||
class="col-9 {classes}" placeholder="{stage}" step="any">
|
||||
class="col-9 {classes}" placeholder="{stage}" step="any" {disabled}>
|
||||
</div>
|
||||
""".format(
|
||||
id=self.id,
|
||||
stage=stage,
|
||||
value=value_dict[stage] if stage in value_dict else "",
|
||||
classes=self.input_classes(),
|
||||
disabled="disabled" if self.disabled else "",
|
||||
)
|
||||
for stage in self.gain_stages
|
||||
)
|
||||
|
@ -22,7 +22,7 @@ class Q65ModeMatrix(Input):
|
||||
id=self.checkbox_id(mode, interval),
|
||||
checked="checked" if "{}{}".format(mode.name, interval.value) in value else "",
|
||||
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):
|
||||
@ -69,7 +69,7 @@ class WsjtDecodingDepthsInput(Input):
|
||||
)
|
||||
|
||||
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;">
|
||||
<select class="form-control form-control-sm">{options}</select>
|
||||
<input class="form-control form-control-sm" type="number" step="1">
|
||||
@ -79,6 +79,7 @@ class WsjtDecodingDepthsInput(Input):
|
||||
classes=self.input_classes(),
|
||||
value=html.escape(value),
|
||||
options="".join(render_mode(m) for m in Modes.getAvailableModes() if isinstance(m, WsjtMode)),
|
||||
disabled="disabled" if self.disabled else ""
|
||||
)
|
||||
|
||||
def input_classes(self):
|
||||
|
Reference in New Issue
Block a user