Skip to content

Commit

Permalink
Merge pull request #83 from epics-containers/implement_missing_widgets
Browse files Browse the repository at this point in the history
Implement missing widgets
  • Loading branch information
GDYendell authored Feb 20, 2024
2 parents b2f67a3 + 02ba3dd commit e1d5556
Show file tree
Hide file tree
Showing 27 changed files with 479 additions and 43 deletions.
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"editor.formatOnSave": true,
"[python]": {
"editor.codeActionsOnSave": {
"source.fixAll.ruff": false,
"source.organizeImports.ruff": true
"source.fixAll.ruff": "explicit",
"source.organizeImports.ruff": "explicit"
}
}
}
30 changes: 21 additions & 9 deletions schemas/pvi.device.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,34 @@
"description": "LED and label for each bit of an int PV",
"properties": {
"labels": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Label for each bit",
"items": {
"type": "string"
},
"title": "Labels",
"type": "array"
"title": "Labels"
},
"number_of_bits": {
"default": 8,
"description": "Number of bits to display",
"exclusiveMinimum": 0,
"title": "Number Of Bits",
"type": "integer"
},
"type": {
"const": "BitField",
"default": "BitField",
"title": "Type"
}
},
"required": [
"labels"
],
"title": "BitField",
"type": "object"
},
Expand All @@ -88,7 +100,7 @@
"type": "string"
},
"default": {
"go": "1"
"Go": "1"
},
"description": "PV poker buttons",
"title": "Actions",
Expand Down
4 changes: 3 additions & 1 deletion src/pvi/_format/adl.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ def set(
case TextWrite(format=format) | TextRead(format=format) if (
is_text_widget(template) and format is not None
):
template = add_property(template, "format", ADL_TEXT_FORMATS[format])
template = add_property(
template, "format", ADL_TEXT_FORMATS[TextFormat(format)]
)

return template

Expand Down
20 changes: 20 additions & 0 deletions src/pvi/_format/aps.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,26 @@ def format(self, device: Device, path: Path):
search='"SubScreenFile"',
property_map={"name": "file_name"},
),
bitfield_formatter_cls=PVWidgetFormatter.from_template(
template,
search='"Label"',
property_map={"textix": "pv"},
),
array_trace_formatter_cls=PVWidgetFormatter.from_template(
template,
search='"Label"',
property_map={"textix": "pv"},
),
image_read_formatter_cls=PVWidgetFormatter.from_template(
template,
search='"Label"',
property_map={"textix": "pv"},
),
button_panel_formatter_cls=PVWidgetFormatter.from_template(
template,
search='"Label"',
property_map={"textix": "pv"},
),
)

label_background_formatter = WidgetFormatter.from_template(
Expand Down
14 changes: 12 additions & 2 deletions src/pvi/_format/bob.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from pvi._format.widget import UITemplate, WidgetFormatter
from pvi.device import (
LED,
BitField,
ComboBox,
TableRead,
TableWrite,
Expand Down Expand Up @@ -49,6 +50,10 @@ def set(
properties["width"] = bounds.w
properties["height"] = bounds.h

if isinstance(widget, BitField):
properties["width"] = widget.number_of_bits * 20
properties.pop("height")

widget_type = template.attrib.get("type", "")

t_copy = deepcopy(template)
Expand Down Expand Up @@ -81,7 +86,9 @@ def set(
"textupdate",
TextRead(format=format),
) if format is not None:
add_format(t_copy, BOB_TEXT_FORMATS[format])
add_format(t_copy, BOB_TEXT_FORMATS[TextFormat(format)])
case ("byte_monitor", BitField() as bit_field):
add_byte_number_of_bits(t_copy, bit_field.number_of_bits)

return t_copy

Expand Down Expand Up @@ -206,14 +213,17 @@ def add_format(element: ElementBase, format: str):
SubElement(element, "format").text = format


def add_byte_number_of_bits(element: ElementBase, number_of_bits: int):
SubElement(element, "numBits").text = str(number_of_bits)


def find_element(root_element: ElementBase, tag: str, index: int = 0) -> ElementBase:
"""Iterate tree to find tag and replace text.
Args:
root_element: Root of tree to search
tag: Tag to search for in tree
index: Match to return if multiple matches are found
"""
match list(root_element.iter(tag=tag)):
case []:
Expand Down
54 changes: 51 additions & 3 deletions src/pvi/_format/dls.bob
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<display version="2.0.0">
<name>Display</name>
<x>12</x>
<y>12</y>
<y use_class="true">0</y>
<width>1000</width>
<height>800</height>
<grid_step_x>4</grid_step_x>
Expand Down Expand Up @@ -192,9 +192,57 @@
<widget type="table" version="2.0.0">
<name>Table</name>
<pv_name>pva://Table</pv_name>
<x>300</x>
<y>300</y>
<x>671</x>
<y>670</y>
<width>200</width>
<height>100</height>
<columns>
<column>
<name>Column 1</name>
<width>100</width>
<editable>true</editable>
</column>
</columns>
</widget>
<widget type="byte_monitor" version="2.0.0">
<name>BitField</name>
<pv_name>ByteMonitor</pv_name>
<x>172</x>
<y>412</y>
<width>165</width>
</widget>
<widget type="xyplot" version="3.0.0">
<name>ArrayTrace</name>
<x>348</x>
<y>412</y>
<width>230</width>
<height>230</height>
<traces>
<trace>
<name>$(traces[0].y_pv)</name>
<x_pv></x_pv>
<y_pv>ArrayTrace</y_pv>
<err_pv></err_pv>
<axis>0</axis>
<trace_type>1</trace_type>
<color>
<color red="0" green="0" blue="255">
</color>
</color>
<line_width>1</line_width>
<line_style>0</line_style>
<point_type>0</point_type>
<point_size>10</point_size>
<visible>true</visible>
</trace>
</traces>
</widget>
<widget type="image" version="2.0.0">
<name>ImageRead</name>
<pv_name>ImageRead</pv_name>
<x>566</x>
<y>58</y>
<width>300</width>
<height>301</height>
</widget>
</display>
40 changes: 40 additions & 0 deletions src/pvi/_format/dls.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,26 @@ def format_edl(self, device: Device, path: Path):
search='"SubScreenFile"',
property_map={"displayFileName": "file_name"},
),
bitfield_formatter_cls=PVWidgetFormatter.from_template(
template,
search='"LED"',
property_map={"controlPv": "pv"},
),
button_panel_formatter_cls=PVWidgetFormatter.from_template(
template,
search='"Label"',
property_map={"value": "pv"},
),
array_trace_formatter_cls=PVWidgetFormatter.from_template(
template,
search='"Label"',
property_map={"value": "pv"},
),
image_read_formatter_cls=PVWidgetFormatter.from_template(
template,
search='"Label"',
property_map={"value": "pv"},
),
)
screen_title_cls = LabelWidgetFormatter.from_template(
template,
Expand Down Expand Up @@ -228,6 +248,26 @@ def format_bob(self, device: Device, path: Path):
search="ProgressBar",
property_map={"pv_name": "pv"},
),
bitfield_formatter_cls=PVWidgetFormatter.from_template(
template,
search="BitField",
property_map={"pv_name": "pv"},
),
button_panel_formatter_cls=PVWidgetFormatter.from_template(
template,
search="ButtonPanel",
property_map={"pv_name": "pv"},
),
array_trace_formatter_cls=PVWidgetFormatter.from_template(
template,
search="ArrayTrace",
property_map={"y_pv": "pv"},
),
image_read_formatter_cls=PVWidgetFormatter.from_template(
template,
search="ImageRead",
property_map={"pv_name": "pv"},
),
text_read_formatter_cls=PVWidgetFormatter.from_template(
template,
search="TextUpdate",
Expand Down
2 changes: 1 addition & 1 deletion src/pvi/_format/edl.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def set(
is_text_widget(template) and format is not None
):
template = add_property(
template, "displayMode", EDL_TEXT_FORMATS[format]
template, "displayMode", EDL_TEXT_FORMATS[TextFormat(format)]
)

return template
Expand Down
2 changes: 1 addition & 1 deletion src/pvi/_format/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ def generate_component_formatters(
for action, value in c.write_widget.actions.items()
]
# If this is a SignalRW, recreate the readback with a SignalR
if isinstance(c, SignalRW):
if isinstance(c, SignalRW) and c.read_widget is not None:
row_components += [
SignalR(name=c.name, read_pv=c.read_pv, read_widget=c.read_widget)
]
Expand Down
15 changes: 13 additions & 2 deletions src/pvi/_format/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
from pvi._format.utils import Bounds, GroupType
from pvi.device import (
LED,
ArrayTrace,
BitField,
ButtonPanel,
CheckBox,
ComboBox,
Group,
ImageRead,
ProgressBar,
TableRead,
TableWrite,
Expand Down Expand Up @@ -260,14 +264,17 @@ class WidgetFormatterFactory(BaseModel, Generic[T]):
label_formatter_cls: Type[LabelWidgetFormatter[T]]
led_formatter_cls: Type[PVWidgetFormatter[T]]
progress_bar_formatter_cls: Type[PVWidgetFormatter[T]]
# TODO: add bitfield, progress_bar, plot, image
text_read_formatter_cls: Type[PVWidgetFormatter[T]]
check_box_formatter_cls: Type[PVWidgetFormatter[T]]
combo_box_formatter_cls: Type[PVWidgetFormatter[T]]
text_write_formatter_cls: Type[PVWidgetFormatter[T]]
table_formatter_cls: Type[PVWidgetFormatter]
table_formatter_cls: Type[PVWidgetFormatter[T]]
action_formatter_cls: Type[ActionWidgetFormatter[T]]
sub_screen_formatter_cls: Type[SubScreenWidgetFormatter[T]]
bitfield_formatter_cls: Type[PVWidgetFormatter[T]]
array_trace_formatter_cls: Type[PVWidgetFormatter[T]]
button_panel_formatter_cls: Type[PVWidgetFormatter[T]]
image_read_formatter_cls: Type[PVWidgetFormatter[T]]

def pv_widget_formatter(
self,
Expand Down Expand Up @@ -296,6 +303,10 @@ def pv_widget_formatter(
ComboBox: self.combo_box_formatter_cls,
TextWrite: self.text_write_formatter_cls,
TableWrite: self.table_formatter_cls,
BitField: self.bitfield_formatter_cls,
ArrayTrace: self.array_trace_formatter_cls,
ButtonPanel: self.button_panel_formatter_cls,
ImageRead: self.image_read_formatter_cls,
}
if isinstance(widget, (TextRead, TextWrite)):
bounds.h *= widget.get_lines()
Expand Down
11 changes: 7 additions & 4 deletions src/pvi/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import json
import re
from enum import IntEnum
from enum import Enum
from pathlib import Path
from typing import (
TYPE_CHECKING,
Expand Down Expand Up @@ -65,7 +65,7 @@ def enforce_pascal_case(s: str) -> str:
return s[0].upper() + s[1:]


class TextFormat(IntEnum):
class TextFormat(Enum):
"""Format to use for display of Text{Read,Write} widgets on a UI"""

decimal = 0
Expand Down Expand Up @@ -94,7 +94,10 @@ class LED(ReadWidget):
class BitField(ReadWidget):
"""LED and label for each bit of an int PV"""

labels: Sequence[str] = Field(description="Label for each bit")
labels: Sequence[str] | None = Field(default=None, description="Label for each bit")
number_of_bits: int = Field(
default=8, description="Number of bits to display", gt=0
)


class ProgressBar(ReadWidget):
Expand Down Expand Up @@ -156,7 +159,7 @@ class ButtonPanel(WriteWidget):
"""

actions: Dict[str, str] = Field(default={"go": "1"}, description="PV poker buttons")
actions: Dict[str, str] = Field(default={"Go": "1"}, description="PV poker buttons")


class TextWrite(WriteWidget):
Expand Down
19 changes: 19 additions & 0 deletions tests/format/input/mixedWidgets.pvi.device.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,22 @@ children:
read_pv: $(P)$(R)ProgressBarTest_RBV
read_widget:
type: ProgressBar

- type: SignalR
name: ByteMonitorTest
read_pv: $(P)$(R)ByteMonitor_RBV
read_widget:
type: BitField

- type: SignalR
name: ArrayTraceTest
read_pv: $(P)$(R)ArrayTrace_RBV
read_widget:
type: ArrayTrace
axis: "x"

- type: SignalR
name: ImageReadTest
read_pv: $(P)$(R)ImageRead_RBV
read_widget:
type: ImageRead
Loading

0 comments on commit e1d5556

Please sign in to comment.