Skip to content

Commit 800222b

Browse files
authored
Merge pull request #114 from ZLLentz/enh_error_clear_options
ENH: add property to MotorClassicFull to change error reset PV/value
2 parents e856503 + ccf8179 commit 800222b

5 files changed

Lines changed: 110 additions & 21 deletions

File tree

pcdswidgets/generated/motion/common/motor_classic_full_base.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ class MotorClassicFullBase(DesignerWidget):
5656
"PyDMLabel_egu",
5757
"PyDMByteIndicator_hls",
5858
"PyDMLabel_name",
59-
"PyDMPushButton_clear_error",
6059
"PyDMLineEdit_setpoint",
6160
"PyDMByteIndicator_mvn",
6261
"PyDMPushButton_stop",
@@ -88,9 +87,6 @@ class MotorClassicFullBase(DesignerWidget):
8887
"PyDMLineEdit_twVal": [
8988
"MOTOR",
9089
],
91-
"PyDMPushButton_clear_error": [
92-
"MOTOR",
93-
],
9490
"PyDMPushButton_stop": [
9591
"MOTOR",
9692
],
@@ -129,9 +125,6 @@ class MotorClassicFullBase(DesignerWidget):
129125
"PyDMLineEdit_twVal": [
130126
("channel", """ca://${MOTOR}.TWV"""),
131127
],
132-
"PyDMPushButton_clear_error": [
133-
("channel", "ca://${MOTOR}:SEQ_SELN"),
134-
],
135128
"PyDMPushButton_stop": [
136129
("channel", """ca://${MOTOR}.STOP"""),
137130
],

pcdswidgets/generated/motion/common/motor_classic_full_form.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,10 +344,7 @@ def retranslateUi(self, Form):
344344
self.PyDMByteIndicator_hls.setChannel(_translate("Form", "ca://${MOTOR}.HLS"))
345345
self.PyDMLabel_name.setChannel(_translate("Form", "ca://${MOTOR}.DESC"))
346346
self.PyDMPushButton_clear_error.setText(_translate("Form", "Clear Error"))
347-
self.PyDMPushButton_clear_error.setChannel(_translate("Form", "ca://${MOTOR}:SEQ_SELN"))
348347
self.PyDMPushButton_clear_error.setConfirmMessage(_translate("Form", "Are you sure you want to proceed?"))
349-
self.PyDMPushButton_clear_error.setPressValue(_translate("Form", "48"))
350-
self.PyDMPushButton_clear_error.setReleaseValue(_translate("Form", "None"))
351348
self.PyDMLineEdit_setpoint.setChannel(_translate("Form", "ca://${MOTOR}.VAL"))
352349
self.PyDMByteIndicator_mvn.setChannel(_translate("Form", "ca://${MOTOR}.MOVN"))
353350
self.PyDMPushButton_stop.setText(_translate("Form", "Stop"))

pcdswidgets/motion/common/motor_classic_full.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,121 @@
44
This file can be safely edited to change the runtime behavior of the widget.
55
"""
66

7+
from pydm.utilities import ACTIVE_QT_WRAPPER, QtWrapperTypes
8+
from qtpy.QtWidgets import QWidget
9+
710
from pcdswidgets.builder.designer_options import DesignerOptions
811
from pcdswidgets.builder.icon_options import IconOptions
912
from pcdswidgets.generated.motion.common.motor_classic_full_base import MotorClassicFullBase
1013

14+
try:
15+
from qtpy.QtCore import pyqtProperty
16+
except ImportError:
17+
from qtpy.QtCore import Property as pyqtProperty # type: ignore
18+
19+
# Note: for forward compat, setting up enum properties is completely different
20+
# depending on if we use pyqt5 or pyside6.
21+
# I'm following the examples in PyDM here.
22+
if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYSIDE6:
23+
from enum import Enum
24+
25+
from PySide6.QtCore import QEnum # type: ignore
26+
27+
@QEnum
28+
class MotorTypes(Enum): # type: ignore
29+
"""Options for motor type to select error reset PV."""
30+
31+
GENERIC = 0
32+
IMS = 1
33+
BECKHOFF = 2
34+
BECKHOFF_LEGACY = 3
35+
36+
else:
37+
# pyqt5 can't use real python enums for this, unfortunately
38+
class MotorTypes: # type: ignore
39+
"""Options for motor type to select error reset PV."""
40+
41+
GENERIC = 0
42+
IMS = 1
43+
BECKHOFF = 2
44+
BECKHOFF_LEGACY = 3
45+
1146

1247
class MotorClassicFull(MotorClassicFullBase):
48+
"""
49+
Generic motor widget at full size.
50+
51+
This class is extended to allow us to support multiple
52+
types of motor IOCs with the same ui layout.
53+
54+
The user can set the `motor_type` enum to change the
55+
following behavior:
56+
57+
- Error reset PV and value
58+
59+
There are four options:
60+
61+
- GENERIC: default, no error reset.
62+
- IMS: standard IMS IOC (not motor record).
63+
- BECKHOFF: Motor that uses the 2026+ version of our TwinCAT motion libraries.
64+
- BECKHOFF_LEGACY: uses the pre-2026 version of the above.
65+
"""
66+
1367
designer_options = DesignerOptions(
1468
group="ECS Motion Common",
1569
is_container=False,
1670
icon=IconOptions.NONE,
1771
)
72+
# Boilerplate to make the enum property work
73+
if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYQT5:
74+
from PyQt5.QtCore import Q_ENUM
75+
76+
Q_ENUM(MotorTypes)
77+
MotorTypes = MotorTypes
78+
GENERIC = MotorTypes.GENERIC
79+
IMS = MotorTypes.IMS
80+
BECKHOFF = MotorTypes.BECKHOFF
81+
BECKHOFF_LEGACY = MotorTypes.BECKHOFF_LEGACY
82+
83+
def __init__(self, parent: QWidget | None = None):
84+
super().__init__(parent)
85+
self._motor_type = MotorTypes.GENERIC
86+
self._clear_error_suffix = ""
87+
self.PyDMPushButton_clear_error.hide()
88+
89+
def after_set_macro(self, macro_name: str, value: str):
90+
"""Puts motor prefix into error reset PV."""
91+
if macro_name == "MOTOR":
92+
self.new_clear_error_motor(value)
93+
94+
def get_motor_type(self) -> MotorTypes | int:
95+
return self._motor_type
96+
97+
def set_motor_type(self, value: MotorTypes | int) -> None:
98+
self._motor_type = value
99+
match value:
100+
case MotorTypes.IMS:
101+
self.new_clear_error_suffix(":SEQ_SELN")
102+
self.PyDMPushButton_clear_error.setPressValue(48)
103+
self.PyDMPushButton_clear_error.show()
104+
case MotorTypes.BECKHOFF:
105+
self.new_clear_error_suffix(":bReset")
106+
self.PyDMPushButton_clear_error.setPressValue(1)
107+
self.PyDMPushButton_clear_error.show()
108+
case MotorTypes.BECKHOFF_LEGACY:
109+
self.new_clear_error_suffix(":PLC:bReset")
110+
self.PyDMPushButton_clear_error.setPressValue(1)
111+
self.PyDMPushButton_clear_error.show()
112+
case _:
113+
self.PyDMPushButton_clear_error.hide()
114+
115+
motor_type = pyqtProperty(MotorTypes, get_motor_type, set_motor_type)
116+
117+
def new_clear_error_suffix(self, suffix: str):
118+
self._clear_error_suffix = suffix
119+
if motor := self.get_macro("MOTOR"):
120+
self.new_clear_error_motor(motor)
121+
122+
def new_clear_error_motor(self, motor: str):
123+
if self._clear_error_suffix:
124+
self.PyDMPushButton_clear_error.set_channel(f"ca://{motor}{self._clear_error_suffix}")

pcdswidgets/tests/builder/test_builder.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import pcdswidgets
88
from pcdswidgets.builder.designer_widget import DesignerWidget
99

10-
MODULE_ROOT = Path(pcdswidgets.__file__).parent
10+
MODULE_ROOT = Path(pcdswidgets.__file__).parent # type: ignore
1111
UI_SOURCES = sorted((MODULE_ROOT / "ui").rglob("*.ui"))
1212

1313
TEST_UI = str(Path(__file__).parent / "pytest.ui")
@@ -90,7 +90,8 @@ def test_built_is_importable(ui_source: Path):
9090
assert issubclass(base_classes[0], DesignerWidget)
9191
assert base_classes[0].ui_form is form_classes[0]
9292

93-
assert len(main_classes) == 1
93+
# User can add additional classes
94+
assert len(main_classes) >= 1
9495
assert hasattr(main_classes[0], "designer_options")
9596
assert hasattr(main_classes[0], "_qt_designer_")
9697
assert issubclass(main_classes[0], base_classes[0])

pcdswidgets/ui/motion/common/motor_classic_full.ui

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -634,9 +634,6 @@ background-color: rgb(250, 250, 250);</string>
634634
<property name="monitorDisp">
635635
<bool>false</bool>
636636
</property>
637-
<property name="channel">
638-
<string>ca://${MOTOR}:SEQ_SELN</string>
639-
</property>
640637
<property name="PyDMIcon">
641638
<string/>
642639
</property>
@@ -655,12 +652,6 @@ background-color: rgb(250, 250, 250);</string>
655652
<property name="confirmMessage">
656653
<string>Are you sure you want to proceed?</string>
657654
</property>
658-
<property name="pressValue">
659-
<string>48</string>
660-
</property>
661-
<property name="releaseValue">
662-
<string>None</string>
663-
</property>
664655
<property name="relativeChange">
665656
<bool>false</bool>
666657
</property>

0 commit comments

Comments
 (0)