"""The data model for localization dataThis module contains a data model to serve as container for SMLM data.The individual SMLM datasets are provided as :class:`locan.LocData` instances.SMLM data serves as data model for other napari-locan widgets to process orrender the localization data. It is entirely independent of napari layers.Upon rendering a SMLM dataset a new image is created in a new napari layer."""from__future__importannotationsimportloggingfromtypingimportAnyimportlocanaslcfromqtpy.QtCoreimportQObject,Signal# type: ignore[attr-defined]logger=logging.getLogger(__name__)
[docs]classSmlmData(QObject):# type: ignore""" Container for one or more LocData objects. Attributes ---------- index_changed_signal A Qt signal for index locdata_names_changed_signal A Qt signal for locdata_names locdatas Localization datasets locdata_names Localization string identifier index Current selection of locdatas locdata The selected LocData object locdata_name The selected LocData identifier """index_changed_signal:Signal=Signal(int)locdata_names_changed_signal:Signal=Signal(list)def__init__(self,locdatas:list[lc.LocData]|None=None,locdata_names:list[str]|None=None,)->None:super().__init__()iflocdatasisNoneandlocdata_namesisNone:self._locdatas:list[lc.LocData]=[]self._locdata_names:list[str]=[]self._index:int=-1eliflocdata_namesisNone:assertlocdatasisnotNone# type narrowing # noqa: S101self._locdatas=locdatasself._locdata_names=[item.meta.identifierforiteminself._locdatas]self._index=len(locdatas)-1eliflocdatasisnotNoneand(len(locdatas)!=len(locdata_names)):raiseValueError("locdata and locdata_names must correspond and be of same length.")else:assertlocdatasisnotNone# type narrowing # noqa: S101self._locdatas=locdatasself._locdata_names=locdata_namesself._index=len(locdatas)-1def__getstate__(self)->dict[str,Any]:"""Modify pickling behavior."""state:dict[str,Any]={}state["_locdatas"]=self._locdatasstate["_locdata_names"]=self._locdata_namesstate["_index"]=self._indexreturnstatedef__setstate__(self,state:dict[str,Any])->None:"""Modify pickling behavior."""# Restore instance attributes.self.__dict__.update(state)super().__init__()@propertydeflocdatas(self)->list[lc.LocData]:returnself._locdatas@propertydeflocdata_names(self)->list[str]:returnself._locdata_names@propertydefindex(self)->int:returnself._index@index.setterdefindex(self,value:int)->None:ifvalue>len(self.locdatas)-1:raiseIndexError(f"Index is larger than n_locdatas - 1: {len(self.locdatas)-1}")elifvalue<0:self._index=-1else:self._index=valueself.index_changed_signal.emit(self._index)
[docs]defset_index_slot(self,value:int)->None:"""QT slot for property self.index."""self.index=value
@propertydeflocdata(self)->lc.LocData|None:ifself._index==-1:returnNoneelse:returnself._locdatas[self._index]@locdata.setterdeflocdata(self,item:lc.LocData)->None:ifself._index==-1:raiseValueError("Locdatas is empty. ""There is no item available to be replaced.""Use self.append_item instead.")else:self._locdatas[self._index]=itemself.index_changed_signal.emit(self._index)@propertydeflocdata_name(self)->str:ifself._index==-1:return""else:returnself._locdata_names[self._index]@locdata_name.setterdeflocdata_name(self,text:str)->None:ifself._index==-1:raiseValueError("Locdata_names is empty. ""There is no item available to be replaced.""Use self.append_item instead.")else:self._locdata_names[self._index]=textself.locdata_names_changed_signal.emit(self._locdata_names)
[docs]defappend_item(self,locdata:lc.LocData|None,locdata_name:str|None=None,set_index:bool=True,)->None:current_index=self.indexiflocdataisNoneandlocdata_nameisNone:returneliflocdata_nameisNone:assertlocdataisnotNone# type narrowing # noqa: S101self._locdatas.append(locdata)self._locdata_names.append(locdata.meta.identifier)else:assertlocdataisnotNone# type narrowing # noqa: S101assertlocdata_nameisnotNone# type narrowing # noqa: S101self._locdatas.append(locdata)self._locdata_names.append(locdata_name)ifset_index:self._index=len(self.locdatas)-1else:self._index=current_indexself.locdata_names_changed_signal.emit(self.locdata_names)self.index_changed_signal.emit(self.index)
[docs]defdelete_item(self)->None:current_index=self.indextry:self._locdatas.pop(current_index)self._locdata_names.pop(current_index)exceptIndexErrorasexception:raiseIndexError("Index is out of range. No item available to be deleted.")fromexceptioniflen(self._locdatas)==0:self._index=-1elifcurrent_index==0:self._index=0else:self._index=current_index-1self.locdata_names_changed_signal.emit(self.locdata_names)self.index_changed_signal.emit(self.index)