Library for building dynamic forms
Project description
opengui
Library for building dynamic forms, forms whose options and even fields change based on values in other fields.
This doesn't involve rendering dynamic forms, just making creating and altering the structures thereof.
GUI Example
Some Python code in service.py that generate some basic field dynamically
# Create a single multi select field
fields = opengui.Fields(
values=values,
fields=[
{
"name": "types",
"options": [
"textarea",
"options",
"fields"
],
"multi": True,
"trigger": True
}
]
)
# If they select textarea, add it
if "textarea" in (fields["types"].value or []):
fields.append({
"name": "people",
"style": "textarea"
})
fields.ready = True
# If they selected option, add a format, then check what format they selected
if "options" in (fields["types"].value or []):
fields.append({
"name": "style",
"options": [
"radios",
"select"
],
"default": "radios",
"trigger": True
})
fields.append({
"name": "stuff",
"options": [
"fee",
"fie",
"foe",
"fum"
],
"style": fields["style"].value
})
fields.ready = True
# If they add subfields, add two, and make the second optional
if "fields" in (fields["types"].value or []):
fields.append({
"name": "things",
"fields": [
{
"name": "yin",
},
{
"name": "yang",
"optional": True
}
]
})
fields.ready = True
# Retrn as a dict
return fields.to_dict(), 200
Universal doTRoute.js form.html to display a dynamic form:
{{?it.message}}
<div class="uk-alert uk-alert-success">
{{=it.message}}
</div>
{{?}}
{{?it.errors && it.errors.length}}
<div class="uk-alert uk-alert-danger">
{{~it.errors :error}}
{{=error}}<br/>
{{~}}
</div>
{{?}}
<form class="uk-form uk-form-horizontal">
{{= DRApp.templates.Fields(it) }}
<br/>
</form>
Universal doTRoute.js fields.html to display all sorts of combinations, including sub fields:
{{~it.fields :field}}
{{ var prefix = it.prefix || []; }}
{{ var full_name = prefix.concat(field.name).join('-').replace(/\./g, '-'); }}
{{ var value = field.value || field.default || (field.multi ? [] : ''); }}
{{ var readonly = field.readonly || it.readonly; }}
{{?field.fields || field.name == "yaml"}}
<div class="uk-form-row"><hr/></div>
{{?}}
<div class="uk-form-row">
<label class="uk-form-label" for="{{=field.name}}"><strong>{{=field.label || field.name}}</strong></label>
<div class="uk-form-controls">
{{?field.style == "textarea"}}
{{?readonly}}
{{?field.name == "yaml"}}<pre>{{?}}{{=value}}{{?field.name == "yaml"}}</pre>{{?}}
{{??}}
<textarea
rows='7' cols='42'
id="{{!full_name}}"
placeholder="{{!field.label || field.name}}"
{{?field.trigger}}OnInput="DRApp.current.controller.fields_change();"{{?}}
>{{=value}}</textarea>
{{?}}
{{??field.style == "select" && !readonly}}
<select id="{{!full_name}}" {{?field.trigger}}OnChange="DRApp.current.controller.fields_change();"{{?}}>
{{?field.optional}}
<option value=''></option>
{{?}}
{{~field.options :option}}
<option value='{{!option}}' {{?value == option}}selected{{?}}>
{{= field.labels ? field.labels[option] : option}}
</option>
{{~}}
</select>
{{??field.options}}
{{?readonly}}
{{?field.multi}}
{{~value :option}}
{{= field.labels ? field.labels[option] : option}}<br/>
{{~}}
{{??}}
{{= field.labels ? field.labels[value] : value}}<br/>
{{?}}
{{??}}
{{~field.options :option}}
<input
value="{{!option}}"
{{?field.multi}}
type="checkbox" name="{{!full_name}}"
{{?value.indexOf(option) > -1}}checked{{?}}
{{??}}
type="radio" name="{{!full_name}}"
{{?value == option}}checked{{?}}
{{?}}
{{?field.trigger}}OnClick="DRApp.current.controller.fields_change();"{{?}}
/>
{{= field.labels ? field.labels[option] : option}}<br/>
{{~}}
{{?}}
{{??!field.fields}}
{{?readonly}}
{{= field.style == "datetime" ? (new Date(value*1000)).toLocaleString() : value}}
{{??}}
<input
id="{{!full_name}}"
placeholder="{{!field.label || field.name}}"
value="{{!value}}"
{{?field.trigger}}OnInput="DRApp.current.controller.fields_change();"{{?}}
type="text"
/>
{{?}}
<br/>
{{?}}
{{?field.errors}}
<span class='uk-form uk-text-danger'>
{{~field.errors :error}}
{{=error}}
{{~}}
</span>
{{?}}
{{?field.description}}
<dfn>{{=field.description.replace(/\n/g, "<br/>")}}</dfn><br/>
{{?}}
{{?field.link}}
{{ var links = Array.isArray(field.link) ? field.link : [field.link]; }}
{{~links: link}}
<a href="{{!link.url || link}}" target="{{!link.target || '_blank'}}">{{=link.name || link.url || link}}</a><br/>
{{~}}
{{?}}
</div>
</div>
{{?field.fields}}
{{= DRApp.templates.Fields({fields: field.fields, readonly: readonly, prefix: prefix.concat(field.name)}) }}
{{?}}
{{~}}
Which looks like this:
If you're familer with Docker Desktop, Kubernetes, and Tilt make up
and hit space.
Navigate to http://localhost:7971/
and give it a whirl.
Cli Example
Just took this from the from the unittests:
@unittest.mock.patch("builtins.print")
@unittest.mock.patch("builtins.input")
def test_ask(self, mock_input, mock_print):
cli = opengui.Cli(
fields=[
{
"name": "basic",
"description": "be basic",
"default": "badass",
"validation": "^bitch$"
},
{
"name": "single",
"options": ["yin", "yang"],
"labels": {
"yin": "Yin",
"yang": "Yang"
},
"default": "yin"
},
{
"name": "multiple",
"multi": True,
"options": "{[ fs ]}",
"default": ["fun", "foe"]
},
{
"name": "yah",
"bool": True,
"default": True
},
{
"name": "sure",
"bool": True
},
{
"name": "nah",
"bool": True
}
],
values={
"fs": ["fee", "fie", "foe", "fum"]
}
)
mock_input.side_effect = [
"",
"bitch",
"fish",
"0",
"3",
"1",
"fish 0 6",
"",
"1 3",
"",
"y",
"n"
]
self.assertEqual(cli.ask(), {
"basic": "bitch",
"single": "yin",
"multiple": ["fee", "foe"],
"yah": True,
"sure": True,
"nah": False,
"fs": ["fee", "fie", "foe", "fum"]
})
mock_print.assert_has_calls([
unittest.mock.call(' be basic'),
unittest.mock.call("must match '^bitch$'"),
unittest.mock.call('[1] Yin'),
unittest.mock.call('[2] Yang'),
unittest.mock.call('invalid choice: fish'),
unittest.mock.call('[1] Yin'),
unittest.mock.call('[2] Yang'),
unittest.mock.call('invalid choice: 0'),
unittest.mock.call('[1] Yin'),
unittest.mock.call('[2] Yang'),
unittest.mock.call('invalid choice: 3'),
unittest.mock.call('[1] Yin'),
unittest.mock.call('[2] Yang'),
unittest.mock.call('[1] fee'),
unittest.mock.call('[2] fie'),
unittest.mock.call('[3] foe'),
unittest.mock.call('[4] fum'),
unittest.mock.call("invalid choices: ['fish', '0', '6']"),
unittest.mock.call('[1] fee'),
unittest.mock.call('[2] fie'),
unittest.mock.call('[3] foe'),
unittest.mock.call('[4] fum'),
unittest.mock.call("invalid values ['fun']"),
unittest.mock.call('[1] fee'),
unittest.mock.call('[2] fie'),
unittest.mock.call('[3] foe'),
unittest.mock.call('[4] fum')
])
And fields use yaes.Engine for transformaton each time a question is asked:
fields = opengui.Fields(
fields=[
{"name": "a", "label": "{{ lab }}", "stuff": "{[ things ]}"},
{"name": "b"}
],
errors=['boo'],
valid=True,
ready=False
)
values = {"lab": "A", "things": [1, 2, 3]}
self.assertEqual(fields.question(values).to_dict(), {
"name": "a",
"label": "A",
"stuff": [1, 2, 3]
})
Documentation
It's lacking at this point, but check out the tests to see what you can do.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file opengui-0.9.6.tar.gz
.
File metadata
- Download URL: opengui-0.9.6.tar.gz
- Upload date:
- Size: 11.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.8.5
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 9efc97ac7c791ad1eaf44c7596be3020d1bc07048886fb72da6e0f0bc6343dd6 |
|
MD5 | f0989c27153b578a4799024279afcf90 |
|
BLAKE2b-256 | f8beca112799f545fbc3e5f493255f1e6924d161f222fff4d43eb48d919093bd |
File details
Details for the file opengui-0.9.6-py3-none-any.whl
.
File metadata
- Download URL: opengui-0.9.6-py3-none-any.whl
- Upload date:
- Size: 11.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.8.5
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0faa50f856f1f3b892d99e4e482708b947258322472b6ca7d3b125fad6e1d4bb |
|
MD5 | a9db7f13d782696f0baa31e3821ac952 |
|
BLAKE2b-256 | ca8e0d07d6a8874057e47cbfae6b1f0d64276fde7e323e54f18fa329d9f33509 |