Skip to content
Snippets Groups Projects
Commit c73a6987 authored by David Hammer's avatar David Hammer
Browse files

Improve scene generation code

parent d57d666e
No related branches found
No related tags found
2 merge requests!12Snapshot: field test deployed version as of end of run 202201,!3Base correction device, CalCat interaction, DSSC and AGIPD devices
......@@ -318,6 +318,7 @@ class CalibrationManager(DeviceClientBase, Device):
# Assumes there are correction devices known to manager
scene_data = scenes.manager_device_overview_scene(
self.deviceId,
self.getDeviceSchema(),
self._correction_device_schema,
sorted(self._correction_device_ids))
payload = Hash('success', True, 'name', name, 'data', scene_data)
......
import enum
import textwrap
import karathon
......@@ -34,6 +35,14 @@ _type_to_line_editable = {
}
class Align(enum.Enum):
CENTER = enum.auto()
TOP = enum.auto()
BOTTOM = enum.auto()
LEFT = enum.auto()
RIGHT = enum.auto()
def titled(title, width=8):
def actual_decorator(component_class):
# should this create subclass instead of mutating?
......@@ -56,9 +65,9 @@ def titled(title, width=8):
orig_height = component_class.height
def new_height(self):
return orig_height(self) + BASE_INC
return orig_height.fget(self) + BASE_INC
component_class.height = new_height
component_class.height = property(fget=new_height)
return component_class
......@@ -68,16 +77,16 @@ def titled(title, width=8):
def boxed(component_class):
# should this create subclass instead of mutating?
orig_render = component_class.render
orig_height = component_class.height
orig_width = component_class.width
orig_height = component_class.height
def new_render(self, x, y, *args, **kwargs):
return [
RectangleModel(
x=x,
y=y,
width=orig_width(self) + 2 * PADDING,
height=orig_height(self) + 2 * PADDING,
width=orig_width.fget(self) + 2 * PADDING,
height=orig_height.fget(self) + 2 * PADDING,
stroke="#000000",
)
] + orig_render(self, x + PADDING, y + PADDING, *args, **kwargs)
......@@ -85,18 +94,87 @@ def boxed(component_class):
component_class.render = new_render
def new_height(self):
return orig_height(self) + 2 * PADDING
return orig_height.fget(self) + 2 * PADDING
component_class.height = new_height
component_class.height = property(fget=new_height)
def new_width(self):
return orig_width(self) + 2 * PADDING
return orig_width.fget(self) + 2 * PADDING
component_class.width = new_width
component_class.width = property(fget=new_width)
return component_class
class HorizontalLayout:
def __init__(self, children=None, padding=PADDING):
if children is None:
self.children = []
else:
self.children = children
self.padding = padding
def render(self, x, y, align=Align.TOP):
if align is not Align.TOP:
height = self.height
res = []
for child in self.children:
if align is Align.TOP:
res.extend(child.render(x, y))
elif align is Align.CENTER:
res.extend(child.render(x, y + (height - child.height) / 2))
elif align is Align.BOTTOM:
res.extend(child.render(x, y + (height - child.height)))
else:
raise ValueError(f"Invalid align {align} for HorizontalLayout")
x += child.width + self.padding
return res
@property
def width(self):
if not self.children:
return 0
return self.padding * (len(self.children) - 1) + sum(
c.width for c in self.children
)
@property
def height(self):
if not self.children:
return 0
return max(c.height for c in self.children)
class VerticalLayout:
def __init__(self, children=None, padding=PADDING):
if children is None:
self.children = []
else:
self.children = children
self.padding = padding
def render(self, x, y):
res = []
for child in self.children:
res.extend(child.render(x, y))
y += child.height + self.padding
return res
@property
def width(self):
if not self.children:
return 0
return max(c.width for c in self.children)
@property
def height(self):
if not self.children:
return 0
return self.padding * (len(self.children) - 1) + sum(
c.height for c in self.children
)
class MaybeEditableRow:
def __init__(
self,
......@@ -146,13 +224,15 @@ class MaybeEditableRow:
height=BASE_INC,
)
def height(self):
return BASE_INC
@property
def width(self):
return self.label_width + self.display_width + self.edit_width
def render(self, x=None, y=None):
@property
def height(self):
return BASE_INC
def render(self, x, y):
self.label.x = x
self.label.y = y
self.value_display.x = x + self.label_width
......@@ -176,36 +256,44 @@ class MaybeEditableRow:
@boxed
class ConstantParameterColumn:
def __init__(self, device_id, schema_hash, prefix="constantParameters"):
if "." in prefix:
self.extra_path_prefix = prefix[: prefix.rfind(".") + 1]
else:
self.extra_path_prefix = ""
self.device_id = device_id
self.rows = [
MaybeEditableRow(device_id, schema_hash, f"{prefix}.{key}")
MaybeEditableRow(
device_id,
schema_hash,
f"{prefix}.{key}",
)
for key in schema_hash.get(prefix).getKeys()
]
self.load_button = DisplayCommandModel(
keys=[f"{self.device_id}.{self.extra_path_prefix}loadMostRecentConstants"],
width=10 * BASE_INC,
height=BASE_INC,
)
def render(self, x, y):
res = []
y_offset = 0
for row in self.rows:
res.extend(row.render(x=x, y=y + y_offset))
y_offset += row.height()
res.append(
DisplayCommandModel(
keys=[f"{self.device_id}.loadMostRecentConstants"],
x=x + 7 * BASE_INC,
y=y + y_offset,
width=10 * BASE_INC,
height=BASE_INC,
)
)
y_offset += row.height
self.load_button.x = x + 7 * BASE_INC
self.load_button.y = y + y_offset
res.append(self.load_button)
return res
def height(self):
return sum(row.height() for row in self.rows) + BASE_INC
@property
def width(self):
return max(row.width() for row in self.rows)
return max(row.width for row in self.rows)
@property
def height(self):
return sum(row.height for row in self.rows) + BASE_INC
class ConstantNode:
......@@ -227,12 +315,14 @@ class ConstantNode:
y += BASE_INC
return [self.title, self.ampel]
def height(self):
return BASE_INC
@property
def width(self):
return 8 * BASE_INC
@property
def height(self):
return BASE_INC
@titled("Found constants", width=6)
@boxed
......@@ -248,14 +338,16 @@ class FoundConstantsColumn:
y_offset = 0
for row in self.rows:
res.extend(row.render(x, y + y_offset))
y_offset += row.height()
y_offset += row.height
return res
def height(self):
return sum(row.height() for row in self.rows)
@property
def width(self):
return max(row.width() for row in self.rows)
return max(row.width for row in self.rows)
@property
def height(self):
return sum(row.height for row in self.rows)
class CorrectionStepNode:
......@@ -265,14 +357,14 @@ class CorrectionStepNode:
)
self.checkbox_main = CheckBoxModel(
keys=[f"{device_id}.{step_path}.enable"],
height=BASE_INC,
width=BASE_INC,
height=BASE_INC,
klass="EditableCheckBox",
)
self.checkbox_preview = CheckBoxModel(
keys=[f"{device_id}.{step_path}.preview"],
height=BASE_INC,
width=BASE_INC,
height=BASE_INC,
klass="EditableCheckBox",
)
......@@ -287,23 +379,19 @@ class CorrectionStepNode:
self.checkbox_preview.y = y
return [self.label, self.checkbox_main, self.checkbox_preview]
def height(self):
return BASE_INC
@property
def width(self):
return 10 * BASE_INC
@property
def height(self):
return BASE_INC
@titled("Correction steps", width=6)
@boxed
class CorrectionStepsColumn:
def __init__(
self, device_id, schema_hash, prefix="corrections", control_prefix=None
):
# separate control prefix allows iterating device schema, but setting on manager
if control_prefix is None:
control_prefix = prefix
def __init__(self, device_id, schema_hash, prefix="corrections"):
self.header_main = LabelModel(
text="enable", width=3 * BASE_INC, height=BASE_INC
)
......@@ -311,7 +399,7 @@ class CorrectionStepsColumn:
text="preview", width=3 * BASE_INC, height=BASE_INC
)
self.steps = [
CorrectionStepNode(device_id, f"{control_prefix}.{step_path}")
CorrectionStepNode(device_id, f"{prefix}.{step_path}")
for step_path in schema_hash.get(prefix).getKeys()
]
......@@ -326,14 +414,16 @@ class CorrectionStepsColumn:
step.x = x
step.y = y
res.extend(step.render(x, y))
y += step.height()
y += step.height
return res
@property
def width(self):
return max(step.width() for step in self.steps)
return max(step.width for step in self.steps)
@property
def height(self):
return sum(step.height() for step in self.steps) + BASE_INC
return sum(step.height for step in self.steps) + BASE_INC
class ConstantLoadedAmpeln:
......@@ -355,9 +445,11 @@ class ConstantLoadedAmpeln:
for i, key in enumerate(self.keys)
]
@property
def width(self):
return BASE_INC * len(self.keys)
@property
def height(self):
return BASE_INC
......@@ -425,9 +517,11 @@ class CorrectionDeviceStatus:
self.status_log,
]
@property
def width(self):
return 14 * BASE_INC
@property
def height(self):
return 23 * BASE_INC
......@@ -475,9 +569,11 @@ class CompactCorrectionDeviceOverview:
x += self.tid.width
return [self.name, self.status, self.rate, self.tid] + self.ampeln.render(x, y)
@property
def width(self):
return 19 * BASE_INC + self.ampeln.width()
return 19 * BASE_INC + self.ampeln.width
@property
def height(self):
return BASE_INC
......@@ -488,63 +584,68 @@ def correction_device_overview_scene(device_id, schema):
else:
schema_hash = schema.hash
status_overview = CorrectionDeviceStatus(device_id)
cpc = ConstantParameterColumn(device_id, schema_hash)
fcc = FoundConstantsColumn(device_id, schema_hash)
csc = CorrectionStepsColumn(device_id, schema_hash)
subscenes = []
x = PADDING
y = PADDING
subscenes.extend(status_overview.render(x, y))
x += status_overview.width() + PADDING
subscenes.extend(cpc.render(x, y))
x += cpc.width() + PADDING
subscenes.extend(fcc.render(x, y))
y += fcc.height() + PADDING
subscenes.extend(csc.render(x, y))
content = HorizontalLayout(
children=[
CorrectionDeviceStatus(device_id),
ConstantParameterColumn(device_id, schema_hash),
VerticalLayout(
children=[
FoundConstantsColumn(device_id, schema_hash),
CorrectionStepsColumn(device_id, schema_hash),
]
),
]
)
scene = SceneModel(
height=max(
status_overview.height(),
cpc.height(),
fcc.height() + PADDING + csc.height(),
)
+ 2 * PADDING,
width=2 * PADDING
+ 2 * PADDING
+ status_overview.width()
+ cpc.width()
+ csc.width(),
children=subscenes,
children=content.render(PADDING, PADDING),
width=content.width + 2 * PADDING,
height=content.height + 2 * PADDING,
)
return write_scene(scene)
def manager_device_overview_scene(
manager_device_id, correction_device_schema, correction_device_ids
manager_device_id,
manager_device_schema,
correction_device_schema,
correction_device_ids,
):
if isinstance(correction_device_schema, karathon.Schema):
schema_hash = correction_device_schema.getParameterHash()
else:
schema_hash = correction_device_schema.hash
x = PADDING
y = PADDING
subscenes = []
correction_steps = CorrectionStepsColumn(
manager_device_id, schema_hash, control_prefix="managed.corrections"
mds_hash = schema_to_hash(manager_device_schema)
cds_hash = schema_to_hash(correction_device_schema)
content = VerticalLayout(
children=[
HorizontalLayout(
children=[
ConstantParameterColumn(
manager_device_id, mds_hash, prefix="managed.constantParameters"
),
CorrectionStepsColumn(
manager_device_id, mds_hash, prefix="managed.corrections"
),
]
),
VerticalLayout(
children=[
CompactCorrectionDeviceOverview(device_id, cds_hash)
for device_id in correction_device_ids
],
padding=0,
),
]
)
subscenes.extend(correction_steps.render(x, y))
y += correction_steps.height() + PADDING
for device_id in correction_device_ids:
ccdo = CompactCorrectionDeviceOverview(device_id, schema_hash)
subscenes.extend(ccdo.render(x, y))
y += ccdo.height()
scene = SceneModel(
height=y + PADDING,
width=ccdo.width() + 2 * PADDING,
children=subscenes,
width=content.width + 2 * PADDING,
height=content.height + 2 * PADDING,
children=content.render(PADDING, PADDING),
)
return write_scene(scene)
def schema_to_hash(schema):
if isinstance(schema, karathon.Schema):
return schema.getParameterHash()
else:
return schema.hash
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment