Source code for pymodaq_gui.managers.roi_manager


import os
import sys
from typing import List, TYPE_CHECKING, Tuple, Union

from qtpy import QtCore, QtGui, QtWidgets
from qtpy.QtCore import QObject, Slot, Signal, QPointF
from qtpy.QtGui import QIcon, QPixmap
from collections import OrderedDict


from pymodaq_gui.parameter import utils as putils
from pymodaq_gui.parameter import ParameterTree, Parameter, ioxml, pymodaq_ptypes
from pyqtgraph.parametertree.parameterTypes.basetypes import GroupParameter

from pymodaq_gui.managers.action_manager import QAction

from pyqtgraph import ROI as pgROI

from pyqtgraph import functions as fn
from pyqtgraph import LinearRegionItem as pgLinearROI
from pymodaq_utils.utils import plot_colors
from pymodaq_utils.logger import get_module_name, set_logger
from pymodaq_gui.config import get_set_roi_path
from pymodaq_gui.utils import select_file
from pymodaq_gui.plotting.utils import plot_utils

import numpy as np
from pathlib import Path
from pymodaq_data.post_treatment.process_to_scalar import DataProcessorFactory

data_processors = DataProcessorFactory()

roi_path = get_set_roi_path()
logger = set_logger(get_module_name(__file__))


[docs]class ROIPositionMapper(QtWidgets.QWidget): """ Widget presenting a Tree structure representing a ROI positions. """ def __init__(self, roi_pos, roi_size): super().__init__() self.roi_pos = roi_pos self.roi_size = roi_size
[docs] def show_dialog(self): self.params = [ {'name': 'position', 'type': 'group', 'children': [ {'name': 'x0', 'type': 'float', 'value': self.roi_pos[0] + self.roi_size[0] / 2, 'step': 1}, {'name': 'y0', 'type': 'float', 'value': self.roi_pos[1] + self.roi_size[1] / 2, 'step': 1} ]}, {'name': 'size', 'type': 'group', 'children': [ {'name': 'width', 'type': 'float', 'value': self.roi_size[0], 'step': 1}, {'name': 'height', 'type': 'float', 'value': self.roi_size[1], 'step': 1}] }] dialog = QtWidgets.QDialog(self) vlayout = QtWidgets.QVBoxLayout() self.settings_tree = ParameterTree() vlayout.addWidget(self.settings_tree, 10) self.settings_tree.setMinimumWidth(300) self.settings = Parameter.create(name='settings', type='group', children=self.params) self.settings_tree.setParameters(self.settings, showTop=False) dialog.setLayout(vlayout) buttonBox = QtWidgets.QDialogButtonBox(parent=self) buttonBox.addButton('Apply', buttonBox.AcceptRole) buttonBox.accepted.connect(dialog.accept) buttonBox.addButton('Cancel', buttonBox.RejectRole) buttonBox.rejected.connect(dialog.reject) vlayout.addWidget(buttonBox) self.setWindowTitle('Set Precise positions for the ROI') res = dialog.exec() if res == dialog.Accepted: return self.settings else: return None
[docs]class ROI(pgROI): index_signal = Signal(int) def __init__(self, *args, index=0, name='roi', **kwargs): super().__init__(*args, **kwargs) self.name = name self.index = index self._menu = QtWidgets.QMenu() self._menu.addAction('Set ROI positions', self.set_positions) self._menu.addAction('Copy ROI to clipboard', self.copy_clipboard) self.sigRegionChangeFinished.connect(self.emit_index_signal) self._clipboard = QtGui.QGuiApplication.clipboard()
[docs] def emit_index_signal(self): self.index_signal.emit(self.index)
@property def color(self): return self.pen.color()
[docs] def center(self) -> QPointF: """ Get the center position of the ROI """ return QPointF(self.pos().x() + self.size().x() / 2, self.pos().y() + self.size().y() / 2)
[docs] def set_center(self, center: Union[QPointF, Tuple[float, float]]): size = self.size() self.setPos(np.array(center) - np.array(size) / 2)
[docs] def set_positions(self): mapper = ROIPositionMapper(self.pos(), self.size()) settings = mapper.show_dialog() if settings is not None: self.setSize((settings['size', 'width'], settings['size', 'height'])) self.setPos((settings['position', 'x0'] - settings['size', 'width'] / 2, settings['position', 'y0'] - settings['size', 'height'] / 2))
[docs] def copy_clipboard(self): info = plot_utils.RoiInfo.info_from_rect_roi(self) self._clipboard.setText(str(info.to_slices()))
[docs] def contextMenuEvent(self, event): if self._menu is not None: self._menu.exec(event.screenPos())
[docs] def width(self) -> float: return self.size().x()
[docs] def height(self) -> float: return self.size().y()
[docs]class ROIBrushable(ROI): def __init__(self, brush=None, *args, **kwargs): super().__init__(*args, **kwargs) if brush is None: brush = QtGui.QBrush(QtGui.QColor(0, 0, 255, 50)) self.setBrush(brush)
[docs] def setBrush(self, *br, **kargs): """Set the brush that fills the region. Can have any arguments that are valid for :func:`mkBrush <pyqtgraph.mkBrush>`. """ self.brush = fn.mkBrush(*br, **kargs) self.currentBrush = self.brush
[docs] def paint(self, p, opt, widget): # p.save() # Note: don't use self.boundingRect here, because subclasses may need to redefine it. r = QtCore.QRectF(0, 0, self.state['size'][0], self.state['size'][1]).normalized() p.setRenderHint(QtGui.QPainter.Antialiasing) p.setPen(self.currentPen) p.setBrush(self.currentBrush) p.translate(r.left(), r.top()) p.scale(r.width(), r.height()) p.drawRect(0, 0, 1, 1)
# p.restore()
[docs]class LinearROI(pgLinearROI): index_signal = Signal(int) def __init__(self, index=0, pos=[0, 10], name = 'roi', **kwargs): super().__init__(values=pos, **kwargs) self.name = name self.index = index self.sigRegionChangeFinished.connect(self.emit_index_signal) self._menu = QtWidgets.QMenu() self._menu.addAction('Copy ROI to clipboard', self.copy_clipboard) self.sigRegionChangeFinished.connect(self.emit_index_signal) self._clipboard = QtGui.QGuiApplication.clipboard()
[docs] def copy_clipboard(self): info = plot_utils.RoiInfo.info_from_linear_roi(self) self._clipboard.setText(str(info.to_slices()))
[docs] def contextMenuEvent(self, event): if self._menu is not None: self._menu.exec(event.screenPos())
[docs] def pos(self) -> Tuple[float, float]: return self.getRegion()
[docs] def center(self) -> float: pos = self.pos() return (pos[0] + pos[1]) / 2
[docs] def setPos(self, pos: Tuple[int, int]): self.setRegion(pos)
[docs] def setPen(self, color): self.setBrush(color)
@property def color(self): return self.brush.color()
[docs] def emit_index_signal(self): self.index_signal.emit(self.index)
[docs]class EllipseROI(ROI): """ Elliptical ROI subclass with one scale handle and one rotation handle. ============== ============================================================= **Arguments** pos (length-2 sequence) The position of the ROI's origin. size (length-2 sequence) The size of the ROI's bounding rectangle. **args All extra keyword arguments are passed to ROI() ============== ============================================================= """ def __init__(self, index=0, pos=[0, 0], size=[10, 10], **kwargs): # QtGui.QGraphicsRectItem.__init__(self, 0, 0, size[0], size[1]) super().__init__(pos=pos, size=size, index=index, **kwargs) self.addRotateHandle([1.0, 0.5], [0.5, 0.5]) self.addScaleHandle([0.5 * 2. ** -0.5 + 0.5, 0.5 * 2. ** -0.5 + 0.5], [0.5, 0.5])
[docs] def getArrayRegion(self, arr, img=None, axes=(0, 1), **kwds): """ Return the result of ROI.getArrayRegion() masked by the elliptical shape of the ROI. Regions outside the ellipse are set to 0. """ # Note: we could use the same method as used by PolyLineROI, but this # implementation produces a nicer mask. if kwds["returnMappedCoords"]: arr, coords = pgROI.getArrayRegion(self, arr, img, axes, **kwds) else: arr = pgROI.getArrayRegion(self, arr, img, axes, **kwds) if arr is None or arr.shape[axes[0]] == 0 or arr.shape[axes[1]] == 0: return arr w = arr.shape[axes[0]] h = arr.shape[axes[1]] # generate an ellipsoidal mask mask = np.fromfunction( lambda x, y: (((x + 0.5) / (w / 2.) - 1) ** 2 + ((y + 0.5) / (h / 2.) - 1) ** 2) ** 0.5 < 1, (w, h)) # reshape to match array axes if axes[0] > axes[1]: mask = mask.T shape = [(n if i in axes else 1) for i, n in enumerate(arr.shape)] mask = mask.reshape(shape) if kwds["returnMappedCoords"]: return arr * mask, coords else: return arr * mask
[docs] def paint(self, p, opt, widget): r = self.boundingRect() p.setRenderHint(QtGui.QPainter.Antialiasing) p.setPen(self.currentPen) p.scale(r.width(), r.height()) # workaround for GL bug r = QtCore.QRectF(r.x() / r.width(), r.y() / r.height(), 1, 1) p.drawEllipse(r)
[docs] def shape(self): self.path = QtGui.QPainterPath() self.path.addEllipse(self.boundingRect()) return self.path
[docs]class CircularROI(EllipseROI): def __init__(self, index=0, pos=[0, 0], size=[10, 10], **kwargs): ROI.__init__(self, pos=pos, size=size, index=index, **kwargs) self.addScaleHandle([0.5 * 2. ** -0.5 + 0.5, 0.5 * 2. ** -0.5 + 0.5], [0.5, 0.5], lockAspect=True)
[docs]class SimpleRectROI(ROI): r""" Rectangular ROI subclass with a single scale handle at the top-right corner. """ def __init__(self, pos=[0, 0], size=[10, 10], centered=False, sideScalers=False, **args): super().__init__(pos, size, **args) if centered: center = [0.5, 0.5] else: center = [0, 0] self.addScaleHandle([1, 1], center) if sideScalers: self.addScaleHandle([1, 0.5], [center[0], 0.5]) self.addScaleHandle([0.5, 1], [0.5, center[1]])
[docs]class RectROI(ROI): def __init__(self, index=0, pos=[0, 0], size=[10, 10], **kwargs): super().__init__(pos=pos, size=size, index=index, **kwargs) # , scaleSnap=True, translateSnap=True) self.addScaleHandle([1, 1], [0, 0]) self.addRotateHandle([0, 0], [0.5, 0.5])
ROI_NAME_PREFIX = 'ROI_' ROI2D_TYPES = ['RectROI', 'EllipseROI', 'CircularROI']
[docs]class ROIScalableGroup(GroupParameter): def __init__(self, roi_type='1D', **opts): opts['type'] = 'group' opts['addText'] = "Add" self.roi_type = roi_type if roi_type != '1D': opts['addList'] = ROI2D_TYPES self.color_list = ROIManager.color_list super().__init__(**opts)
[docs] def addNew(self, typ=''): name_prefix = ROI_NAME_PREFIX child_indexes = [int(par.name()[len(name_prefix) + 1:]) for par in self.children()] if not child_indexes: newindex = 0 else: newindex = max(child_indexes) + 1 child = {'name': ROIManager.roi_format(newindex), 'type': 'group', 'removable': True, 'renamable': False} children = [{'name': 'type', 'type': 'str', 'value': self.roi_type, 'readonly': True, 'visible': False}, ] if self.roi_type == '2D': children.extend([{'title': 'ROI Type', 'name': 'roi_type', 'type': 'str', 'value': typ, 'readonly': True}, {'title': 'Use channel', 'name': 'use_channel', 'type': 'list', 'limits': ['red', 'green', 'blue']}, ]) children.append({'title': 'Math type:', 'name': 'math_function', 'type': 'list', 'limits': data_processors.functions_filtered('Data2D')}) else: children.append({'title': 'Use channel', 'name': 'use_channel', 'type': 'list'}) children.append({'title': 'Math type:', 'name': 'math_function', 'type': 'list', 'limits': data_processors.functions_filtered('Data1D')}) children.extend([ {'name': 'Color', 'type': 'color', 'value': list(np.roll(self.color_list, newindex)[0])}, ]) if self.roi_type == '2D': children.extend([{'name': 'position', 'type': 'group', 'children': [ {'name': 'x', 'type': 'float', 'value': 0, 'step': 1}, {'name': 'y', 'type': 'float', 'value': 0, 'step': 1} ]}, ]) else: children.extend([{'name': 'position', 'type': 'group', 'children': [ {'name': 'left', 'type': 'float', 'value': 0, 'step': 1}, {'name': 'right', 'type': 'float', 'value': 10, 'step': 1} ]}, ]) if self.roi_type == '2D': children.extend([ {'name': 'size', 'type': 'group', 'children': [ {'name': 'width', 'type': 'float', 'value': 10, 'step': 1}, {'name': 'height', 'type': 'float', 'value': 10, 'step': 1} ]}, {'name': 'angle', 'type': 'float', 'value': 0, 'step': 1}]) child['children'] = children self.addChild(child)
[docs]class ROIManager(QObject): new_ROI_signal = Signal(int, str, str) remove_ROI_signal = Signal(str) roi_value_changed = Signal(str, tuple) color_signal = Signal(list) roi_update_children = Signal(list) roi_changed = Signal() color_list = np.array(plot_colors) def __init__(self, viewer_widget=None, ROI_type='1D'): super().__init__() self.ROI_type = ROI_type self.roiwidget = QtWidgets.QWidget() self.viewer_widget = viewer_widget # either a PlotWidget or a ImageWidget self._ROIs: OrderedDict[str, ROI] = OrderedDict([]) self.setupUI()
[docs] @staticmethod def roi_format(index): return f'{ROI_NAME_PREFIX}{index:02d}'
@property def ROIs(self): return self._ROIs def __len__(self): return len(self._ROIs)
[docs] def get_roi_from_index(self, index: int) -> ROI: return self.ROIs[self.roi_format(index)]
def _set_roi_from_index(self, index: int, roi): self.ROIs[self.roi_format(index)] = roi
[docs] def get_roi(self, roi_key): if roi_key in self.ROIs: return self.ROIs[roi_key] else: raise KeyError(f'{roi_key} is not a valid ROI identifier for {self.ROIs}')
[docs] def emit_colors(self): self.color_signal.emit([self._ROIs[roi_key].color for roi_key in self._ROIs])
[docs] def add_roi_programmatically(self, roitype=ROI2D_TYPES[0]): self.settings.child('ROIs').addNew(roitype)
[docs] def remove_roi_programmatically(self, index: int): self.settings.child('ROIs').removeChild(self.settings.child('ROIs', self.roi_format(index)))
[docs] def setupUI(self): vlayout = QtWidgets.QVBoxLayout() self.roiwidget.setLayout(vlayout) self.toolbar = QtWidgets.QToolBar() vlayout.addWidget(self.toolbar) self.save_ROI_pb = QAction(QIcon(QPixmap(":/icons/Icon_Library/save_ROI.png")), 'Save ROIs') self.load_ROI_pb = QAction(QIcon(QPixmap(":/icons/Icon_Library/load_ROI.png")), 'Load ROIs') self.clear_ROI_pb = QAction(QIcon(QPixmap(":/icons/Icon_Library/clear_ROI.png")), 'Clear ROIs') self.toolbar.addActions([self.save_ROI_pb, self.load_ROI_pb, self.clear_ROI_pb]) self.roitree = ParameterTree() vlayout.addWidget(self.roitree) self.roiwidget.setMinimumWidth(250) self.roiwidget.setMaximumWidth(300) params = [ {'title': 'Measurements:', 'name': 'measurements', 'type': 'table', 'value': OrderedDict([]), 'Ncol': 2, 'header': ["LO", "Value"]}, ROIScalableGroup(roi_type=self.ROI_type, name="ROIs")] self.settings = Parameter.create(title='ROIs Settings', name='rois_settings', type='group', children=params) self.roitree.setParameters(self.settings, showTop=False) self.settings.sigTreeStateChanged.connect(self.roi_tree_changed) self.save_ROI_pb.triggered.connect(self.save_ROI) self.load_ROI_pb.triggered.connect(lambda: self.load_ROI(None)) self.clear_ROI_pb.triggered.connect(self.clear_ROI)
[docs] def roi_tree_changed(self, param, changes): for param, change, data in changes: path = self.settings.childPath(param) if path is not None: childName = '.'.join(path) else: childName = param.name() if change == 'childAdded': # new roi to create par: Parameter = data[0] newindex = int(par.name()[-2:]) roi_type = '' if par.child('type').value() == '1D': roi_type = '' pos = self.viewer_widget.plotItem.vb.viewRange()[0] pos = pos[0] + np.diff(pos)*np.array([2,4])/6 newroi = LinearROI(index=newindex, pos=pos) newroi.setZValue(-10) newroi.setBrush(par.child('Color').value()) newroi.setOpacity(0.2) elif par.child('type').value() == '2D': roi_type = par.child('roi_type').value() xrange = self.viewer_widget.plotItem.vb.viewRange()[0] yrange = self.viewer_widget.plotItem.vb.viewRange()[1] width = np.max(((xrange[1] - xrange[0]) / 10, 2)) height = np.max(((yrange[1] - yrange[0]) / 10, 2)) pos = [int(np.mean(xrange) - width / 2), int(np.mean(yrange) - width / 2)] if roi_type == 'RectROI': newroi = RectROI(index=newindex, pos=pos, size=[width, height], name=par.name()) elif roi_type == 'EllipseROI': newroi = EllipseROI(index=newindex, pos=pos, size=[width, height], name=par.name()) elif roi_type == 'CircularROI': newroi = CircularROI(index=newindex, pos=pos, size=[width, height], name=par.name()) newroi.setPen(par['Color']) newroi.sigRegionChangeFinished.connect(lambda: self.roi_changed.emit()) newroi.index_signal[int].connect(self.update_roi_tree) try: self.settings.sigTreeStateChanged.disconnect() except Exception: pass self.settings.sigTreeStateChanged.connect(self.roi_tree_changed) self.viewer_widget.plotItem.addItem(newroi) self._set_roi_from_index(newindex, newroi) self.new_ROI_signal.emit(newindex, roi_type, par.name()) self.update_roi_tree(newindex) self.emit_colors() self.roi_changed.emit() elif change == 'value': if param.name() in putils.iter_children(self.settings.child('ROIs'), []): parent_name = putils.get_param_path(param)[putils.get_param_path(param).index('ROIs')+1] self.update_roi(parent_name, param) self.roi_value_changed.emit(parent_name, (param, param.value())) if param.name() == 'Color': self.emit_colors() elif change == 'parent': if 'ROI' in param.name(): roi = self._ROIs.pop(param.name()) self.viewer_widget.plotItem.removeItem(roi) self.remove_ROI_signal.emit(param.name()) self.emit_colors()
[docs] def update_use_channel(self, channels: List[str]): channels.append('All') for ind in range(len(self)): val = self.settings['ROIs', self.roi_format(ind), 'use_channel'] self.settings.child('ROIs', self.roi_format(ind), 'use_channel').setLimits(channels) if val not in channels: self.settings.child('ROIs', self.roi_format(ind), 'use_channel').setValue(channels[0])
[docs] def update_roi(self, roi_key, param): self._ROIs[roi_key].index_signal[int].disconnect() if param.name() == 'Color': self._ROIs[roi_key].setPen(param.value()) self.emit_colors() elif param.name() == 'left' or param.name() == 'x': pos = self._ROIs[roi_key].pos() poss = [param.value(), pos[1]] if self.settings.child('ROIs', roi_key, 'type').value() == '1D': poss.sort() self._ROIs[roi_key].setPos(poss) elif param.name() == 'right' or param.name() == 'y': pos = self._ROIs[roi_key].pos() poss = [pos[0], param.value()] if self.settings.child('ROIs', roi_key, 'type').value() == '1D': poss.sort() self._ROIs[roi_key].setPos(poss) elif param.name() == 'angle': self._ROIs[roi_key].setAngle(param.value(),center=[0.5,0.5]) elif param.name() == 'width': size = self._ROIs[roi_key].size() self._ROIs[roi_key].setSize((param.value(), size[1])) elif param.name() == 'height': size = self._ROIs[roi_key].size() self._ROIs[roi_key].setSize((size[0], param.value())) self._ROIs[roi_key].index_signal[int].connect(self.update_roi_tree)
[docs] @Slot(int) def update_roi_tree(self, index): roi = self.get_roi_from_index(index) par = self.settings.child(*('ROIs', self.roi_format(index))) if isinstance(roi, LinearROI): pos = roi.getRegion() else: pos = roi.pos() size = roi.size() angle = roi.angle() try: self.settings.sigTreeStateChanged.disconnect() except Exception: pass if isinstance(roi, LinearROI): par.child(*('position', 'left')).setValue(pos[0]) par.child(*('position', 'right')).setValue(pos[1]) if not isinstance(roi, LinearROI): par.child(*('position', 'x')).setValue(pos[0]) par.child(*('position', 'y')).setValue(pos[1]) par.child(*('size', 'width')).setValue(size[0]) par.child(*('size', 'height')).setValue(size[1]) par.child('angle').setValue(angle) self.settings.sigTreeStateChanged.connect(self.roi_tree_changed)
[docs] def save_ROI(self): try: data = ioxml.parameter_to_xml_string(self.settings.child(('ROIs'))) path = select_file(start_path=Path.home(), ext='xml', save=True, force_save_extension=True) if path != '': with open(path, 'wb') as f: f.write(data) except Exception as e: print(e)
[docs] def clear_ROI(self): indexes = [roi.index for roi in self._ROIs.values()] for index in indexes: self.settings.child(*('ROIs', self.roi_format(index))).remove()
# self.settings.sigTreeStateChanged.connect(self.roi_tree_changed)
[docs] def load_ROI(self, path=None, params=None): try: if params is None: if path is None: path = select_file(start_path=Path.home(), save=False, ext='xml', filter='XML files (*.xml)') if path != '': params = Parameter.create(title='Settings', name='settings', type='group', children=ioxml.XML_file_to_parameter(path)) if params is not None: self.clear_ROI() QtWidgets.QApplication.processEvents() for param in params: if 'roi_type' in putils.iter_children(param, []): self.settings.child('ROIs').addNew(param.child('roi_type').value()) else: self.settings.child('ROIs').addNew() QtWidgets.QApplication.processEvents() self.set_roi(self.settings.child('ROIs').children(), params) except Exception as e: logger.exception(str(e))
[docs] def set_roi(self, roi_params, roi_params_new): for child, new_child in zip(roi_params, roi_params_new): if 'group' not in child.opts['type']: child.setValue(new_child.value()) else: self.set_roi(child.children(), new_child.children())
[docs]class ROISaver: def __init__(self, msgbox=False, det_modules=[]): self.roi_presets = None self.detector_modules = det_modules if msgbox: msgBox = QtWidgets.QMessageBox() msgBox.setText("ROI Manager?") msgBox.setInformativeText("What do you want to do?") cancel_button = msgBox.addButton(QtWidgets.QMessageBox.Cancel) modify_button = msgBox.addButton('Modify', QtWidgets.QMessageBox.AcceptRole) msgBox.setDefaultButton(QtWidgets.QMessageBox.Cancel) ret = msgBox.exec() if msgBox.clickedButton() == modify_button: path = select_file(start_path=roi_path, save=False, ext='xml') if path != '': self.set_file_roi(str(path)) else: # cancel pass
[docs] def set_file_roi(self, filename, show=True): """ """ children = ioxml.XML_file_to_parameter(filename) self.roi_presets = Parameter.create(title='roi', name='rois', type='group', children=children) det_children = [child for child in self.roi_presets.children() if 'det' in child.opts['name']] det_names = [child.child('detname').value() for child in self.roi_presets.children() if 'det' in child.opts['name']] det_module_names = [det.title for det in self.detector_modules] for ind_det, det_roi in enumerate(det_children): det_module = self.detector_modules[det_module_names.index(det_names[ind_det])] viewer_children = [child for child in det_roi.children() if 'viewer' in child.opts['name']] for ind_viewer, viewer in enumerate(det_module.viewers): rois_params = [child for child in viewer_children[ind_viewer].children() if 'ROI' in child.opts['name']] if len(rois_params) > 0: if hasattr(viewer, 'roi_manager'): if hasattr(viewer, 'activate_roi'): # because for viewer 0D it is irrelevant viewer.activate_roi() viewer.roi_manager.load_ROI(params=rois_params) QtWidgets.QApplication.processEvents() if show: self.show_rois()
[docs] def set_new_roi(self, file=None): if file is None: file = 'roi_default' self.roi_presets = Parameter.create(name='roi_settings', type='group', children=[ {'title': 'Filename:', 'name': 'filename', 'type': 'str', 'value': file}, ]) for ind_det, det in enumerate(self.detector_modules): det_param = Parameter.create(name=f'det_{ind_det:03d}', type='group', children=[ {'title': 'Det Name:', 'name': 'detname', 'type': 'str', 'value': det.title}, ]) for ind_viewer, viewer in enumerate(det.ui.viewers): viewer_param = Parameter.create( name=f'viewer_{ind_viewer:03d}', type='group', children=[ {'title': 'Viewer:', 'name': 'viewername', 'type': 'str', 'value': det.ui.viewer_docks[ind_viewer].name()}, ]) if hasattr(viewer, 'roi_manager'): viewer_param.addChild( {'title': 'ROI type:', 'name': 'roi_type', 'type': 'str', 'value': viewer.roi_manager.settings.child('ROIs').roi_type}) viewer_param.addChildren(viewer.roi_manager.settings.child('ROIs').children()) det_param.addChild(viewer_param) self.roi_presets.addChild(det_param) ioxml.parameter_to_xml_file(self.roi_presets, os.path.join(roi_path, file)) self.show_rois()
[docs] def show_rois(self): """ """ dialog = QtWidgets.QDialog() vlayout = QtWidgets.QVBoxLayout() tree = ParameterTree() tree.setMinimumWidth(400) tree.setMinimumHeight(500) tree.setParameters(self.roi_presets, showTop=False) vlayout.addWidget(tree) dialog.setLayout(vlayout) buttonBox = QtWidgets.QDialogButtonBox(parent=dialog) buttonBox.addButton('Save', buttonBox.AcceptRole) buttonBox.accepted.connect(dialog.accept) buttonBox.addButton('Cancel', buttonBox.RejectRole) buttonBox.rejected.connect(dialog.reject) vlayout.addWidget(buttonBox) dialog.setWindowTitle('Fill in information about this manager') res = dialog.exec() if res == dialog.Accepted: # save managers parameters in a xml file # start = os.path.split(os.path.split(os.path.realpath(__file__))[0])[0] # start = os.path.join("..",'daq_scan') ioxml.parameter_to_xml_file( self.roi_presets, os.path.join( roi_path, self.roi_presets.child('filename').value()))
if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) from pymodaq_gui.plotting.widgets import ImageWidget from pyqtgraph import PlotWidget im = ImageWidget() im = PlotWidget() prog = ROIManager(im, '2D') widget = QtWidgets.QWidget() layout = QtWidgets.QHBoxLayout() widget.setLayout(layout) layout.addWidget(im) layout.addWidget(prog.roiwidget) widget.show() prog.add_roi_programmatically(ROI2D_TYPES[0]) prog.add_roi_programmatically(ROI2D_TYPES[1]) prog.add_roi_programmatically(ROI2D_TYPES[2]) sys.exit(app.exec_())