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,QSignalBlocker
from qtpy.QtGui import QIcon, QPixmap
from collections import OrderedDict
import pyqtgraph.Point as Point
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 pymodaq_utils.utils import plot_colors
from pymodaq_utils.logger import get_module_name, set_logger
from pymodaq_utils.config import Config
from pymodaq_gui.config_saver_loader import get_set_roi_path
from pymodaq_gui.utils import select_file
from pymodaq_gui.plotting.items.roi import ROIFactory, ROI, LinearROI, RectROI, DataDim
import numpy as np
from pathlib import Path
from pymodaq_data.post_treatment.process_to_scalar import DataProcessorFactory
from pymodaq_gui.utils.utils import first_available_integer
data_processors = DataProcessorFactory()
roi_path = get_set_roi_path()
logger = set_logger(get_module_name(__file__))
config = Config()
ROI_NAME_PREFIX = 'ROI_'
ROI2D_TYPES = ROIFactory.get_descriptors_from_dimensionality(DataDim.Data2D)
[docs]
class ROIScalableGroup(GroupParameter):
def __init__(self, roi_type = DataDim.Data1D, **opts):
opts['type'] = 'group'
opts['addText'] = "Add"
self.roi_type = roi_type
if roi_type == DataDim.Data2D:
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
self.addChild(self.makeChild(newindex, typ))
[docs]
def makeChild(self, index, descriptor: str):
child = {'name': ROIManager.roi_format(index), 'type': 'bool','value':True, 'removable': True, 'renamable': False, 'expanded': False,'context':['Copy',]}
if self.roi_type == DataDim.Data2D:
child['children'] = ROIScalableGroup.make_ROIParam2D(descriptor, index)
elif self.roi_type == DataDim.Data1D:
child['children'] = ROIScalableGroup.make_ROIParam1D(descriptor, index)
return child
[docs]
@staticmethod
def makeChannelsParam(dim=DataDim.Data2D):
if dim == DataDim.Data2D:
child = [{'title': 'Use channel', 'name': 'use_channel', 'type': 'itemselect', 'checkbox': True,
'value': dict(all_items=['red', 'green', 'blue'],
selected=['red',]),
},]
else:
child = [{'title': 'Use channel', 'name': 'use_channel', 'type': 'itemselect','checkbox': True},]
return child
[docs]
@staticmethod
def makeDisplayParam(index):
return [{'name': 'Color', 'type': 'color', 'value': list(np.roll(ROIManager.color_list, index)[0])},
{'name': 'zlevel', 'title':'Z-level','type': 'int', 'expanded': False, 'value':10},]
[docs]
@staticmethod
def makeMathParam(dim=DataDim.Data2D):
return [{'title': 'Math type:', 'name': 'math_function', 'type': 'list',
'limits': data_processors.functions_filtered(dim)},]
[docs]
@staticmethod
def make_ROIParam2D(descriptor: str, index):
children = []
children.extend([{'title': 'Type', 'name': 'roi_type', 'type': 'list', 'value': descriptor,
'limits': ROI2D_TYPES, 'readonly': False,}])
children.append({'title': 'Process data', 'name': 'process_data', 'type': 'led_push',
'value': config.get(('plotting', 'process_roi'), True),})
children.extend(ROIScalableGroup.makeChannelsParam(DataDim.Data2D))
children.extend(ROIScalableGroup.makeMathParam(DataDim.Data2D))
children.extend(ROIScalableGroup.makeDisplayParam(index))
children.extend([{'name': 'center', 'type': 'group', 'expanded': False, 'children': [
{'name': 'x', 'type': 'float', 'value': 0, 'step': 1,'decimals':6},
{'name': 'y', 'type': 'float', 'value': 0, 'step': 1,'decimals':6}
]}, ])
children.extend([{'name': 'position', 'type': 'group', 'expanded': False, 'children': [
{'name': 'x', 'type': 'float', 'value': 0, 'step': 1,'decimals':6},
{'name': 'y', 'type': 'float', 'value': 0, 'step': 1,'decimals':6}
]}, ])
children.extend([
{'name': 'size', 'type': 'group', 'expanded': False, 'children': [
{'name': 'width', 'type': 'float', 'value': 10, 'step': 1,'decimals':6},
{'name': 'height', 'type': 'float', 'value': 10, 'step': 1,'decimals':6}
]},
{'name': 'angle', 'type': 'float', 'value': 0, 'step': 1}])
return children
[docs]
@staticmethod
def make_ROIParam1D(descriptor: str, index):
children = []
children.append({'title': 'Process data', 'name': 'process_data', 'type': 'led_push',
'value': config.get(('plotting', 'process_roi'), True),})
children.extend(ROIScalableGroup.makeChannelsParam(DataDim.Data1D))
children.extend(ROIScalableGroup.makeMathParam(DataDim.Data1D))
children.extend(ROIScalableGroup.makeDisplayParam(index))
children.extend([{'name': 'position', 'type': 'group', 'children': [
{'name': 'left', 'type': 'float', 'value': 0, 'step': 1},
{'name': 'right', 'type': 'float', 'value': 10, 'step': 1}
]}, ])
return children
[docs]
class ROIManager(QObject):
new_ROI_signal = Signal(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=DataDim.Data1D):
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()
@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[roi_format(index)]
def _set_roi_from_index(self, index: int, roi):
self.ROIs[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, descriptor: str = ROI2D_TYPES[0]):
self.settings.child('ROIs').addNew(descriptor)
[docs]
def remove_roi_programmatically(self, index: int):
self.settings.child('ROIs').removeChild(self.settings.child('ROIs', 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:save_ROI.png")), 'Save ROIs')
self.load_ROI_pb = QAction(QIcon(QPixmap("icons:load_ROI.png")), 'Load ROIs')
self.clear_ROI_pb = QAction(QIcon(QPixmap("icons: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.settings_signalBlocker = QSignalBlocker(self.settings)
self.settings_signalBlocker.unblock()
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 get_ROI_indexes(self,):
return [roi.index for roi in self.ROIs.values()]
[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]
roi = self.make_ROI(par)
self.add_ROI(roi)
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]
if parent_name in self._ROIs.keys():
roi_changed = self._ROIs[parent_name]
self.update_roi(roi_changed, 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():
self.remove_ROI(self.ROIs[param.name()])
elif change == 'contextMenu': # MenuSel
if data=='Copy':
self.copy_ROI(self.ROIs[param.name()])
[docs]
def make_ROI(self, param: Parameter):
newindex = int(param.name()[-2:])
pos = self.viewer_widget.plotItem.vb.viewRange()
if self.ROI_type == DataDim.Data1D:
descriptor = ''
pos = pos[0]
pos = pos[0] + np.diff(pos)*np.array([2,4])/6
roi = self.make_ROI1D(newindex, pos, brush=param['Color'],
compute=param['process_data'])
elif self.ROI_type == DataDim.Data2D:
descriptor = param.child('roi_type').value()
xrange,yrange=pos
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)]
roi = self.make_ROI2D(descriptor, index=newindex, pos=pos,size=[width, height],
pen=param['Color'], compute=param['process_data'])
return roi
[docs]
def add_ROI(self, roi):
# Connection roi signals to relevant function
roi.sigRegionChangeFinished.connect(lambda: self.roi_changed.emit())
roi.sigRegionChangeFinished.connect(self.update_roi_tree)
roi.sigRemoveRequested.connect(self.remove_ROI)
roi.sigCopyRequested.connect(self.copy_ROI)
roi.setAcceptedMouseButtons(QtCore.Qt.MouseButton.LeftButton)
roi.sigDoubleClicked.connect(self.expand_roi_tree)
# Updating tree
self.update_roi_tree(roi)
# Adding to dictionnary
self.ROIs[roi.key()] = roi
# Adding to viewer
self.viewer_widget.plotItem.addItem(roi)
# Emitting signal
self.new_ROI_signal.emit(roi.key())
[docs]
def expand_roi_tree(self, roi,):
# Expand roi tree when roi gets double selected
param = self.settings.child(*('ROIs', roi_format(roi.index)))
isExpanded = not param.opts['expanded']
param.setOpts(expanded=isExpanded)
[docs]
def make_ROI1D(self, index, pos, compute=True, **kwargs):
"""Convenience function to make custom ROI_1D
Args:
index (int): Current index of ROI
pos: Initial position of ROI
Returns:
roi: LinearROI
"""
roi = ROIFactory.create(DataDim.Data1D,
ROIFactory.get_descriptors_from_dimensionality(DataDim.Data1D)[0],
index=index, pos=pos, compute=compute, **kwargs)
# roi.setZValue(-10)
roi.setOpacity(0.2)
return roi
[docs]
def make_ROI2D(self, descriptor: str, index, pos, size, compute=True, **kwargs):
"""Convenience function to make custom ROI_2D
Args:
descriptor (str): name of 2D ROI
index (int): Current index of ROI
pos: Initial position of ROI
size: Initial size of ROI
Returns:
roi: pg.ROI
"""
return ROIFactory.create(DataDim.Data2D, descriptor,
index=index, pos=pos,
size=size, name=roi_format(index),
compute=compute, **kwargs)
[docs]
def remove_ROI(self, roi):
"""Function to remove roi from dict and widget
Args:
roi (pg.ROI): roi to be removed
"""
roi_group = self.settings.child('ROIs')
for param in roi_group.children():
if roi.key() == param.name():
self.settings_signalBlocker.reblock()
roi_group.removeChild(param)
self.settings_signalBlocker.unblock()
roi = self.ROIs.pop(roi.key())
self.viewer_widget.plotItem.removeItem(roi)
self.remove_ROI_signal.emit(roi.key())
self.emit_colors()
[docs]
def copy_ROI(self, roi: ROI):
"""Method to copy a ROI and add it to the parameter tree and to the viewer widget
The method extracts the parameters of the copied ROI, create a new parameter, a new ROI and update it with the settings from the copied parameter
Args:
roi (ROI): the ROI to be copied
"""
index = first_available_integer(self.get_ROI_indexes())
roi_group = self.settings.child('ROIs')
#Copy parameter and edit name
param_roi = self.get_parameter(roi)
param = param_roi.saveState() # Transforming parameter in dict
param['name'] = roi_format(index) # Changing name
param = Parameter.create(**param) # Transforming dict in parameter
self.settings_signalBlocker.reblock()
roi_group.addChild(param)
self.settings_signalBlocker.unblock()
new_roi = self.make_ROI(param)
param_to_update = putils.iter_children_params(param_roi,[],filter_name=('roi_type',),filter_type=('group',)) # Parameters to update
# [self.update_roi(new_roi,p) for p in reversed(param_to_update)]
self.add_ROI(new_roi)
[self.update_roi(new_roi,p) for p in reversed(param_to_update)]
[docs]
def update_use_channel(self, channels: List[str], index=None):
"""Function to update the selected channels. If no index is given, the channels are applied to all ROIs.
Args:
channels (List[str]): channels list from a viewer
index (int, optional): ROI index. Defaults to None.
"""
if index is not None:
param = self.settings.child('ROIs', roi_format(index), 'use_channel')
param.setValue(dict(all_items=channels,
selected=channels))
else:
for ind in range(len(self)):
param = self.settings.child('ROIs', roi_format(ind), 'use_channel')
param.setValue(dict(all_items=channels,
selected=channels))
[docs]
def update_roi(self, roi: ROI, param: Parameter):
par = self.get_parameter(roi)
roi.signalBlocker.reblock()
parent_name = param.parent().opts['name']
if param.name() == roi.key():
roi.doShow(param.value())
elif param.name() == 'roi_type':
state = roi.saveState()
self.viewer_widget.plotItem.removeItem(roi)
if self.ROI_type =='2D':
roi = self.make_ROI2D(roi_type=param.value(),index=roi.index,pos=state['pos'],size=state['size'],angle=state['angle'],pen=roi.pen)
self.add_ROI(roi)
elif param.name() == 'Color':
roi.setPen(param.value())
self.emit_colors()
elif parent_name == 'center':
center = roi.center()
pos = self.update_roi_pos(center, param)
if self.ROI_type =='1D':
roi.set_positions()
pos.sort()
else:
roi.set_center(pos)
elif parent_name == 'position':
position = roi.pos()
pos = self.update_roi_pos(position, param)
if self.ROI_type =='1D':
pos = np.sort(pos) #Subclass pg.Point to implement sort?
roi.setPos(pos)
self.settings_signalBlocker.reblock()
par.child(*('position', 'left')).setValue(pos[0])
par.child(*('position', 'right')).setValue(pos[1])
self.settings_signalBlocker.unblock()
roi.setPos(pos)
elif param.name() == 'angle':
roi.setAngle(param.value(),center=[0.5,0.5])
elif param.name() == 'zlevel':
roi.setZValue(param.value())
elif param.name() == 'width':
size = roi.size()
roi.setSize((param.value(), size[1]))
elif param.name() == 'height':
size = roi.size()
roi.setSize((size[0], param.value()))
elif param.name() == 'process_data':
roi.compute = param.value()
self.update_roi_tree(roi)
roi.signalBlocker.unblock()
[docs]
def update_roi_pos(self, pos, param):
if param.name() == 'x' or param.name() == 'left':
poss = Point(param.value(), pos[1])
elif param.name() == 'y' or param.name() == 'right':
poss = Point(pos[0], param.value())
return poss
[docs]
def get_parameter(self, roi):
if type(roi) is int:
par = self.settings.child(*('ROIs', roi_format(roi)))
else:
par = self.settings.child(*('ROIs', roi.key()))
return par
[docs]
@Slot(type(ROI))
def update_roi_tree(self, roi):
par = self.get_parameter(roi)
if isinstance(roi, LinearROI):
pos = roi.getRegion()
else:
pos = roi.pos()
size = roi.size()
angle = roi.angle()
center = roi.center()
Zvalue = roi.zValue()
self.settings_signalBlocker.reblock()
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.x())
par.child(*('position', 'y')).setValue(pos.y())
par.child(*('center', 'x')).setValue(center.x())
par.child(*('center', 'y')).setValue(center.y())
par.child(*('size', 'width')).setValue(size.x())
par.child(*('size', 'height')).setValue(size.y())
par.child('angle').setValue(angle)
par.child('zlevel').setValue(Zvalue)
self.settings_signalBlocker.unblock()
[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):
keys = [roi.key() for roi in self._ROIs.values()]
for roi_key in keys:
self.settings.child(*('ROIs', roi_key)).remove()
[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 new_child.value():
child.setValue(new_child.value())
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.StandardButton.Cancel)
modify_button = msgBox.addButton('Modify', QtWidgets.QMessageBox.ButtonRole.AcceptRole)
msgBox.setDefaultButton(QtWidgets.QMessageBox.StandardButton.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 == QtWidgets.QDialog.DialogCode.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, DataDim.Data2D)
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_())