Skip to content

Commit

Permalink
feat: allow selecting measurements to save to concat df table, see #364
Browse files Browse the repository at this point in the history
  • Loading branch information
ElpadoCan committed Jun 30, 2023
1 parent 5b993fc commit bce94e2
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 22 deletions.
2 changes: 1 addition & 1 deletion cellacdc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
print('Initalising...')
print('Initialising...')
import os
import sys
import shutil
Expand Down
8 changes: 5 additions & 3 deletions cellacdc/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,9 @@ def launchApplyTrackingFromTableUtil(self):
)
)
self.applyTrackWin.show()
func = partial(self._runApplyTrackingFromTableUtil, posPath, self.applyTrackWin)
func = partial(
self._runApplyTrackingFromTableUtil, posPath, self.applyTrackWin
)
QTimer.singleShot(200, func)

def _runApplyTrackingFromTableUtil(self, posPath, win):
Expand Down Expand Up @@ -1334,11 +1336,11 @@ def launchAlignUtil(self, checked=False):
title = 'Align frames'
infoText = 'Aligning frames in X and Y with phase cross-correlation...'
progressDialogueTitle = 'Align frames'
self.concatWindow = utilsAlign.alignWin(
self.alignWindow = utilsAlign.alignWin(
selectedExpPaths, self.app, title, infoText, progressDialogueTitle,
parent=self
)
self.concatWindow.show()
self.alignWindow.show()

def launchConcatUtil(self, checked=False):
self.logger.info(f'Launching utility "{self.sender().text()}"')
Expand Down
84 changes: 75 additions & 9 deletions cellacdc/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -1752,7 +1752,7 @@ def __init__(
self, loadedChNames, notLoadedChNames, isZstack, isSegm3D,
favourite_funcs=None, parent=None, allPos_acdc_df_cols=None,
acdc_df_path=None, posData=None, addCombineMetricCallback=None,
allPosData=None
allPosData=None, is_concat=False
):
super().__init__(parent=parent)

Expand All @@ -1762,6 +1762,7 @@ def __init__(

self.delExistingCols = False
self.okClicked = False
self.is_concat = is_concat
self.allPos_acdc_df_cols = allPos_acdc_df_cols
self.acdc_df_path = acdc_df_path
self.allPosData = allPosData
Expand All @@ -1781,7 +1782,7 @@ def __init__(
for col, chName in enumerate(loadedChNames):
channelGBox = widgets.channelMetricsQGBox(
isZstack, chName, isSegm3D, favourite_funcs=favourite_funcs,
posData=posData
posData=posData, is_concat=is_concat
)
channelGBox.chName = chName
groupsLayout.addWidget(channelGBox, 0, col, 3, 1)
Expand All @@ -1795,7 +1796,7 @@ def __init__(
for col, chName in enumerate(notLoadedChNames):
channelGBox = widgets.channelMetricsQGBox(
isZstack, chName, isSegm3D, favourite_funcs=favourite_funcs,
posData=posData
posData=posData, is_concat=is_concat
)
channelGBox.setChecked(False)
channelGBox.chName = chName
Expand Down Expand Up @@ -1862,14 +1863,15 @@ def __init__(
self.mixedChannelsCombineMetricsQGBox, 2, current_col
)
groupsLayout.setRowStretch(1, 1)
self.setDisabledMetricsRequestedForCombined()
self.mixedChannelsCombineMetricsQGBox.toggled.connect(
self.setDisabledMetricsRequestedForCombined
)
for combCheckbox in self.mixedChannelsCombineMetricsQGBox.checkBoxes:
combCheckbox.toggled.connect(
if not self.is_concat:
self.setDisabledMetricsRequestedForCombined()
self.mixedChannelsCombineMetricsQGBox.toggled.connect(
self.setDisabledMetricsRequestedForCombined
)
for combCheckbox in self.mixedChannelsCombineMetricsQGBox.checkBoxes:
combCheckbox.toggled.connect(
self.setDisabledMetricsRequestedForCombined
)

self.numberCols = current_col

Expand Down Expand Up @@ -1916,6 +1918,11 @@ def channelCheckboxToggled(self, checkbox):
if checkbox.text().find('concentration_') == -1:
return

if self.is_concat:
# When this dialogue is used in concatenate pos utility we do not
# need to check that certain metrics are present
return

pattern = r'.+_from_vol_([a-z]+)(_3D)?(_?[A-Za-z0-9]*)'
repl = r'cell_vol_\1\2'
cell_vol_metric_name = re.sub(pattern, repl, checkbox.text())
Expand Down Expand Up @@ -1957,6 +1964,10 @@ def sizeMetricToggled(self, checked):
checked : bool
State of the checkbox toggled
"""
if self.is_concat:
# When this dialogue is used in concatenate pos utility we do not
# need to check that certain metrics are present
return

checkbox = self.sender()
if not hasattr(checkbox, 'isRequired'):
Expand Down Expand Up @@ -2137,6 +2148,11 @@ def loadLastSelection(self):
self.doNotWarn = False

def setDisabledMetricsRequestedForCombined(self, checked=True):
if self.is_concat:
# When this dialogue is used in concatenate pos utility we do not
# need to check that certain metrics are present
return

# Set checked and disable those metrics that are requested for
# combined measurements
allCheckboxes = []
Expand Down Expand Up @@ -2178,11 +2194,61 @@ def keyPressEvent(self, a0: QKeyEvent) -> None:
def closeEvent(self, event):
if self.cancel:
self.sigCancel.emit()
super().closeEvent(event)

def restart(self):
self.cancel = False
self.close()
self.sigRestart.emit()

def setDisabledNotExistingMeasurements(self, existing_colnames):
for chNameGroupbox in self.chNameGroupboxes:
for checkBox in chNameGroupbox.checkBoxes:
colname = checkBox.text()
if colname in existing_colnames:
checkBox.setChecked(True)
continue

checkBox.setChecked(False)
checkBox.setDisabled(True)
self.setNotExistingMeasurementTooltip(checkBox)

for checkBox in self.sizeMetricsQGBox.checkBoxes:
colname = checkBox.text()
if colname in existing_colnames:
checkBox.setChecked(True)
continue
checkBox.setChecked(False)
checkBox.setDisabled(True)
self.setNotExistingMeasurementTooltip(checkBox)

for checkBox in self.regionPropsQGBox.checkBoxes:
colname = checkBox.text()
if any([col == colname for col in existing_colnames]):
checkBox.setChecked(True)
continue
checkBox.setChecked(False)
checkBox.setDisabled(True)
self.setNotExistingMeasurementTooltip(checkBox)

if self.mixedChannelsCombineMetricsQGBox is None:
return

for combCheckbox in self.mixedChannelsCombineMetricsQGBox.checkBoxes:
colname = combCheckbox.text()
if colname in existing_colnames:
combCheckbox.setChecked(True)
continue
combCheckbox.setChecked(False)
combCheckbox.setDisabled(True)
self.setNotExistingMeasurementTooltip(combCheckbox)

def setNotExistingMeasurementTooltip(self, checkBox):
checkBox.setToolTip(
'Measurement is disabled because it is not present in selected '
'acdc_output tables, hence it cannot be addded to concatenated '
'table. '
)

def ok_cb(self):
if self.allPos_acdc_df_cols is None:
Expand Down
3 changes: 1 addition & 2 deletions cellacdc/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -11925,7 +11925,6 @@ def postProcessSegmValueChanged(self, lab, delObjs: dict):

def readSavedCustomAnnot(self):
tempAnnot = {}
printl(custom_annot_path)
if os.path.exists(custom_annot_path):
self.logger.info('Loading saved custom annotations...')
tempAnnot = load.read_json(
Expand Down Expand Up @@ -20630,7 +20629,7 @@ def showSetMeasurements(self, checked=False):
allPos_acdc_df_cols=list(allPos_acdc_df_cols),
acdc_df_path=posData.images_path, posData=posData,
addCombineMetricCallback=self.addCombineMetric,
allPosData=self.data
allPosData=self.data, parent=self
)
self.measurementsWin.sigClosed.connect(self.setMeasurements)
self.measurementsWin.show()
Expand Down
7 changes: 7 additions & 0 deletions cellacdc/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ def load_segm_file(images_path, end_name_segm_file='segm', return_path=False):
else:
return

def load_metadata_df(images_path):
for file in myutils.listdir(images_path):
if not file.endswith('metadata.csv'):
continue
filepath = os.path.join(images_path, file)
return pd.read_csv(filepath).set_index('Description')

def _add_will_divide_column(acdc_df):
if 'cell_cycle_stage' not in acdc_df.columns:
return acdc_df
Expand Down
2 changes: 2 additions & 0 deletions cellacdc/measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ def get_custom_metrics_func():
scripts = os.listdir(acdc_metrics_path)
custom_func_dict = {}
for file in scripts:
if file == '__init__.py':
continue
module_name, ext = os.path.splitext(file)
if ext != '.py':
# print(f'The file {file} is not a python file. Ignoring it.')
Expand Down
5 changes: 3 additions & 2 deletions cellacdc/myutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1664,6 +1664,7 @@ def check_install_cellpose():
if major < 2:
_install_pip_package('cellpose')
except Exception as e:
printl(traceback.format_exc())
_inform_install_package_failed('cellpose')

def check_install_segment_anything():
Expand Down Expand Up @@ -1697,6 +1698,7 @@ def check_install_package(
else:
_install_pip_package(pkg_name)
except Exception as e:
printl(traceback.format_exc())
_inform_install_package_failed(
pkg_name, parent=parent, do_exit=raise_on_cancel
)
Expand Down Expand Up @@ -1724,6 +1726,7 @@ def check_matplotlib_version(qparent=None):
[sys.executable, '-m', 'pip', 'install', '-U', 'matplotlib']
)
except Exception as e:
printl(traceback.format_exc())
_inform_install_package_failed(
'matplotlib', parent=qparent, do_exit=False
)
Expand All @@ -1747,8 +1750,6 @@ def _inform_install_package_failed(pkg_name, parent=None, do_exit=True):
f'`pip install --upgrade {pkg_name}`'
)
print('^'*50)
if do_exit:
exit()

def _install_package_msg(
pkg_name, note='', parent=None, upgrade=False, caller_name='Cell-ACDC'
Expand Down
89 changes: 88 additions & 1 deletion cellacdc/utils/concat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from qtpy.QtWidgets import QFileDialog

from .. import apps, myutils, workers, widgets, html_utils
from .. import printl

from .base import NewThreadMultipleExpBaseUtil

Expand All @@ -20,11 +21,96 @@ def runWorker(self):
self.worker = workers.ConcatAcdcDfsWorker(self)
self.worker.sigAskFolder.connect(self.askFolderWhereToSaveAllExp)
self.worker.sigAborted.connect(self.workerAborted)
self.worker.sigAskAppendName.connect(self.askAppendName)
self.worker.sigSetMeasurements.connect(self.askSetMeasurements)
super().runWorker(self.worker)

def askSetMeasurements(self, kwargs):
loadedChNames = kwargs['loadedChNames']
notLoadedChNames = kwargs['notLoadedChNames']
isZstack = kwargs['isZstack']
isSegm3D = kwargs['isSegm3D']
self.setMeasurementsWin = apps.setMeasurementsDialog(
loadedChNames, notLoadedChNames, isZstack, isSegm3D,
is_concat=True, parent=self
)
existing_colnames = kwargs['existing_colnames']
self.setMeasurementsWin.setDisabledNotExistingMeasurements(
existing_colnames
)
self.setMeasurementsWin.sigClosed.connect(self.setMeasurements)
self.setMeasurementsWin.sigCancel.connect(self.setMeasurementsCancelled)
self.setMeasurementsWin.show()

def setMeasurements(self):
selectedColumns = []
for chNameGroupbox in self.setMeasurementsWin.chNameGroupboxes:
chName = chNameGroupbox.chName
if not chNameGroupbox.isChecked():
# Skip entire channel
continue

for checkBox in chNameGroupbox.checkBoxes:
if not checkBox.isChecked():
continue
colname = checkBox.text()
selectedColumns.append(colname)

if self.setMeasurementsWin.sizeMetricsQGBox.isChecked():
for checkBox in self.setMeasurementsWin.sizeMetricsQGBox.checkBoxes:
if not checkBox.isChecked():
continue
colname = checkBox.text()
selectedColumns.append(colname)

if self.setMeasurementsWin.regionPropsQGBox.isChecked():
for checkBox in self.setMeasurementsWin.regionPropsQGBox.checkBoxes:
if not checkBox.isChecked():
continue
colname = checkBox.text()
selectedColumns.append(colname)

checkMixedChannel = (
self.setMeasurementsWin.mixedChannelsCombineMetricsQGBox is not None
and self.setMeasurementsWin.mixedChannelsCombineMetricsQGBox.isChecked()
)
if checkMixedChannel:
win = self.setMeasurementsWin
checkBoxes = win.mixedChannelsCombineMetricsQGBox.checkBoxes
for checkBox in checkBoxes:
if not checkBox.isChecked():
continue
colname = checkBox.text()
selectedColumns.append(colname)

self.worker.selectedColumns = selectedColumns
self.worker.abort = False
self.worker.waitCond.wakeAll()

def setMeasurementsCancelled(self):
self.worker.abort = True
self.worker.waitCond.wakeAll()

def showEvent(self, event):
self.runWorker()

def askAppendName(self, basename, existingEndnames):
win = apps.filenameDialog(
basename=basename,
hintText='Insert a name for the <b>concatenated table</b> file:',
existingNames=existingEndnames,
allowEmpty=True,
ext='.csv'
)
win.exec_()
if win.cancel:
self.worker.abort = True
self.worker.waitCond.wakeAll()
return

self.worker.concat_df_filename = win.filename
self.worker.waitCond.wakeAll()

def askFolderWhereToSaveAllExp(self, allExp_filename):
txt = html_utils.paragraph(f"""
After clicking "Ok" you will be asked to <b>select a folder where you
Expand Down Expand Up @@ -53,7 +139,8 @@ def askFolderWhereToSaveAllExp(self, allExp_filename):
self.worker.waitCond.wakeAll()

def workerAborted(self):
self.workerFinished(None, aborted=True)
self.worker.signals.finished.emit(self)
self.workerFinished(self.worker, aborted=True)

def workerFinished(self, worker, aborted=False):
if aborted:
Expand Down

0 comments on commit bce94e2

Please sign in to comment.