Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 100 additions & 25 deletions cellacdc/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -17395,9 +17395,13 @@ def __init__(self, parent=None):
self.filesStructureCombobox = QComboBox()
self.filesStructureCombobox.addItems([
'Positions (aka "series") embedded in the file',
'Positions (aka "series") separated, one for each file'
'Positions (aka "series") separated, one for each file',
'Positions (aka "series") and channels separated, one for each file'
])
gridLayout.addWidget(self.filesStructureCombobox, row, 1)
self.filesStructureCombobox.currentTextChanged.connect(
self.fileStructureChanged
)
infoButton = widgets.infoPushButton()
gridLayout.addWidget(infoButton, row, 2)
infoButton.clicked.connect(self.showInfoFileStructure)
Expand All @@ -17412,6 +17416,16 @@ def __init__(self, parent=None):
browseButton.sigPathSelected.connect(
partial(self.updateFolderPath, lineEdit=self.folderPathLineEdit)
)
self.folderPathLineEdit.textChanged.connect(self.srcFolderPathChanged)

row += 1
label = QLabel('Destination folder: ')
gridLayout.addWidget(label, row, 0)
self.dstfolderPathLineEdit = widgets.ElidingLineEdit()
gridLayout.addWidget(self.dstfolderPathLineEdit, row, 1)
browseButton = widgets.browseFileButton(openFolder=True)
gridLayout.addWidget(browseButton, row, 2)
browseButton.sigPathSelected.connect(self.dstfolderPathLineEdit.setText)

row += 1
label = QLabel('Channel(s) name: ')
Expand All @@ -17420,11 +17434,15 @@ def __init__(self, parent=None):
additionalChars=' ,'
)
gridLayout.addWidget(self.channelNamesLineEdit, row, 1)
checkButton = widgets.TestPushButton('Check')
gridLayout.addWidget(checkButton, row, 3)
checkButton.clicked.connect(self.checkChannelNames)
checkButton.setDisabled(True)
self.checkButton = checkButton
infoButton = widgets.infoPushButton()
gridLayout.addWidget(infoButton, row, 2)
infoButton.clicked.connect(self.showInfoChannelName)


buttonsLayout = widgets.CancelOkButtonsLayout()

buttonsLayout.okButton.clicked.connect(self.ok_cb)
Expand All @@ -17433,13 +17451,50 @@ def __init__(self, parent=None):
gridLayout.setColumnStretch(0, 0)
gridLayout.setColumnStretch(1, 1)
gridLayout.setColumnStretch(2, 0)

gridLayout.setColumnStretch(3, 0)

mainLayout.addLayout(gridLayout)
mainLayout.addSpacing(20)
mainLayout.addLayout(buttonsLayout)

self.setLayout(mainLayout)

def fileStructureChanged(self, text):
self.checkButton.setDisabled(not 'channels separated' in text)

def checkChannelNames(self, checked=False):
proceed = self.validate()
if not proceed:
return

src_folderpath = self.folderPath()
channel_names = self.channelNames()
extension = os.listdir(src_folderpath)[0].split('.')[-1]
basenames = io.move_separate_channels_tiffs_to_pos_folders(
src_folderpath, channel_names, get_only_basenames=True,
extension=extension
)
pos_folders_texts = []
for p, basename in enumerate(basenames):
pos_folders_texts.append(f'Position_{p+1}: <code>{basename}</code>')

pos_folders_html_list = html_utils.to_list(
pos_folders_texts, ordered=True
)
text = html_utils.paragraph(
'The following Position folders will be created based on the provided channel names:<br>'
f'{pos_folders_html_list}'
)
msg = widgets.myMessageBox(wrapText=False)
msg.information(self, 'Position folders', text)

def srcFolderPathChanged(self, text):
if self.dstfolderPathLineEdit.text():
return

folderPath = self.folderPathLineEdit.text()
self.dstfolderPathLineEdit.setText(folderPath)

def showInfoFileStructure(self):
txt = html_utils.paragraph("""
Select whether the microscopy files contains multiple "series".<br><br>
Expand All @@ -17456,9 +17511,14 @@ def showInfoChannelName(self):
Enter the channels name. Separate multiple channels with a comma.<br><br>
The channel names will be used to name the individual TIFF files
(one for each channel).<br><br>
Make sure that you write the <b>channels in the right order</b>.
If you are unsure, open the file in Fiji first<br>
and check the order of channels.
If multiple channels are embedded in the microscopy file, make sure that you write the <b>channels in the right order</b>.<br>
If you are unsure, open the file in Fiji first
and check the order of channels.<br><br>
If the channels are already separated, make sure to write the
full channel name as it appears in the file, including capitalization and spaces.<br>
For example, if the files are named "pos1_ch1.tif", "pos1_ch2.tif", etc., the channels names should be "ch1, ch2".<br><br>
After providing the channel names, you can check that they are correct by clicking on the "Check" button next to the channel names field.<br>
The number of Positions that will be created will be displayed alongside the basename.
""")
msg = widgets.myMessageBox(wrapText=False)
msg.information(self, 'Files structure info', txt)
Expand All @@ -17484,10 +17544,10 @@ def updateFolderPath(self, path, lineEdit=''):
return

lineEdit.setText(path)
def warnPathEmpty(self):
txt = html_utils.paragraph("""
Folder path <b>cannot be empty</b>.

def warnPathEmpty(self, path_name):
txt = html_utils.paragraph(f"""
{path_name} <b>cannot be empty</b>.
""")
msg = widgets.myMessageBox(wrapText=False)
msg.warning(self, 'Empty folder path', txt)
Expand Down Expand Up @@ -17531,19 +17591,25 @@ def warnChannelNamesEmpty(self):

def validate(self):
path = self.folderPath()
if not path:
self.warnPathEmpty()
return False

if not os.path.exists(path):
self.warnSelectedPathDoesNotExist(path)
return False

if not os.path.isdir(path):
self.warnSelectedPathNotAFolder(path)
return False
dst_path = self.dstfolderPathLineEdit.text()
paths = {
'Source folder': path,
'Destination folder': dst_path,
}
for _path_name, _path in paths.items():
if not _path:
self.warnPathEmpty(_path_name)
return False

if not os.path.exists(_path):
self.warnSelectedPathDoesNotExist(_path)
return False

if not os.path.isdir(_path):
self.warnSelectedPathNotAFolder(_path)
return False

files = os.listdir(path)
files = myutils.listdir(path)
extensions = set([os.path.splitext(file)[1] for file in files])
if len(extensions) > 1:
self.warnMultipleExtensionsPresent(path, extensions)
Expand All @@ -17558,6 +17624,11 @@ def validate(self):
def folderPath(self):
return self.folderPathLineEdit.text()

def channelNames(self):
channel_names = self.channelNamesLineEdit.text().split(',')
channel_names = [ch.strip() for ch in channel_names]
return channel_names

def ok_cb(self):
proceed = self.validate()
if not proceed:
Expand All @@ -17566,10 +17637,14 @@ def ok_cb(self):
self.selectedFolderPath = self.folderPath()
self.filesStructure = self.filesStructureCombobox.currentText()
is_multiple_files = self.filesStructure.find('separated') != -1
is_separate_channels = 'channels separated' in self.filesStructure
dst_folderpath = self.dstfolderPathLineEdit.text()
self.init_macro_args = (
self.folderPath(), is_multiple_files,
self.channelNamesLineEdit.text().split(',')

self.folderPath(),
is_multiple_files,
is_separate_channels,
dst_folderpath,
self.channelNames(),
)
self.cancel = False
self.close()
Expand Down
72 changes: 52 additions & 20 deletions cellacdc/dataStruct.py
Original file line number Diff line number Diff line change
Expand Up @@ -2210,11 +2210,11 @@ def __init__(self, acdcLauncher):

def askSelectInstalledFiji(self):
if os.path.exists(myutils.get_fiji_exec_folderpath()):
return
return False, False

txt = html_utils.paragraph(f"""
Do you already have Fiji (ImageJ)?<br><br>
If yes, click on the <code>Select Fiji location</code> button below<br>
If yes, click on the <code>Select Fiji location</code> button below
and select where you have the Fiji app.<br><br>
Alternatively, you can ignore this and let Cell-ACDC automatically
download Fiji for you.
Expand All @@ -2226,6 +2226,7 @@ def askSelectInstalledFiji(self):
widgets.DownloadPushButton('Download Fiji.app')
)
msg = widgets.myMessageBox(wrapText=False)
msg.did_user_selected_fiji = False
msg.question(
self.acdcLauncher, 'Select Fiji location', txt,
buttonsTexts=(
Expand All @@ -2239,7 +2240,7 @@ def askSelectInstalledFiji(self):
)
msg.exec_()

return msg.cancel
return msg.cancel, msg.did_user_selected_fiji

def selectFijiLocation(self, checked=True, messagebox=None):
import qtpy.compat
Expand All @@ -2253,15 +2254,14 @@ def selectFijiLocation(self, checked=True, messagebox=None):

from cellacdc import fiji_location_filepath
with open(fiji_location_filepath, 'w') as txt:
txt.write(
os.path.join(filepath, 'Contents', 'MacOS', 'ImageJ-macosx')
)
txt.write(os.path.join(filepath))

messagebox.did_user_selected_fiji = True
messagebox.cancel = False
messagebox.close()

def run(self):
cancel = self.askSelectInstalledFiji()
cancel, did_user_selected_fiji = self.askSelectInstalledFiji()
if cancel:
self.cancel()
return
Expand All @@ -2278,18 +2278,23 @@ def run(self):
fiji_success = myutils.test_fiji_base_command(self.logger.info)
commands = None
if not fiji_success:
try:
shutil.rmtree(acdc_fiji_path)
except Exception as err:
pass
if not did_user_selected_fiji:
try:
shutil.rmtree(acdc_fiji_path)
except Exception as err:
pass

href = html_utils.href_tag('here', urls.fiji_downloads)
note_download_txt = (f"""
Before continuing, Fiji will be <b>automatically downloaded
now</b>.<br><br>
If the download fails, please download the zip file from {href}
and unzip it in the following location:
""")
txt = f'{txt}<br><br>{note_download_txt}'
admon = html_utils.to_admonition(
note_download_txt, admonition_type='note'
)
txt = f'{txt}<br>{admon}'
commands = (acdc_fiji_path,)

txt = html_utils.paragraph(txt)
Expand All @@ -2311,28 +2316,55 @@ def run(self):
self.cancel()
return

macro_filepath = fiji_macros.init_macro(*win.init_macro_args)
init_macro_args = win.init_macro_args
is_separate_channels = init_macro_args[2]
macro_filepath = fiji_macros.init_macro(*init_macro_args)
macro_command = fiji_macros.command_run_macro(macro_filepath)

txt = html_utils.paragraph("""
txt = ("""
Cell-ACDC will now run the macro in the terminal.<br><br>
During the process, the <b>GUI will be unresponsive</b>, while
progress will be displayed in the terminal.<br><br>
If you prefer, you can stop the process now and run the command
yourself, or even run the macro directly from the Fiji GUI.<br><br>
Command to run the macro:
yourself, or even run the macro directly from the Fiji GUI.<br>
""")

if is_separate_channels:
important_admon = html_utils.to_admonition(
'There are still steps to run after the macro finishes, so '
'if you run it yourself, '
'please close this dialogue only after the macro completes.',
admonition_type='important'
)
txt = f'{txt}{important_admon}'

txt = f'{txt}<br>Command to run the macro:'

txt = html_utils.paragraph(txt)
msg = widgets.myMessageBox(wrapText=False)
msg.information(
_, _, okButton = msg.information(
self.acdcLauncher, 'Fiji macro command', txt,
buttonsTexts=('Cancel', 'Ok'),
commands=(macro_filepath)
buttonsTexts=('Cancel', 'I already ran the macro', 'Ok'),
commands=(macro_filepath),
path_to_browse=os.path.dirname(macro_filepath)
)
if msg.cancel:
self.cancel()
return

success = fiji_macros.run_macro(macro_command)
success = True
if msg.clickedButton == okButton:
success = fiji_macros.run_macro(macro_command)

files_folderpath = init_macro_args[0]
dst_folderpath = init_macro_args[3]
channels = init_macro_args[4]
if success and is_separate_channels:
self.logger.info('Moving files to Position folders...')
success = io.move_separate_channels_tiffs_to_pos_folders(
dst_folderpath, channels
)

if success:
txt = html_utils.paragraph("""
Macro execution completed.
Expand Down
20 changes: 16 additions & 4 deletions cellacdc/fiji_macros/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,21 @@
def init_macro(
files_folderpath: os.PathLike,
is_multiple_files: bool,
is_separate_channels: bool,
dst_folderpath: os.PathLike,
channels: Iterable[str]
):
macros_folderpath = os.path.join(acdc_fiji_path, 'macros')
os.makedirs(macros_folderpath, exist_ok=True)

macros_template_folderpath = os.path.dirname(os.path.abspath(__file__))
macro_template_filename = (
'multiple_files.ijm' if is_multiple_files else 'single_file.ijm'
)
if is_separate_channels:
macro_template_filename = 'multiple_files_separate_channels.ijm'
elif is_multiple_files:
macro_template_filename = 'multiple_files.ijm'
else:
macro_template_filename = 'single_file.ijm'

macro_template_filepath = os.path.join(
macros_template_folderpath, macro_template_filename
)
Expand All @@ -31,11 +37,17 @@ def init_macro(
'channels = newArray(...)',
f'channels = newArray({channels_macro})'
)

files_path = files_folderpath.replace('\\', '/')
files_path = f'"{files_path}/"'
macro_txt = macro_txt.replace('id = ...', f'id = {files_path}')

date_time = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
dst_folderpath = dst_folderpath.replace('\\', '/')
macro_txt = macro_txt.replace(
'dst_folderpath = ...', f'dst_folderpath = "{dst_folderpath}"'
)

date_time = datetime.datetime.now().strftime(r'%Y-%m-%d_%H-%M-%S')
id = uuid4()
macro_filename = f'{date_time}_{id}_{macro_template_filename}'
macro_filepath = os.path.join(macros_folderpath, macro_filename)
Expand Down
Loading
Loading