diff --git a/src/calng/scenes.py b/src/calng/scenes.py index 35273dd41f49c90c6dd0ccedc01f7e483911f64d..c39d584fb6fa5991403689f1b38949a43f94f2b7 100644 --- a/src/calng/scenes.py +++ b/src/calng/scenes.py @@ -1,5 +1,4 @@ import enum -import textwrap import karathon from karabo.common.scenemodel.api import ( @@ -17,11 +16,13 @@ from karabo.common.scenemodel.api import ( LineEditModel, RectangleModel, SceneModel, + SceneTargetWindow, write_scene, ) -BASE_INC = 30 -PADDING = 10 +BASE_INC = 25 +NARROW_INC = 20 +PADDING = 5 RECONFIGURABLE = 4 # TODO: look up proper enum _type_to_line_editable = { @@ -185,287 +186,197 @@ class VerticalLayout: ) -class MaybeEditableRow: +class MaybeEditableRow(HorizontalLayout): def __init__( self, device_id, schema_hash, key_path, - label_width=7, - display_width=5, - edit_width=5, + label_width=7 * NARROW_INC, + display_width=5 * NARROW_INC, + edit_width=5 * NARROW_INC, + height=NARROW_INC, ): - self.label_width = label_width * BASE_INC - self.display_width = display_width * BASE_INC - self.edit_width = edit_width * BASE_INC - + super().__init__(padding=0) key_attr = schema_hash.getAttributes(key_path) - label_text = textwrap.shorten( + label_text = ( key_attr["displayedName"] if "displayedName" in key_attr - else key_path.split(".")[-1], - 24, - ) - - self.label = LabelModel( - text=label_text, - width=self.label_width, - height=BASE_INC, + else key_path.split(".")[-1] ) - self.value_display = DisplayLabelModel( - keys=[f"{device_id}.{key_path}"], - width=self.display_width, - height=BASE_INC, + self.children.extend( + [ + LabelModel( + text=label_text, + width=label_width, + height=height, + ), + DisplayLabelModel( + keys=[f"{device_id}.{key_path}"], + width=display_width, + height=height, + ), + ] ) if key_attr["accessMode"] == RECONFIGURABLE: value_type = key_attr["valueType"] if value_type in _type_to_line_editable: line_editable_class, extra_args = _type_to_line_editable[value_type] - self.value_edit = line_editable_class( - keys=[f"{device_id}.{key_path}"], - width=self.edit_width, - height=BASE_INC, - **extra_args, + self.children.append( + line_editable_class( + keys=[f"{device_id}.{key_path}"], + width=edit_width, + height=height, + **extra_args, + ) ) else: - self.value_edit = LabelModel( - text=f"Not yet supported: editing {key_path} of type {value_type}", - width=self.edit_width, - height=BASE_INC, + self.children.append( + value_edit=LabelModel( + text=f"Not yet supported: editing {key_path} of type {value_type}", + width=edit_width, + height=height, + ) ) - @property - def width(self): - return self.label_width + self.display_width + self.edit_width - - @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 - self.value_display.y = y - if hasattr(self, "value_edit"): - self.value_edit.x = x + self.label_width + self.display_width - self.value_edit.y = y - return [ - self.label, - self.value_display, - self.value_edit, - ] - else: - return [ - self.label, - self.value_display, - ] - @titled("Parameters used for CalCat queries", width=10) @boxed -class ConstantParameterColumn: +class ConstantParameterColumn(VerticalLayout): def __init__(self, device_id, schema_hash, prefix="constantParameters"): + super().__init__(padding=0) if "." in prefix: - self.extra_path_prefix = prefix[: prefix.rfind(".") + 1] + extra_path_prefix = prefix[: prefix.rfind(".") + 1] else: - self.extra_path_prefix = "" - device_id = device_id - self.rows = [ - MaybeEditableRow( - device_id, - schema_hash, - f"{prefix}.{key}", + extra_path_prefix = "" + self.children.extend( + [ + MaybeEditableRow( + device_id, + schema_hash, + f"{prefix}.{key}", + ) + for key in schema_hash.get(prefix).getKeys() + ] + ) + self.children.append( + DisplayCommandModel( + keys=[f"{device_id}.{extra_path_prefix}loadMostRecentConstants"], + width=10 * BASE_INC, + height=BASE_INC, ) - for key in schema_hash.get(prefix).getKeys() - ] - self.load_button = DisplayCommandModel( - keys=[f"{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 - self.load_button.x = x + 7 * BASE_INC - self.load_button.y = y + y_offset - res.append(self.load_button) - - return res - - @property - def width(self): - 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: +class ConstantNode(HorizontalLayout): def __init__(self, device_id, constant_path): - self.title = LabelModel( - text=constant_path.split(".")[-1], - width=7 * BASE_INC, - height=BASE_INC, - ) - self.ampel = ColorBoolModel( - height=BASE_INC, width=BASE_INC, keys=[f"{device_id}.{constant_path}.found"] + super().__init__(padding=0) + self.children.extend( + [ + LabelModel( + text=constant_path.split(".")[-1], + width=7 * NARROW_INC, + height=NARROW_INC, + ), + ColorBoolModel( + width=NARROW_INC, + height=NARROW_INC, + keys=[f"{device_id}.{constant_path}.found"], + ), + ] ) - def render(self, x, y): - self.title.x = x - self.title.y = y - self.ampel.x = x + 7 * BASE_INC - self.ampel.y = y - y += BASE_INC - return [self.title, self.ampel] - - @property - def width(self): - return 8 * BASE_INC - - @property - def height(self): - return BASE_INC - @titled("Found constants", width=6) @boxed -class FoundConstantsColumn: +class FoundConstantsColumn(VerticalLayout): def __init__(self, device_id, schema_hash, prefix="foundConstants"): - self.rows = [ - ConstantNode(device_id, f"{prefix}.{constant_name}") - for constant_name in schema_hash.get(prefix).getKeys() - ] + super().__init__(padding=0) + self.children.extend( + [ + ConstantNode(device_id, f"{prefix}.{constant_name}") + for constant_name in schema_hash.get(prefix).getKeys() + ] + ) - def render(self, x, y): - res = [] - y_offset = 0 - for row in self.rows: - res.extend(row.render(x, y + y_offset)) - y_offset += row.height - return res - @property - def width(self): - return max(row.width for row in self.rows) +class Space: + def __init__(self, width, height): + self.width = width + self.height = height - @property - def height(self): - return sum(row.height for row in self.rows) + def render(self, x, y): + return [] -class CorrectionStepNode: +class CorrectionStepNode(HorizontalLayout): def __init__(self, device_id, step_path): - self.label = LabelModel( - text=step_path.split(".")[-1], width=7 * BASE_INC, height=BASE_INC - ) - self.checkbox_main = CheckBoxModel( - keys=[f"{device_id}.{step_path}.enable"], - width=BASE_INC, - height=BASE_INC, - klass="EditableCheckBox", - ) - self.checkbox_preview = CheckBoxModel( - keys=[f"{device_id}.{step_path}.preview"], - width=BASE_INC, - height=BASE_INC, - klass="EditableCheckBox", + super().__init__(padding=0) + self.children.extend( + [ + LabelModel( + text=step_path.split(".")[-1], width=7 * BASE_INC, height=BASE_INC + ), + CheckBoxModel( + keys=[f"{device_id}.{step_path}.enable"], + width=BASE_INC, + height=BASE_INC, + klass="EditableCheckBox", + ), + CheckBoxModel( + keys=[f"{device_id}.{step_path}.preview"], + width=BASE_INC, + height=BASE_INC, + klass="EditableCheckBox", + ), + ] ) - def render(self, x, y): - self.label.x = x - self.label.y = y - x += 5 * BASE_INC - self.checkbox_main.x = x - self.checkbox_main.y = y - x += 3 * BASE_INC - self.checkbox_preview.x = x - self.checkbox_preview.y = y - return [self.label, self.checkbox_main, self.checkbox_preview] - - @property - def width(self): - return 10 * BASE_INC - - @property - def height(self): - return BASE_INC - @titled("Correction steps", width=6) @boxed -class CorrectionStepsColumn: +class CorrectionStepsColumn(VerticalLayout): def __init__(self, device_id, schema_hash, prefix="corrections"): - self.header_main = LabelModel( - text="enable", width=3 * BASE_INC, height=BASE_INC + super().__init__(padding=0) + self.children.append( + HorizontalLayout( + children=[ + Space(width=7 * BASE_INC, height=BASE_INC), + LabelModel( + text="enable/preview", width=6 * BASE_INC, height=BASE_INC + ), + ], + padding=0, + ) ) - self.header_preview = LabelModel( - text="preview", width=3 * BASE_INC, height=BASE_INC + self.children.extend( + [ + CorrectionStepNode(device_id, f"{prefix}.{step_path}") + for step_path in schema_hash.get(prefix).getKeys() + ] ) - self.steps = [ - CorrectionStepNode(device_id, f"{prefix}.{step_path}") - for step_path in schema_hash.get(prefix).getKeys() - ] - - def render(self, x, y): - self.header_main.x = x + 5 * BASE_INC - self.header_main.y = y - self.header_preview.x = x + 8 * BASE_INC - self.header_preview.y = y - res = [self.header_main, self.header_preview] - y += BASE_INC - for step in self.steps: - step.x = x - step.y = y - res.extend(step.render(x, y)) - y += step.height - return res - - @property - def width(self): - return max(step.width for step in self.steps) - - @property - def height(self): - return sum(step.height for step in self.steps) + BASE_INC -class ConstantLoadedAmpeln: +class ConstantLoadedAmpeln(HorizontalLayout): def __init__(self, device_id, schema_hash, prefix="foundConstants"): - self.content = HorizontalLayout( - children=[ + super().__init__(padding=0) + self.children.extend( + [ ColorBoolModel( keys=[f"{device_id}.{prefix}.{key}.found"], height=BASE_INC, width=BASE_INC, ) for key in schema_hash.get(prefix).getKeys() - ], - padding=0, + ] ) - def render(self, x, y): - return self.content.render(x, y) - - @property - def width(self): - return self.content.width - - @property - def height(self): - return self.content.height - @titled("Manager status", width=6) @boxed -class ManagerDeviceStatus: +class ManagerDeviceStatus(VerticalLayout): def __init__(self, device_id): + super().__init__(padding=0) self.name = DisplayLabelModel( keys=[f"{device_id}.deviceId"], width=14 * BASE_INC, @@ -497,8 +408,8 @@ class ManagerDeviceStatus: width=14 * BASE_INC, height=7 * BASE_INC, ) - self.content = VerticalLayout( - children=[ + self.children.extend( + [ self.name, HorizontalLayout( children=[ @@ -512,26 +423,15 @@ class ManagerDeviceStatus: padding=0, ), self.status_log, - ], - padding=0, + ] ) - def render(self, x, y): - return self.content.render(x, y) - - @property - def width(self): - return self.content.width - - @property - def height(self): - return self.content.height - @titled("Device status", width=6) @boxed -class CorrectionDeviceStatus: +class CorrectionDeviceStatus(VerticalLayout): def __init__(self, device_id): + super().__init__(padding=0) self.name = DisplayLabelModel( keys=[f"{device_id}.deviceId"], width=14 * BASE_INC, @@ -563,10 +463,10 @@ class CorrectionDeviceStatus: self.status_log = DisplayTextLogModel( keys=[f"{device_id}.status"], width=14 * BASE_INC, - height=20 * BASE_INC, + height=14 * BASE_INC, ) - self.content = VerticalLayout( - children=[ + self.children.extend( + [ self.name, HorizontalLayout( children=[ @@ -583,71 +483,44 @@ class CorrectionDeviceStatus: padding=0, ), self.status_log, - ], - padding=0, + ] ) - def render(self, x, y): - return self.content.render(x, y) - - @property - def width(self): - return 14 * BASE_INC - @property - def height(self): - return 23 * BASE_INC - - -class CompactCorrectionDeviceOverview: +class CompactCorrectionDeviceOverview(HorizontalLayout): def __init__(self, device_id, schema_hash): - self.name = DeviceSceneLinkModel( - text=device_id.split("/")[-1], - keys=[f"{device_id}.availableScenes"], - target="overview", - width=5 * BASE_INC, - height=BASE_INC, - ) - self.status = DisplayStateColorModel( - show_string=True, - keys=[f"{device_id}.state"], - width=6 * BASE_INC, - height=BASE_INC, - ) - self.rate = EvaluatorModel( - expression="f'{x:.02f}'", - keys=[f"{device_id}.performance.rate"], - width=4 * BASE_INC, - height=BASE_INC, - ) - self.tid = DisplayLabelModel( - keys=[f"{device_id}.trainId"], - width=4 * BASE_INC, - height=BASE_INC, - ) - self.ampeln = ConstantLoadedAmpeln(device_id, schema_hash) - self.content = HorizontalLayout( - children=[ - self.name, - self.status, - self.rate, - self.tid, - self.ampeln, - ], - padding=0, + super().__init__(padding=0) + self.children.extend( + [ + DeviceSceneLinkModel( + text=device_id.split("/")[-1], + keys=[f"{device_id}.availableScenes"], + target="overview", + target_window=SceneTargetWindow.Dialog, + width=5 * BASE_INC, + height=BASE_INC, + ), + DisplayStateColorModel( + show_string=True, + keys=[f"{device_id}.state"], + width=6 * BASE_INC, + height=BASE_INC, + ), + EvaluatorModel( + expression="f'{x:.02f}'", + keys=[f"{device_id}.performance.rate"], + width=4 * BASE_INC, + height=BASE_INC, + ), + DisplayLabelModel( + keys=[f"{device_id}.trainId"], + width=4 * BASE_INC, + height=BASE_INC, + ), + ConstantLoadedAmpeln(device_id, schema_hash), + ] ) - def render(self, x, y): - return self.content.render(x, y) - - @property - def width(self): - return 19 * BASE_INC + self.ampeln.width - - @property - def height(self): - return BASE_INC - def correction_device_overview_scene(device_id, schema): if isinstance(schema, karathon.Schema):