import numpy as np
from qtpy.QtGui import QPalette, QColor
from qtpy.QtCore import Qt, Slot, Signal
from qtpy.QtWidgets import QWidget, QGridLayout, QHBoxLayout, \
QComboBox, QCheckBox, QLabel, QPushButton
import qtawesome as qta
from siriuspy.namesys import SiriusPVName
from siriushla.widgets import SiriusSpectrogramView, QSpinBoxPlus
[docs]
class BOTuneSpectrogram(SiriusSpectrogramView):
"""BO Tune Spectrogram View."""
new_data = Signal(np.ndarray)
buffer_curr_size = Signal(str)
buffer_data_size = Signal(int)
buffer_size_changed = Signal(int)
idx2send_changed = Signal(int)
def __init__(self, parent=None, prefix='', orientation='H',
background='w'):
"""Init."""
self.prefix = prefix
self.orientation = orientation
self.device = SiriusPVName('BO-Glob:DI-Tune-O')
self.device = self.device.substitute(
prefix=self.prefix, idx=orientation)
image_channel = self.device.substitute(
dev='TuneProc', propty='SpecArray-Mon')
xaxis_channel = self.device.substitute(propty='TuneFracArray-Mon')
yaxis_channel = self.device.substitute(propty='TimeArray-Mon')
roioffx_channel = self.device.substitute(propty='ROIOffXConv-RB')
roioffy_channel = self.device.substitute(propty='ROIOffYConv-RB')
roiwidth_channel = self.device.substitute(propty='ROIWidthConv-RB')
roiheight_channel = self.device.substitute(propty='ROIHeightConv-RB')
super().__init__(parent=parent,
image_channel=image_channel,
xaxis_channel=xaxis_channel,
yaxis_channel=yaxis_channel,
roioffsetx_channel=roioffx_channel,
roioffsety_channel=roioffy_channel,
roiwidth_channel=roiwidth_channel,
roiheight_channel=roiheight_channel,
background=background)
self.normalizeData = True
self.ROIColor = QColor('cyan')
self.format_tooltip = '{0:.3f}, {1:.3f}'
self._idx2send = 0
self.buffer = list()
self.last_data = None
self.nravgs = 1
[docs]
@Slot(np.ndarray)
def image_value_changed(self, new_image):
"""Reimplement image_value_changed slot."""
if new_image is None or new_image.size == 0:
return
spec_size = self._image_height*self._image_width
self.image_waveform = new_image[:spec_size]
self.needs_redraw = True
[docs]
def process_image(self, image):
"""Process data."""
# Flip data in X axis
image = np.flip(image, 0)
# Truncate image
if self.nravgs > 1 and len(self.buffer) >= 1:
last_buff_shape = self.buffer[-1].shape
image_shape = image.shape
aux = np.zeros(last_buff_shape)
if last_buff_shape > image_shape:
aux[:image_shape[0], :image_shape[1]] += image
elif last_buff_shape < image_shape:
aux += image[:last_buff_shape[0], :last_buff_shape[1]]
else:
aux += image
image = aux
# Manage buffer
self.buffer.append(image)
if len(self.buffer) > self.nravgs:
self.buffer.pop(0)
self.buffer_curr_size.emit(str(len(self.buffer)))
# Perform average
image = np.mean(self.buffer, axis=0)
# update last data
self.last_data = image
last_data_size = self.last_data.shape[0]-1
self.buffer_data_size.emit(last_data_size)
if not self._idx2send > last_data_size:
# Emit spectrum data
self.new_data.emit(image[self._idx2send, :])
# Return image
return image
[docs]
def toggleXChannel(self):
"""Toggle X channel between FreqArray and TuneFracArray."""
if 'TuneFracArray' in self._xaxischannel.address:
new_ch = 'FreqArray-Mon'
elif 'FreqArray' in self._xaxischannel.address:
new_ch = 'TuneFracArray-Mon'
# TODO: remove this command when bug in Tune is resolved
self.resetBuffer()
self.xAxisChannel = self.device.substitute(propty=new_ch)
[docs]
def setBufferSize(self, new_size):
"""Set number of averages, or, buffer size."""
if new_size >= 1:
self.nravgs = new_size
while len(self.buffer) > self.nravgs:
self.buffer.pop(0)
self.buffer_size_changed.emit(self.nravgs)
[docs]
def resetBuffer(self):
"""Reset buffer."""
self.buffer = list()
[docs]
def getDataIndex(self):
"""Return index of the spectrogram to send in new_data signal."""
return self._idx2send
[docs]
def setIndex2Send(self, new_idx):
"""Set index of the spectrogram to send in new_data signal."""
max_idx = self.buffer[-1].shape[0] - 1
if new_idx > max_idx:
self._idx2send = max_idx
else:
self._idx2send = new_idx
self.new_data.emit(self.last_data[self._idx2send, :])
[docs]
def mouseDoubleClickEvent(self, ev):
if ev.button() == Qt.LeftButton:
pos = self._image_item.mapFromDevice(ev.pos())
if not self._image_item.height():
pass
elif pos.y() > 0 and pos.y() <= self._image_item.height() and\
pos.x() > 0 and pos.x() <= self._image_item.width():
self._idx2send = int(pos.y())
self.idx2send_changed.emit(self._idx2send)
if self.last_data is not None:
self.new_data.emit(self.last_data[self._idx2send, :])
super().mouseDoubleClickEvent(ev)
[docs]
class BOTuneSpectrogramControls(QWidget):
"""Booster Tune Spectrogram Controls."""
def __init__(self, parent=None, prefix='', orientation='H',
title='', background='w'):
"""Init."""
super().__init__(parent)
self.prefix = prefix
self.orientation = orientation
self.title = title
self.background = background
self._setupUi()
def _setupUi(self):
self.lb_title = QLabel(self.title, self, alignment=Qt.AlignCenter)
self.spectrogram = BOTuneSpectrogram(
self, self.prefix, self.orientation)
self.cb_show_roi = QCheckBox('Show ROI', self)
self.cb_show_roi.stateChanged.connect(self.spectrogram.showROI)
self.cb_show_roi.setChecked(True)
self.sb_idx2plot = QSpinBoxPlus(self)
self.sb_idx2plot.editingFinished.connect(self.update_idx2plot)
self.lb_idx2plot = QLabel('0')
self.spectrogram.idx2send_changed.connect(self.update_idx2plot)
self.spectrogram.buffer_data_size.connect(self.sb_idx2plot.setMaximum)
self.sb_buffsz = QSpinBoxPlus(self)
self.sb_buffsz.setValue(1)
self.sb_buffsz.setMinimum(1)
self.sb_buffsz.setMaximum(100)
self.sb_buffsz.editingFinished.connect(self.update_buffsize)
self.lb_buffsz = QLabel('1', self)
self.lb_buffsz.setStyleSheet('min-width:1.29em;max-width:1.29em;')
self.spectrogram.buffer_curr_size.connect(self.lb_buffsz.setText)
self.pb_resetbuff = QPushButton(
qta.icon('mdi.delete-empty'), '', self)
self.pb_resetbuff.setToolTip('Reset buffer')
self.pb_resetbuff.setObjectName('resetbuff')
self.pb_resetbuff.setStyleSheet(
"#resetbuff{min-width:25px; max-width:25px; icon-size:20px;}")
self.pb_resetbuff.clicked.connect(self.spectrogram.resetBuffer)
self.cb_choose_x = QComboBox(self)
self.cb_choose_x.addItem('Tune Frac.')
self.cb_choose_x.addItem('Frequency')
self.cb_choose_x.currentIndexChanged.connect(
self.spectrogram.toggleXChannel)
hbox_ctrls = QHBoxLayout()
hbox_ctrls.setContentsMargins(0, 0, 0, 0)
hbox_ctrls.setSpacing(6)
hbox_ctrls.addWidget(self.cb_show_roi)
hbox_ctrls.addStretch()
hbox_ctrls.addWidget(QLabel('Plot Index:'), alignment=Qt.AlignLeft)
hbox_ctrls.addWidget(self.sb_idx2plot, alignment=Qt.AlignLeft)
hbox_ctrls.addWidget(self.lb_idx2plot, alignment=Qt.AlignLeft)
hbox_ctrls.addStretch()
hbox_ctrls.addWidget(QLabel('Buff.:'), alignment=Qt.AlignLeft)
hbox_ctrls.addWidget(self.sb_buffsz, alignment=Qt.AlignLeft)
hbox_ctrls.addWidget(self.lb_buffsz, alignment=Qt.AlignLeft)
hbox_ctrls.addWidget(self.pb_resetbuff, alignment=Qt.AlignLeft)
hbox_ctrls.addStretch()
hbox_ctrls.addWidget(QLabel('X Axis:'), alignment=Qt.AlignRight)
hbox_ctrls.addWidget(self.cb_choose_x, alignment=Qt.AlignRight)
pal = self.palette()
pal.setColor(QPalette.Background, self.background)
self.setAutoFillBackground(True)
self.setPalette(pal)
lay = QGridLayout(self)
lay.setHorizontalSpacing(9)
lay.setVerticalSpacing(6)
lay.setContentsMargins(6, 6, 6, 6)
lay.addWidget(self.lb_title, 0, 0, 1, 2)
lay.addWidget(self.spectrogram, 1, 0)
lay.addLayout(hbox_ctrls, 2, 0, 1, 2)
[docs]
def update_idx2plot(self, value=None):
if value is None:
value = self.sender().value()
self.sb_idx2plot.blockSignals(True)
self.sb_idx2plot.setValue(value)
self.lb_idx2plot.setText(str(value))
self.spectrogram.setIndex2Send(value)
self.sb_idx2plot.blockSignals(False)
[docs]
def update_buffsize(self):
value = self.sender().value()
self.sb_buffsz.blockSignals(True)
self.sb_buffsz.setValue(value)
self.spectrogram.setBufferSize(value)
self.sb_buffsz.blockSignals(False)