Source code for napari_locan.data_model.data_model_base
"""Abstract base class for a data model.A data model holds either smlm_data, filter_specifications, regionsor other data structures.The interface provides methods to manipulate the data contents."""from__future__importannotationsimportloggingfromabcimportABC,ABCMetafromtypingimportAnyfromqtpy.QtCoreimportQObject,Signal# type: ignore[attr-defined]logger=logging.getLogger(__name__)
[docs]classQABCMeta(type(QObject),ABCMeta):# type: ignore[misc]# this is required to avoid metaclass conflicts with Qt metaclassespass
[docs]classDataModel(QObject,ABC,metaclass=QABCMeta):# type: ignore""" Abstract base class for container classes holding various data structures. Attributes ---------- count Monotonically increasing integer counting the overall created datasets. datasets_changed_signal A Qt signal for index names_changed_signal A Qt signal for names index_changed_signal A Qt signal for index datasets Data structures names Data structure string identifier index Current selection of data structure dataset The selected data object name The selected data identifier """count:int=0datasets_changed_signal:Signal=Signal(int)names_changed_signal:Signal=Signal(list)index_changed_signal:Signal=Signal(int)def__init__(self,datasets:list[Any]|None=None,names:list[str]|None=None,)->None:super().__init__()self._datasets:list[Any]=[]self._names:list[str]=[]self._index:int=-1self.set_datasets_and_names(datasets=datasets,names=names)def__getstate__(self)->dict[str,Any]:"""Modify pickling behavior."""state:dict[str,Any]={}state["count"]=self.countstate["_datasets"]=self._datasetsstate["_names"]=self._namesstate["_index"]=self._indexreturnstatedef__setstate__(self,state:dict[str,Any])->None:"""Modify pickling behavior."""# Restore instance attributes.self.__dict__.update(state)super().__init__()@propertydefdatasets(self)->list[Any]:returnself._datasets@propertydefnames(self)->list[str]:returnself._names
[docs]defset_datasets_and_names(self,datasets:list[Any]|None=None,names:list[str]|None=None)->None:""" Set datasets and names to the given values and point index to the last item. """ifdatasetsisNoneandnamesisNone:self._datasets=[]self._names=[]self._index=-1elifnamesisNone:assertdatasetsisnotNone# type narrowing # noqa: S101self._datasets=datasetsself._names=[str(i)fori,iteminenumerate(self._datasets)]self._index=len(datasets)-1elifdatasetsisnotNoneand(len(datasets)!=len(names)):raiseValueError("Datasets and names must correspond and be of same length.")else:assertdatasetsisnotNone# type narrowing # noqa: S101self._datasets=datasetsself._names=namesself._index=len(datasets)-1self.count+=len(self._datasets)self.datasets_changed_signal.emit(self._datasets)self.names_changed_signal.emit(self._names)self.index_changed_signal.emit(self._index)
@propertydefindex(self)->int:returnself._index@index.setterdefindex(self,value:int)->None:ifvalue>len(self.datasets)-1:raiseIndexError(f"Index is larger than n_datasets - 1: {len(self.datasets)-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
@propertydefdataset(self)->Any|None:ifself._index==-1:returnNoneelse:returnself._datasets[self._index]@dataset.setterdefdataset(self,item:Any)->None:ifself._index==-1:raiseValueError("Datasets is empty. ""There is no item available to be replaced.""Use self.append_item instead.")else:self._datasets[self._index]=itemself.count+=1self.datasets_changed_signal.emit(self._datasets)self.index_changed_signal.emit(self._index)@propertydefname(self)->str:ifself._index==-1:return""else:returnself._names[self._index]@name.setterdefname(self,text:str)->None:ifself._index==-1:raiseValueError("names is empty. ""There is no item available to be replaced.""Use self.append_item instead.")else:self._names[self._index]=textself.names_changed_signal.emit(self._names)
[docs]defappend_item(self,dataset:Any|None,name:str|None=None,set_index:bool=True,)->None:""" Append a new item to the end of datasets and point index to new dataset if set_index is true. """current_index=self.indexifdatasetisNoneandnameisNone:returnelifnameisNone:assertdatasetisnotNone# type narrowing # noqa: S101self._datasets.append(dataset)identifier=f"{len(self._datasets)}"self._names.append(identifier)else:assertdatasetisnotNone# type narrowing # noqa: S101assertnameisnotNone# type narrowing # noqa: S101self._datasets.append(dataset)self._names.append(name)ifset_index:self._index=len(self.datasets)-1else:self._index=current_indexself.count+=1self.datasets_changed_signal.emit(self.datasets)self.names_changed_signal.emit(self.names)self.index_changed_signal.emit(self.index)
[docs]defdelete_item(self)->None:""" Delete current dataset and set index to the previous dataset. """current_index=self.indextry:self._datasets.pop(current_index)self._names.pop(current_index)exceptIndexErrorasexception:raiseIndexError("Index is out of range. No item available to be deleted.")fromexceptioniflen(self._datasets)==0:self._index=-1elifcurrent_index==0:self._index=0else:self._index=current_index-1self.names_changed_signal.emit(self.names)self.index_changed_signal.emit(self.index)
[docs]defdelete_all(self)->None:""" Delete all datasets and set index to -1. """self._datasets=[]self._names=[]self._index=-1self.names_changed_signal.emit(self.names)self.index_changed_signal.emit(self.index)