539 lines
19 KiB
Python
539 lines
19 KiB
Python
|
|
import urwid
|
|||
|
|
|
|||
|
|
from nomadnet.ui.textui.ReadlineEdit import ReadlineMixin, ReadlineEdit
|
|||
|
|
|
|||
|
|
class DialogLineBox(urwid.LineBox):
|
|||
|
|
def __init__(self, body, parent=None, title="?"):
|
|||
|
|
super().__init__(body, title=title)
|
|||
|
|
self.parent = parent
|
|||
|
|
|
|||
|
|
def keypress(self, size, key):
|
|||
|
|
if key == "esc":
|
|||
|
|
if self.parent and hasattr(self.parent, "dismiss_dialog"):
|
|||
|
|
self.parent.dismiss_dialog()
|
|||
|
|
return None
|
|||
|
|
return super().keypress(size, key)
|
|||
|
|
|
|||
|
|
class Placeholder(ReadlineMixin, urwid.Edit):
|
|||
|
|
def __init__(self, caption="", edit_text="", placeholder="", **kwargs):
|
|||
|
|
super().__init__(caption, edit_text, **kwargs)
|
|||
|
|
self.placeholder = placeholder
|
|||
|
|
|
|||
|
|
def render(self, size, focus=False):
|
|||
|
|
if not self.edit_text and not focus:
|
|||
|
|
placeholder_widget = urwid.Text(("placeholder", self.placeholder))
|
|||
|
|
return placeholder_widget.render(size, focus)
|
|||
|
|
else:
|
|||
|
|
return super().render(size, focus)
|
|||
|
|
|
|||
|
|
class Dropdown(urwid.WidgetWrap):
|
|||
|
|
signals = ['change'] # emit for urwid.connect_signal fn
|
|||
|
|
|
|||
|
|
def __init__(self, label, options, default=None):
|
|||
|
|
self.label = label
|
|||
|
|
self.options = options
|
|||
|
|
self.selected = default if default is not None else options[0]
|
|||
|
|
|
|||
|
|
self.main_text = f"{self.selected}"
|
|||
|
|
self.main_button = urwid.SelectableIcon(self.main_text, 0)
|
|||
|
|
self.main_button = urwid.AttrMap(self.main_button, "button_normal", "button_focus")
|
|||
|
|
|
|||
|
|
self.option_widgets = []
|
|||
|
|
for opt in options:
|
|||
|
|
icon = urwid.SelectableIcon(opt, 0)
|
|||
|
|
icon = urwid.AttrMap(icon, "list_normal", "list_focus")
|
|||
|
|
self.option_widgets.append(icon)
|
|||
|
|
|
|||
|
|
self.options_walker = urwid.SimpleFocusListWalker(self.option_widgets)
|
|||
|
|
self.options_listbox = urwid.ListBox(self.options_walker)
|
|||
|
|
self.dropdown_box = None # will be created on open_dropdown
|
|||
|
|
|
|||
|
|
self.pile = urwid.Pile([self.main_button])
|
|||
|
|
self.dropdown_visible = False
|
|||
|
|
|
|||
|
|
super().__init__(self.pile)
|
|||
|
|
|
|||
|
|
def open_dropdown(self):
|
|||
|
|
if not self.dropdown_visible:
|
|||
|
|
height = len(self.options)
|
|||
|
|
self.dropdown_box = urwid.BoxAdapter(self.options_listbox, height)
|
|||
|
|
self.pile.contents.append((self.dropdown_box, self.pile.options()))
|
|||
|
|
self.dropdown_visible = True
|
|||
|
|
self.pile.focus_position = 1
|
|||
|
|
self.options_walker.set_focus(0)
|
|||
|
|
|
|||
|
|
def close_dropdown(self):
|
|||
|
|
if self.dropdown_visible:
|
|||
|
|
self.pile.contents.pop() # remove the dropdown_box
|
|||
|
|
self.dropdown_visible = False
|
|||
|
|
self.pile.focus_position = 0
|
|||
|
|
self.dropdown_box = None
|
|||
|
|
|
|||
|
|
def keypress(self, size, key):
|
|||
|
|
if not self.dropdown_visible:
|
|||
|
|
if key == "enter":
|
|||
|
|
self.open_dropdown()
|
|||
|
|
return None
|
|||
|
|
return self.main_button.keypress(size, key)
|
|||
|
|
else:
|
|||
|
|
if key == "enter":
|
|||
|
|
focus_result = self.options_walker.get_focus()
|
|||
|
|
if focus_result is not None:
|
|||
|
|
focus_widget = focus_result[0]
|
|||
|
|
new_val = focus_widget.base_widget.text
|
|||
|
|
old_val = self.selected
|
|||
|
|
self.selected = new_val
|
|||
|
|
self.main_button.base_widget.set_text(f"{self.selected}")
|
|||
|
|
|
|||
|
|
if old_val != new_val:
|
|||
|
|
self._emit('change', new_val)
|
|||
|
|
|
|||
|
|
self.close_dropdown()
|
|||
|
|
return None
|
|||
|
|
return self.dropdown_box.keypress(size, key)
|
|||
|
|
|
|||
|
|
def get_value(self):
|
|||
|
|
return self.selected
|
|||
|
|
|
|||
|
|
class ValidationError(urwid.Text):
|
|||
|
|
def __init__(self, message=""):
|
|||
|
|
super().__init__(("error", message))
|
|||
|
|
|
|||
|
|
class FormField:
|
|||
|
|
def __init__(self, config_key, transform=None):
|
|||
|
|
self.config_key = config_key
|
|||
|
|
self.transform = transform or (lambda x: x)
|
|||
|
|
|
|||
|
|
class FormEdit(Placeholder, FormField):
|
|||
|
|
def __init__(self, config_key, caption="", edit_text="", placeholder="", validation_types=None, transform=None, **kwargs):
|
|||
|
|
Placeholder.__init__(self, caption, edit_text, placeholder, **kwargs)
|
|||
|
|
FormField.__init__(self, config_key, transform)
|
|||
|
|
self.validation_types = validation_types or []
|
|||
|
|
self.error_widget = urwid.Text("")
|
|||
|
|
self.error = None
|
|||
|
|
|
|||
|
|
def get_value(self):
|
|||
|
|
return self.transform(self.edit_text.strip())
|
|||
|
|
|
|||
|
|
def validate(self):
|
|||
|
|
value = self.edit_text.strip()
|
|||
|
|
self.error = None
|
|||
|
|
|
|||
|
|
for validation in self.validation_types:
|
|||
|
|
if validation == "required":
|
|||
|
|
if not value:
|
|||
|
|
self.error = "This field is required"
|
|||
|
|
break
|
|||
|
|
elif validation == "number":
|
|||
|
|
if value and not value.replace('-', '').replace('.', '').isdigit():
|
|||
|
|
self.error = "This field must be a number"
|
|||
|
|
break
|
|||
|
|
elif validation == "float":
|
|||
|
|
try:
|
|||
|
|
if value:
|
|||
|
|
float(value)
|
|||
|
|
except ValueError:
|
|||
|
|
self.error = "This field must be decimal number"
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
self.error_widget.set_text(("error", self.error or ""))
|
|||
|
|
return self.error is None
|
|||
|
|
|
|||
|
|
class FormCheckbox(urwid.CheckBox, FormField):
|
|||
|
|
def __init__(self, config_key, label="", state=False, validation_types=None, transform=None, **kwargs):
|
|||
|
|
urwid.CheckBox.__init__(self, label, state, **kwargs)
|
|||
|
|
FormField.__init__(self, config_key, transform)
|
|||
|
|
self.validation_types = validation_types or []
|
|||
|
|
self.error_widget = urwid.Text("")
|
|||
|
|
self.error = None
|
|||
|
|
|
|||
|
|
def get_value(self):
|
|||
|
|
return self.transform(self.get_state())
|
|||
|
|
|
|||
|
|
def validate(self):
|
|||
|
|
|
|||
|
|
value = self.get_state()
|
|||
|
|
self.error = None
|
|||
|
|
|
|||
|
|
for validation in self.validation_types:
|
|||
|
|
if validation == "required":
|
|||
|
|
if not value:
|
|||
|
|
self.error = "This field is required"
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
self.error_widget.set_text(("error", self.error or ""))
|
|||
|
|
return self.error is None
|
|||
|
|
|
|||
|
|
class FormDropdown(Dropdown, FormField):
|
|||
|
|
signals = ['change']
|
|||
|
|
|
|||
|
|
def __init__(self, config_key, label, options, default=None, validation_types=None, transform=None):
|
|||
|
|
self.options = [str(opt) for opt in options]
|
|||
|
|
|
|||
|
|
if default is not None:
|
|||
|
|
default_str = str(default)
|
|||
|
|
if default_str in self.options:
|
|||
|
|
default = default_str
|
|||
|
|
elif transform:
|
|||
|
|
try:
|
|||
|
|
default_transformed = transform(default_str)
|
|||
|
|
for opt in self.options:
|
|||
|
|
if transform(opt) == default_transformed:
|
|||
|
|
default = opt
|
|||
|
|
break
|
|||
|
|
except:
|
|||
|
|
default = self.options[0]
|
|||
|
|
else:
|
|||
|
|
default = self.options[0]
|
|||
|
|
else:
|
|||
|
|
default = self.options[0]
|
|||
|
|
|
|||
|
|
Dropdown.__init__(self, label, self.options, default)
|
|||
|
|
FormField.__init__(self, config_key, transform)
|
|||
|
|
|
|||
|
|
self.validation_types = validation_types or []
|
|||
|
|
self.error_widget = urwid.Text("")
|
|||
|
|
self.error = None
|
|||
|
|
|
|||
|
|
if hasattr(self, 'main_button'):
|
|||
|
|
self.main_button.base_widget.set_text(str(default))
|
|||
|
|
|
|||
|
|
def get_value(self):
|
|||
|
|
return self.transform(self.selected)
|
|||
|
|
|
|||
|
|
def validate(self):
|
|||
|
|
value = self.get_value()
|
|||
|
|
self.error = None
|
|||
|
|
|
|||
|
|
for validation in self.validation_types:
|
|||
|
|
if validation == "required":
|
|||
|
|
if not value:
|
|||
|
|
self.error = "This field is required"
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
self.error_widget.set_text(("error", self.error or ""))
|
|||
|
|
return self.error is None
|
|||
|
|
|
|||
|
|
def open_dropdown(self):
|
|||
|
|
if not self.dropdown_visible:
|
|||
|
|
super().open_dropdown()
|
|||
|
|
try:
|
|||
|
|
current_index = self.options.index(self.selected)
|
|||
|
|
self.options_walker.set_focus(current_index)
|
|||
|
|
except ValueError:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
class FormMultiList(urwid.Pile, FormField):
|
|||
|
|
def __init__(self, config_key, placeholder="", validation_types=None, transform=None, **kwargs):
|
|||
|
|
self.entries = []
|
|||
|
|
self.error_widget = urwid.Text("")
|
|||
|
|
self.error = None
|
|||
|
|
self.placeholder = placeholder
|
|||
|
|
self.validation_types = validation_types or []
|
|||
|
|
|
|||
|
|
first_entry = self.create_entry_row()
|
|||
|
|
self.entries.append(first_entry)
|
|||
|
|
|
|||
|
|
self.add_button = urwid.Button("+ Add Another", on_press=self.add_entry)
|
|||
|
|
add_button_padded = urwid.Padding(self.add_button, left=2, right=2)
|
|||
|
|
|
|||
|
|
pile_widgets = [first_entry, add_button_padded]
|
|||
|
|
urwid.Pile.__init__(self, pile_widgets)
|
|||
|
|
FormField.__init__(self, config_key, transform)
|
|||
|
|
|
|||
|
|
def create_entry_row(self):
|
|||
|
|
edit = ReadlineEdit("", "")
|
|||
|
|
entry_row = urwid.Columns([
|
|||
|
|
('weight', 1, edit),
|
|||
|
|
(3, urwid.Button("×", on_press=lambda button: self.remove_entry(button, entry_row))),
|
|||
|
|
])
|
|||
|
|
return entry_row
|
|||
|
|
|
|||
|
|
def remove_entry(self, button, entry_row):
|
|||
|
|
if len(self.entries) > 1:
|
|||
|
|
self.entries.remove(entry_row)
|
|||
|
|
self.contents = [(w, self.options()) for w in self.get_pile_widgets()]
|
|||
|
|
|
|||
|
|
def add_entry(self, button):
|
|||
|
|
new_entry = self.create_entry_row()
|
|||
|
|
self.entries.append(new_entry)
|
|||
|
|
|
|||
|
|
self.contents = [(w, self.options()) for w in self.get_pile_widgets()]
|
|||
|
|
|
|||
|
|
def get_pile_widgets(self):
|
|||
|
|
return self.entries + [urwid.Padding(self.add_button, left=2, right=2)]
|
|||
|
|
|
|||
|
|
def get_value(self):
|
|||
|
|
values = []
|
|||
|
|
for entry in self.entries:
|
|||
|
|
edit_widget = entry.contents[0][0]
|
|||
|
|
value = edit_widget.edit_text.strip()
|
|||
|
|
if value:
|
|||
|
|
values.append(value)
|
|||
|
|
return self.transform(values)
|
|||
|
|
|
|||
|
|
def validate(self):
|
|||
|
|
values = self.get_value()
|
|||
|
|
self.error = None
|
|||
|
|
|
|||
|
|
for validation in self.validation_types:
|
|||
|
|
if validation == "required" and not values:
|
|||
|
|
self.error = "At least one entry is required"
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
self.error_widget.set_text(("error", self.error or ""))
|
|||
|
|
return self.error is None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class FormMultiTable(urwid.Pile, FormField):
|
|||
|
|
def __init__(self, config_key, fields, validation_types=None, transform=None, **kwargs):
|
|||
|
|
self.entries = []
|
|||
|
|
self.fields = fields
|
|||
|
|
self.error_widget = urwid.Text("")
|
|||
|
|
self.error = None
|
|||
|
|
self.validation_types = validation_types or []
|
|||
|
|
|
|||
|
|
header_columns = [('weight', 3, urwid.Text(("list_focus", "Name")))]
|
|||
|
|
for field_key, field_config in self.fields.items():
|
|||
|
|
header_columns.append(('weight', 2, urwid.Text(("list_focus", field_config.get("label", field_key)))))
|
|||
|
|
header_columns.append((4, urwid.Text(("list_focus", ""))))
|
|||
|
|
|
|||
|
|
self.header_row = urwid.Columns(header_columns)
|
|||
|
|
|
|||
|
|
first_entry = self.create_entry_row()
|
|||
|
|
self.entries.append(first_entry)
|
|||
|
|
|
|||
|
|
self.add_button = urwid.Button("+ Add ", on_press=self.add_entry)
|
|||
|
|
add_button_padded = urwid.Padding(self.add_button, left=2, right=2)
|
|||
|
|
|
|||
|
|
pile_widgets = [
|
|||
|
|
self.header_row,
|
|||
|
|
urwid.Divider("-"),
|
|||
|
|
first_entry,
|
|||
|
|
add_button_padded
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
urwid.Pile.__init__(self, pile_widgets)
|
|||
|
|
FormField.__init__(self, config_key, transform)
|
|||
|
|
|
|||
|
|
def create_entry_row(self, name="", values=None):
|
|||
|
|
if values is None:
|
|||
|
|
values = {}
|
|||
|
|
|
|||
|
|
name_edit = ReadlineEdit("", name)
|
|||
|
|
|
|||
|
|
columns = [('weight', 3, name_edit)]
|
|||
|
|
|
|||
|
|
field_widgets = {}
|
|||
|
|
for field_key, field_config in self.fields.items():
|
|||
|
|
field_value = values.get(field_key, "")
|
|||
|
|
|
|||
|
|
if field_config.get("type") == "checkbox":
|
|||
|
|
widget = urwid.CheckBox("", state=bool(field_value))
|
|||
|
|
elif field_config.get("type") == "dropdown":
|
|||
|
|
# TODO: dropdown in MultiTable
|
|||
|
|
widget = ReadlineEdit("", str(field_value))
|
|||
|
|
else:
|
|||
|
|
widget = ReadlineEdit("", str(field_value))
|
|||
|
|
|
|||
|
|
field_widgets[field_key] = widget
|
|||
|
|
columns.append(('weight', 2, widget))
|
|||
|
|
|
|||
|
|
remove_button = urwid.Button("×", on_press=lambda button: self.remove_entry(button, entry_row))
|
|||
|
|
columns.append((4, remove_button))
|
|||
|
|
|
|||
|
|
entry_row = urwid.Columns(columns)
|
|||
|
|
entry_row.name_edit = name_edit
|
|||
|
|
entry_row.field_widgets = field_widgets
|
|||
|
|
|
|||
|
|
return entry_row
|
|||
|
|
|
|||
|
|
def remove_entry(self, button, entry_row):
|
|||
|
|
if len(self.entries) > 1:
|
|||
|
|
self.entries.remove(entry_row)
|
|||
|
|
self.contents = [(w, self.options()) for w in self.get_pile_widgets()]
|
|||
|
|
|
|||
|
|
def add_entry(self, button):
|
|||
|
|
new_entry = self.create_entry_row()
|
|||
|
|
self.entries.append(new_entry)
|
|||
|
|
|
|||
|
|
self.contents = [(w, self.options()) for w in self.get_pile_widgets()]
|
|||
|
|
|
|||
|
|
def get_pile_widgets(self):
|
|||
|
|
return [
|
|||
|
|
self.header_row,
|
|||
|
|
urwid.Divider("-")
|
|||
|
|
] + self.entries + [
|
|||
|
|
urwid.Padding(self.add_button, left=2, right=2)
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
def get_value(self):
|
|||
|
|
values = {}
|
|||
|
|
for entry in self.entries:
|
|||
|
|
name = entry.name_edit.edit_text.strip()
|
|||
|
|
if name:
|
|||
|
|
subinterface = {}
|
|||
|
|
subinterface["interface_enabled"] = True
|
|||
|
|
|
|||
|
|
for field_key, widget in entry.field_widgets.items():
|
|||
|
|
field_config = self.fields.get(field_key, {})
|
|||
|
|
|
|||
|
|
if hasattr(widget, "get_state"):
|
|||
|
|
value = widget.get_state()
|
|||
|
|
elif hasattr(widget, "edit_text"):
|
|||
|
|
value = widget.edit_text.strip()
|
|||
|
|
|
|||
|
|
transform = field_config.get("transform")
|
|||
|
|
if transform and value:
|
|||
|
|
try:
|
|||
|
|
value = transform(value)
|
|||
|
|
except (ValueError, TypeError):
|
|||
|
|
value = ""
|
|||
|
|
|
|||
|
|
if value:
|
|||
|
|
subinterface[field_key] = value
|
|||
|
|
|
|||
|
|
values[name] = subinterface
|
|||
|
|
|
|||
|
|
return self.transform(values) if self.transform else values
|
|||
|
|
|
|||
|
|
def set_value(self, value):
|
|||
|
|
self.entries = []
|
|||
|
|
|
|||
|
|
if not value:
|
|||
|
|
self.entries.append(self.create_entry_row())
|
|||
|
|
else:
|
|||
|
|
for name, config in value.items():
|
|||
|
|
self.entries.append(self.create_entry_row(name=name, values=config))
|
|||
|
|
|
|||
|
|
self.contents = [(w, self.options()) for w in self.get_pile_widgets()]
|
|||
|
|
|
|||
|
|
def validate(self):
|
|||
|
|
values = self.get_value()
|
|||
|
|
self.error = None
|
|||
|
|
|
|||
|
|
for validation in self.validation_types:
|
|||
|
|
if validation == "required" and not values:
|
|||
|
|
self.error = "At least one subinterface is required"
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
self.error_widget.set_text(("error", self.error or ""))
|
|||
|
|
return self.error is None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class FormKeyValuePairs(urwid.Pile, FormField):
|
|||
|
|
def __init__(self, config_key, validation_types=None, transform=None, **kwargs):
|
|||
|
|
self.entries = []
|
|||
|
|
self.error_widget = urwid.Text("")
|
|||
|
|
self.error = None
|
|||
|
|
self.validation_types = validation_types or []
|
|||
|
|
|
|||
|
|
header_columns = [
|
|||
|
|
('weight', 1, urwid.AttrMap(urwid.Text("Parameter Key"), "multitable_header")),
|
|||
|
|
('weight', 1, urwid.AttrMap(urwid.Text("Parameter Value"), "multitable_header")),
|
|||
|
|
(4, urwid.AttrMap(urwid.Text("Action"), "multitable_header"))
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
self.header_row = urwid.AttrMap(urwid.Columns(header_columns), "multitable_header")
|
|||
|
|
|
|||
|
|
first_entry = self.create_entry_row()
|
|||
|
|
self.entries.append(first_entry)
|
|||
|
|
|
|||
|
|
self.add_button = urwid.Button("+ Add Parameter", on_press=self.add_entry)
|
|||
|
|
add_button_padded = urwid.Padding(self.add_button, left=2, right=2)
|
|||
|
|
|
|||
|
|
pile_widgets = [
|
|||
|
|
self.header_row,
|
|||
|
|
urwid.Divider("-"),
|
|||
|
|
first_entry,
|
|||
|
|
add_button_padded
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
urwid.Pile.__init__(self, pile_widgets)
|
|||
|
|
FormField.__init__(self, config_key, transform)
|
|||
|
|
|
|||
|
|
def create_entry_row(self, key="", value=""):
|
|||
|
|
key_edit = ReadlineEdit("", key)
|
|||
|
|
value_edit = ReadlineEdit("", value)
|
|||
|
|
|
|||
|
|
remove_button = urwid.Button("×", on_press=lambda button: self.remove_entry(button, entry_row))
|
|||
|
|
|
|||
|
|
entry_row = urwid.Columns([
|
|||
|
|
('weight', 1, key_edit),
|
|||
|
|
('weight', 1, value_edit),
|
|||
|
|
(4, remove_button)
|
|||
|
|
])
|
|||
|
|
|
|||
|
|
entry_row.key_edit = key_edit
|
|||
|
|
entry_row.value_edit = value_edit
|
|||
|
|
|
|||
|
|
return entry_row
|
|||
|
|
|
|||
|
|
def remove_entry(self, button, entry_row):
|
|||
|
|
if len(self.entries) > 1:
|
|||
|
|
self.entries.remove(entry_row)
|
|||
|
|
self.contents = [(w, self.options()) for w in self.get_pile_widgets()]
|
|||
|
|
|
|||
|
|
def add_entry(self, button):
|
|||
|
|
new_entry = self.create_entry_row()
|
|||
|
|
self.entries.append(new_entry)
|
|||
|
|
|
|||
|
|
self.contents = [(w, self.options()) for w in self.get_pile_widgets()]
|
|||
|
|
|
|||
|
|
def get_pile_widgets(self):
|
|||
|
|
return [
|
|||
|
|
self.header_row,
|
|||
|
|
urwid.Divider("-")
|
|||
|
|
] + self.entries + [
|
|||
|
|
urwid.Padding(self.add_button, left=2, right=2)
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
def get_value(self):
|
|||
|
|
values = {}
|
|||
|
|
for entry in self.entries:
|
|||
|
|
key = entry.key_edit.edit_text.strip()
|
|||
|
|
value = entry.value_edit.edit_text.strip()
|
|||
|
|
|
|||
|
|
if key:
|
|||
|
|
if value.isdigit():
|
|||
|
|
values[key] = int(value)
|
|||
|
|
elif value.replace('.', '', 1).isdigit() and value.count('.') <= 1:
|
|||
|
|
values[key] = float(value)
|
|||
|
|
elif value.lower() == 'true':
|
|||
|
|
values[key] = True
|
|||
|
|
elif value.lower() == 'false':
|
|||
|
|
values[key] = False
|
|||
|
|
else:
|
|||
|
|
values[key] = value
|
|||
|
|
|
|||
|
|
return self.transform(values) if self.transform else values
|
|||
|
|
|
|||
|
|
def set_value(self, value):
|
|||
|
|
self.entries = []
|
|||
|
|
|
|||
|
|
if not value or not isinstance(value, dict):
|
|||
|
|
self.entries.append(self.create_entry_row())
|
|||
|
|
else:
|
|||
|
|
for key, val in value.items():
|
|||
|
|
self.entries.append(self.create_entry_row(key=key, value=str(val)))
|
|||
|
|
|
|||
|
|
self.contents = [(w, self.options()) for w in self.get_pile_widgets()]
|
|||
|
|
|
|||
|
|
def validate(self):
|
|||
|
|
values = self.get_value()
|
|||
|
|
self.error = None
|
|||
|
|
|
|||
|
|
keys = [entry.key_edit.edit_text.strip() for entry in self.entries
|
|||
|
|
if entry.key_edit.edit_text.strip()]
|
|||
|
|
if len(keys) != len(set(keys)):
|
|||
|
|
self.error = "Duplicate keys are not allowed"
|
|||
|
|
self.error_widget.set_text(("error", self.error))
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
for validation in self.validation_types:
|
|||
|
|
if validation == "required" and not values:
|
|||
|
|
self.error = "Atleast one parameter is required"
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
self.error_widget.set_text(("error", self.error or ""))
|
|||
|
|
return self.error is None
|