Skip to content

Commit

Permalink
Added min/max and keyboard.
Browse files Browse the repository at this point in the history
  • Loading branch information
washad committed Jul 8, 2019
1 parent ef31108 commit bd963f6
Show file tree
Hide file tree
Showing 12 changed files with 471 additions and 69 deletions.
126 changes: 78 additions & 48 deletions pyrediseasyio/html/html_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
from pyrediseasyio.io.trigger_io import TriggerIO
from pyrediseasyio.io.single_io import SingleIO
from pyrediseasyio.io.boolean_io import BooleanIO
from pyrediseasyio.io.float_io import FloatIO
from dominate.tags import div, tr, td, button

_ = gettext.gettext


class HTMLIO:


def __init__(self, io: SingleIO):
key = io.key
self.io = io
Expand All @@ -24,39 +26,28 @@ def __init__(self, io: SingleIO):
self.default_set_value = True if isinstance(io, BooleanIO) or isinstance(io, TriggerIO) else io.default
self.default_reset_value = False if isinstance(io, BooleanIO) or isinstance(io, TriggerIO) else 0

def build(self, row_tag: dominate.tags, cell_tag: dominate.tags, **kwargs):
"""
Creates html containing relevant parts of the IO
:param row_tag: The html tag to use for rows
:param cell_tag: The html tage to use for cells
:param kwargs:
show_name: bool: optional value, set to false to hide name column.
show_value: bool: optional, set to false to hide the value column.
show_set: bool: optional, set to True to show the 'set' button column.
show_reset: bool: optional, set to False to show the 'reset' button column.
show_units: bool: optional, set to true so thow the units column.
set_text: str: The text to place on the 'set' button
reset_text: str: The text to place on the 'reset' button)
:return: dominate tag object that can be rendered to html.
"""

def _build(self, row_tag: dominate.tags, cell_tag: dominate.tags, **kwargs):
show_name = bool(kwargs.get('show_name', True))
show_value = bool(kwargs.get('show_value', True))
show_set = bool(kwargs.get('show_set', False))
show_reset = bool(kwargs.get('show_reset', False))
show_change = bool(kwargs.get('show_change', False))
show_units = bool(kwargs.get('show_units', False))
set_text = str(kwargs.get('set_text', _("Set")))
reset_text = str(kwargs.get('reset_text', _("Reset")))
change_text = str(kwargs.get('change_text', _("Change")))

io = self.io
name, addr, val, units, ns = io.name, io.addr, io.value, io.units, self.namespace

with row_tag(cls=f'easyio_io', id=self.container_id, data_type=type(io).__name__) as container:

if show_name:
self.html_name_cell(cell_tag)
self.name_cell(cell_tag)

if show_value:
self.html_value_cell(cell_tag)
self.value_cell(cell_tag)

if show_units:
units = '' if units is None else units
Expand All @@ -70,29 +61,16 @@ def build(self, row_tag: dominate.tags, cell_tag: dominate.tags, **kwargs):
with cell_tag(cls='easyio_btn_cell easyio_rst_btn_cell'):
self.reset_button(button, reset_text)

if show_change:
with cell_tag(cls='easyio_btn_cell easyio_change_btn_cell'):
self.change_button(button, change_text)

return container


def set_button(self, tag: dominate.tags = button, txt: str = "On"):
io = self.io
name, addr, val, units, ns = io.name, io.addr, io.value, io.units, self.namespace
ns = "na" if not ns else ns
value = self.default_set_value
value = str(value).lower()
return tag(txt, cls='easyio_set', onclick=f"EasyIOSet('{ns}','{addr}','{self.value_id}',{value})")


def reset_button(self, tag: dominate.tags = button, txt: str = "Off") -> dominate.tags:
io = self.io
name, addr, val, units, ns = io.name, io.addr, io.value, io.units, self.namespace
ns = "na" if not ns else ns
value = self.default_reset_value
value = str(value).lower()
return tag(txt, cls='easyio_reset', onclick=f"EasyIOSet('{ns}','{addr}','{self.value_id}',{value})")
return container


def html(self, **kwargs) -> dominate.tags:
def as_divs(self, **kwargs) -> dominate.tags:
"""
Creates html containing relevant parts of the IO using div elements
:param kwargs:
Expand All @@ -105,10 +83,10 @@ def html(self, **kwargs) -> dominate.tags:
reset_text: str: The text to place on the 'reset' button)
:return: dominate tag object that can be rendered to html.
"""
return self.build(div, div, **kwargs)
return self._build(div, div, **kwargs)


def html_row(self, **kwargs) -> dominate.tags:
def as_table_row(self, **kwargs) -> dominate.tags:
"""
Creates html containing relevant parts of the IO using table elements
:param kwargs:
Expand All @@ -121,29 +99,81 @@ def html_row(self, **kwargs) -> dominate.tags:
reset_text: str: The text to place on the 'reset' button)
:return: dominate tag object that can be rendered to html.
"""
return self.build(tr, td, **kwargs)
return self._build(tr, td, **kwargs)


def html_name_cell(self, tag: dominate.tags = div) -> dominate.tags:
return tag(self.io.name, cls=f'easyio_name')
def change_button(self, tag: dominate.tags = button, txt: str = _("Change")):
io = self.io
name, addr, val, units, ns = io.name, io.addr, io.value, io.units, self.namespace
ns = "na" if not ns else ns
_max = io.max if hasattr(io, "max") else None
_min = io.min if hasattr(io, "min") else None
allow_decimal = isinstance(io, FloatIO)
allow_negative = _min is None or _min < 0
allow_negative = str(allow_negative).lower()
allow_decimal = str(allow_decimal).lower()
return tag(txt, cls='easyio_change',
onclick=f"EasyIOChange('{io.name}', '{io.key}','{io.units}', '{_min}', "
f"'{_max}', {allow_decimal}, {allow_negative})")


def html_value_cell(self, tag: dominate.tags = div) -> dominate.tags:
"""Generate the html for generating the cell that contains the object value."""
def set_button(self, tag: dominate.tags = button, txt: str = "On"):
io = self.io
name, addr, val, units, ns = io.name, io.addr, io.value, io.units, self.namespace
val = False if val is None else val
return tag(val, cls=f'easyio_value', id=self.value_id, onchange='OnEasyIOValueChange(event)',
data_namespace=ns, data_addr=addr)

def html_value_svg_circle(self, x: int, y: int, r: int):
return raw(f'<circle cls=easyio_value" id="{self.value_id}" cx="{x}" cy="{y}" r="{r}"></circle>')
ns = "na" if not ns else ns
value = self.default_set_value
value = str(value).lower()
return tag(txt, cls='easyio_set', onclick=f"EasyIOSet('{ns}','{addr}','{self.value_id}',{value})")


def reset_button(self, tag: dominate.tags = button, txt: str = "Off") -> dominate.tags:
io = self.io
name, addr, val, units, ns = io.name, io.addr, io.value, io.units, self.namespace
ns = "na" if not ns else ns
value = self.default_reset_value
value = str(value).lower()
return tag(txt, cls='easyio_reset', onclick=f"EasyIOSet('{ns}','{addr}','{self.value_id}',{value})")


def with_custom_tags(self, outer: dominate.tags, inner: dominate.tags, **kwargs):
"""
Creates html containing relevant parts of the IO using table elements
:param outer: The tag type to use for the cell container (row)
:param inner: The tag type to use for the cell
:param kwargs:
show_name: bool: optional value, set to false to hide name column.
show_value: bool: optional, set to false to hide the value column.
show_set: bool: optional, set to True to show the 'set' button column.
show_reset: bool: optional, set to False to show the 'reset' button column.
show_change: bool: optional, on numeric entries, adds a change button.
show_units: bool: optional, set to true so thow the units column.
set_text: str: The text to place on the 'set' button
reset_text: str: The text to place on the 'reset' button)
:return: dominate tag object that can be rendered to html.
"""
return self._build(tr, td, **kwargs)


def name_cell(self, tag: dominate.tags = div) -> dominate.tags:
return tag(self.io.name, cls=f'easyio_name')


def value_cell(self, tag: dominate.tags = div, allow_entry: bool=False) -> dominate.tags:
"""Generate the html for generating the cell that contains the object value."""
io = self.io
name, addr, val, units, ns = io.name, io.addr, io.value, io.units, self.namespace
val = False if val is None else val
t = tag(val, cls=f'easyio_value', id=self.value_id, onchange='OnEasyIOValueChange(event)',
data_namespace=ns, data_addr=addr)

if io.namespace is not None:
t['data-namespace'] = io.namespace
if hasattr(io, 'max') and io.max is not None:
t['data-max'] = io.max
if hasattr(io, 'min') and io.min is not None:
t['data-min'] = io.min
if io.units is not None:
t['data-units'] = io.units

if allow_entry:
t['onenter'] = 'OnEasyIOValueMouseEnter(event)'
4 changes: 2 additions & 2 deletions pyrediseasyio/html/html_io_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def html(self, headers: List[str] = None, html_id: str = None, **kwargs):
with div(cls=f'easyio_container', id=html_id) as container:
self._heading(headers, div, div)
for attr in attrs:
HTMLIO(attr).html(**kwargs)
HTMLIO(attr).as_divs(**kwargs)
return container


Expand All @@ -54,7 +54,7 @@ def html_table(self, headers: List[str] = None, html_id: str = None, **kwargs) -
with table(cls=f'easyio_container', id=html_id) as container:
self._heading(headers, tr, th)
for attr in attrs:
HTMLIO(attr).html_row(**kwargs)
HTMLIO(attr).as_table_row(**kwargs)
return container


Expand Down
4 changes: 3 additions & 1 deletion pyrediseasyio/io/float_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


class FloatIO(NumericIO):
def __init__(self, name: str, addr: str = None, default: float=0.0, **kwargs):
def __init__(self, name: str, addr: str = None, default: float = 0.0, **kwargs):
"""
:param name: A human readable name to give to the IO
:param addr: An optional address (key), if not given, them namespace + member name will be used
Expand All @@ -14,6 +14,8 @@ def __init__(self, name: str, addr: str = None, default: float=0.0, **kwargs):
- on_value: str: The 'display_value' property will optionally return this value when the value is True
- on_value: str: The 'display_value' property will optionally return this value when the value is False
- namespace: str: Optional leading text to apply to the address to makes its key unique
- min: The smallest allowable number for the entry, reads and writes will be limited by this.
- max: The largest allowable number for the entry, reads and writes will be limited by this.
"""
super().__init__(name, addr, default, **kwargs)

Expand Down
2 changes: 2 additions & 0 deletions pyrediseasyio/io/integer_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ def __init__(self, name: str, addr: str = None, default: int=0, **kwargs):
- on_value: str: The 'display_value' property will optionally return this value when the value is True
- on_value: str: The 'display_value' property will optionally return this value when the value is False
- namespace: str: Optional leading text to apply to the address to makes its key unique
- min: The smallest allowable number for the entry, reads and writes will be limited by this.
- max: The largest allowable number for the entry, reads and writes will be limited by this.
"""
super().__init__(name, addr, default, **kwargs)

Expand Down
42 changes: 40 additions & 2 deletions pyrediseasyio/io/numeric_io.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,43 @@
from pyrediseasyio import SingleIO
from pyrediseasyio.io.single_io import SingleIO


class NumericIO(SingleIO):
pass
def __init__(self, name: str, addr: str = None, default: float = 0.0, **kwargs):
"""
:param name: A human readable name to give to the IO
:param addr: An optional address (key), if not given, them namespace + member name will be used
:param default: The value to assign to the IO if no key is found during read
:param kwargs:
- units: str: Optional units to assign to the IO
- on_value: str: The 'display_value' property will optionally return this value when the value is True
- on_value: str: The 'display_value' property will optionally return this value when the value is False
- namespace: str: Optional leading text to apply to the address to makes its key unique
- min: The smallest allowable number for the entry, reads and writes will be limited by this.
- max: The largest allowable number for the entry, reads and writes will be limited by this.
"""
super().__init__(name, addr, default, **kwargs)

self.min = kwargs.get('min')
self.max = kwargs.get('max')


def read(self):
val = super().read()
if val is None:
return val
if self.max is not None and val > self.max:
val = self.max
if self.min is not None and val < self.min:
val = self.min
return val

def write(self, value):
if value is None:
return super().write(self, value)
value = self._convert_type(value)
if self.max is not None and value > self._convert_type(self.max):
value = self.max
if self.min is not None and value < self._convert_type(self.min):
value = self.min
super().write(value)

18 changes: 11 additions & 7 deletions tests/hmi/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from dominate.tags import head, script, body, link, div, table
import dominate
import random
from tests.hmi.keyboard import keyboard


app = Flask(__name__)
Expand All @@ -17,8 +18,10 @@ class TestGroup(IOGroup):
IsFather = BooleanIO("Is a father", on_value="Definately", off_value="Not yet", default=True)
BathroomBreaks = IntIO("Bathroom breaks", default=3)
NameChange = TriggerIO("Change Name")
IntWithMinMax = IntIO("Int with min max", units="int", min=15, max=225)
FloatWithMinMax = FloatIO("Float with min max", units="float", min=-40, max=200)

test_group = TestGroup()
test_group = TestGroup(namespace="Test")

def on_name_change(value):
test_group.FirstName = "Steven"
Expand All @@ -33,21 +36,22 @@ def on_name_change(value):
with doc.head:
link(rel='stylesheet', href='https://www.w3schools.com/w3css/4/w3.css')
link(rel='stylesheet', href="/static/easyio.css")
link(rel='stylesheet', href='/static/keyboard.css')
script(type='text/javascript', src='https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js')
script(type='text/javascript', src='static/easyio.js')
script(type='text/javascript', src='static/keyboard.js')
with doc.body:
keyboard(doc)
with div(cls="flex-container"):
with div(cls="w3-card w3-margin w3-border w3-padding"):
html_test_group.html_table(
headers=["ITEM", "VALUE", "UNITS"],
show_units=True)
with div(cls="w3-card w3-margin w3-border w3-padding"):
html_test_group.html(
headers=["ITEM", "VALUE", "UNITS"],
show_set=False, show_reset=False, show_units=True, by_lambda_each=lambda x : 'Bathroom' not in x.addr)
with table():
HTMLIO(test_group.BathroomBreaks).html_row(show_reset=True, show_set=True, show_value=False)
HTMLIO(test_group.NameChange).html_row(show_set = True, show_value=False)
HTMLIO(test_group.BathroomBreaks).as_table_row(show_reset=True, show_set=True, show_value=False)
HTMLIO(test_group.NameChange).as_table_row(show_set = True, show_value=False)
HTMLIO(test_group.IntWithMinMax).as_table_row(show_change=True)
HTMLIO(test_group.FloatWithMinMax).as_table_row(show_change=True)



Expand Down
41 changes: 41 additions & 0 deletions tests/hmi/keyboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from dominate import document
from dominate.tags import div, button
from collections import namedtuple
import gettext

_ = gettext.gettext

Btn = namedtuple("Btn", "cls val action")

keys = [
Btn('number-button', _("1"), 1),
Btn('number-button', _("2"), 2),
Btn('number-button', _("3"), 3),
Btn('action-button', _("Del"), 'Del'),
Btn('number-button', _("4"), 4),
Btn('number-button', _("5"), 5),
Btn('number-button', _("6"), 6),
Btn('action-button', _("Clear"), 'Clear'),
Btn('number-button', _("7"), 7),
Btn('number-button', _("8"), 8),
Btn('number-button', _("9"), 9),
Btn('action-button', _("Cancel"), 'Cancel'),
Btn('number-button plus-minus-button', _("+/-"), 'Sign'),
Btn('number-button', _("0"), 0),
Btn('number-button decimal_button', _("."), 'Dec'),
Btn('action-button ok-button', _("Ok"), 'Ok')
]

def keyboard(doc: document):
with doc.body:
with div(cls="easyio_number_keyboard"):
with div(cls='grid-container'):
div("Title", cls = "title")
div(0, cls='entry', id="easy_io_number_keyboard_entry")
div('', cls='original')

for key in keys:
button(key.val, cls=key.cls, onclick=f"EasyIoNumericEntryKeyPressed('{key.action}')")

div('', cls='max')
div('', cls='min')
2 changes: 1 addition & 1 deletion tests/hmi/static/easyio.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ $(document).ready(function() {
let id = item.id;
let element = document.getElementById(id);
if(!(!(element))){
element.innerHTML = item.display_value;
element.innerText = item.display_value;
}
});
setTimeout(pollPhysicalIO,500);
Expand Down
Loading

0 comments on commit bd963f6

Please sign in to comment.