add epics widgets, CustomROI

This commit is contained in:
2022-07-14 15:10:49 +02:00
parent 98297263bc
commit 7445a5aae6
8 changed files with 1694 additions and 0 deletions

355
epics_widgets/MotorTweak.py Normal file
View File

@@ -0,0 +1,355 @@
import math
import logging
from time import sleep
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QPainter, QBrush, QColor, QPainterPath, QPen, QDoubleValidator
from PyQt5.QtWidgets import QMenu, QInputDialog, QAction
from PyQt5.uic import loadUiType
from epics import Motor
from epics.ca import pend_event
from app_utils import DeltatauMotorStatus, assert_motor_positions
Ui_MotorTweak, QWidget = loadUiType('epics_widgets/MotorTweak.ui')
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
SPMG_STOP = 0
SPMG_PAUSE = 1
SPMG_MOVE = 2
SPMG_GO = 3
class MotorTweak(QWidget, Ui_MotorTweak):
event_val = pyqtSignal(str, dict)
event_readback = pyqtSignal(str, float, dict)
event_soft_limit = pyqtSignal(str, dict)
event_high_hard_limit = pyqtSignal(str, dict)
event_low_hard_limit = pyqtSignal(str, dict)
event_axis_fault = pyqtSignal(str, dict)
def __init__(self, parent=None):
super(MotorTweak, self).__init__(parent=parent)
self.setupUi(self)
self._wheel_tweaks = True
self._auto_labels = True
self._locked = False
self._label_style = 'basic'
self._templates_source = {
'basic': '<b>{short_name}</b> <font color="#080">{{rbv:.{precision}f}} {units}</font>',
'small': '<small>{short_name} <font size="small" color="#080">{{rbv:.{precision}f}} {units}</font><small>',
'2 lines': '<b>{short_name}</b><br><font size="small" color="#080">{{rbv:.{precision}f}} {units}</font>',
'busy': '<b>{short_name}</b> <font color="#080">{{rbv:.{precision}f}} {units}</font>'
}
self._templates = {}
def connect_motor(self, motor_base, short_name=None, **kwargs):
m = Motor(motor_base)
m.get_position()
self._ignore_limits = m.HLM == m.LLM # if both high/low limits are equal they are meaningless
self.motor = m
if not short_name:
short_name = m.description
self.short_name = short_name
self._pvname = motor_base
for attr in ['RTYP', 'JVEL', 'HLS', 'LLS', 'TWV', 'RBV', 'VAL', 'LVIO', 'HLM', 'LLM']:
# logger.debug('connecting to field {}'.format(attr))
m.PV(attr, connect=True)
self.set_motor_validator()
self._drive_val.setText(m.get('VAL', as_string=True))
self._drive_val.returnPressed.connect(self.move_motor_to_position)
tweak_min = kwargs.get("tweak_min", 0.0001)
tweak_max = kwargs.get("tweak_max", 10.0)
self._tweak_val.setText(m.get('TWV', as_string=True))
self._tweak_val.setValidator(QDoubleValidator(tweak_min, tweak_max, 4, self._tweak_val))
self._tweak_val.editingFinished.connect(lambda m=m: m.PV('TWV').put(self._tweak_val.text()))
self.jog_reverse.hide()
self.jog_forward.hide()
# self.jog_forward.pressed.connect(lambda: self.jog('forward', True))
# self.jog_reverse.pressed.connect(lambda: self.jog('reverse', True))
# self.jog_forward.released.connect(lambda: self.jog('forward', False))
# self.jog_reverse.released.connect(lambda: self.jog('reverse', False))
self.tweak_forward.clicked.connect(lambda m: self.motor.tweak('forward'))
self.tweak_reverse.clicked.connect(lambda m: self.motor.tweak('reverse'))
self.bind_wheel()
self.update_label_template()
m.add_callback('VAL', self.set_val)
m.add_callback('RBV', self.update_label)
m.set_callback('HLS', self.update_label)
m.set_callback('LLS', self.update_label)
m.set_callback('VAL', self.emit_signals, {'source_field': 'VAL'})
m.set_callback('RBV', self.emit_signals, {'source_field': 'RBV'})
m.set_callback('HLS', self.emit_signals, {'source_field': 'HLS'})
m.set_callback('LLS', self.emit_signals, {'source_field': 'LLS'})
m.set_callback('LVIO', self.emit_signals, {'source_field': 'LVIO'})
m.set_callback('STAT', self.emit_signals, {'source_field': 'STAT'})
m.set_callback('HLM', self.set_motor_validator)
def set_val(self, **kw):
v = kw['char_value']
logger.debug('updating VAL = {}'.format(v))
self._drive_val.setText(v)
def set_motor_validator(self, **kwargs):
m = self.motor
lineedit = self._drive_val
min, max = m.PV('LLM').get(), m.PV('HLM').get()
if min == max:
min = -1e6
max = 1e6
lineedit.setValidator(QDoubleValidator(min, max, m.PV('PREC').get(), lineedit))
def move_relative(self, dist):
self.motor.move(dist, ignore_limits=True, relative=True)
def is_done(self):
self.motor.refresh()
return 1 == self.motor.done_moving
def get_position(self):
return self.motor.get_position(readback=True)
def is_homed(self):
self.motor.refresh()
status = DeltatauMotorStatus(self.motor.motor_status)
return bool(DeltatauMotorStatus.MOTOR_HAS_BEEN_HOMED & status)
def move_motor_to_position(self, drive=None, wait=False, assert_position=False):
if drive is None:
drive = float(self._drive_val.text())
if assert_position:
wait=True
self.motor.move(drive, wait=wait, ignore_limits=True, relative=False)
if assert_position:
assert_motor_positions([(self.motor, drive, 0.1)], timeout=1)
def emit_signals(self, **kw):
'''
:param kw: info about the channel {
'access': 'read-only',
'char_value': '-0.105',
'chid': 36984304,
'count': 1,
'enum_strs': None,
'ftype': 20,
'host': 'SAR-CPPM-EXPMX1.psi.ch:5064',
'lower_alarm_limit': None,
'lower_ctrl_limit': 0.0,
'lower_disp_limit': 0.0,
'lower_warning_limit': None,
'motor_field': 'RBV',
'nelm': 1,
'precision': 3,
'pvname': 'SAR-EXPMX:MOT_ROT_Y.RBV',
'read_access': True,
'severity': 0,
'source_field': 'RBV',
'status': 0,
'timestamp': 1522314072.878331,
'type': 'time_double',
'typefull': 'time_double',
'units': b'deg',
'upper_alarm_limit': None,
'upper_ctrl_limit': 0.0,
'upper_disp_limit': 0.0,
'upper_warning_limit': None,
'value': -0.105,
'write_access': False}
:return:
'''
field = kw['motor_field']
src = kw['source_field']
kw['alias'] = self.short_name
if field != src:
return
if field == 'VAL':
self.event_val.emit(self._pvname, kw)
elif field == 'RBV':
self.event_readback.emit(kw['alias'], kw['value'], kw)
elif field == 'LVIO':
self.event_soft_limit.emit(self._pvname, kw)
elif field == 'HLS':
self.event_high_hard_limit.emit(self._pvname, kw)
self.event_axis_fault.emit(self._pvname, kw)
elif field == 'LVIO':
self.event_low_hard_limit.emit(self._pvname, kw)
self.event_axis_fault.emit(self._pvname, kw)
elif field == 'STAT':
self.event_axis_fault.emit(self._pvname, kw)
def update_label(self, **kwargs):
m = self.motor
self.label.setText(self._templates[self._label_style].format(rbv=m.readback))
self.jog_forward.setToolTip('jog forward at {:.3f} {}/s'.format(m.jog_speed, m.units))
self.jog_reverse.setToolTip('jog reverse at {:.3f} {}/s'.format(m.jog_speed, m.units))
self.tweak_forward.setToolTip('tweak forward by {:.3f} {}'.format(m.tweak_val, m.units))
self.tweak_reverse.setToolTip('tweak reverse by {:.3f} {}'.format(m.tweak_val, m.units))
def update_jog_speed(self, event):
m = self.motor
speed = m.jog_speed
sign = math.copysign(1, event.angleDelta().y())
m.jog_speed = speed + (sign * 0.1 * speed)
self.jog_forward.setToolTip('jog forward at {:.3f} {}/s'.format(m.jog_speed, m.units))
self.jog_reverse.setToolTip('jog reverse at {:.3f} {}/s'.format(m.jog_speed, m.units))
def tweak_event(self, event):
m = self.motor
sign = event.angleDelta().y()
if sign < 0:
m.tweak_reverse = 1
else:
m.tweak_forward = 1
def bind_wheel(self):
self.tweak_forward.wheelEvent = self.tweak_event
self.tweak_reverse.wheelEvent = self.tweak_event
def jog(self, direction, enable):
m = self.motor
if 'forw' in direction:
m.jog_forward = int(enable)
else:
m.jog_reverse = int(enable)
def contextMenuEvent(self, event):
m = self.motor
menu = QMenu(self)
menu.setTitle(self.short_name)
lockmotor = QAction('lock motor', menu, checkable=True)
lockmotor.setChecked(self._locked)
menu.addAction(lockmotor)
autolabelsAction = QAction('auto', menu, checkable=True)
autolabelsAction.setChecked(self._auto_labels)
menu.addAction(autolabelsAction)
wheel_tweaks = QAction('Mouse wheel tweaks motor', menu, checkable=True)
wheel_tweaks.setChecked(self._wheel_tweaks)
menu.addAction(wheel_tweaks)
stopmotorAction = QAction('Stopped', menu, checkable=True)
stopmotorAction.setChecked(SPMG_STOP == m.stop_go)
menu.addAction(stopmotorAction)
changeprecAction = menu.addAction("Change Precision")
changejogspeedAction = menu.addAction("Jog speed {:.3f} {}/s".format(m.jog_speed, m.units))
changetweakstepAction = menu.addAction("Tweak step {:.3f} {}".format(m.tweak_val, m.units))
tozeroAction = menu.addAction("move to Zero")
action = menu.exec_(self.mapToGlobal(event.pos()))
if action == lockmotor:
self._locked = not self._locked
if self._locked:
self._controlbox.setDisabled(True)
else:
self._controlbox.setDisabled(False)
elif action == changeprecAction:
name = m.NAME
prec = m.PREC
msg = 'Precision for motor {}'.format(name)
logger.debug('prec before %d', prec)
prec, ok = QInputDialog.getInt(self, msg, msg, prec, 0, 10)
logger.debug('prec after (%d) %d', ok, prec)
if ok:
self.motor.put('PREC', prec, wait=True)
elif action == changejogspeedAction:
name = m.NAME
speed = m.jog_speed
msg = 'Jog speed for motor {}'.format(name)
speed, ok = QInputDialog.getDouble(self, msg, msg, speed, 0.01, m.slew_speed, m.precision)
if ok:
self.motor.put('JVEL', speed, wait=True)
elif action == changetweakstepAction:
name = m.NAME
tv = m.tweak_val
msg = 'Tweak step for motor {} at {} {}'.format(name, tv, m.units)
tv, ok = QInputDialog.getDouble(self, msg, msg, tv, pow(10, -m.precision), m.slew_speed, m.precision)
if ok:
self.motor.put('TWV', tv, wait=True)
elif action == tozeroAction:
m.move(0.0, ignore_limits=True)
elif action == stopmotorAction:
self.motor.stop_go = SPMG_STOP if m.stop_go == SPMG_GO else SPMG_GO
elif action == autolabelsAction:
self._auto_labels = not self._auto_labels
elif action == wheel_tweaks:
self._wheel_tweaks = not self._wheel_tweaks
self.bind_wheel()
self.update_label_template()
def resizeEvent(self, event):
return # FIXME disable for the time being
if not self._auto_labels:
return
w, h = event.size().width(), event.size().height()
if w < 400:
self.jog_reverse.hide()
self.jog_forward.hide()
else:
self.jog_reverse.show()
self.jog_forward.show()
self.update_label()
def update_label_template(self):
m = self.motor
source = self._templates_source
target = self._templates
for k in source:
target[k] = source[k].format(
short_name=self.short_name,
precision=m.PREC,
units=m.units)
self.label.setText(target[self._label_style].format(rbv=m.readback))
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
qp.setRenderHint(QPainter.Antialiasing)
self._draw_limits(qp)
qp.end()
def _draw_limits(self, qp):
m = self.motor
width, height = self.size().width(), self.size().height()
pad = 5
rounding = 2
size = 10
if m.HLS:
x, y, w, h = width - size, pad, size, height - 2 * pad
elif m.LLS:
x, y, w, h = 0, pad, size, height - 2 * pad
else:
return
color = QColor('indianred')
qp.setBrush(QBrush(color, Qt.SolidPattern))
qp.setPen(QPen(color))
path = QPainterPath()
path.setFillRule(Qt.WindingFill)
path.addRoundedRect(x, y, w, h, rounding, rounding)
qp.drawPath(path)

172
epics_widgets/MotorTweak.ui Normal file
View File

@@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Frame</class>
<widget class="QFrame" name="Frame">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>650</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>10</height>
</size>
</property>
<property name="windowTitle">
<string>Frame</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="_controlbox" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QLineEdit" name="_drive_val">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="maxLength">
<number>10</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tweak_reverse">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>35</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>↶</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tweak_forward">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>35</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>↷</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="_tweak_val">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="jog_reverse">
<property name="text">
<string>◀</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="jog_forward">
<property name="text">
<string>▶</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,334 @@
import math
import logging
from time import sleep
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QPainter, QBrush, QColor, QPainterPath, QPen, QDoubleValidator
from PyQt5.QtWidgets import QMenu, QInputDialog, QAction
from PyQt5.uic import loadUiType
from epics import PV
from epics.ca import pend_event
from app_utils import assert_tweaker_positions
Ui_MotorTweak, QWidget = loadUiType('epics_widgets/MotorTweak.ui')
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
SPMG_STOP = 0
SPMG_PAUSE = 1
SPMG_MOVE = 2
SPMG_GO = 3
class SmaractMotorTweak(QWidget, Ui_MotorTweak):
event_val = pyqtSignal(str, dict)
event_readback = pyqtSignal(str, dict)
event_soft_limit = pyqtSignal(str, dict)
event_high_hard_limit = pyqtSignal(str, dict)
event_low_hard_limit = pyqtSignal(str, dict)
event_axis_fault = pyqtSignal(str, dict)
def __init__(self, parent=None):
super(SmaractMotorTweak, self).__init__(parent=parent)
self.setupUi(self)
self._wheel_tweaks = True
self._auto_labels = True
self._locked = False
self._label_style = 'basic'
self._templates_source = {
'basic': '<b>{short_name}</b> <font color="#080">{{rbv:.{precision}f}} {units}</font>',
'small': '<small>{short_name} <font size="small" color="#080">{{rbv:.{precision}f}} {units}</font><small>',
'2 lines': '<b>{short_name}</b><br><font size="small" color="#080">{{rbv:.{precision}f}} {units}</font>',
'busy': '<b>{short_name}</b> <font color="#080">{{rbv:.{precision}f}} {units}</font>'
}
self._templates = {}
def connect_motor(self, motor_base, short_name=None, *args, **kwargs):
self._pvname = motor_base+':DRIVE'
self._pv_name = PV(motor_base+':NAME')
self._pv_drive = PV(motor_base+':DRIVE')
self._pv_readback = PV(motor_base+':MOTRBV')
self._pv_tweak_r = PV(motor_base+':TWR.PROC')
self._pv_tweak_f = PV(motor_base+':TWF.PROC')
self._pv_tweak_val = PV(motor_base+':TWV')
self._pv_status = PV(motor_base + ':STATUS')
self._pv_home_f = PV(motor_base + ':FRM_FORW.PROC')
self._pv_home_b = PV(motor_base + ':FRM_BACK.PROC')
self._pv_is_homed = PV(motor_base + ':GET_HOMED')
self._pv_llm = PV(motor_base + ':LLM')
self._pv_hlm = PV(motor_base + ':HLM')
self.label.setToolTip('{} => {}'.format(motor_base, self._pv_name.get()))
try:
self._prec = kwargs['prec']
except:
self._prec = 5
try:
self._units = kwargs['units']
except:
self._units = 'mm'
self._ignore_limits = self._pv_llm.get() == self._pv_hlm.get() # if both high/low limits are equal they are meaningless
if not short_name:
short_name = self._pv_name.value
self.short_name = short_name
self.set_motor_validator()
self._drive_val.setText(self._pv_drive.get(as_string=True))
self._drive_val.returnPressed.connect(self.move_motor_to_position)
self.jog_forward.hide()
self.jog_reverse.hide()
tweak_min = kwargs.get("tweak_min", 0.0001)
tweak_max = kwargs.get("tweak_max", 20.0)
self._tweak_val.setText(self._pv_tweak_val.get(as_string=True))
self._tweak_val.setValidator(QDoubleValidator(tweak_min, tweak_max, 4, self._tweak_val))
self._tweak_val.editingFinished.connect(lambda m=self._pv_tweak_val: m.put(self._tweak_val.text()))
self.tweak_forward.clicked.connect(lambda m: self._pv_tweak_f.put(1))
self.tweak_reverse.clicked.connect(lambda m: self._pv_tweak_r.put(1))
self.bind_wheel()
self.update_label_template()
self._pv_readback.add_callback(self.update_label)
self._pv_status.add_callback(self.set_val_on_status_change)
# self._pv_drive.add_callback(self.set_val)
self._pv_drive.add_callback(self.emit_signals, source_field='VAL')
self._pv_readback.add_callback(self.emit_signals, source_field='RBV')
self._pv_hlm.add_callback(self.emit_signals, source_field='HLS')
self._pv_llm.add_callback(self.emit_signals, source_field='LLS')
# m.set_callback('HLM', self.set_motor_validator)
def set_motor_validator(self, **kwargs):
lineedit = self._drive_val
min, max = self._pv_llm.get(), self._pv_hlm.get()
if min == max:
min = -1e6
max = 1e6
lineedit.setValidator(QDoubleValidator(min, max, 5, lineedit))
def move_relative(self, dist, delay=None):
cur = self._pv_readback.get()
target = cur + dist
if delay:
sleep(delay)
self._pv_drive.put(target)
def get_position(self):
return self._pv_readback.get(use_monitor=False)
def is_homed(self):
pend_event()
return 1 == self._pv_is_homed.get(use_monitor=False)
def get_status(self, as_string=False):
"""
States:
0 Stopped
1 Stepping
2 Scanning
3 Holding
4 Targeting
5 Move Delay
6 Calibrating
7 Finding Ref
8 Locked
:return:
"""
return self._pv_status.get(as_string=as_string, use_monitor=False)
def wait(self):
pend_event(.01)
while not self.is_done():
pend_event(0.2)
def is_done(self):
return self._pv_status.get() in (0, 3)
def move_motor_to_position(self, drive=None, wait=False, assert_position=False):
if assert_position:
wait=True
if drive is None:
logger.debug('{} abs target from widget'.format(self.short_name))
drive = float(self._drive_val.text())
logger.debug('{} abs move => {}'.format(self.short_name, drive))
self._pv_drive.put(drive)
if wait:
self.wait()
if assert_position:
assert_tweaker_positions([(self, drive, 0.05)], timeout=3.)
def emit_signals(self, **kw):
field = kw['source_field']
if field == 'VAL':
self.event_val.emit(self._pvname, kw)
elif field == 'RBV':
self.event_readback.emit(self._pvname, kw)
elif field == 'LVIO':
self.event_soft_limit.emit(self._pvname, kw)
elif field == 'HLS':
self.event_high_hard_limit.emit(self._pvname, kw)
self.event_axis_fault.emit(self._pvname, kw)
elif field == 'LVIO':
self.event_low_hard_limit.emit(self._pvname, kw)
self.event_axis_fault.emit(self._pvname, kw)
elif field == 'STAT':
self.event_axis_fault.emit(self._pvname, kw)
def set_val_on_status_change(self, **kw):
'''
0 Stopped
1 Stepping
2 Scanning
3 Holding
4 Targeting
5 Move Delay
6 Calibrating
7 Finding Ref
8 Locked
:param kw:
:return:
'''
status = kw['char_value']
if status in ('Holding', 'Stopped'):
v = self._pv_readback.get(as_string=True)
logger.debug('updating VAL on status change to holding/stopped = {}'.format(v))
self._drive_val.setText(v)
def set_val(self, **kw):
v = kw['char_value']
logger.debug('updating VAL = {}'.format(v))
self._drive_val.setText(v)
def update_label(self, **kwargs):
self.label.setText(self._templates[self._label_style].format(rbv=self._pv_readback.get()))
self.tweak_forward.setToolTip('tweak forward by {:.3f} {}'.format(self._pv_tweak_val.get(), self._units))
self.tweak_reverse.setToolTip('tweak reverse by {:.3f} {}'.format(self._pv_tweak_val.get(), self._units))
def tweak_event(self, event):
sign = event.angleDelta().y()
if sign < 0:
self._pv_tweak_r.put(1)
else:
self._pv_tweak_f.put(1)
def bind_wheel(self):
self.tweak_forward.wheelEvent = self.tweak_event
self.tweak_reverse.wheelEvent = self.tweak_event
def contextMenuEvent(self, event):
name = self._pv_name.get()
unit = self._units
prec = self._prec
menu = QMenu(self)
menu.setTitle(self.short_name)
lockmotor = QAction('lock motor', menu, checkable=True)
lockmotor.setChecked(self._locked)
menu.addAction(lockmotor)
autolabelsAction = QAction('auto', menu, checkable=True)
autolabelsAction.setChecked(self._auto_labels)
menu.addAction(autolabelsAction)
wheel_tweaks = QAction('Mouse wheel tweaks motor', menu, checkable=True)
wheel_tweaks.setChecked(self._wheel_tweaks)
menu.addAction(wheel_tweaks)
# stopmotorAction = QAction('Stopped', menu, checkable=True)
# stopmotorAction.setChecked(SPMG_STOP == m.stop_go)
# menu.addAction(stopmotorAction)
changeprecAction = menu.addAction("Change Precision")
changetweakstepAction = menu.addAction("Tweak step {:.3f} {}".format(self._pv_tweak_val.get(), unit))
tozeroAction = menu.addAction("move to Zero")
action = menu.exec_(self.mapToGlobal(event.pos()))
if action == lockmotor:
self._locked = not self._locked
if self._locked:
self._controlbox.setDisabled(True)
else:
self._controlbox.setDisabled(False)
elif action == changeprecAction:
msg = 'Precision for motor {}'.format(name)
logger.debug('prec before %d', prec)
prec, ok = QInputDialog.getInt(self, msg, msg, prec, 0, 10)
logger.debug('prec after (%d) %d', ok, prec)
if ok:
self._prec = prec
elif action == changetweakstepAction:
tv = self._pv_tweak_val.get()
msg = 'Tweak step for motor {} at {} {}'.format(name, tv, unit)
tv, ok = QInputDialog.getDouble(self, msg, msg, tv, pow(10, -prec), 10.0, prec)
if ok:
self._pv_tweak_val.put(tv)
elif action == tozeroAction:
self._pv_drive.put(0.0)
elif action == autolabelsAction:
self._auto_labels = not self._auto_labels
elif action == wheel_tweaks:
self._wheel_tweaks = not self._wheel_tweaks
self.bind_wheel()
self.update_label_template()
def resizeEvent(self, event):
pass
def update_label_template(self):
source = self._templates_source
target = self._templates
for k in source:
target[k] = source[k].format(
short_name=self.short_name,
precision=self._prec,
units=self._units)
self.label.setText(target[self._label_style].format(rbv=self._pv_readback.get()))
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
qp.setRenderHint(QPainter.Antialiasing)
self._draw_limits(qp)
qp.end()
def _draw_limits(self, qp):
pass
# width, height = self.size().width(), self.size().height()
# pad = 5
# rounding = 2
# size = 10
# if m.HLS:
# x, y, w, h = width - size, pad, size, height - 2 * pad
# elif m.LLS:
# x, y, w, h = 0, pad, size, height - 2 * pad
# else:
# return
# color = QColor('indianred')
# qp.setBrush(QBrush(color, Qt.SolidPattern))
# qp.setPen(QPen(color))
# path = QPainterPath()
# path.setFillRule(Qt.WindingFill)
# path.addRoundedRect(x, y, w, h, rounding, rounding)
# qp.drawPath(path)

View File