Source code for siriushla.common.cam_basler.base

"""SiriusScrnView widget."""

import time
from copy import deepcopy as _dcopy
import numpy as np
from qtpy.QtWidgets import QHBoxLayout, QSizePolicy as QSzPlcy, QVBoxLayout, \
    QToolTip
from qtpy.QtCore import Qt, Slot, Signal, Property
from pydm.widgets import PyDMImageView, PyDMPushButton, PyDMEnumComboBox, \
    PyDMLineEdit
from pydm.widgets.channel import PyDMChannel

from siriushla.widgets import PyDMStateButton, SiriusLedState, SiriusLabel, \
    SiriusSpinbox


[docs] class SiriusImageView(PyDMImageView): """A PyDMImageView with methods to handle screens calibration grids.""" receivedData = Signal() failToSaveGrid = Signal() def __init__(self, parent=None, image_channel=None, width_channel=None, offsetx_channel=None, offsety_channel=None, maxwidth_channel=None, maxheight_channel=None): """Initialize the object.""" super().__init__( parent=parent, image_channel=image_channel, width_channel=width_channel) self._channels.extend(4*[None, ]) self._calibration_grid_image = None self._calibration_grid_maxdata = None self._calibration_grid_width = None self._calibration_grid_filterfactor = 0.25 self._calibration_grid_removeborder = 150 self._image_roi_offsetx = 0 self._offsetxchannel = None self._image_roi_offsety = 0 self._offsetychannel = None self._image_maxwidth = 0 self._maxwidthchannel = None self._image_maxheight = 0 self._maxheightchannel = None self._show_calibration_grid = False # Set live channels if requested on initialization if offsetx_channel: self.ROIOffsetXChannel = offsetx_channel if offsety_channel: self.ROIOffsetYChannel = offsety_channel if maxwidth_channel: self.maxWidthChannel = maxwidth_channel if maxheight_channel: self.maxHeightChannel = maxheight_channel self.colorMap = self.Jet # connect sigMouseMoved self.scene.sigMouseMoved.connect(self._handle_mouse_moved)
[docs] @Slot() def saveCalibrationGrid(self): """Save current image as calibration_grid_image.""" for i in range(40): if self.image_waveform.size == ( self.image_maxwidth*self.image_maxheight): img = self.image_waveform.copy() self._calibration_grid_orig = img self._calibration_grid_width = self.imageWidth self._calibration_grid_maxdata = img.max() self._update_calibration_grid_image() break time.sleep(0.05) else: self.failToSaveGrid.emit()
def _update_calibration_grid_image(self): img = _dcopy(self._calibration_grid_orig) maxdata = self._calibration_grid_maxdata border = self._calibration_grid_removeborder if self.readingOrder == self.ReadingOrder.Clike: roi = img.reshape((-1, self._calibration_grid_width), order='C') else: roi = img.reshape((self._calibration_grid_width, -1), order='F') if border > 0: roi[:, 0:border] = np.full((roi.shape[0], border), maxdata) roi[:, -border:] = np.full((roi.shape[0], border), maxdata) roi[0:border, :] = np.full((border, roi.shape[1]), maxdata) roi[-border:, :] = np.full((border, roi.shape[1]), maxdata) self._calibration_grid_image = np.where( roi < self._calibration_grid_filterfactor*maxdata, True, False)
[docs] @Slot(bool) def showCalibrationGrid(self, show): """Show calibration_grid_image over the current image_waveform.""" self._show_calibration_grid = show self.needs_redraw = True
@property def calibrationGrid(self): """Return Numpy array containing original grid.""" return _dcopy(self._calibration_grid_orig) @calibrationGrid.setter def calibrationGrid(self, data): """Receive a list 'data' which elements are: [width, new_grid:].""" self._calibration_grid_orig = data[1:] self._calibration_grid_width = int(data[0]) self._calibration_grid_maxdata = data[1:].max() self._update_calibration_grid_image() self.needs_redraw = True @property def calibration_grid_filterfactor(self): """Factor used to filter calibration grid. Pixels with values smaller than filterfactor*img_maxdata are set to zero. Returns ------- float Calibration Grid Filter Factor """ return self._calibration_grid_filterfactor*100 @property def calibration_grid_removeborder(self): """Factor used to remove border of the calibration grid. Returns ------- float Number of Rows/Columns to be removed """ return self._calibration_grid_removeborder
[docs] def set_calibration_grid_filterfactor(self, value): """Set factor used to filter calibration grid. Pixels with values smaller than filterfactor*img_maxdata are set to zero. Parameters ---------- value: int Calibration Grid Filter Factor """ value /= 100 if value >= 0 and self._calibration_grid_filterfactor != value: self._calibration_grid_filterfactor = value if self._calibration_grid_image is not None: self._update_calibration_grid_image() self.needs_redraw = True
[docs] def set_calibration_grid_border2remove(self, value): """Set factor used to remove border of the calibration grid. Parameters ---------- value: int Number of Rows/Columns to be removed """ self._calibration_grid_removeborder = value if self._calibration_grid_image is not None: self._update_calibration_grid_image() self.needs_redraw = True
[docs] def process_image(self, image): """Reimplement process_image method to add grid to image.""" self.receivedData.emit() image2process = _dcopy(image) if ((self._show_calibration_grid) and (self._calibration_grid_image is not None)): try: grid = self._adjust_calibration_grid(image2process) image2process[grid] = self._calibration_grid_maxdata except Exception: print('Grid dimentions do not match image dimentions!') return image2process
def _adjust_calibration_grid(self, img): height = np.size(img, 0) width = np.size(img, 1) grid = self._calibration_grid_image[ self._image_roi_offsety:(self._image_roi_offsety+height), self._image_roi_offsetx:(self._image_roi_offsetx+width)] return grid
[docs] def roioffsetx_connection_state_changed(self, conn): """ Run when the ROIOffsetX Channel connection state changes. Parameters ---------- conn : bool The new connection state. """ if not conn: self._image_roi_offsetx = 0
[docs] def roioffsety_connection_state_changed(self, conn): """ Run when the ROIOffsetY Channel connection state changes. Parameters ---------- conn : bool The new connection state. """ if not conn: self._image_roi_offsety = 0
[docs] def image_roioffsetx_changed(self, new_offset): """ Run when the ROIOffsetX Channel value changes. Parameters ---------- new_offsetx : int The new image ROI horizontal offset """ if new_offset is None: return self._image_roi_offsetx = new_offset
[docs] def image_roioffsety_changed(self, new_offset): """ Run when the ROIOffsetY Channel value changes. Parameters ---------- new_offsety : int The new image ROI vertical offset """ if new_offset is None: return self._image_roi_offsety = new_offset
@property def image_maxwidth(self): """Return image maximum width.""" return self._image_maxwidth
[docs] def image_maxwidth_changed(self, new_max): """ Run when the maxWidth Channel value changes. Parameters ---------- new_max : int The new image max width """ if new_max is None: return self._image_maxwidth = int(new_max)
@property def image_maxheight(self): """Return image maximum height.""" return self._image_maxheight
[docs] def image_maxheight_changed(self, new_max): """ Run when the maxHeight Channel value changes. Parameters ---------- new_max : int The new image max height """ if new_max is None: return self._image_maxheight = int(new_max)
@Property(str) def ROIOffsetXChannel(self): """ Return the channel address in use for the image ROI horizontal offset. Returns ------- str Channel address """ if self._offsetxchannel: return str(self._offsetxchannel.address) else: return '' @ROIOffsetXChannel.setter def ROIOffsetXChannel(self, value): """ Return the channel address in use for the image ROI horizontal offset. Parameters ---------- value : str Channel address """ if self._offsetxchannel != value: # Disconnect old channel if self._offsetxchannel: self._offsetxchannel.disconnect() # Create and connect new channel self._offsetxchannel = PyDMChannel( address=value, connection_slot=self.roioffsetx_connection_state_changed, value_slot=self.image_roioffsetx_changed, severity_slot=self.alarmSeverityChanged) self._channels[2] = self._offsetxchannel self._offsetxchannel.connect() @Property(str) def ROIOffsetYChannel(self): """ Return the channel address in use for the image ROI vertical offset. Returns ------- str Channel address """ if self._offsetychannel: return str(self._offsetychannel.address) else: return '' @ROIOffsetYChannel.setter def ROIOffsetYChannel(self, value): """ Return the channel address in use for the image ROI vertical offset. Parameters ---------- value : str Channel address """ if self._offsetychannel != value: # Disconnect old channel if self._offsetychannel: self._offsetychannel.disconnect() # Create and connect new channel self._offsetychannel = PyDMChannel( address=value, connection_slot=self.roioffsety_connection_state_changed, value_slot=self.image_roioffsety_changed, severity_slot=self.alarmSeverityChanged) self._channels[3] = self._offsetychannel self._offsetychannel.connect() @Property(str) def maxWidthChannel(self): """ Return the channel address in use for the image maximum width. Returns ------- str Channel address """ if self._maxwidthchannel: return str(self._maxwidthchannel.address) else: return '' @maxWidthChannel.setter def maxWidthChannel(self, value): """ Return the channel address in use for the image maximum width. Parameters ---------- value : str Channel address """ if self._maxwidthchannel != value: # Disconnect old channel if self._maxwidthchannel: self._maxwidthchannel.disconnect() # Create and connect new channel self._maxwidthchannel = PyDMChannel( address=value, value_slot=self.image_maxwidth_changed, severity_slot=self.alarmSeverityChanged) self._channels[4] = self._maxwidthchannel self._maxwidthchannel.connect() @Property(str) def maxHeightChannel(self): """ Return the channel address in use for the image maximum height. Returns ------- str Channel address """ if self._maxheightchannel: return str(self._maxheightchannel.address) else: return '' @maxHeightChannel.setter def maxHeightChannel(self, value): """ Return the channel address in use for the image maximum height. Parameters ---------- value : str Channel address """ if self._maxheightchannel != value: # Disconnect old channel if self._maxheightchannel: self._maxheightchannel.disconnect() # Create and connect new channel self._maxheightchannel = PyDMChannel( address=value, value_slot=self.image_maxheight_changed, severity_slot=self.alarmSeverityChanged) self._channels[5] = self._maxheightchannel self._maxheightchannel.connect() def _handle_mouse_moved(self, pos): pos_scene = self.view.vb.mapSceneToView(pos) txt = '{0}, {1}'.format(round(pos_scene.x()), round(pos_scene.y())) QToolTip.showText( self.mapToGlobal(pos.toPoint()), txt, self, self.geometry(), 5000)
[docs] def create_propty_layout(parent, prefix, propty, propty_type='', cmd=dict(), layout='hbox', width=7.10, height=1.29, use_linedit=False): """Return a layout that handles a property according to 'propty_type'.""" if layout == 'hbox': layout = QHBoxLayout() elif layout == 'vbox': layout = QVBoxLayout() if propty_type == 'sprb': if use_linedit: sp = PyDMLineEdit(parent, prefix.substitute( propty_name=propty, propty_suffix='SP')) setattr(parent, 'le_'+propty, sp) else: sp = SiriusSpinbox(parent, prefix.substitute( propty_name=propty, propty_suffix='SP')) setattr(parent, 'sb_'+propty, sp) sp.setStyleSheet(""" min-width:wvalem; max-width:wvalem; min-height:hvalem; max-height:hvalem;""".replace('wval', str(width)).replace( 'hval', str(height))) sp.setAlignment(Qt.AlignCenter) layout.addWidget(sp) label = SiriusLabel(parent, prefix.substitute( propty_name=propty, propty_suffix='RB')) setattr(parent, 'lb_'+propty, label) if not cmd: label.setStyleSheet(""" min-width:wvalem; max-width:wvalem; min-height:hvalem; max-height:hvalem;""".replace('wval', str(width)).replace( 'hval', str(height))) label.setAlignment(Qt.AlignCenter) layout.addWidget(label) elif propty_type == 'enbldisabl': statebutton = PyDMStateButton(parent, prefix.substitute( propty_name=propty, propty_suffix='Sel')) setattr(parent, 'bt_'+propty, statebutton) statebutton.setStyleSheet(""" min-width:wvalem; max-width:wvalem; min-height:hvalem; max-height:hvalem;""".replace('wval', str(width)).replace( 'hval', str(height))) statebutton.shape = 1 layout.addWidget(statebutton) led = SiriusLedState(parent, prefix.substitute( propty_name=propty, propty_suffix='Sts')) setattr(parent, 'led_'+propty, led) led.setStyleSheet( "min-width:1.29em; max-width:1.29em; " "min-height:1.29em; max-height:1.29em;") led.setSizePolicy(QSzPlcy.Minimum, QSzPlcy.Maximum) layout.addWidget(led) elif propty_type == 'offon': statebutton = PyDMStateButton(parent, prefix.substitute( propty_name=propty, propty_suffix='Sel')) setattr(parent, 'bt_'+propty, statebutton) statebutton.setStyleSheet(""" min-width:wvalem; max-width:wvalem; min-height:hvalem; max-height:hvalem;""".replace('wval', str(width)).replace( 'hval', str(height))) statebutton.shape = 1 layout.addWidget(statebutton) label = SiriusLabel(parent, prefix.substitute( propty_name=propty, propty_suffix='Sts')) setattr(parent, 'lb_'+propty, label) if not cmd: label.setStyleSheet(""" min-width:wvalem; max-width:wvalem; min-height:hvalem; max-height:hvalem;""".replace('wval', str(width)).replace( 'hval', str(height))) label.setAlignment(Qt.AlignCenter) layout.addWidget(label) elif propty_type == 'enum': combobox = PyDMEnumComboBox(parent, prefix.substitute( propty_name=propty, propty_suffix='Sel')) setattr(parent, 'cb_'+propty, combobox) combobox.setStyleSheet(""" min-width:wvalem; max-width:wvalem; min-height:hvalem; max-height:hvalem;""".replace('wval', str(width)).replace( 'hval', str(height))) layout.addWidget(combobox) label = SiriusLabel(parent, prefix.substitute( propty_name=propty, propty_suffix='Sts')) setattr(parent, 'lb_'+propty, label) if not cmd: label.setStyleSheet(""" min-width:wvalem; max-width:wvalem; min-height:hvalem; max-height:hvalem;""".replace('wval', str(width)).replace( 'hval', str(height))) label.setAlignment(Qt.AlignCenter) layout.addWidget(label) elif propty_type == 'mon': label = SiriusLabel(parent, prefix.substitute( propty_name=propty, propty_suffix='Mon')) setattr(parent, 'lb_'+propty, label) label.setStyleSheet(""" min-width:wvalem; max-width:wvalem; min-height:hvalem; max-height:hvalem;""".replace('wval', str(width)).replace( 'hval', str(height))) label.setAlignment(Qt.AlignCenter) layout.addWidget(label) elif propty_type == 'cte': label = SiriusLabel(parent, prefix.substitute( propty_name=propty, propty_suffix='Cte')) setattr(parent, 'lb_'+propty, label) label.setStyleSheet(""" min-width:wvalem; max-width:wvalem; min-height:hvalem; max-height:hvalem;""".replace('wval', str(width)).replace( 'hval', str(height))) label.setAlignment(Qt.AlignCenter) layout.addWidget(label) if cmd: pb = PyDMPushButton( parent=parent, label=cmd['label'], pressValue=cmd['pressValue']) setattr(parent, 'pb_'+propty, pb) pb.setObjectName('pb_'+propty) if 'icon' in cmd.keys(): pb.setIcon(cmd['icon']) if 'toolTip' in cmd.keys(): pb.setToolTip(cmd['toolTip']) pb.channel = prefix.substitute( propty_name=cmd['name'], propty_suffix='Cmd') _w = cmd['width']+'px' if 'width' in cmd.keys() else str(width)+'em' _h = cmd['height']+'px' if 'height' in cmd.keys() else str(height)+'em' stylesheet = """ #pb_""" + propty + """{ min-width:wval; max-width:wval; min-height:hval; max-height:hval; iconsize; }""".replace('wval', _w).replace('hval', _h) _is = 'icon-size:'+cmd['icon-size']+'px;' \ if 'icon-size' in cmd.keys() else '' stylesheet.replace('iconsize;', _is) pb.setStyleSheet(stylesheet) layout.addWidget(pb) layout.setAlignment(Qt.AlignVCenter) return layout