Source code for populse_mia.user_interface.pop_ups
# -*- coding: utf-8 -*-
""" Module that defines all the pop-ups used across the software
:Contains:
:Class:
- ClickableLabel
- DefaultValueListCreation
- DefaultValueQLineEdit
- PopUpAddPath
- PopUpAddTag
- PopUpCloneTag
- PopUpClosePipeline
- PopUpDataBrowserCurrentSelection
- PopUpDeletedProject
- PopUpDeleteProject
- PopUpFilterSelection
- PopUpInformation
- PopUpInheritanceDict
- PopUpMultipleSort
- PopUpNewProject
- PopUpOpenProject
- PopUpPreferences
- PopUpProperties
- PopUpQuit
- PopUpRemoveScan
- PopUpRemoveTag
- PopUpSaveProjectAs
- PopUpSeeAllProjects
- PopUpSelectFilter
- PopUpSelectIteration
- PopUpTagSelection (must precede PopUpSelectTag)
- PopUpSelectTag
- PopUpSelectTagCountTable
- PopUpShowHistory
- PopUpVisualizedTags
- QLabel_clickable
"""
##########################################################################
# Populse_mia - Copyright (C) IRMaGe/CEA, 2018
# Distributed under the terms of the CeCILL license, as published by
# the CEA-CNRS-INRIA. Refer to the LICENSE file or to
# http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html
# for details.
##########################################################################
import ast
import glob
import hashlib
import os
import platform
import shutil
import subprocess
from datetime import datetime
from functools import partial
import yaml
# Capsul imports
from capsul.api import capsul_engine
from capsul.pipeline.pipeline_nodes import PipelineNode
from capsul.qt_gui.widgets.pipeline_developer_view import PipelineDeveloperView
from capsul.qt_gui.widgets.settings_editor import SettingsEditor
# Populse_db imports
from populse_db.database import (
FIELD_TYPE_BOOLEAN,
FIELD_TYPE_DATE,
FIELD_TYPE_DATETIME,
FIELD_TYPE_FLOAT,
FIELD_TYPE_INTEGER,
FIELD_TYPE_LIST_BOOLEAN,
FIELD_TYPE_LIST_DATE,
FIELD_TYPE_LIST_DATETIME,
FIELD_TYPE_LIST_FLOAT,
FIELD_TYPE_LIST_INTEGER,
FIELD_TYPE_LIST_STRING,
FIELD_TYPE_LIST_TIME,
FIELD_TYPE_STRING,
FIELD_TYPE_TIME,
)
# PyQt5 imports
from PyQt5 import Qt, QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QCoreApplication, pyqtSignal
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtWidgets import (
QApplication,
QCheckBox,
QComboBox,
QDialog,
QDialogButtonBox,
QFileDialog,
QFormLayout,
QHBoxLayout,
QHeaderView,
QInputDialog,
QLabel,
QLineEdit,
QMessageBox,
QPlainTextEdit,
QPushButton,
QRadioButton,
QScrollArea,
QSplitter,
QTableWidget,
QTableWidgetItem,
QTreeWidget,
QTreeWidgetItem,
QVBoxLayout,
QWidget,
)
# Populse_mia imports
from populse_mia.data_manager.database_mia import (
TAG_ORIGIN_USER,
TAG_UNIT_DEGREE,
TAG_UNIT_HZPIXEL,
TAG_UNIT_MHZ,
TAG_UNIT_MM,
TAG_UNIT_MS,
)
from populse_mia.data_manager.project import (
BRICK_EXEC,
BRICK_EXEC_TIME,
BRICK_INIT,
BRICK_INIT_TIME,
BRICK_INPUTS,
BRICK_NAME,
BRICK_OUTPUTS,
COLLECTION_BRICK,
COLLECTION_CURRENT,
COLLECTION_HISTORY,
COLLECTION_INITIAL,
HISTORY_BRICKS,
HISTORY_PIPELINE,
TAG_CHECKSUM,
TAG_FILENAME,
TAG_HISTORY,
TAG_TYPE,
TYPE_MAT,
TYPE_NII,
TYPE_TXT,
TYPE_UNKNOWN,
Project,
)
from populse_mia.software_properties import Config
from populse_mia.user_interface.data_browser import data_browser
[docs]
class ClickableLabel(QLabel):
"""
QLabel with a clicked signal
.. Methods:
- mousePressEvent: overrides the mousePressEvent method by emitting
the clicked signal
"""
# Signal that will be emitted when the widget is clicked
clicked = pyqtSignal()
[docs]
def mousePressEvent(self, event):
"""
Overrides the mousePressEvent method by emitting the clicked signal
:param event: clicked event
"""
self.clicked.emit()
[docs]
class DefaultValueListCreation(QDialog):
"""Widget that is called when to create a list's default value.
.. Methods:
- add_element: one more element added to the list
- default_init_table: default init table when no previous value
- remove_element: removes the last element of the list
- resize_table: to resize the pop up depending on the table
- update_default_value: checks if the values are correct and updates
the parent value
"""
[docs]
def __init__(self, parent, type):
"""Initialization.
:param parent: the DefaultValueQLineEdit parent object
:param type: type of the list (e.g. list of int, list of float, etc.)
"""
super().__init__()
self.setModal(True)
# Current type chosen
self.type = type
self.parent = parent
self.setWindowTitle("Adding a " + self.type.replace("_", " of "))
# The table that will be filled
self.table = QtWidgets.QTableWidget()
self.table.setRowCount(1)
value = self.parent.text()
if value != "":
try:
list_value = ast.literal_eval(value)
if isinstance(list_value, list):
# If the previous value was already a list, we fill it
self.table.setColumnCount(len(list_value))
for i in range(0, self.table.columnCount()):
item = QtWidgets.QTableWidgetItem()
item.setText(str(list_value[i]))
self.table.setItem(0, i, item)
else:
self.default_init_table()
except Exception:
self.default_init_table()
else:
self.default_init_table()
self.resize_table()
# Ok button
self.ok_button = QtWidgets.QPushButton("Ok")
self.ok_button.clicked.connect(self.update_default_value)
# Cancel button
cancel_button = QtWidgets.QPushButton("Cancel")
cancel_button.clicked.connect(self.close)
# Button to add an element to the list
sources_images_dir = Config().getSourceImageDir()
self.add_element_label = ClickableLabel()
self.add_element_label.setObjectName("plus")
add_element_picture = QtGui.QPixmap(
os.path.relpath(os.path.join(sources_images_dir, "green_plus.png"))
)
add_element_picture = add_element_picture.scaledToHeight(15)
self.add_element_label.setPixmap(add_element_picture)
self.add_element_label.clicked.connect(self.add_element)
# Button to remove the last element of the list
self.remove_element_label = ClickableLabel()
self.remove_element_label.setObjectName("minus")
remove_element_picture = QtGui.QPixmap(
os.path.relpath(os.path.join(sources_images_dir, "red_minus.png"))
)
remove_element_picture = remove_element_picture.scaledToHeight(20)
self.remove_element_label.setPixmap(remove_element_picture)
self.remove_element_label.clicked.connect(self.remove_element)
# Layouts
self.v_box_final = QVBoxLayout()
self.h_box_final = QHBoxLayout()
self.list_layout = QHBoxLayout()
self.h_box_final.addWidget(self.ok_button)
self.h_box_final.addWidget(cancel_button)
self.list_layout.addWidget(self.table)
self.list_layout.addWidget(self.remove_element_label)
self.list_layout.addWidget(self.add_element_label)
self.v_box_final.addLayout(self.list_layout)
self.v_box_final.addLayout(self.h_box_final)
self.setLayout(self.v_box_final)
[docs]
def add_element(self):
"""One more element added to the list."""
self.table.setColumnCount(self.table.columnCount() + 1)
item = QtWidgets.QTableWidgetItem()
self.table.setItem(0, self.table.columnCount() - 1, item)
self.resize_table()
[docs]
def default_init_table(self):
"""
Default init table when no previous value
"""
# Table filled with a single element at the beginning if no value
self.table.setColumnCount(1)
item = QtWidgets.QTableWidgetItem()
self.table.setItem(0, 0, item)
[docs]
def remove_element(self):
"""Removes the last element of the list."""
if self.table.columnCount() > 1:
self.table.setColumnCount(self.table.columnCount() - 1)
self.resize_table()
self.adjustSize()
[docs]
def resize_table(self):
"""To resize the pop up depending on the table."""
self.table.resizeColumnsToContents()
total_width = 0
total_height = 0
i = 0
while i < self.table.columnCount():
total_width += self.table.columnWidth(i)
total_height += self.table.rowHeight(i)
i += 1
if total_width + 20 < 900:
self.table.setFixedWidth(total_width + 20)
self.table.setFixedHeight(total_height + 25)
else:
self.table.setFixedWidth(900)
self.table.setFixedHeight(total_height + 40)
[docs]
def update_default_value(self):
"""Checks if the values are correct and updates the parent value."""
database_value = []
valid_values = True
# For each value
for i in range(0, self.table.columnCount()):
item = self.table.item(0, i)
text = item.text()
try:
if self.type == FIELD_TYPE_LIST_INTEGER:
database_value.append(int(text))
elif self.type == FIELD_TYPE_LIST_FLOAT:
database_value.append(float(text))
elif self.type == FIELD_TYPE_LIST_BOOLEAN:
if text == "True":
database_value.append(True)
elif text == "False":
database_value.append(False)
else:
raise ValueError("Not a boolean value")
elif self.type == FIELD_TYPE_LIST_STRING:
database_value.append(str(text))
elif self.type == FIELD_TYPE_LIST_DATE:
format = "%d/%m/%Y"
datetime.strptime(text, format).date()
database_value.append(text)
elif self.type == FIELD_TYPE_LIST_DATETIME:
format = "%d/%m/%Y %H:%M:%S.%f"
datetime.strptime(text, format)
database_value.append(text)
elif self.type == FIELD_TYPE_LIST_TIME:
format = "%H:%M:%S.%f"
datetime.strptime(text, format).time()
database_value.append(text)
except Exception:
# Error if invalid value
valid_values = False
msg = QMessageBox()
msg.setIcon(QMessageBox.Warning)
msg.setText("Invalid value")
msg.setInformativeText(
"The value "
+ text
+ " is invalid with the type "
+ self.type
)
msg.setWindowTitle("Warning")
msg.setStandardButtons(QMessageBox.Ok)
msg.buttonClicked.connect(msg.close)
msg.exec()
break
if valid_values:
self.parent.setText(str(database_value))
self.close()
[docs]
class DefaultValueQLineEdit(QtWidgets.QLineEdit):
"""Overrides the QLineEdit for the default value.
We need to override the mousePressEvent.
.. Methods:
- mousePressEvent: mouse pressed on the QLineEdit
"""
[docs]
def mousePressEvent(self, event):
"""Mouse pressed on the QLineEdit.
:param event: used ?
"""
if self.parent.type.startswith("list_"):
# We display the pop up to create the list if the checkbox is
# checked, otherwise we do nothing
self.list_creation = DefaultValueListCreation(
self, self.parent.type
)
self.list_creation.show()
[docs]
class PopUpAddPath(QDialog):
"""Is called when the user wants to add a document to the project
without importing from MRI Files Manager (File > Import).
.. Methods:
- file_to_choose: lets the user choose a file to import
- find_type: tries to find the document type when the document is
changed
- save_path: adds the path to the database and the data browser
"""
[docs]
def __init__(self, project, databrowser):
"""Initialization of the PopUp AddPath.
:param project: current project in the software
:param databrowser: data browser instance of the software
"""
super().__init__()
self.project = project
self.databrowser = databrowser
self.setWindowTitle("Add a document")
self.setModal(True)
vbox_layout = QVBoxLayout()
hbox_layout = QHBoxLayout()
file_label = QLabel("File: ")
self.file_line_edit = QLineEdit()
self.file_line_edit.setFixedWidth(300)
self.file_line_edit.textChanged.connect(self.find_type)
file_button = QPushButton("Choose a document")
file_button.clicked.connect(self.file_to_choose)
hbox_layout.addWidget(file_label)
hbox_layout.addWidget(self.file_line_edit)
hbox_layout.addWidget(file_button)
vbox_layout.addLayout(hbox_layout)
hbox_layout = QHBoxLayout()
type_label = QLabel("Type: ")
self.type_line_edit = QLineEdit()
hbox_layout.addWidget(type_label)
hbox_layout.addWidget(self.type_line_edit)
vbox_layout.addLayout(hbox_layout)
hbox_layout = QHBoxLayout()
self.ok_button = QPushButton("Ok")
self.ok_button.clicked.connect(self.save_path)
cancel_button = QPushButton("Cancel")
cancel_button.clicked.connect(self.close)
hbox_layout.addWidget(self.ok_button)
hbox_layout.addWidget(cancel_button)
vbox_layout.addLayout(hbox_layout)
self.setLayout(vbox_layout)
[docs]
def file_to_choose(self):
"""Lets the user choose a file to import."""
fname, _ = QFileDialog.getOpenFileNames(
self, "Choose a document to import", os.path.expanduser("~")
)
if fname != []:
self.file_line_edit.setText(str(fname))
[docs]
def find_type(self):
"""Tries to find the document type when the document is changed."""
new_file = self.file_line_edit.text()
new_file = ast.literal_eval(new_file)
new_type = []
for elmt in new_file:
filename, file_extension = os.path.splitext(elmt)
if file_extension == ".nii":
new_type.append(TYPE_NII)
elif file_extension == ".mat":
new_type.append(TYPE_MAT)
elif file_extension == ".txt":
new_type.append(TYPE_TXT)
else:
new_type.append(TYPE_UNKNOWN)
self.type_line_edit.setText(str(new_type))
[docs]
def save_path(self):
"""Adds the path to the database and the data browser."""
path_list = self.file_line_edit.text()
if path_list != "":
path_list = ast.literal_eval(path_list)
else:
path_list = [path_list]
path_type_list = self.type_line_edit.text()
if path_type_list != "":
path_type_list = ast.literal_eval(path_type_list)
else:
path_type_list = [path_type_list]
for path, path_type in zip(path_list, path_type_list):
docInDb = [
os.path.basename(i)
for i in self.project.session.get_documents_names(
COLLECTION_CURRENT
)
]
self.project.unsavedModifications = True
if os.path.basename(path) in docInDb:
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Warning)
self.msg.setText("- {0} -".format(os.path.basename(path)))
self.msg.setInformativeText(
"The document '{0}' \n "
"already exists in the Data Browser!".format(path)
)
self.msg.setWindowTitle("Warning: existing data!")
self.msg.setStandardButtons(QMessageBox.Ok)
self.msg.buttonClicked.connect(self.msg.close)
self.msg.show()
elif path != "" and os.path.exists(path) and path_type != "":
# For history
history_maker = []
history_maker.append("add_scans")
path = os.path.relpath(path)
filename = os.path.basename(path)
copy_path = os.path.join(
self.project.folder, "data", "downloaded_data", filename
)
shutil.copy(path, copy_path)
with open(path, "rb") as scan_file:
data = scan_file.read()
checksum = hashlib.md5(data).hexdigest()
path = os.path.join("data", "downloaded_data", filename)
self.project.session.add_document(COLLECTION_CURRENT, path)
self.project.session.add_document(COLLECTION_INITIAL, path)
values_added = []
self.project.session.add_value(
COLLECTION_INITIAL, path, TAG_TYPE, path_type
)
self.project.session.add_value(
COLLECTION_CURRENT, path, TAG_TYPE, path_type
)
values_added.append([path, TAG_TYPE, path_type, path_type])
self.project.session.add_value(
COLLECTION_INITIAL, path, TAG_CHECKSUM, checksum
)
self.project.session.add_value(
COLLECTION_CURRENT, path, TAG_CHECKSUM, checksum
)
values_added.append([path, TAG_CHECKSUM, checksum, checksum])
# For history
history_maker.append([path])
history_maker.append(values_added)
self.project.undos.append(history_maker)
self.project.redos.clear()
# Databrowser updated
(self.databrowser.table_data.scans_to_visualize) = (
self.project.session.get_documents_names(
COLLECTION_CURRENT
)
)
(self.databrowser.table_data.scans_to_search) = (
self.project.session.get_documents_names(
COLLECTION_CURRENT
)
)
self.databrowser.table_data.add_columns()
self.databrowser.table_data.fill_headers()
self.databrowser.table_data.add_rows([path])
self.databrowser.reset_search_bar()
self.databrowser.frame_advanced_search.setHidden(True)
self.databrowser.advanced_search.rows = []
self.close()
else:
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Warning)
self.msg.setText("Invalid arguments")
self.msg.setInformativeText(
"The path must exist.\nThe path type can't be empty."
)
self.msg.setWindowTitle("Warning")
self.msg.setStandardButtons(QMessageBox.Ok)
self.msg.buttonClicked.connect(self.msg.close)
self.msg.show()
[docs]
class PopUpAddTag(QDialog):
"""Is called when the user wants to add a tag to the project.
.. Methods:
- ok_action: verifies that each field is correct and send the new tag
to the data browser
- on_activated: type updated
"""
# Signal that will be emitted at the end to tell that the project
# has been created
signal_add_tag = pyqtSignal()
[docs]
def __init__(self, databrowser, project):
"""Initialization.
:param project: current project in the software
:param databrowser: data browser instance of the software
:param type: type of the tag to add
"""
super().__init__()
self.project = project
self.databrowser = databrowser
self.type = FIELD_TYPE_STRING # Type is string by default
self.setObjectName("Add a tag")
# The 'OK' push button
self.push_button_ok = QtWidgets.QPushButton(self)
self.push_button_ok.setObjectName("push_button_ok")
# The 'Tag name' label
self.label_tag_name = QtWidgets.QLabel(self)
self.label_tag_name.setTextFormat(QtCore.Qt.AutoText)
self.label_tag_name.setObjectName("tag_name")
# The 'Tag name' text edit
self.text_edit_tag_name = QtWidgets.QLineEdit(self)
self.text_edit_tag_name.setObjectName("textEdit_tag_name")
# The 'Default value' label
self.label_default_value = QtWidgets.QLabel(self)
self.label_default_value.setTextFormat(QtCore.Qt.AutoText)
self.label_default_value.setObjectName("default_value")
# The 'Default value' text edit
self.text_edit_default_value = DefaultValueQLineEdit(self)
self.text_edit_default_value.setObjectName("textEdit_default_value")
# By default the tag is a string
self.text_edit_default_value.setText("Undefined")
# The 'Description value' label
self.label_description_value = QtWidgets.QLabel(self)
self.label_description_value.setTextFormat(QtCore.Qt.AutoText)
self.label_description_value.setObjectName("description_value")
# The 'Description value' text edit
self.text_edit_description_value = QtWidgets.QLineEdit(self)
self.text_edit_description_value.setObjectName(
"textEdit_description_value"
)
# The 'Unit value' label
self.label_unit_value = QtWidgets.QLabel(self)
self.label_unit_value.setTextFormat(QtCore.Qt.AutoText)
self.label_unit_value.setObjectName("unit_value")
# The 'Unit value' text edit
self.combo_box_unit = QtWidgets.QComboBox(self)
self.combo_box_unit.setObjectName("combo_box_unit")
self.combo_box_unit.addItem(None)
self.combo_box_unit.addItem(TAG_UNIT_MS)
self.combo_box_unit.addItem(TAG_UNIT_MM)
self.combo_box_unit.addItem(TAG_UNIT_MHZ)
self.combo_box_unit.addItem(TAG_UNIT_HZPIXEL)
self.combo_box_unit.addItem(TAG_UNIT_DEGREE)
# The 'Type' label
self.label_type = QtWidgets.QLabel(self)
self.label_type.setTextFormat(QtCore.Qt.AutoText)
self.label_type.setObjectName("type")
# The 'Type' text edit
self.combo_box_type = QtWidgets.QComboBox(self)
self.combo_box_type.setObjectName("combo_box_type")
self.combo_box_type.addItem("String")
self.combo_box_type.addItem("Integer")
self.combo_box_type.addItem("Float")
self.combo_box_type.addItem("Boolean")
self.combo_box_type.addItem("Date")
self.combo_box_type.addItem("Datetime")
self.combo_box_type.addItem("Time")
self.combo_box_type.addItem("String List")
self.combo_box_type.addItem("Integer List")
self.combo_box_type.addItem("Float List")
self.combo_box_type.addItem("Boolean List")
self.combo_box_type.addItem("Date List")
self.combo_box_type.addItem("Datetime List")
self.combo_box_type.addItem("Time List")
self.combo_box_type.currentTextChanged.connect(self.on_activated)
# Layouts
v_box_labels = QVBoxLayout()
v_box_labels.addWidget(self.label_tag_name)
v_box_labels.addWidget(self.label_default_value)
v_box_labels.addWidget(self.label_description_value)
v_box_labels.addWidget(self.label_unit_value)
v_box_labels.addWidget(self.label_type)
v_box_edits = QVBoxLayout()
v_box_edits.addWidget(self.text_edit_tag_name)
default_layout = QHBoxLayout()
default_layout.addWidget(self.text_edit_default_value)
v_box_edits.addLayout(default_layout)
v_box_edits.addWidget(self.text_edit_description_value)
v_box_edits.addWidget(self.combo_box_unit)
v_box_edits.addWidget(self.combo_box_type)
h_box_top = QHBoxLayout()
h_box_top.addLayout(v_box_labels)
h_box_top.addSpacing(50)
h_box_top.addLayout(v_box_edits)
h_box_ok = QHBoxLayout()
h_box_ok.addStretch(1)
h_box_ok.addWidget(self.push_button_ok)
v_box_total = QVBoxLayout()
v_box_total.addLayout(h_box_top)
v_box_total.addLayout(h_box_ok)
self.setLayout(v_box_total)
# Filling the title of the labels and push buttons
_translate = QtCore.QCoreApplication.translate
self.push_button_ok.setText(_translate("Add a tag", "OK"))
self.label_tag_name.setText(_translate("Add a tag", "Tag name:"))
self.label_default_value.setText(
_translate("Add a tag", "Default value:")
)
self.label_description_value.setText(
_translate("Add a tag", "Description:")
)
self.label_unit_value.setText(_translate("Add a tag", "Unit:"))
self.label_type.setText(_translate("Add a tag", "Tag type:"))
# Connecting the OK push button
self.push_button_ok.clicked.connect(self.ok_action)
self.setMinimumWidth(700)
self.setWindowTitle("Add a tag")
self.setModal(True)
[docs]
def ok_action(self):
"""Verifies that each field is correct and send the new tag
to the data browser.
"""
# import check_value_type only here to prevent circular import issue
from populse_mia.utils import check_value_type
name_already_exists = False
# Tag name checked
if (
self.text_edit_tag_name.text()
in self.project.session.get_fields_names(COLLECTION_CURRENT)
):
name_already_exists = True
# Tag name can't be empty
if self.text_edit_tag_name.text() == "":
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Critical)
self.msg.setText("The tag name cannot be empty")
self.msg.setInformativeText("Please enter a tag name")
self.msg.setWindowTitle("Error")
self.msg.setStandardButtons(QMessageBox.Close)
self.msg.exec()
return
# Tag name can't exist already
elif name_already_exists:
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Critical)
self.msg.setText("This tag name already exists")
self.msg.setInformativeText("Please select another tag name")
self.msg.setWindowTitle("Error")
self.msg.setStandardButtons(QMessageBox.Close)
self.msg.exec()
return
# Default value checked
wrong_default_value_type = not check_value_type(
self.text_edit_default_value.text(), self.type, False
)
# The default value must be valid
if wrong_default_value_type:
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Critical)
self.msg.setText("Invalid default value")
self.msg.setInformativeText(
"The default value '{0}' is invalid "
"with the '{1}' type!".format(
self.text_edit_default_value.text(), self.type
)
)
self.msg.setWindowTitle("Error")
self.msg.setStandardButtons(QMessageBox.Close)
self.msg.exec()
return
# Everything is Ok
self.accept()
self.new_tag_name = self.text_edit_tag_name.text()
self.new_default_value = self.text_edit_default_value.text()
self.new_tag_description = self.text_edit_description_value.text()
self.new_tag_unit = self.combo_box_unit.currentText()
if self.new_tag_unit == "":
self.new_tag_unit = None
self.databrowser.add_tag_infos(
self.new_tag_name,
self.new_default_value,
self.type,
self.new_tag_description,
self.new_tag_unit,
)
self.close()
[docs]
def on_activated(self, text):
"""Type updated.
:param text: New type
"""
if text == "String":
self.type = FIELD_TYPE_STRING
self.text_edit_default_value.setText("Undefined")
elif text == "Integer":
self.type = FIELD_TYPE_INTEGER
self.text_edit_default_value.setText("0")
elif text == "Float":
self.type = FIELD_TYPE_FLOAT
self.text_edit_default_value.setText("0.0")
elif text == "Boolean":
self.type = FIELD_TYPE_BOOLEAN
self.text_edit_default_value.setText("True")
elif text == "Date":
self.type = FIELD_TYPE_DATE
date_value = datetime.now()
date_format = date_value.strftime("%d/%m/%Y")
self.text_edit_default_value.setText(date_format)
elif text == "Datetime":
self.type = FIELD_TYPE_DATETIME
datetime_value = datetime.now()
datetime_format = datetime_value.strftime("%d/%m/%Y %H:%M:%S.%f")
self.text_edit_default_value.setText(datetime_format)
elif text == "Time":
self.type = FIELD_TYPE_TIME
time_value = datetime.now()
time_format = time_value.strftime("%H:%M:%S.%f")
self.text_edit_default_value.setText(time_format)
elif text == "String List":
self.type = FIELD_TYPE_LIST_STRING
self.text_edit_default_value.setText("['Undefined', 'Undefined']")
elif text == "Integer List":
self.type = FIELD_TYPE_LIST_INTEGER
self.text_edit_default_value.setText("[0, 0]")
elif text == "Float List":
self.type = FIELD_TYPE_LIST_FLOAT
self.text_edit_default_value.setText("[0.0, 0.0]")
elif text == "Boolean List":
self.type = FIELD_TYPE_LIST_BOOLEAN
self.text_edit_default_value.setText("[True, True]")
elif text == "Date List":
self.type = FIELD_TYPE_LIST_DATE
date_value = datetime.now()
date_format = date_value.strftime("%d/%m/%Y")
self.text_edit_default_value.setText(
"{}".format([date_format, date_format])
)
elif text == "Datetime List":
self.type = FIELD_TYPE_LIST_DATETIME
datetime_value = datetime.now()
datetime_format = datetime_value.strftime("%d/%m/%Y %H:%M:%S.%f")
self.text_edit_default_value.setText(
"{}".format([datetime_format, datetime_format])
)
elif text == "Time List":
self.type = FIELD_TYPE_LIST_TIME
time_value = datetime.now()
time_format = time_value.strftime("%H:%M:%S.%f")
self.text_edit_default_value.setText(
"{}".format([time_format, time_format])
)
[docs]
class PopUpCloneTag(QDialog):
"""Is called when the user wants to clone a tag to the project.
.. Methods:
- ok_action: verifies the specified name is correct and send the
information to the data browser
- search_str: matches the searched pattern with the tags of the project
"""
# Signal that will be emitted at the end to tell that the project
# has been created
signal_clone_tag = pyqtSignal()
[docs]
def __init__(self, databrowser, project):
"""Initialization.
:param project: current project in the software
:param databrowser: data browser instance of the software
"""
super().__init__()
self.setWindowTitle("Clone a tag")
self.databrowser = databrowser
self.project = project
self.setModal(True)
_translate = QtCore.QCoreApplication.translate
self.setObjectName("Clone a tag")
# The 'OK' push button
self.push_button_ok = QtWidgets.QPushButton(self)
self.push_button_ok.setObjectName("push_button_ok")
self.push_button_ok.setText(_translate("Clone a tag", "OK"))
# The 'New tag name' text edit
self.line_edit_new_tag_name = QtWidgets.QLineEdit(self)
self.line_edit_new_tag_name.setObjectName("lineEdit_new_tag_name")
# The 'New tag name' label
self.label_new_tag_name = QtWidgets.QLabel(self)
self.label_new_tag_name.setTextFormat(QtCore.Qt.AutoText)
self.label_new_tag_name.setObjectName("label_new_tag_name")
self.label_new_tag_name.setText(
_translate("Clone a tag", "New tag name:")
)
hbox_buttons = QHBoxLayout()
hbox_buttons.addWidget(self.label_new_tag_name)
hbox_buttons.addWidget(self.line_edit_new_tag_name)
hbox_buttons.addStretch(1)
hbox_buttons.addWidget(self.push_button_ok)
# The "Tag list" label
self.label_tag_list = QtWidgets.QLabel(self)
self.label_tag_list.setTextFormat(QtCore.Qt.AutoText)
self.label_tag_list.setObjectName("label_tag_list")
self.label_tag_list.setText(
_translate("Clone a tag", "Available tags:")
)
self.search_bar = QtWidgets.QLineEdit(self)
self.search_bar.setObjectName("lineEdit_search_bar")
self.search_bar.setPlaceholderText("Search")
self.search_bar.textChanged.connect(partial(self.search_str, project))
hbox_top = QHBoxLayout()
hbox_top.addWidget(self.label_tag_list)
hbox_top.addStretch(1)
hbox_top.addWidget(self.search_bar)
# The list of tags
self.list_widget_tags = QtWidgets.QListWidget(self)
self.list_widget_tags.setObjectName("listWidget_tags")
self.list_widget_tags.setSelectionMode(
QtWidgets.QAbstractItemView.SingleSelection
)
vbox = QVBoxLayout()
vbox.addLayout(hbox_top)
vbox.addWidget(self.list_widget_tags)
vbox.addLayout(hbox_buttons)
self.setLayout(vbox)
tags_lists = project.session.get_fields_names(COLLECTION_CURRENT)
tags_lists.remove(TAG_CHECKSUM)
tags_lists.remove(TAG_HISTORY)
for tag in tags_lists:
item = QtWidgets.QListWidgetItem()
self.list_widget_tags.addItem(item)
item.setText(_translate("Dialog", tag))
self.list_widget_tags.sortItems()
self.setLayout(vbox)
# Connecting the OK push button
self.push_button_ok.clicked.connect(lambda: self.ok_action(project))
[docs]
def ok_action(self, project):
"""Verifies the specified name is correct and send the
information to the data browser.
:param project: current project
"""
name_already_exists = False
for tag in project.session.get_fields(COLLECTION_CURRENT):
if tag.field_name == self.line_edit_new_tag_name.text():
name_already_exists = True
if name_already_exists:
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Critical)
self.msg.setText("This tag name already exists")
self.msg.setInformativeText("Please select another tag name")
self.msg.setWindowTitle("Error")
self.msg.setStandardButtons(QMessageBox.Ok)
self.msg.buttonClicked.connect(self.msg.close)
self.msg.show()
elif self.line_edit_new_tag_name.text() == "":
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Critical)
self.msg.setText("The tag name can't be empty")
self.msg.setInformativeText("Please select a tag name")
self.msg.setWindowTitle("Error")
self.msg.setStandardButtons(QMessageBox.Ok)
self.msg.buttonClicked.connect(self.msg.close)
self.msg.show()
elif len(self.list_widget_tags.selectedItems()) == 0:
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Critical)
self.msg.setText("The tag to clone must be selected")
self.msg.setInformativeText("Please select a tag to clone")
self.msg.setWindowTitle("Error")
self.msg.setStandardButtons(QMessageBox.Ok)
self.msg.buttonClicked.connect(self.msg.close)
self.msg.show()
else:
self.accept()
self.tag_to_replace = self.list_widget_tags.selectedItems()[
0
].text()
self.new_tag_name = self.line_edit_new_tag_name.text()
self.databrowser.clone_tag_infos(
self.tag_to_replace, self.new_tag_name
)
self.close()
[docs]
def search_str(self, project, str_search):
"""Matches the searched pattern with the tags of the project.
:param project: current project
:param str_search: string pattern to search
"""
_translate = QtCore.QCoreApplication.translate
return_list = []
tags_lists = project.session.get_fields_names(COLLECTION_CURRENT)
tags_lists.remove(TAG_CHECKSUM)
tags_lists.remove(TAG_HISTORY)
if str_search != "":
for tag in tags_lists:
if str_search.upper() in tag.upper():
return_list.append(tag)
else:
for tag in tags_lists:
return_list.append(tag)
self.list_widget_tags.clear()
for tag_name in return_list:
item = QtWidgets.QListWidgetItem()
self.list_widget_tags.addItem(item)
item.setText(_translate("Dialog", tag_name))
self.list_widget_tags.sortItems()
[docs]
class PopUpClosePipeline(QDialog):
"""Is called when the user closes a pipeline editor that has been modified.
:param bool_save_as: boolean to True if the pipeline needs to be saved
:param bool_exit: boolean to True if we can exit the editor
:param save_as_signal: signal emitted to save the pipeline under
another name
:param do_not_save_signal: signal emitted to close the editor
:param cancel_signal: signal emitted to cancel the action
.. Methods:
- can_exit: returns the value of bool_exit
- cancel_clicked: makes the actions to cancel the action
- do_not_save_clicked: makes the actions not to save the pipeline
- save_as_clicked: makes the actions to save the pipeline
"""
save_as_signal = pyqtSignal()
do_not_save_signal = pyqtSignal()
cancel_signal = pyqtSignal()
[docs]
def __init__(self, pipeline_name):
"""Initialization.
:param pipeline_name: name of the pipeline (basename)
"""
super().__init__()
self.pipeline_name = pipeline_name
self.bool_exit = False
self.bool_save_as = False
self.setWindowTitle("Confirm pipeline closing")
label = QLabel(self)
label.setText(
"Do you want to close the pipeline without saving "
+ self.pipeline_name
+ "?"
)
self.push_button_save_as = QPushButton("Save", self)
self.push_button_do_not_save = QPushButton("Do not save", self)
self.push_button_cancel = QPushButton("Cancel", self)
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(self.push_button_save_as)
hbox.addWidget(self.push_button_do_not_save)
hbox.addWidget(self.push_button_cancel)
hbox.addStretch(1)
vbox = QVBoxLayout()
vbox.addWidget(label)
vbox.addLayout(hbox)
self.setLayout(vbox)
self.push_button_save_as.clicked.connect(self.save_as_clicked)
self.push_button_do_not_save.clicked.connect(self.do_not_save_clicked)
self.push_button_cancel.clicked.connect(self.cancel_clicked)
[docs]
def can_exit(self):
"""Returns the value of bool_exit.
:return: bool_exit value
"""
return self.bool_exit
[docs]
def cancel_clicked(self):
"""Makes the actions to cancel the action."""
self.bool_exit = False
self.close()
[docs]
def do_not_save_clicked(self):
"""Makes the actions not to save the pipeline."""
self.bool_exit = True
self.close()
[docs]
def save_as_clicked(self):
"""Makes the actions to save the pipeline."""
self.save_as_signal.emit()
self.bool_save_as = True
self.bool_exit = True
self.close()
[docs]
class PopUpDataBrowserCurrentSelection(QDialog):
"""Is called to display the current data_browser selection.
.. Methods:
- ok_clicked: updates the "scan_list" attribute of several widgets
"""
[docs]
def __init__(self, project, databrowser, filter, main_window):
"""Initialization.
:param project: current project in the software
:param databrowser: data browser instance of the software
:param filter: list of the current documents in the data browser
:param main_window: main window of the software
"""
super().__init__()
self.project = project
self.databrowser = databrowser
self.filter = filter
self.main_window = main_window
self.setWindowTitle("Confirm the selection")
self.setModal(True)
vbox_layout = QVBoxLayout()
# Adding databrowser table
databrowser_table = data_browser.TableDataBrowser(
self.project, self.databrowser, [TAG_FILENAME], False, False
)
old_scan_list = databrowser_table.scans_to_visualize
databrowser_table.scans_to_visualize = self.filter
databrowser_table.update_visualized_rows(old_scan_list)
buttons = QDialogButtonBox(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel
)
vbox_layout.addWidget(databrowser_table)
vbox_layout.addWidget(buttons)
buttons.accepted.connect(self.ok_clicked)
buttons.rejected.connect(self.close)
self.setLayout(vbox_layout)
screen_resolution = QApplication.instance().desktop().screenGeometry()
width, height = screen_resolution.width(), screen_resolution.height()
self.setMinimumWidth(round(0.5 * width))
self.setMinimumHeight(round(0.8 * height))
[docs]
def ok_clicked(self):
"""Updates the "scan_list" attribute of several widgets."""
self.main_window.pipeline_manager.scan_list = self.filter
self.main_window.pipeline_manager.nodeController.scan_list = (
self.filter
)
self.main_window.pipeline_manager.pipelineEditorTabs.scan_list = (
self.filter
)
self.main_window.pipeline_manager.iterationTable.scan_list = (
self.filter
)
self.databrowser.data_sent = True
self.close()
[docs]
class PopUpDeletedProject(QMessageBox):
"""Indicate the names of deleted project when the software starts."""
[docs]
def __init__(self, deleted_projects):
super().__init__()
message = "These projects have been renamed, moved or deleted:\n"
for deleted_project in deleted_projects:
message += "- {0}\n".format(deleted_project)
self.setIcon(QMessageBox.Warning)
self.setText("Deleted projects")
self.setInformativeText(message)
self.setWindowTitle("Warning")
self.setStandardButtons(QMessageBox.Ok)
self.buttonClicked.connect(self.close)
self.exec()
[docs]
class PopUpDeleteProject(QDialog):
"""Is called when the user wants to delete a project.
.. Methods:
- ok_clicked: delete the selected projects after confirmation
"""
[docs]
def __init__(self, main_window):
"""Initialization."""
super().__init__()
self.setWindowTitle("Delete project")
config = Config()
self.project_path = config.getPathToProjectsFolder()
self.main_window = main_window
project_list = os.listdir(self.project_path)
self.v_box = QVBoxLayout()
# Label
self.label = QLabel("Select projects to delete:")
self.v_box.addWidget(self.label)
self.check_boxes = []
for project in project_list:
if os.path.isdir(os.path.join(self.project_path, project)):
check_box = QCheckBox(project)
self.check_boxes.append(check_box)
self.v_box.addWidget(check_box)
self.h_box_bottom = QHBoxLayout()
self.h_box_bottom.addStretch(1)
# The 'OK' push button
self.push_button_ok = QtWidgets.QPushButton("OK")
self.push_button_ok.setObjectName("pushButton_ok")
self.push_button_ok.clicked.connect(self.ok_clicked)
self.h_box_bottom.addWidget(self.push_button_ok)
# The 'Cancel' push button
self.push_button_cancel = QtWidgets.QPushButton("Cancel")
self.push_button_cancel.setObjectName("pushButton_cancel")
self.push_button_cancel.clicked.connect(self.close)
self.h_box_bottom.addWidget(self.push_button_cancel)
self.scroll = QScrollArea()
self.widget = QWidget()
self.widget.setLayout(self.v_box)
self.scroll.setWidget(self.widget)
self.final = QVBoxLayout()
self.final.addWidget(self.scroll)
self.final_layout = QVBoxLayout()
self.final_layout.addLayout(self.final)
self.final_layout.addLayout(self.h_box_bottom)
self.setLayout(self.final_layout)
[docs]
def ok_clicked(self):
"""Delete the selected projects after confirmation."""
final_values = []
for check_box in self.check_boxes:
if check_box.isChecked():
final_values.append(check_box.text())
reply = None
config = Config()
opened_projects = config.get_opened_projects()
for name in final_values:
project = os.path.join(self.project_path, name)
if reply != QMessageBox.YesToAll and reply != QMessageBox.NoToAll:
msgtext = (
"Do you really want to delete the " + name + " project ?"
)
msg = QMessageBox()
msg.setIcon(QMessageBox.Warning)
title = "populse_mia - Warning: Delete project"
reply = msg.question(
self,
title,
msgtext,
QMessageBox.Yes
| QMessageBox.No
| QMessageBox.YesToAll
| QMessageBox.NoToAll,
)
if reply == QMessageBox.Yes or reply == QMessageBox.YesToAll:
if os.path.abspath(
self.main_window.project.folder
) == os.path.abspath(project):
self.main_window.project = Project(None, True)
self.main_window.update_project("")
if (
os.path.relpath(project)
in self.main_window.saved_projects.pathsList
):
self.main_window.saved_projects.removeSavedProject(
os.path.relpath(project)
)
self.main_window.update_recent_projects_actions()
if os.path.relpath(project) in opened_projects:
opened_projects.remove(os.path.relpath(project))
config.set_opened_projects(opened_projects)
config.saveConfig()
shutil.rmtree(project)
self.accept()
self.close()
[docs]
class PopUpFilterSelection(QDialog):
"""Is called when the user wants to open a filter that has already been
saved.
:Methods:
- cancel_clicked: closes the pop-up
- ok_clicked: actions when the "OK" button is clicked
- search_str: matches the searched pattern with the saved filters
"""
[docs]
def __init__(self, project):
"""Initialization.
:param project: current project in the software
"""
super().__init__()
self.project = project
self.setModal(True)
_translate = QtCore.QCoreApplication.translate
# The "Filter list" label
self.label_filter_list = QtWidgets.QLabel(self)
self.label_filter_list.setTextFormat(QtCore.Qt.AutoText)
self.label_filter_list.setObjectName("label_filter_list")
self.label_filter_list.setText(
_translate("main_window", "Available filters:")
)
# The search bar to search in the list of filters
self.search_bar = QtWidgets.QLineEdit(self)
self.search_bar.setObjectName("lineEdit_search_bar")
self.search_bar.setPlaceholderText("Search")
self.search_bar.textChanged.connect(self.search_str)
# The list of filters
self.list_widget_filters = QtWidgets.QListWidget(self)
self.list_widget_filters.setObjectName("listWidget_tags")
self.list_widget_filters.setSelectionMode(
QtWidgets.QAbstractItemView.SingleSelection
)
self.push_button_ok = QtWidgets.QPushButton(self)
self.push_button_ok.setObjectName("pushButton_ok")
self.push_button_ok.setText("OK")
self.push_button_ok.clicked.connect(self.ok_clicked)
self.push_button_cancel = QtWidgets.QPushButton(self)
self.push_button_cancel.setObjectName("pushButton_cancel")
self.push_button_cancel.setText("Cancel")
self.push_button_cancel.clicked.connect(self.cancel_clicked)
hbox_top_left = QHBoxLayout()
hbox_top_left.addWidget(self.label_filter_list)
hbox_top_left.addWidget(self.search_bar)
vbox_top_left = QVBoxLayout()
vbox_top_left.addLayout(hbox_top_left)
vbox_top_left.addWidget(self.list_widget_filters)
hbox_buttons = QHBoxLayout()
hbox_buttons.addStretch(1)
hbox_buttons.addWidget(self.push_button_ok)
hbox_buttons.addWidget(self.push_button_cancel)
vbox_final = QVBoxLayout()
vbox_final.addLayout(vbox_top_left)
vbox_final.addLayout(hbox_buttons)
self.setLayout(vbox_final)
[docs]
def ok_clicked(self):
"""Actions when the "OK" button is clicked."""
# Has to be override in the PopUpSelectFilter* classes
pass
[docs]
def search_str(self, str_search):
"""Matches the searched pattern with the saved filters.
:param str_search: string pattern to search
"""
return_list = []
if str_search != "":
for filter in self.project.filters:
if str_search.upper() in filter.name.upper():
return_list.append(filter.name)
else:
for filter in self.project.filters:
return_list.append(filter.name)
for idx in range(self.list_widget_filters.count()):
item = self.list_widget_filters.item(idx)
if item.text() in return_list:
item.setHidden(False)
else:
item.setHidden(True)
[docs]
class PopUpInformation(QWidget):
"""Is called when the user wants to display the current project's
information."""
# Signal that will be emitted at the end to tell that the project
# has been created
signal_preferences_change = pyqtSignal()
[docs]
def __init__(self, project):
"""Initialization.
:param project: current project in the software
"""
super().__init__()
name_label = QLabel("Name: ")
self.name_value = QLineEdit(project.getName())
folder_label = QLabel("Root folder: " + project.folder)
date_label = QLabel("Date of creation: " + project.getDate())
box = QVBoxLayout()
row = QHBoxLayout()
row.addWidget(name_label)
row.addWidget(self.name_value)
box.addLayout(row)
box.addWidget(folder_label)
box.addWidget(date_label)
box.addStretch(1)
self.setLayout(box)
[docs]
class PopUpInheritanceDict(QDialog):
"""Is called to select from which input the output will inherit the tags.
:Methods:
- on_clicked: event when radiobutton is clicked
- ok_clicked: event when ok button is clicked
- okall_clicked: event when Ok all button is clicked
- ignore_clicked: event when ignore button is clicked
- ignoreall_clicked:event when ignore all plugs button is clicked
- ignore_node_clicked: event when ignore all nodes button is clicked
"""
[docs]
def __init__(self, values, node_name, plug_name, iterate):
"""Initialization
:param values: A dictionary with input name as key and their paths
as values
:param node_name: name of the current node
:param plug_name: name of the current output plug
:param iterate: boolean, iteration or not
"""
super().__init__()
self.setModal(True)
self.setObjectName("Dialog")
self.setWindowTitle("Plug inherited in " + node_name)
self.ignore = False
self.all = False
self.everything = False
label = (
"In the node <b><i>" + node_name + "</i></b>, from which "
"input plug, the output plug <b><i>" + plug_name + "</i></b>"
" should inherit the tags:"
)
v_box_values = QtWidgets.QVBoxLayout()
v_box_values.addWidget(QLabel(label))
checked = True
for key in values:
radiobutton = QRadioButton(key, self)
radiobutton.value = values[key]
radiobutton.key = key
radiobutton.setChecked(checked)
if checked:
self.value = radiobutton.value
self.key = radiobutton.key
checked = False
radiobutton.toggled.connect(self.on_clicked)
v_box_values.addWidget(radiobutton)
v_box_values.addStretch(1)
h_box_buttons = QtWidgets.QHBoxLayout()
self.push_button_ok = QtWidgets.QPushButton(self)
self.push_button_ok.setText("OK")
self.push_button_ok.clicked.connect(self.ok_clicked)
self.push_button_ok.setToolTip(
"<i>" + plug_name + "</i> will inherit tags from " + self.key
)
h_box_buttons.addWidget(self.push_button_ok)
self.push_button_ignore = QtWidgets.QPushButton(self)
self.push_button_ignore.setText("Ignore")
self.push_button_ignore.clicked.connect(self.ignore_clicked)
self.push_button_ignore.setToolTip(
"<i>" + plug_name + "</i> will not inherit any tags."
)
h_box_buttons.addWidget(self.push_button_ignore)
self.push_button_okall = QtWidgets.QPushButton(self)
self.push_button_okall.setText("OK for all output plugs")
self.push_button_okall.clicked.connect(self.okall_clicked)
self.push_button_okall.setToolTip(
"All the output plugs from <i>"
+ node_name
+ "</i> will inherit tags"
" from " + self.key
)
h_box_buttons.addWidget(self.push_button_okall)
self.push_button_ignoreall = QtWidgets.QPushButton(self)
self.push_button_ignoreall.setText("Ignore for all output plugs")
self.push_button_ignoreall.clicked.connect(self.ignoreall_clicked)
self.push_button_ignoreall.setToolTip(
"All the output plugs from <i>" + node_name + "</i> will not "
"inherit any tags."
)
h_box_buttons.addWidget(self.push_button_ignoreall)
self.push_button_ignore_node = QtWidgets.QPushButton(self)
self.push_button_ignore_node.setText(
"Ignore for all nodes in the pipeline"
)
self.push_button_ignore_node.clicked.connect(self.ignore_node_clicked)
self.push_button_ignore_node.setToolTip(
"No tags will be inherited for the whole pipeline."
)
v_box_values.addLayout(h_box_buttons)
v_box_values.addWidget(self.push_button_ignore_node)
if iterate:
label = "<i>Theses choices will be valid for each iteration.</i>"
v_box_values.addWidget(QLabel(label))
self.setLayout(v_box_values)
[docs]
def on_clicked(self):
"""Event when radiobutton is clicked"""
radiobutton = self.sender()
self.value = radiobutton.value
self.key = radiobutton.key
[docs]
def okall_clicked(self):
"""Event when Ok all button is clicked"""
self.all = True
self.accept()
self.close()
[docs]
def ignore_clicked(self):
"""Event when ignore button is clicked"""
self.ignore = True
self.accept()
self.close()
[docs]
def ignoreall_clicked(self):
"""Event when ignore all plugs button is clicked"""
self.ignore = True
self.all = True
self.accept()
self.close()
[docs]
def ignore_node_clicked(self):
"""Event when ignore all nodes button is clicked"""
self.ignore = True
self.all = True
self.everything = True
self.accept()
self.close()
[docs]
class PopUpMultipleSort(QDialog):
"""Is called to sort the data browser's table depending on multiple tags.
.. Methods:
- add_tag: adds a push button
- fill_values: fills the values list when a tag is added or removed
- refresh_layout: updates the layouts (especially when a tag push
button is added or removed)
- remove_tag: removes a push buttons and makes the changes in the
list of values
- select_tag: calls a pop-up to choose a tag
- sort_scans: collects the information and send them to the data
browser
"""
[docs]
def __init__(self, project, table_data_browser):
"""Initialization.
:param project: current project in the software
:param table_data_browser: data browser's table of the software
"""
super().__init__()
self.project = project
self.table_data_browser = table_data_browser
self.setModal(True)
self.setWindowTitle("Multiple sort")
# values_list will contain the different values of each selected tag
self.values_list = [[], []]
self.list_tags = []
self.label_tags = QLabel("Tags: ")
# Each push button will allow the user to add a tag to the count table
push_button_tag_1 = QPushButton()
push_button_tag_1.setText("Tag n°1")
push_button_tag_1.clicked.connect(lambda: self.select_tag(0))
push_button_tag_2 = QPushButton()
push_button_tag_2.setText("Tag n°2")
push_button_tag_2.clicked.connect(lambda: self.select_tag(1))
# The list of all the push buttons (the user can add as many as
# he or she wants)
self.push_buttons = []
self.push_buttons.insert(0, push_button_tag_1)
self.push_buttons.insert(1, push_button_tag_2)
# Labels to add/remove a tag (a push button)
sources_images_dir = Config().getSourceImageDir()
self.remove_tag_label = ClickableLabel()
remove_tag_picture = QPixmap(
os.path.relpath(os.path.join(sources_images_dir, "red_minus.png"))
)
remove_tag_picture = remove_tag_picture.scaledToHeight(20)
self.remove_tag_label.setPixmap(remove_tag_picture)
self.remove_tag_label.clicked.connect(self.remove_tag)
self.add_tag_label = ClickableLabel()
self.add_tag_label.setObjectName("plus")
add_tag_picture = QPixmap(
os.path.relpath(os.path.join(sources_images_dir, "green_plus.png"))
)
add_tag_picture = add_tag_picture.scaledToHeight(15)
self.add_tag_label.setPixmap(add_tag_picture)
self.add_tag_label.clicked.connect(self.add_tag)
# Combobox to choose if the sort order is ascending or descending
self.combo_box = QComboBox()
self.combo_box.addItems(["Ascending", "Descending"])
# Push button that is pressed to launch the computations
self.push_button_sort = QPushButton()
self.push_button_sort.setText("Sort scans")
self.push_button_sort.clicked.connect(self.sort_scans)
# Layouts
self.v_box_final = QVBoxLayout()
self.setLayout(self.v_box_final)
self.refresh_layout()
[docs]
def add_tag(self):
"""Adds a push button."""
push_button = QPushButton()
push_button.setText("Tag n°" + str(len(self.push_buttons) + 1))
push_button.clicked.connect(
lambda: self.select_tag(len(self.push_buttons) - 1)
)
self.push_buttons.insert(len(self.push_buttons), push_button)
self.refresh_layout()
[docs]
def fill_values(self, idx):
"""Fills the values list when a tag is added or removed.
:param idx: index of the pressed push button
"""
tag_name = self.push_buttons[idx].text()
if len(self.values_list) <= idx:
self.values_list.insert(idx, [])
if self.values_list[idx] is not None:
self.values_list[idx] = []
for scan in self.project.session.get_fields_names(COLLECTION_CURRENT):
current_value = self.project.session.get_value(
COLLECTION_CURRENT, scan, tag_name
)
if current_value not in self.values_list[idx]:
self.values_list[idx].append(current_value)
[docs]
def refresh_layout(self):
"""Updates the layouts (especially when a tag push button is added or
removed).
"""
self.h_box_top = QHBoxLayout()
self.h_box_top.setSpacing(10)
self.h_box_top.addWidget(self.label_tags)
for tag_label in self.push_buttons:
self.h_box_top.addWidget(tag_label)
self.h_box_top.addWidget(self.add_tag_label)
self.h_box_top.addWidget(self.remove_tag_label)
self.h_box_top.addWidget(self.combo_box)
self.h_box_top.addWidget(self.push_button_sort)
self.h_box_top.addStretch(1)
self.v_box_final.addLayout(self.h_box_top)
[docs]
def remove_tag(self):
"""Removes a push buttons and makes the changesn in the list of
values.
"""
push_button = self.push_buttons[-1]
push_button.deleteLater()
push_button = None
del self.push_buttons[-1]
del self.values_list[-1]
self.refresh_layout()
[docs]
def select_tag(self, idx):
"""Calls a pop-up to choose a tag.
:param idx: index of the pressed push button
"""
pop_up = PopUpSelectTagCountTable(
self.project,
self.project.session.get_shown_tags(),
self.push_buttons[idx].text(),
)
if pop_up.exec_():
self.push_buttons[idx].setText(pop_up.selected_tag)
self.fill_values(idx)
[docs]
def sort_scans(self):
"""Collects the information and send them to the data browser."""
self.order = self.combo_box.itemText(self.combo_box.currentIndex())
for push_button in self.push_buttons:
if push_button.text() in self.project.session.get_fields_names(
COLLECTION_CURRENT
):
self.list_tags.append(push_button.text())
self.accept()
self.table_data_browser.multiple_sort_infos(self.list_tags, self.order)
[docs]
class PopUpNewProject(QFileDialog):
"""Is called when the user wants to create a new project.
.. Method:
- get_filename: sets the widget's attributes depending on the
selected file name
"""
# Signal that will be emitted at the end to tell that the project
# has been created
signal_create_project = pyqtSignal()
[docs]
def __init__(self):
"""Initialization."""
# import set_projects_directory_as_default only here to prevent
# circular import issue
from populse_mia.utils import set_projects_directory_as_default
super().__init__()
self.setLabelText(QFileDialog.Accept, "Create")
self.setAcceptMode(QFileDialog.AcceptSave)
# Setting the projects directory as default
set_projects_directory_as_default(self)
[docs]
def get_filename(self, file_name_tuple):
"""Sets the widget's attributes depending on the selected file name.
:param file_name_tuple: tuple obtained with the selectedFiles method
:return: real file name
"""
# import message_already_exists only here to prevent
# circular import issue
from populse_mia.utils import message_already_exists
file_name = file_name_tuple[0]
if file_name:
entire_path = os.path.abspath(file_name)
self.path, self.name = os.path.split(entire_path)
self.relative_path = os.path.relpath(file_name)
self.relative_subpath = os.path.relpath(self.path)
if not os.path.exists(self.relative_path):
self.close()
# A signal is emitted to tell that the project has been created
self.signal_create_project.emit()
else:
message_already_exists()
return file_name
[docs]
class PopUpOpenProject(QFileDialog):
"""Is called when the user wants to open project.
.. Method:
- get_filename: sets the widget's attributes depending on the selected
file name
"""
# Signal that will be emitted at the end to tell that the project
# has been created
signal_create_project = pyqtSignal()
[docs]
def __init__(self):
# import set_projects_directory_as_default only here to prevent
# circular import issue
from populse_mia.utils import set_projects_directory_as_default
super().__init__()
self.setOption(QFileDialog.DontUseNativeDialog, True)
self.setFileMode(QFileDialog.Directory)
# Setting the projects directory as default
set_projects_directory_as_default(self)
[docs]
def get_filename(self, file_name_tuple):
"""Sets the widget's attributes depending on the selected file name.
:param file_name_tuple: tuple obtained with the selectedFiles method
"""
# import message_already_exists only here to prevent
# circular import issue
from populse_mia.utils import message_already_exists
file_name = file_name_tuple[0]
if file_name:
entire_path = os.path.abspath(file_name)
self.path, self.name = os.path.split(entire_path)
self.relative_path = os.path.relpath(file_name)
# If the file exists
if os.path.exists(entire_path):
self.close()
# A signal is emitted to tell that the project has been created
self.signal_create_project.emit()
else:
message_already_exists()
[docs]
class PopUpPreferences(QDialog):
"""Is called when the user wants to change the software preferences.
.. Methods:
- browse_afni: called when afni browse button is clicked
- browse_ants: called when ants browse button is clicked
- browse_fsl: called when fsl browse button is clicked
- browse_matlab: called when matlab browse button is clicked
- browse_matlab_standalone: called when matlab browse button is clicked
- browse_mri_conv_path: called when "MRIManager.jar" browse
button is clicked
- browse_mrtrix: called when mrtrix browse button is clicked
- browse_projects_save_path: called when "Projects folder" browse
button is clicked
- browse_resources_path: called when "resources" browse button is
clicked
- browse_spm: called when spm browse button is clicked
- browse_spm_standalone: called when spm standalone browse button
is clicked
- change_admin_psswd: method to change the admin password
- control_checkbox_toggled: check before changing controller version
- edit_capsul_config: capsul engine edition
- edit_config_file: create a window to view, edit the mia
configuration file
- findChar: highlights characters in red when using the Find button
when editing configuration
- ok_clicked: saves the modifications to the config file and apply them
- use_afni_changed: called when the use_afni checkbox is changed
- use_ants_changed: called when the use_ants checkbox is changed
- use_fsl_changed: called when the use_fsl checkbox is changed
- use_matlab_changed: called when the use_matlab checkbox is changed
- use_matlab_standalone_changed: called when the use_matlab_standalone
checkbox is changed
- use_mrtrix_changed: called when the use_mrtrix checkbox is changed
- use_spm_changed: called when the use_spm checkbox is changed
- use_spm_standalone_changed: called when the use_spm_standalone
checkbox is changed
- admin_mode_switch: called when the admin mode checkbox
is clicked
"""
# Signal that will be emitted at the end to tell that the project
# has been created
signal_preferences_change = pyqtSignal()
use_clinical_mode_signal = pyqtSignal()
not_use_clinical_mode_signal = pyqtSignal()
[docs]
def __init__(self, main_window):
"""Initialization.
:param main_window: main window object of the software
"""
super().__init__()
self.setModal(True)
self.main_window = main_window
_translate = QtCore.QCoreApplication.translate
self.setObjectName("Dialog")
self.setWindowTitle("MIA preferences")
self.clicked = 0
self.tab_widget = QtWidgets.QTabWidget(self)
self.tab_widget.setEnabled(True)
self.salt = "P0pulseM1@"
config = Config()
# The 'Tools" tab
self.tab_tools = QtWidgets.QWidget()
self.tab_tools.setObjectName("tab_tools")
self.tab_widget.addTab(self.tab_tools, _translate("Dialog", "Tools"))
# Groupbox "Global preferences"
self.groupbox_global = QtWidgets.QGroupBox("Global preferences")
# Auto save
self.save_checkbox = QCheckBox("", self)
self.save_label = QLabel("Auto save")
if config.isAutoSave() is True:
self.save_checkbox.setChecked(1)
h_box_auto_save = QtWidgets.QHBoxLayout()
h_box_auto_save.addWidget(self.save_checkbox)
h_box_auto_save.addWidget(self.save_label)
h_box_auto_save.addStretch(1)
# Clinical mode
self.clinical_mode_checkbox = QCheckBox("", self)
if config.get_use_clinical() is True:
self.clinical_mode_checkbox.setChecked(1)
self.clinical_mode_label = QLabel("Clinical mode")
h_box_clinical = QtWidgets.QHBoxLayout()
h_box_clinical.addWidget(self.clinical_mode_checkbox)
h_box_clinical.addWidget(self.clinical_mode_label)
h_box_clinical.addStretch(1)
# Admin mode + Change password + Edit config
self.admin_mode_checkbox = QCheckBox("", self)
self.admin_mode_checkbox.clicked.connect(self.admin_mode_switch)
self.admin_mode_label = QLabel("Admin mode")
self.change_psswd = QPushButton(
"Change password", default=False, autoDefault=False
)
self.edit_config = QPushButton(
"Edit config", default=False, autoDefault=False
)
self.change_psswd.clicked.connect(partial(self.change_admin_psswd, ""))
self.edit_config.clicked.connect(self.edit_config_file)
if not config.get_user_mode():
self.admin_mode_checkbox.setChecked(1)
self.change_psswd.setVisible(True)
self.edit_config.setVisible(True)
else:
self.admin_mode_checkbox.setChecked(0)
self.change_psswd.setVisible(False)
self.edit_config.setVisible(False)
h_box_admin_mode = QtWidgets.QHBoxLayout()
h_box_admin_mode.addWidget(self.admin_mode_checkbox)
h_box_admin_mode.addWidget(self.admin_mode_label)
h_box_admin_mode.addStretch(1)
h_box_change_psswd = QtWidgets.QHBoxLayout()
h_box_change_psswd.addWidget(self.change_psswd)
h_box_change_psswd.addStretch(1)
h_box_edit_config = QtWidgets.QHBoxLayout()
h_box_edit_config.addWidget(self.edit_config)
h_box_edit_config.addStretch(1)
# Version 1 controller
self.control_checkbox = QCheckBox("", self)
self.control_label = QLabel("Version 1 controller")
if config.isControlV1() is True:
self.control_checkbox.setChecked(1)
self.control_checkbox_changed = main_window.get_controller_version()
self.control_checkbox.clicked.connect(
partial(self.control_checkbox_toggled, main_window)
)
h_box_control = QtWidgets.QHBoxLayout()
h_box_control.addWidget(self.control_checkbox)
h_box_control.addWidget(self.control_label)
h_box_control.addStretch(1)
# Max thumbnails number at the data browser bottom
self.max_thumbnails_label = QLabel(
"Number of thumbnails in Data Browser:"
)
self.max_thumbnails_box = QtWidgets.QSpinBox()
self.max_thumbnails_box.setMinimum(1)
self.max_thumbnails_box.setMaximum(15)
self.max_thumbnails_box.setValue(config.get_max_thumbnails())
self.max_thumbnails_box.setSingleStep(1)
h_box_max_thumbnails = QtWidgets.QHBoxLayout()
h_box_max_thumbnails.addWidget(self.max_thumbnails_box)
h_box_max_thumbnails.addStretch(1)
# Radiological vs neurological orientation in miniviewer data browser
self.radioView_checkbox = QCheckBox("", self)
self.radioView_label = QLabel(
"Radiological orientation in miniviewer (data browser)"
)
if config.isRadioView() is True:
self.radioView_checkbox.setChecked(1)
h_box_radioView = QtWidgets.QHBoxLayout()
h_box_radioView.addWidget(self.radioView_checkbox)
h_box_radioView.addWidget(self.radioView_label)
h_box_radioView.addStretch(1)
# Draws graphic objects
v_box_global = QtWidgets.QVBoxLayout()
v_box_global.addLayout(h_box_auto_save)
v_box_global.addLayout(h_box_clinical)
v_box_global.addLayout(h_box_admin_mode)
v_box_global.addLayout(h_box_change_psswd)
v_box_global.addLayout(h_box_edit_config)
v_box_global.addLayout(h_box_control)
v_box_global.addWidget(self.max_thumbnails_label)
v_box_global.addLayout(h_box_max_thumbnails)
v_box_global.addLayout(h_box_radioView)
self.groupbox_global.setLayout(v_box_global)
# Groupbox "Projects preferences"
self.groupbox_projects = QtWidgets.QGroupBox("Projects preferences")
# Projects folder label/line edit
self.projects_save_path_label = QLabel("Projects folder:")
self.projects_save_path_line_edit = QLineEdit(
config.get_projects_save_path()
)
self.projects_save_path_browse = QPushButton("Browse")
self.projects_save_path_browse.clicked.connect(
self.browse_projects_save_path
)
# Max projects in "Saved projects"
self.max_projects_label = QLabel(
'Number of projects in "Saved projects":'
)
self.max_projects_box = QtWidgets.QSpinBox()
self.max_projects_box.setMinimum(1)
self.max_projects_box.setMaximum(20)
self.max_projects_box.setValue(config.get_max_projects())
# self.max_projects_box.setDecimals(0)
self.max_projects_box.setSingleStep(1)
# Draws graphic objects
h_box_projects_save = QtWidgets.QHBoxLayout()
h_box_projects_save.addWidget(self.projects_save_path_line_edit)
h_box_projects_save.addWidget(self.projects_save_path_browse)
v_box_projects_save = QtWidgets.QVBoxLayout()
v_box_projects_save.addWidget(self.projects_save_path_label)
v_box_projects_save.addLayout(h_box_projects_save)
h_box_max_projects = QtWidgets.QHBoxLayout()
h_box_max_projects.addWidget(self.max_projects_box)
h_box_max_projects.addStretch(1)
v_box_max_projects = QtWidgets.QVBoxLayout()
v_box_max_projects.addWidget(self.max_projects_label)
v_box_max_projects.addLayout(h_box_max_projects)
projects_layout = QVBoxLayout()
projects_layout.addLayout(v_box_projects_save)
projects_layout.addLayout(v_box_max_projects)
self.groupbox_projects.setLayout(projects_layout)
# Groupbox "POPULSE third party preferences"
self.groupbox_populse = QtWidgets.QGroupBox(
"POPULSE third party preference"
)
# MRI File Manager folder label/line edit
self.mri_conv_path_label = QLabel(
"Absolute path to MRIManager.jar "
"file (e.g., mri_conv_dir/"
"MRIFileManager/MRIManager.jar):"
)
self.mri_conv_path_line_edit = QLineEdit(config.get_mri_conv_path())
self.mri_conv_path_browse = QPushButton("Browse")
self.mri_conv_path_browse.clicked.connect(self.browse_mri_conv_path)
# Draws graphic objects
h_box_mri_conv = QtWidgets.QHBoxLayout()
h_box_mri_conv.addWidget(self.mri_conv_path_line_edit)
h_box_mri_conv.addWidget(self.mri_conv_path_browse)
v_box_mri_conv = QtWidgets.QVBoxLayout()
v_box_mri_conv.addWidget(self.mri_conv_path_label)
v_box_mri_conv.addLayout(h_box_mri_conv)
populse_layout = QVBoxLayout()
populse_layout.addLayout(v_box_mri_conv)
self.groupbox_populse.setLayout(populse_layout)
# Groupbox "External resources preferences"
self.groupbox_resources = QtWidgets.QGroupBox(
"External resources preferences"
)
# Resources folder label/line edit
self.resources_path_label = QLabel(
"Absolute path to the external resources data (some processes may "
"require external data to function properly):"
)
self.resources_path_line_edit = QLineEdit(config.get_resources_path())
self.resources_path_browse = QPushButton("Browse")
self.resources_path_browse.clicked.connect(self.browse_resources_path)
# Draws graphic objects
h_box_resources = QtWidgets.QHBoxLayout()
h_box_resources.addWidget(self.resources_path_line_edit)
h_box_resources.addWidget(self.resources_path_browse)
v_box_resources = QtWidgets.QVBoxLayout()
v_box_resources.addWidget(self.resources_path_label)
v_box_resources.addLayout(h_box_resources)
resources_layout = QVBoxLayout()
resources_layout.addLayout(v_box_resources)
self.groupbox_resources.setLayout(resources_layout)
# Final tab layouts
h_box_top = QtWidgets.QHBoxLayout()
h_box_top.addWidget(self.groupbox_global)
h_box_top.addStretch(1)
self.tab_tools_layout = QtWidgets.QVBoxLayout()
self.tab_tools_layout.addLayout(h_box_top)
self.tab_tools_layout.addWidget(self.groupbox_projects)
self.tab_tools_layout.addWidget(self.groupbox_populse)
self.tab_tools_layout.addWidget(self.groupbox_resources)
self.tab_tools_layout.addStretch(1)
self.tab_tools.setLayout(self.tab_tools_layout)
# The 'OK' push button
self.push_button_ok = QtWidgets.QPushButton("OK")
self.push_button_ok.setObjectName("pushButton_ok")
self.push_button_ok.clicked.connect(self.ok_clicked)
# The 'Cancel' push button
self.push_button_cancel = QtWidgets.QPushButton("Cancel")
self.push_button_cancel.setObjectName("pushButton_cancel")
self.push_button_cancel.clicked.connect(self.close)
self.status_label = QLabel("")
# Buttons layouts
hbox_buttons = QHBoxLayout()
hbox_buttons.addWidget(self.status_label)
hbox_buttons.addStretch(1)
hbox_buttons.addWidget(self.push_button_ok)
hbox_buttons.addWidget(self.push_button_cancel)
vbox = QVBoxLayout()
vbox.addWidget(self.tab_widget)
vbox.addLayout(hbox_buttons)
# The 'Pipeline' tab
self.tab_pipeline = QtWidgets.QWidget()
self.tab_pipeline.setObjectName("tab_pipeline")
self.tab_widget.addTab(
self.tab_pipeline, _translate("Dialog", "Pipeline")
)
# Groupbox "Matlab"
self.groupbox_matlab = QtWidgets.QGroupBox("Matlab")
self.use_matlab_label = QLabel("Use Matlab")
self.use_matlab_checkbox = QCheckBox("", self)
self.matlab_label = QLabel(
"Matlab path (e.g., matlab_dir/bin/matlab):"
)
self.matlab_choice = QLineEdit(config.get_matlab_path())
self.matlab_browse = QPushButton("Browse")
self.matlab_browse.clicked.connect(self.browse_matlab)
self.use_matlab_standalone_label = QLabel("Use Matlab standalone")
self.use_matlab_standalone_checkbox = QCheckBox("", self)
self.matlab_standalone_label = QLabel(
"Matlab standalone path (e.g., MCR_dir/v95):"
)
self.matlab_standalone_choice = QLineEdit(
config.get_matlab_standalone_path()
)
self.matlab_standalone_browse = QPushButton("Browse")
self.matlab_standalone_browse.clicked.connect(
self.browse_matlab_standalone
)
h_box_use_matlab = QtWidgets.QHBoxLayout()
h_box_use_matlab.addWidget(self.use_matlab_checkbox)
h_box_use_matlab.addWidget(self.use_matlab_label)
h_box_use_matlab.addStretch(1)
h_box_matlab_path = QtWidgets.QHBoxLayout()
h_box_matlab_path.addWidget(self.matlab_choice)
h_box_matlab_path.addWidget(self.matlab_browse)
v_box_matlab_path = QtWidgets.QVBoxLayout()
v_box_matlab_path.addWidget(self.matlab_label)
v_box_matlab_path.addLayout(h_box_matlab_path)
h_box_use_matlab_standalone = QtWidgets.QHBoxLayout()
h_box_use_matlab_standalone.addWidget(
self.use_matlab_standalone_checkbox
)
h_box_use_matlab_standalone.addWidget(self.use_matlab_standalone_label)
h_box_use_matlab_standalone.addStretch(1)
h_box_matlab_standalone_path = QtWidgets.QHBoxLayout()
h_box_matlab_standalone_path.addWidget(self.matlab_standalone_choice)
h_box_matlab_standalone_path.addWidget(self.matlab_standalone_browse)
v_box_matlab_standalone_path = QtWidgets.QVBoxLayout()
v_box_matlab_standalone_path.addLayout(h_box_use_matlab_standalone)
v_box_matlab_standalone_path.addWidget(self.matlab_standalone_label)
v_box_matlab_standalone_path.addLayout(h_box_matlab_standalone_path)
v_box_matlab = QtWidgets.QVBoxLayout()
v_box_matlab.addLayout(h_box_use_matlab)
v_box_matlab.addLayout(v_box_matlab_path)
v_box_matlab.addLayout(v_box_matlab_standalone_path)
self.groupbox_matlab.setLayout(v_box_matlab)
# Groupbox "SPM"
self.groupbox_spm = QtWidgets.QGroupBox("SPM")
self.use_spm_label = QLabel("Use SPM")
self.use_spm_checkbox = QCheckBox("", self)
self.spm_label = QLabel("SPM path (e.g., spm_dir/spm12):")
self.spm_choice = QLineEdit(config.get_spm_path())
self.spm_browse = QPushButton("Browse")
self.spm_browse.clicked.connect(self.browse_spm)
h_box_use_spm = QtWidgets.QHBoxLayout()
h_box_use_spm.addWidget(self.use_spm_checkbox)
h_box_use_spm.addWidget(self.use_spm_label)
h_box_use_spm.addStretch(1)
h_box_spm_path = QtWidgets.QHBoxLayout()
h_box_spm_path.addWidget(self.spm_choice)
h_box_spm_path.addWidget(self.spm_browse)
v_box_spm_path = QtWidgets.QVBoxLayout()
v_box_spm_path.addWidget(self.spm_label)
v_box_spm_path.addLayout(h_box_spm_path)
self.use_spm_standalone_label = QLabel("Use SPM standalone")
self.use_spm_standalone_checkbox = QCheckBox("", self)
self.spm_standalone_label = QLabel(
"SPM standalone path (e.g., the "
"directory hosting the run_spm12.sh "
"file):"
)
self.spm_standalone_choice = QLineEdit(
config.get_spm_standalone_path()
)
self.spm_standalone_browse = QPushButton("Browse")
self.spm_standalone_browse.clicked.connect(self.browse_spm_standalone)
h_box_use_spm_standalone = QtWidgets.QHBoxLayout()
h_box_use_spm_standalone.addWidget(self.use_spm_standalone_checkbox)
h_box_use_spm_standalone.addWidget(self.use_spm_standalone_label)
h_box_use_spm_standalone.addStretch(1)
h_box_spm_standalone_path = QtWidgets.QHBoxLayout()
h_box_spm_standalone_path.addWidget(self.spm_standalone_choice)
h_box_spm_standalone_path.addWidget(self.spm_standalone_browse)
v_box_spm_standalone_path = QtWidgets.QVBoxLayout()
v_box_spm_standalone_path.addWidget(self.spm_standalone_label)
v_box_spm_standalone_path.addLayout(h_box_spm_standalone_path)
v_box_spm = QtWidgets.QVBoxLayout()
v_box_spm.addLayout(h_box_use_spm)
v_box_spm.addLayout(v_box_spm_path)
v_box_spm.addLayout(h_box_use_spm_standalone)
v_box_spm.addLayout(v_box_spm_standalone_path)
self.groupbox_spm.setLayout(v_box_spm)
# Groupbox "FSL"
self.groupbox_fsl = QtWidgets.QGroupBox("FSL")
self.use_fsl_label = QLabel("Use FSL")
self.use_fsl_checkbox = QCheckBox("", self)
self.fsl_label = QLabel(
"FSL config file (e.g., fsl_dir/etc/fslconf/fsl.sh):"
)
self.fsl_choice = QLineEdit(config.get_fsl_config())
self.fsl_browse = QPushButton("Browse")
self.fsl_browse.clicked.connect(self.browse_fsl)
h_box_use_fsl = QtWidgets.QHBoxLayout()
h_box_use_fsl.addWidget(self.use_fsl_checkbox)
h_box_use_fsl.addWidget(self.use_fsl_label)
h_box_use_fsl.addStretch(1)
h_box_fsl_path = QtWidgets.QHBoxLayout()
h_box_fsl_path.addWidget(self.fsl_choice)
h_box_fsl_path.addWidget(self.fsl_browse)
v_box_fsl_path = QtWidgets.QVBoxLayout()
v_box_fsl_path.addWidget(self.fsl_label)
v_box_fsl_path.addLayout(h_box_fsl_path)
v_box_fsl = QtWidgets.QVBoxLayout()
v_box_fsl.addLayout(h_box_use_fsl)
v_box_fsl.addLayout(v_box_fsl_path)
self.groupbox_fsl.setLayout(v_box_fsl)
# Groupbox "AFNI"
self.groupbox_afni = QtWidgets.QGroupBox("AFNI")
self.use_afni_label = QLabel("Use AFNI")
self.use_afni_checkbox = QCheckBox("", self)
self.afni_label = QLabel("AFNI path (e.g. dir_containing_abin/abin):")
self.afni_choice = QLineEdit(config.get_afni_path())
self.afni_browse = QPushButton("Browse")
self.afni_browse.clicked.connect(self.browse_afni)
h_box_use_afni = QtWidgets.QHBoxLayout()
h_box_use_afni.addWidget(self.use_afni_checkbox)
h_box_use_afni.addWidget(self.use_afni_label)
h_box_use_afni.addStretch(1)
h_box_afni_path = QtWidgets.QHBoxLayout()
h_box_afni_path.addWidget(self.afni_choice)
h_box_afni_path.addWidget(self.afni_browse)
v_box_afni_path = QtWidgets.QVBoxLayout()
v_box_afni_path.addWidget(self.afni_label)
v_box_afni_path.addLayout(h_box_afni_path)
v_box_afni = QtWidgets.QVBoxLayout()
v_box_afni.addLayout(h_box_use_afni)
v_box_afni.addLayout(v_box_afni_path)
self.groupbox_afni.setLayout(v_box_afni)
# Groupbox "ANTS"
self.groupbox_ants = QtWidgets.QGroupBox("ANTS")
self.use_ants_label = QLabel("Use ANTS")
self.use_ants_checkbox = QCheckBox("", self)
self.ants_label = QLabel("ANTS path (e.g. ANTs_dir/bin):")
self.ants_choice = QLineEdit(config.get_ants_path())
self.ants_browse = QPushButton("Browse")
self.ants_browse.clicked.connect(self.browse_ants)
h_box_use_ants = QtWidgets.QHBoxLayout()
h_box_use_ants.addWidget(self.use_ants_checkbox)
h_box_use_ants.addWidget(self.use_ants_label)
h_box_use_ants.addStretch(1)
h_box_ants_path = QtWidgets.QHBoxLayout()
h_box_ants_path.addWidget(self.ants_choice)
h_box_ants_path.addWidget(self.ants_browse)
v_box_ants_path = QtWidgets.QVBoxLayout()
v_box_ants_path.addWidget(self.ants_label)
v_box_ants_path.addLayout(h_box_ants_path)
v_box_ants = QtWidgets.QVBoxLayout()
v_box_ants.addLayout(h_box_use_ants)
v_box_ants.addLayout(v_box_ants_path)
self.groupbox_ants.setLayout(v_box_ants)
# Groupbox "freesurfer"
self.groupbox_freesurfer = QtWidgets.QGroupBox("FreeSurfer")
self.use_freesurfer_label = QLabel("Use FreeSurfer")
self.use_freesurfer_checkbox = QCheckBox("", self)
self.freesurfer_label = QLabel(
"FreeSurfer path (e.g. FreeSurfer_dir/FreeSurferEnv.sh):"
)
self.freesurfer_choice = QLineEdit(config.get_freesurfer_setup())
self.freesurfer_browse = QPushButton("Browse")
self.freesurfer_browse.clicked.connect(self.browse_freesurfer)
h_box_use_freesurfer = QtWidgets.QHBoxLayout()
h_box_use_freesurfer.addWidget(self.use_freesurfer_checkbox)
h_box_use_freesurfer.addWidget(self.use_freesurfer_label)
h_box_use_freesurfer.addStretch(1)
h_box_freesurfer_path = QtWidgets.QHBoxLayout()
h_box_freesurfer_path.addWidget(self.freesurfer_choice)
h_box_freesurfer_path.addWidget(self.freesurfer_browse)
v_box_freesurfer_path = QtWidgets.QVBoxLayout()
v_box_freesurfer_path.addWidget(self.freesurfer_label)
v_box_freesurfer_path.addLayout(h_box_freesurfer_path)
v_box_freesurfer = QtWidgets.QVBoxLayout()
v_box_freesurfer.addLayout(h_box_use_freesurfer)
v_box_freesurfer.addLayout(v_box_freesurfer_path)
self.groupbox_freesurfer.setLayout(v_box_freesurfer)
# Groupbox "mrtrix"
self.groupbox_mrtrix = QtWidgets.QGroupBox("mrtrix")
self.use_mrtrix_label = QLabel("Use mrtrix")
self.use_mrtrix_checkbox = QCheckBox("", self)
self.mrtrix_label = QLabel("mrtrix path (e.g. mrtrix_dir/bin):")
self.mrtrix_choice = QLineEdit(config.get_mrtrix_path())
self.mrtrix_browse = QPushButton("Browse")
self.mrtrix_browse.clicked.connect(self.browse_mrtrix)
h_box_use_mrtrix = QtWidgets.QHBoxLayout()
h_box_use_mrtrix.addWidget(self.use_mrtrix_checkbox)
h_box_use_mrtrix.addWidget(self.use_mrtrix_label)
h_box_use_mrtrix.addStretch(1)
h_box_mrtrix_path = QtWidgets.QHBoxLayout()
h_box_mrtrix_path.addWidget(self.mrtrix_choice)
h_box_mrtrix_path.addWidget(self.mrtrix_browse)
v_box_mrtrix_path = QtWidgets.QVBoxLayout()
v_box_mrtrix_path.addWidget(self.mrtrix_label)
v_box_mrtrix_path.addLayout(h_box_mrtrix_path)
v_box_mrtrix = QtWidgets.QVBoxLayout()
v_box_mrtrix.addLayout(h_box_use_mrtrix)
v_box_mrtrix.addLayout(v_box_mrtrix_path)
self.groupbox_mrtrix.setLayout(v_box_mrtrix)
# Groupbox "CAPSUL"
groupbox_capsul = Qt.QGroupBox("CAPSUL")
capsul_config_button = Qt.QPushButton(
"Edit CAPSUL config", default=False, autoDefault=False
)
capsul_config_button.clicked.connect(self.edit_capsul_config)
h_box_capsul = Qt.QHBoxLayout()
h_box_capsul.addWidget(capsul_config_button)
h_box_capsul.addStretch(1)
v_box_capsul = Qt.QVBoxLayout()
v_box_capsul.addLayout(h_box_capsul)
groupbox_capsul.setLayout(v_box_capsul)
# general layout
self.tab_pipeline_layout = QtWidgets.QVBoxLayout()
self.tab_pipeline_layout.addWidget(self.groupbox_matlab)
self.tab_pipeline_layout.addWidget(self.groupbox_spm)
self.tab_pipeline_layout.addWidget(self.groupbox_fsl)
self.tab_pipeline_layout.addWidget(self.groupbox_afni)
self.tab_pipeline_layout.addWidget(self.groupbox_ants)
self.tab_pipeline_layout.addWidget(self.groupbox_freesurfer)
self.tab_pipeline_layout.addWidget(self.groupbox_mrtrix)
self.tab_pipeline_layout.addWidget(groupbox_capsul)
self.tab_pipeline_layout.addStretch(1)
self.tab_pipeline.setLayout(self.tab_pipeline_layout)
if config.get_use_spm_standalone():
archi = platform.architecture()
if "Windows" in archi[1]:
self.use_matlab_standalone_checkbox.setChecked(False)
else:
self.use_matlab_standalone_checkbox.setChecked(True)
self.use_spm_standalone_checkbox.setChecked(True)
self.use_matlab_checkbox.setChecked(False)
self.use_spm_checkbox.setChecked(False)
elif config.get_use_spm():
self.use_matlab_checkbox.setChecked(True)
self.use_spm_checkbox.setChecked(True)
self.use_matlab_standalone_checkbox.setChecked(False)
self.use_spm_standalone_checkbox.setChecked(False)
elif config.get_use_matlab():
self.use_matlab_checkbox.setChecked(True)
self.use_matlab_standalone_checkbox.setChecked(False)
self.use_spm_standalone_checkbox.setChecked(False)
self.use_spm_checkbox.setChecked(False)
elif config.get_use_matlab_standalone():
self.use_matlab_standalone_checkbox.setChecked(True)
self.use_matlab_checkbox.setChecked(False)
self.use_spm_standalone_checkbox.setChecked(False)
self.use_spm_checkbox.setChecked(False)
# elif config.get_use_matlab():
#
# if config.get_use_matlab_standalone():
# self.use_matlab_standalone_checkbox.setChecked(True)
#
# else:
# self.use_matlab_checkbox.setChecked(True)
#
# else:
# self.use_matlab_checkbox.setChecked(False)
# self.use_matlab_standalone_checkbox.setChecked(False)
if config.get_use_fsl():
self.use_fsl_checkbox.setChecked(True)
if config.get_use_afni():
self.use_afni_checkbox.setChecked(True)
if config.get_use_ants():
self.use_ants_checkbox.setChecked(True)
if config.get_use_freesurfer():
self.use_freesurfer_checkbox.setChecked(True)
if config.get_use_mrtrix():
self.use_mrtrix_checkbox.setChecked(True)
# The 'Appearance' tab
self.tab_appearance = QtWidgets.QWidget()
self.tab_appearance.setObjectName("tab_appearance")
self.tab_widget.addTab(
self.tab_appearance, _translate("Dialog", "Appearance")
)
colors = [
"Black",
"Blue",
"Green",
"Grey",
"Orange",
"Red",
"Yellow",
"White",
]
self.appearance_layout = QVBoxLayout()
self.label_background_color = QLabel("Background color")
self.background_color_combo = QComboBox(self)
self.background_color_combo.addItem("")
self.label_text_color = QLabel("Text color")
self.text_color_combo = QComboBox(self)
self.text_color_combo.addItem("")
txt = config.getTextColor()
bkgnd = config.getBackgroundColor()
if txt == "":
txt = "Black"
if bkgnd == "":
bkgnd = "White"
for color in colors:
if txt != color:
self.background_color_combo.addItem(color)
if bkgnd != color:
self.text_color_combo.addItem(color)
background_color = config.getBackgroundColor()
self.background_color_combo.setCurrentText(background_color)
text_color = config.getTextColor()
self.text_color_combo.setCurrentText(text_color)
self.fullscreen_cbox = QCheckBox("Use full screen")
mainwindow_size_lay = QHBoxLayout()
mainwindow_size_lay.addWidget(QLabel("Main window size"))
self.mainwindow_size_x_spinbox = QtWidgets.QSpinBox()
mainwindow_size_lay.addWidget(self.mainwindow_size_x_spinbox)
mainwindow_size_lay.addWidget(QLabel(" x "))
self.mainwindow_size_y_spinbox = QtWidgets.QSpinBox()
mainwindow_size_lay.addWidget(self.mainwindow_size_y_spinbox)
self.fullscreen_cbox.setChecked(config.get_mainwindow_maximized())
wsize = config.get_mainwindow_size()
self.mainwindow_size_x_spinbox.setMaximum(
QApplication.instance().desktop().width()
)
self.mainwindow_size_y_spinbox.setMaximum(
QApplication.instance().desktop().height()
)
if isinstance(wsize, list) and len(wsize) >= 2:
self.mainwindow_size_x_spinbox.setValue(wsize[0])
self.mainwindow_size_y_spinbox.setValue(wsize[1])
self.mainwindow_size_button = QPushButton("use current size")
mainwindow_size_lay.addWidget(self.mainwindow_size_button)
self.mainwindow_size_button.clicked.connect(
partial(self.use_current_mainwindow_size, main_window)
)
self.appearance_layout.addWidget(self.label_background_color)
self.appearance_layout.addWidget(self.background_color_combo)
self.appearance_layout.addWidget(self.label_text_color)
self.appearance_layout.addWidget(self.text_color_combo)
self.appearance_layout.addWidget(self.fullscreen_cbox)
self.appearance_layout.addLayout(mainwindow_size_lay)
self.appearance_layout.addStretch(1)
self.tab_appearance.setLayout(self.appearance_layout)
# Global layout - scrollable global window
self.scroll = QScrollArea()
self.scroll.setWidgetResizable(True)
self.widget = QtWidgets.QWidget()
self.widget.setLayout(vbox)
self.scroll.setWidget(self.widget)
self.final_layout = QVBoxLayout()
self.final_layout.addWidget(self.scroll)
self.setLayout(self.final_layout)
# Disabling widgets
self.use_spm_changed()
self.use_matlab_changed()
self.use_matlab_standalone_changed()
self.use_spm_standalone_changed()
self.use_fsl_changed()
self.use_afni_changed()
self.use_ants_changed()
self.use_freesurfer_changed()
self.use_mrtrix_changed()
# Signals
self.use_matlab_checkbox.stateChanged.connect(self.use_matlab_changed)
self.use_matlab_standalone_checkbox.stateChanged.connect(
self.use_matlab_standalone_changed
)
self.use_spm_checkbox.stateChanged.connect(self.use_spm_changed)
self.use_spm_standalone_checkbox.stateChanged.connect(
self.use_spm_standalone_changed
)
self.use_fsl_checkbox.stateChanged.connect(self.use_fsl_changed)
self.use_afni_checkbox.stateChanged.connect(self.use_afni_changed)
self.use_ants_checkbox.stateChanged.connect(self.use_ants_changed)
self.use_mrtrix_checkbox.stateChanged.connect(self.use_mrtrix_changed)
self.use_freesurfer_checkbox.stateChanged.connect(
self.use_freesurfer_changed
)
[docs]
def browse_fsl(self):
"""Called when fsl browse button is clicked."""
fname = QFileDialog.getOpenFileName(
self, "Choose FSL config file", os.path.expanduser("~")
)[0]
if fname:
self.fsl_choice.setText(fname)
[docs]
def browse_afni(self):
"""Called when afni browse button is clicked."""
fname = QFileDialog.getExistingDirectory(
self, "Choose AFNI directory", os.path.expanduser("~")
)
if fname:
self.afni_choice.setText(fname)
[docs]
def browse_ants(self):
"""Called when ants browse button is clicked."""
fname = QFileDialog.getExistingDirectory(
self, "Choose ANTS directory", os.path.expanduser("~")
)
if fname:
self.ants_choice.setText(fname)
[docs]
def browse_mrtrix(self):
"""Called when mrtrix browse button is clicked."""
fname = QFileDialog.getExistingDirectory(
self, "Choose mrtrix directory", os.path.expanduser("~")
)
if fname:
self.mrtrix_choice.setText(fname)
[docs]
def browse_freesurfer(self):
"""Called when freesurfer browse button is clicked."""
fname = QFileDialog.getOpenFileName(
self, "Choose freesurfer env file", os.path.expanduser("~")
)[0]
if fname:
self.freesurfer_choice.setText(fname)
[docs]
def browse_matlab(self):
"""Called when matlab browse button is clicked."""
fname = QFileDialog.getOpenFileName(
self, "Choose Matlab file", os.path.expanduser("~")
)[0]
if fname:
self.matlab_choice.setText(fname)
[docs]
def browse_matlab_standalone(self):
"""Called when matlab browse button is clicked."""
fname = QFileDialog.getExistingDirectory(
self, "Choose MCR directory", os.path.expanduser("~")
)
if fname:
self.matlab_standalone_choice.setText(fname)
[docs]
def browse_mri_conv_path(self):
"""Called when "MRIFileManager.jar" browse button is clicked."""
fname = QFileDialog.getOpenFileName(
self,
"Select the location of the MRIManager.jar file",
os.path.expanduser("~"),
)[0]
if fname:
self.mri_conv_path_line_edit.setText(fname)
[docs]
def browse_projects_save_path(self):
"""Called when "Projects folder" browse button is clicked."""
fname = QFileDialog.getExistingDirectory(
self,
"Select a folder where to save the projects",
os.path.expanduser("~"),
)
if fname:
self.projects_save_path_line_edit.setText(fname)
with open(os.path.join(fname, ".gitignore"), "w") as myFile:
myFile.write("/*")
[docs]
def browse_resources_path(self):
"""Called when "resources" browse button is clicked."""
fname = QFileDialog.getExistingDirectory(
self,
"Select the location of the External resources folder",
os.path.expanduser("~"),
)
if fname:
self.resources_path_line_edit.setText(fname)
[docs]
def browse_spm(self):
"""Called when spm browse button is clicked."""
fname = QFileDialog.getExistingDirectory(
self, "Choose SPM directory", os.path.expanduser("~")
)
if fname:
self.spm_choice.setText(fname)
[docs]
def browse_spm_standalone(self):
"""Called when spm standalone browse button is clicked."""
fname = QFileDialog.getExistingDirectory(
self, "Choose SPM standalone directory", os.path.expanduser("~")
)
if fname:
self.spm_standalone_choice.setText(fname)
[docs]
def change_admin_psswd(self, status):
"""Method to change the admin password.
:param status: String
"""
change = QDialog()
change.old_psswd = QLineEdit()
change.new_psswd = QLineEdit()
change.new_psswd_conf = QLineEdit()
status = "<i>" + status + "</i>"
change.status = QLabel(status)
change.status.setStyleSheet("color:red")
change.old_psswd.setEchoMode(QLineEdit.Password)
change.new_psswd.setEchoMode(QLineEdit.Password)
change.new_psswd_conf.setEchoMode(QLineEdit.Password)
buttonBox = QDialogButtonBox(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self
)
layout = QFormLayout()
layout.addRow("Old password", change.old_psswd)
layout.addRow("New password", change.new_psswd)
layout.addRow("Confirm new password", change.new_psswd_conf)
layout.addRow(change.status)
layout.addWidget(buttonBox)
buttonBox.accepted.connect(change.accept)
buttonBox.rejected.connect(change.reject)
change.setLayout(layout)
event = change.exec()
if not event:
change.close()
else:
config = Config()
old_psswd = self.salt + change.old_psswd.text()
hash_psswd = hashlib.sha256(old_psswd.encode()).hexdigest()
if (
hash_psswd == config.get_admin_hash()
and change.new_psswd.text() == change.new_psswd_conf.text()
and len(change.new_psswd.text()) > 6
):
new_psswd = self.salt + change.new_psswd.text()
config.set_admin_hash(
hashlib.sha256(new_psswd.encode()).hexdigest()
)
elif hash_psswd != config.get_admin_hash():
self.change_admin_psswd("The old password is incorrect.")
elif len(change.new_psswd.text()) <= 6:
self.change_admin_psswd(
"Your password must have more than 6 characters"
)
elif change.new_psswd.text() != change.new_psswd_conf.text():
self.change_admin_psswd("The new passwords are not the same.")
[docs]
def control_checkbox_toggled(self, main_window):
"""Check if the user really wants to change the controller version.
:param main_window: main window object of the software
"""
self.control_checkbox.toggle()
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Warning)
self.msg.setText("Controller version change")
self.msg.setWindowTitle("Warning")
self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
config = Config()
if not self.control_checkbox_changed:
self.msg.setInformativeText(
"To change the controller from {0} to {1}, "
"MIA must be restarted. Would you like to plan "
"this change for next "
"start-up?".format(
"V1" if config.isControlV1() else "V2",
"V2" if config.isControlV1() else "V1",
)
)
else:
self.msg.setInformativeText(
"Change of controller from {0} to {1} is already "
"planned for next start-up. Would you like to "
"cancel this "
"change?".format(
"V1" if config.isControlV1() else "V2",
"V2" if config.isControlV1() else "V1",
)
)
return_value = self.msg.exec()
if return_value == QMessageBox.Yes:
self.control_checkbox_changed = not self.control_checkbox_changed
main_window.set_controller_version()
QApplication.restoreOverrideCursor()
[docs]
def edit_config_file(self):
"""Create a window to view, edit the mia configuration file."""
# import verCmp only here to prevent circular import issue
from populse_mia.utils import verCmp
config = Config()
self.editConf = QDialog()
self.editConf.setWindowTitle(
os.path.join(
config.get_properties_path(), "properties", "config.yml"
)
)
self.editConf.txt = QPlainTextEdit()
stream = yaml.dump(
config.config, default_flow_style=False, allow_unicode=True
)
self.editConf.txt.insertPlainText(str(stream))
textWidth = self.editConf.txt.width() + 100
textHeight = self.editConf.txt.height() + 200
self.editConf.txt.setMinimumSize(textWidth, textHeight)
self.editConf.txt.resize(textWidth, textHeight)
buttonBox = QDialogButtonBox(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self
)
buttonBox.button(QDialogButtonBox.Ok).setDefault(False)
buttonBox.button(QDialogButtonBox.Cancel).setDefault(False)
self.findChar_line_edit = QLineEdit()
findChar_button = QPushButton("Find")
findChar_button.setDefault(True)
h_box_find = QtWidgets.QHBoxLayout()
h_box_find.addWidget(self.findChar_line_edit)
h_box_find.addWidget(findChar_button)
findChar_button.clicked.connect(self.findChar)
layout = QFormLayout()
layout.addWidget(self.editConf.txt)
layout.addRow(h_box_find)
layout.addWidget(buttonBox)
buttonBox.accepted.connect(self.editConf.accept)
buttonBox.rejected.connect(self.editConf.reject)
self.editConf.setLayout(layout)
event = self.editConf.exec()
if not event:
self.editConf.close()
else:
stream = self.editConf.txt.toPlainText()
if verCmp(yaml.__version__, "5.1", "sup"):
config.config = yaml.load(stream, Loader=yaml.FullLoader)
else:
config.config = yaml.load(stream)
config.saveConfig()
self.editConf.close()
[docs]
def findChar(self):
"""Highlights characters in red when using the Find button'
when editing configuration.
"""
cursor = self.editConf.txt.textCursor()
cursor.select(QtGui.QTextCursor.Document)
cursor.setCharFormat(QtGui.QTextCharFormat())
cursor.clearSelection()
self.editConf.txt.setTextCursor(cursor)
pattern = self.findChar_line_edit.text()
if pattern == "":
return
cursor = self.editConf.txt.textCursor()
format = QtGui.QTextCharFormat()
format.setBackground(QtGui.QBrush(QtGui.QColor(255, 0, 0, 50)))
regex = QtCore.QRegExp(pattern)
pos = 0
index = regex.indexIn(self.editConf.txt.toPlainText(), pos)
while index != -1:
cursor.setPosition(index)
for _ in pattern:
cursor.movePosition(QtGui.QTextCursor.Right, 1)
cursor.mergeCharFormat(format)
pos = index + regex.matchedLength()
index = regex.indexIn(self.editConf.txt.toPlainText(), pos)
[docs]
def edit_capsul_config(self):
"""Capsul engine edition
This method is used when user hit the Edit CAPSUL config button (File >
MIA preferences, Pipeline tab).
"""
# from capsul.api import capsul_engine
# from capsul.qt_gui.widgets.settings_editor import SettingsEditor
# validate the current Mia config first
if not self.validate_and_save():
return
config = Config()
capsul_config = config.get_capsul_config(sync_from_engine=False)
modules = capsul_config.get("engine_modules", [])
# build a temporary new engine (because it may not be validated)
engine = capsul_engine()
for module in modules + [
"fom",
"axon",
"python",
"fsl",
"freesurfer",
"nipype",
"afni",
"ants",
"mrtrix",
"somaworkflow",
]:
engine.load_module(module)
envs = capsul_config.get("engine", {})
for env, conf in envs.items():
c = dict(conf)
if "capsul_engine" not in c or "uses" not in c["capsul_engine"]:
c["capsul_engine"] = {
"uses": {
engine.settings.module_name(m): "ALL"
for m in conf.keys()
}
}
# for mod, val in conf.items():
# if 'config_id' not in val:
# val['config_id'] = mod.split('.')[-1]
engine.settings.import_configs(env, c, cont_on_error=True)
dialog = SettingsEditor(engine)
try:
result = dialog.exec()
except Exception as e:
print(e)
return
if result:
settings = engine.settings.export_config_dict()
capsul_config["engine"] = settings
capsul_config["engine_modules"] = list(engine._loaded_modules)
try:
config.set_capsul_config(capsul_config)
except Exception as e:
print(e)
return
# update Mia preferences GUI which might have changed
# afni
use_afni = config.get_use_afni()
if use_afni:
self.afni_choice.setText(config.get_afni_path())
use_afni = Qt.Qt.Checked if use_afni else Qt.Qt.Unchecked
self.use_afni_checkbox.setCheckState(use_afni)
# ants
use_ants = config.get_use_ants()
if use_ants:
self.ants_choice.setText(config.get_ants_path())
use_ants = Qt.Qt.Checked if use_ants else Qt.Qt.Unchecked
self.use_ants_checkbox.setCheckState(use_ants)
# freesurfer
use_freesurfer = config.get_use_freesurfer()
if use_freesurfer:
self.freesurfer_choice.setText(config.get_freesurfer_setup())
use_freesurfer = (
Qt.Qt.Checked if use_freesurfer else Qt.Qt.Unchecked
)
self.use_freesurfer_checkbox.setCheckState(use_freesurfer)
# fsl
use_fsl = config.get_use_fsl()
if use_fsl:
self.fsl_choice.setText(config.get_fsl_config())
use_fsl = Qt.Qt.Checked if use_fsl else Qt.Qt.Unchecked
self.use_fsl_checkbox.setCheckState(use_fsl)
# matlab
use_matlab = config.get_use_matlab()
use_matlab = Qt.Qt.Checked if use_matlab else Qt.Qt.Unchecked
self.use_matlab_checkbox.setCheckState(use_matlab)
self.matlab_choice.setText(config.get_matlab_path())
use_matlab_sa = config.get_use_matlab_standalone()
use_matlab_sa = Qt.Qt.Checked if use_matlab_sa else Qt.Qt.Unchecked
self.use_matlab_standalone_checkbox.setCheckState(use_matlab_sa)
self.matlab_standalone_choice.setText(
config.get_matlab_standalone_path()
)
# mrtrix
use_mrtrix = config.get_use_mrtrix()
if use_mrtrix:
self.mrtrix_choice.setText(config.get_mrtrix_path())
use_mrtrix = Qt.Qt.Checked if use_mrtrix else Qt.Qt.Unchecked
self.use_mrtrix_checkbox.setCheckState(use_mrtrix)
# spm
use_spm = config.get_use_spm()
use_spm = Qt.Qt.Checked if use_spm else Qt.Qt.Unchecked
self.use_spm_checkbox.setCheckState(use_spm)
self.spm_choice.setText(config.get_spm_path())
use_spm_sa = config.get_use_spm_standalone()
use_spm_sa = Qt.Qt.Checked if use_spm_sa else Qt.Qt.Unchecked
self.use_spm_standalone_checkbox.setCheckState(use_spm_sa)
self.spm_standalone_choice.setText(
config.get_spm_standalone_path()
)
del dialog
del engine
[docs]
def validate_and_save(self, OK_clicked=False):
"""Saves the modifications to the config file and apply them.
:param OK_clicked: a boolean. If False, only make a minimal backup of
the settings to allow synchronisation with
capsul config. If True, should only correspond to
the moment when we finally exit the Mia config, all
parameters are saved and tested.
:return: True if all is fine, False if a problem has been encountered
"""
config = Config()
# Minimum config backup (for Edit CAPSUL config synchronisation):
if not OK_clicked:
# Use AFNI
afni_dir = self.afni_choice.text()
config.set_afni_path(afni_dir)
if self.use_afni_checkbox.isChecked():
config.set_use_afni(True)
else:
config.set_use_afni(False)
# Use ANTS
ants_dir = self.ants_choice.text()
config.set_ants_path(ants_dir)
if self.use_ants_checkbox.isChecked():
config.set_use_ants(True)
else:
config.set_use_ants(False)
# Use freesurfer
freesurfer_setup = self.freesurfer_choice.text()
config.set_freesurfer_setup(freesurfer_setup)
if self.use_freesurfer_checkbox.isChecked():
config.set_use_freesurfer(True)
else:
config.set_use_freesurfer(False)
# Use FSL
fsl_conf = self.fsl_choice.text()
config.set_fsl_config(fsl_conf)
if self.use_fsl_checkbox.isChecked():
config.set_use_fsl(True)
else:
config.set_use_fsl(False)
# Use Matlab
matlab_input = self.matlab_choice.text()
config.set_matlab_path(matlab_input)
if self.use_matlab_checkbox.isChecked():
config.set_use_matlab(True)
else:
config.set_use_matlab(False)
# Use Matlab Runtime:
matlab_input = self.matlab_standalone_choice.text()
config.set_matlab_standalone_path(matlab_input)
if self.use_matlab_standalone_checkbox.isChecked():
config.set_use_matlab_standalone(True)
else:
config.set_use_matlab_standalone(False)
# Use mrtrix
mrtrix_dir = self.mrtrix_choice.text()
config.set_mrtrix_path(mrtrix_dir)
if self.use_mrtrix_checkbox.isChecked():
config.set_use_mrtrix(True)
else:
config.set_use_mrtrix(False)
# Use SPM
spm_input = self.spm_choice.text()
config.set_spm_path(spm_input)
if self.use_spm_checkbox.isChecked():
config.set_use_spm(True)
else:
config.set_use_spm(False)
# Use SPM standalone
spm_input = self.spm_standalone_choice.text()
config.set_spm_standalone_path(spm_input)
if self.use_spm_standalone_checkbox.isChecked():
config.set_use_spm_standalone(True)
else:
config.set_use_spm_standalone(False)
# complete backup and testing
else:
# Auto-save
if self.save_checkbox.isChecked():
config.setAutoSave(True)
else:
config.setAutoSave(False)
# RadioView in miniviewer (databrowser)
if self.radioView_checkbox.isChecked():
config.set_radioView(True)
else:
config.set_radioView(False)
# Version 1 controller
if self.control_checkbox.isChecked():
config.setControlV1(True)
else:
config.setControlV1(False)
# Max thumbnails number at the data browser bottom
max_thumbnails = min(max(self.max_thumbnails_box.value(), 1), 15)
config.set_max_thumbnails(max_thumbnails)
# Max projects in "Saved projects"
max_projects = min(max(self.max_projects_box.value(), 1), 20)
config.set_max_projects(max_projects)
# User / Admin mode
main_window = self.main_window
main_window.windowName = "MIA - Multiparametric Image Analysis"
if self.admin_mode_checkbox.isChecked():
config.set_user_mode(False)
main_window.windowName += " (Admin mode)"
else:
config.set_user_mode(True)
# Clinical mode
if self.clinical_mode_checkbox.isChecked():
config.set_clinical_mode(True)
self.use_clinical_mode_signal.emit()
else:
config.set_clinical_mode(False)
self.not_use_clinical_mode_signal.emit()
# Window name
main_window.windowName += " - "
main_window.setWindowTitle(
main_window.windowName + main_window.projectName
)
# Configuration test
QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
self.status_label.setText("Testing configuration ...")
QCoreApplication.processEvents()
# AFNI config test
if self.use_afni_checkbox.isChecked():
afni_dir = self.afni_choice.text()
afni_cmd = "afni"
if os.path.isdir(afni_dir):
afni_cmd = os.path.join(afni_dir, afni_cmd)
else:
self.wrong_path(afni_dir, "AFNI")
QApplication.restoreOverrideCursor()
return False
try:
p = subprocess.Popen(
[afni_cmd, "-version"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output, err = p.communicate()
if err == b"":
config.set_afni_path(afni_dir)
config.set_use_afni(True)
else:
self.wrong_path(afni_dir, "AFNI")
QApplication.restoreOverrideCursor()
return False
except Exception:
self.wrong_path(afni_dir, "AFNI")
QApplication.restoreOverrideCursor()
return False
else:
config.set_use_afni(False)
# ANTS config test
if self.use_ants_checkbox.isChecked():
ants_dir = self.ants_choice.text()
ants_cmd = "SmoothImage"
if os.path.isdir(ants_dir):
ants_cmd = os.path.join(ants_dir, ants_cmd)
else:
self.wrong_path(ants_dir, "ANTS")
QApplication.restoreOverrideCursor()
return False
try:
p = subprocess.Popen(
[ants_cmd, "-version"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output, err = p.communicate()
if err == b"":
config.set_ants_path(ants_dir)
config.set_use_ants(True)
else:
self.wrong_path(ants_dir, "ANTS")
QApplication.restoreOverrideCursor()
return False
except Exception:
self.wrong_path(ants_dir, "ANTS")
QApplication.restoreOverrideCursor()
return False
else:
config.set_use_ants(False)
# freesurfer config test
if self.use_freesurfer_checkbox.isChecked():
freesurfer_setup = self.freesurfer_choice.text()
freesurfer_dir = os.path.dirname(freesurfer_setup)
if "FREESURFER_HOME" not in os.environ:
os.environ["FREESURFER_HOME"] = freesurfer_dir
freesurfer_cmd = "recon-all"
if os.path.isdir(freesurfer_dir):
freesurfer_cmd = os.path.join(
freesurfer_dir, "bin", freesurfer_cmd
)
else:
self.wrong_path(freesurfer_dir, "freesurfer")
QApplication.restoreOverrideCursor()
return False
try:
p = subprocess.Popen(
[freesurfer_cmd, "--version"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output, err = p.communicate()
if err == b"":
config.set_freesurfer_setup(freesurfer_setup)
config.set_use_freesurfer(True)
else:
self.wrong_path(freesurfer_dir, "freesurfer")
QApplication.restoreOverrideCursor()
return False
except Exception:
self.wrong_path(freesurfer_dir, "freesurfer")
QApplication.restoreOverrideCursor()
return False
else:
config.set_use_freesurfer(False)
# FSL config test
if self.use_fsl_checkbox.isChecked():
fsl_conf = self.fsl_choice.text()
if fsl_conf == "":
fsl_cmd = "flirt"
else:
fsl_dir = os.path.dirname(fsl_conf)
if fsl_dir.endswith(os.path.join("etc", "fslconf")):
fsl_dir = os.path.dirname(os.path.dirname(fsl_dir))
elif fsl_dir.endswith("etc"):
fsl_dir = os.path.dirname(fsl_dir)
fsl_cmd = os.path.join(fsl_dir, "bin", "flirt")
try:
p = subprocess.Popen(
[fsl_cmd, "-version"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output, err = p.communicate()
if err == b"":
config.set_fsl_config(fsl_conf)
config.set_use_fsl(True)
else:
self.wrong_path(fsl_conf, "FSL", "config file")
QApplication.restoreOverrideCursor()
return False
except Exception:
self.wrong_path(fsl_conf, "FSL", "config file")
QApplication.restoreOverrideCursor()
return False
else:
config.set_use_fsl(False)
# mrtrix config test
if self.use_mrtrix_checkbox.isChecked():
mrtrix_dir = self.mrtrix_choice.text()
mrtrix_cmd = "mrinfo"
if os.path.isdir(mrtrix_dir):
mrtrix_cmd = os.path.join(mrtrix_dir, mrtrix_cmd)
else:
self.wrong_path(mrtrix_dir, "mrtrix")
QApplication.restoreOverrideCursor()
return False
try:
p = subprocess.Popen(
[mrtrix_cmd, "-version"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output, err = p.communicate()
if err == b"":
config.set_mrtrix_path(mrtrix_dir)
config.set_use_mrtrix(True)
else:
self.wrong_path(mrtrix_dir, "mrtrix")
QApplication.restoreOverrideCursor()
return False
except Exception:
self.wrong_path(mrtrix_dir, "mrtrix")
QApplication.restoreOverrideCursor()
return False
else:
config.set_use_mrtrix(False)
# SPM & Matlab (license) config test
matlab_input = self.matlab_choice.text()
spm_input = self.spm_choice.text()
if (
matlab_input != "" and spm_input != ""
) or self.use_spm_checkbox.isChecked():
if not os.path.isfile(matlab_input):
self.wrong_path(matlab_input, "Matlab")
QApplication.restoreOverrideCursor()
return False
if (
matlab_input == config.get_matlab_path()
and spm_input == config.get_spm_path()
):
if self.use_spm_checkbox.isChecked():
config.set_use_spm(True)
config.set_use_matlab(True)
config.set_use_matlab_standalone(False)
config.set_use_spm_standalone(False)
elif os.path.isdir(spm_input):
try:
matlab_cmd = (
"restoredefaultpath; "
"addpath('" + spm_input + "'); "
"[name, ~]=spm('Ver'); "
"exit"
)
p = subprocess.Popen(
[
matlab_input,
"-nodisplay",
"-nodesktop",
"-nosplash",
"-singleCompThread",
"-r",
matlab_cmd,
],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output, err = p.communicate()
if err == b"":
config.set_matlab_path(matlab_input)
if self.use_spm_checkbox.isChecked():
config.set_use_matlab(True)
config.set_use_matlab_standalone(False)
config.set_use_spm(True)
config.set_use_spm_standalone(False)
config.set_spm_path(spm_input)
elif "spm" in str(err):
self.wrong_path(spm_input, "SPM")
QApplication.restoreOverrideCursor()
return False
else:
self.wrong_path(matlab_input, "Matlab")
QApplication.restoreOverrideCursor()
return False
except Exception:
self.wrong_path(matlab_input, "Matlab")
QApplication.restoreOverrideCursor()
return False
else:
self.wrong_path(spm_input, "SPM")
QApplication.restoreOverrideCursor()
return False
# Matlab alone config test
if matlab_input != "" or self.use_matlab_checkbox.isChecked():
if matlab_input == config.get_matlab_path():
if (
self.use_matlab_checkbox.isChecked()
and not self.use_spm_checkbox.isChecked()
):
config.set_use_matlab(True)
config.set_use_matlab_standalone(False)
config.set_use_spm(False)
config.set_use_spm_standalone(False)
elif os.path.isfile(matlab_input):
try:
matlab_cmd = "ver; exit"
p = subprocess.Popen(
[
matlab_input,
"-nodisplay",
"-nodesktop",
"-nosplash",
"-singleCompThread",
"-r",
matlab_cmd,
],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output, err = p.communicate()
if err == b"":
config.set_matlab_path(matlab_input)
if self.use_matlab_checkbox.isChecked():
config.set_use_matlab(True)
config.set_use_matlab_standalone(False)
config.set_use_spm(False)
config.set_use_spm_standalone(False)
else:
self.wrong_path(matlab_input, "Matlab")
QApplication.restoreOverrideCursor()
return False
except Exception:
self.wrong_path(matlab_input, "Matlab")
QApplication.restoreOverrideCursor()
return False
else:
self.wrong_path(matlab_input, "Matlab")
QApplication.restoreOverrideCursor()
return False
# SPM (standalone) & Matlab (MCR) config test
spm_input = self.spm_standalone_choice.text()
matlab_input = self.matlab_standalone_choice.text()
archi = platform.architecture()
if (
matlab_input != "" and spm_input != ""
) or self.use_spm_standalone_checkbox.isChecked():
if (not os.path.isdir(matlab_input)) and (
"Windows" not in archi[1]
):
self.wrong_path(matlab_input, "Matlab standalone")
QApplication.restoreOverrideCursor()
return False
if (matlab_input == config.get_matlab_standalone_path()) and (
spm_input == config.get_spm_standalone_path()
):
if self.use_spm_standalone_checkbox.isChecked():
config.set_use_spm_standalone(True)
if "Windows" in archi[1]:
config.set_use_matlab(True)
config.set_use_matlab_standalone(False)
else:
config.set_use_matlab_standalone(True)
elif os.path.isdir(spm_input):
if "Windows" in archi[1]:
mcr = glob.glob(
os.path.join(spm_input, "spm*_win*.exe")
)
pos = -1
nb_bit_sys = archi[0]
for i in range(len(mcr)):
spm_path, spm_file_name = os.path.split(mcr[i])
if nb_bit_sys[:2] in spm_file_name:
pos = i
if pos == -1:
self.wrong_path(spm_input, "SPM standalone")
QApplication.restoreOverrideCursor()
return False
elif os.path.isdir(matlab_input):
mcr = glob.glob(os.path.join(spm_input, "run_spm*.sh"))
if mcr:
try:
if "Windows" in archi[1]:
p = subprocess.Popen(
[mcr[pos], "--version"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
else:
p = subprocess.Popen(
[mcr[0], matlab_input, "--version"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output, err = p.communicate()
if (
err == b"" and output != b""
) or output.startswith(b"SPM8 "):
# spm8 standalone doesn't accept --version
# argument but prints a message that we can
# interpret as saying that SPM8 is working
# anyway.
if (
self.use_spm_standalone_checkbox.isChecked
)():
config.set_use_spm_standalone(True)
config.set_use_matlab_standalone(True)
config.set_spm_standalone_path(spm_input)
config.set_matlab_standalone_path(matlab_input)
elif (
(err != b"")
and (b"version" in output.split()[2:])
and (b"(standalone)" in output.split()[2:])
):
if (
self.use_spm_standalone_checkbox.isChecked
)():
config.set_use_spm_standalone(True)
config.set_use_matlab_standalone(True)
config.set_spm_standalone_path(spm_input)
config.set_matlab_standalone_path(matlab_input)
if isinstance(err, bytes):
err = err.decode("utf-8")
print(
"\nWarning: The configuration for Matlab"
" MCR and SPM standalone as defined in"
" Mia's preferences seems to be valid "
"but the following issue has been "
"detected:\n{}\nPlease fix this issue"
" to avoid a malfunction ...".format(err)
)
elif err != b"":
if "shared libraries" in str(err):
self.wrong_path(
matlab_input, "Matlab standalone"
)
QApplication.restoreOverrideCursor()
return False
else:
self.wrong_path(
spm_input, "SPM standalone"
)
QApplication.restoreOverrideCursor()
return False
else:
self.wrong_path(spm_input, "SPM standalone")
QApplication.restoreOverrideCursor()
return False
except Exception:
self.wrong_path(spm_input, "SPM standalone")
QApplication.restoreOverrideCursor()
return False
else:
self.wrong_path(spm_input, "SPM standalone")
QApplication.restoreOverrideCursor()
return False
else:
self.wrong_path(spm_input, "SPM standalone")
QApplication.restoreOverrideCursor()
return False
# Matlab (MCR) alone config test
if (
matlab_input != ""
or self.use_matlab_standalone_checkbox.isChecked()
):
if "Windows" in archi[1]:
print(
"WARNING: Matlab Standalone Path enter, this "
"is unnecessary to use SPM12."
)
config.set_use_matlab(True)
config.set_use_matlab_standalone(False)
config.set_matlab_standalone_path(matlab_input)
elif os.path.isdir(matlab_input):
if (
self.use_matlab_standalone_checkbox.isChecked()
and not self.use_spm_standalone_checkbox.isChecked()
):
config.set_use_matlab_standalone(True)
config.set_matlab_standalone_path(matlab_input)
config.set_use_matlab(False)
config.set_use_spm_standalone(False)
config.set_use_spm(False)
else:
self.wrong_path(matlab_input, "Matlab standalone")
QApplication.restoreOverrideCursor()
return False
# Colors
background_color = self.background_color_combo.currentText()
text_color = self.text_color_combo.currentText()
config.setBackgroundColor(background_color)
config.setTextColor(text_color)
main_window.setStyleSheet(
"background-color:"
+ background_color
+ ";color:"
+ text_color
+ ";"
)
# main window setup
fullscreen = self.fullscreen_cbox.isChecked()
config.set_mainwindow_maximized(fullscreen)
w = self.mainwindow_size_x_spinbox.value()
h = self.mainwindow_size_y_spinbox.value()
config.set_mainwindow_size([w, h])
if fullscreen:
main_window.showMaximized()
else:
main_window.showNormal()
# Projects folder
projects_folder = self.projects_save_path_line_edit.text()
if os.path.isdir(projects_folder):
config.set_projects_save_path(projects_folder)
else:
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Critical)
self.msg.setText("Invalid projects folder path")
self.msg.setInformativeText(
"The projects folder path entered "
"{0} is "
"invalid.".format(projects_folder)
)
self.msg.setWindowTitle("Error")
self.msg.setStandardButtons(QMessageBox.Ok)
self.msg.buttonClicked.connect(self.msg.close)
self.msg.show()
QApplication.restoreOverrideCursor()
return False
# MRIFileManager.jar path
mri_conv_path = self.mri_conv_path_line_edit.text()
if mri_conv_path == "":
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Warning)
self.msg.setText("Empty MRIFileManager.jar path")
self.msg.setInformativeText(
"No path has been entered for MRIFileManager.jar."
)
self.msg.setWindowTitle("Warning")
self.msg.setStandardButtons(QMessageBox.Ok)
self.msg.buttonClicked.connect(self.msg.close)
self.msg.show()
config.set_mri_conv_path(mri_conv_path)
elif os.path.isfile(mri_conv_path):
config.set_mri_conv_path(mri_conv_path)
else:
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Critical)
self.msg.setText("Invalid MRIFileManager.jar path")
self.msg.setInformativeText(
"The MRIFileManager.jar path "
"entered {0} "
"is invalid.".format(mri_conv_path)
)
self.msg.setWindowTitle("Error")
self.msg.setStandardButtons(QMessageBox.Ok)
self.msg.buttonClicked.connect(self.msg.close)
self.msg.show()
QApplication.restoreOverrideCursor()
return False
# Resources folder
resources_folder = self.resources_path_line_edit.text()
if os.path.isdir(resources_folder):
config.set_resources_path(resources_folder)
else:
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Critical)
self.msg.setText("Invalid resources folder path")
self.msg.setInformativeText(
"The resources folder path entered "
"{0} is "
"invalid.".format(resources_folder)
)
self.msg.setWindowTitle("Error")
self.msg.setStandardButtons(QMessageBox.Ok)
self.msg.buttonClicked.connect(self.msg.close)
self.msg.show()
QApplication.restoreOverrideCursor()
return False
self.signal_preferences_change.emit()
QApplication.restoreOverrideCursor()
c_c = config.config.setdefault("capsul_config", {})
c_e = config.get_capsul_engine()
if c_c and c_e:
# sync capsul config from mia config, if module is not used:
# AFNI CapsulConfig
if not config.get_use_afni():
# TODO: We only deal here with the global environment
cif = c_e.settings.config_id_field
with c_e.settings as settings:
configafni = settings.config("afni", "global")
if configafni:
settings.remove_config(
"afni", "global", getattr(configafni, cif)
)
# TODO: We could use a generic method to deal with c_c?
try:
del c_c["engine"]["global"]["capsul.engine.module.afni"][
"afni"
]["directory"]
except KeyError:
pass
# ANTS CapsulConfig
if not config.get_use_ants():
# TODO: We only deal here with the global environment
cif = c_e.settings.config_id_field
with c_e.settings as settings:
configants = settings.config("ants", "global")
if configants:
settings.remove_config(
"ants", "global", getattr(configants, cif)
)
# TODO: We could use a generic method to deal with c_c?
try:
del c_c["engine"]["global"]["capsul.engine.module.ants"][
"ants"
]["directory"]
except KeyError:
pass
# freesurfer CapsulConfig
if not config.get_use_freesurfer():
# TODO: We only deal here with the global environment
cif = c_e.settings.config_id_field
with c_e.settings as settings:
configants = settings.config("freesurfer", "global")
if configants:
settings.remove_config(
"freesurfer", "global", getattr(configants, cif)
)
# TODO: We could use a generic method to deal with c_c?
# try:
# del c_c["engine"]["global"][
# "capsul.engine.module.freesurfer"
# ]["freesurfer"
# ]["directory"]
#
# except KeyError:
# pass
# FSL CapsulConfig
if not config.get_use_fsl():
# TODO: We only deal here with the global environment
cif = c_e.settings.config_id_field
with c_e.settings as settings:
configfsl = settings.config("fsl", "global")
if configfsl:
settings.remove_config(
"fsl", "global", getattr(configfsl, cif)
)
# TODO: We could use a generic method to deal with c_c?
try:
del c_c["engine"]["global"]["capsul.engine.module.fsl"][
"fsl"
]["directory"]
except KeyError:
pass
try:
del c_c["engine"]["global"]["capsul.engine.module.fsl"][
"fsl"
]["config"]
except KeyError:
pass
# mrtrix CapsulConfig
if not config.get_use_mrtrix():
# TODO: We only deal here with the global environment
cif = c_e.settings.config_id_field
with c_e.settings as settings:
configants = settings.config("mrtrix", "global")
if configants:
settings.remove_config(
"mrtrix", "global", getattr(configants, cif)
)
# TODO: We could use a generic method to deal with c_c?
try:
del c_c["engine"]["global"]["capsul.engine.module.mrtrix"][
"mrtrix"
]["directory"]
except KeyError:
pass
# SPM standalone CapsulConfig
if not config.get_use_spm_standalone():
try:
keys = c_c["engine"]["global"][
"capsul.engine.module.spm"
].keys()
except KeyError:
pass
else:
dict4clean = dict.fromkeys(keys, False)
for i in keys:
if (
"standalone"
in c_c["engine"]["global"][
"capsul.engine.module.spm"
][i]
):
if (
c_c["engine"]["global"][
"capsul.engine.module.spm"
][i]["standalone"]
is True
):
dict4clean[i] = True
else:
# TODO: What we do if standalone is not a key ?
pass
for i in dict4clean:
if dict4clean[i]:
del c_c["engine"]["global"][
"capsul.engine.module.spm"
][i]
if not config.get_use_spm():
try:
keys = c_c["engine"]["global"][
"capsul.engine.module.spm"
].keys()
except KeyError:
pass
else:
dict4clean = dict.fromkeys(keys, False)
for i in keys:
if (
"standalone"
in c_c["engine"]["global"][
"capsul.engine.module.spm"
][i]
):
if (
c_c["engine"]["global"][
"capsul.engine.module.spm"
][i]["standalone"]
is False
):
dict4clean[i] = True
for i in dict4clean:
if dict4clean[i]:
del c_c["engine"]["global"][
"capsul.engine.module.spm"
][i]
try:
if not c_c["engine"]["global"]["capsul.engine.module.spm"]:
del c_c["engine"]["global"]["capsul.engine.module.spm"]
except KeyError:
pass
if not config.get_use_matlab():
try:
keys = c_c["engine"]["global"][
"capsul.engine.module.matlab"
].keys()
except KeyError:
pass
else:
dict4clean = dict.fromkeys(keys, False)
for i in keys:
if (
"executable"
in c_c["engine"]["global"][
"capsul.engine.module.matlab"
][i]
):
dict4clean[i] = True
for i in dict4clean:
if dict4clean[i]:
del c_c["engine"]["global"][
"capsul.engine.module.matlab"
][i]["executable"]
if not config.get_use_matlab_standalone():
try:
keys = c_c["engine"]["global"][
"capsul.engine.module.matlab"
].keys()
except KeyError:
pass
else:
dict4clean = dict.fromkeys(keys, False)
for i in keys:
if (
"mcr_directory"
in c_c["engine"]["global"][
"capsul.engine.module.matlab"
][i]
):
dict4clean[i] = True
for i in dict4clean:
if dict4clean[i]:
del c_c["engine"]["global"][
"capsul.engine.module.matlab"
][i]["mcr_directory"]
try:
if not c_c["engine"]["global"]["capsul.engine.module.matlab"]:
del c_c["engine"]["global"]["capsul.engine.module.matlab"]
except KeyError:
pass
# if not config.get_use_spm_standalone():
#
# try:
# del c_c['engine']['global'][
# 'capsul.engine.module.spm'][
# 'spm12-standalone']
#
# except KeyError:
# pass
#
# # try:
# # del c_c['engine']['global'][
# # 'capsul.engine.module.spm'][
# # 'spm12-standalone']['standalone']
# #
# # except KeyError:
# # pass
#
# cif = c_e.settings.config_id_field
#
# with c_e.settings as settings:
#
# for c in settings.configs('spm', 'global'):
# settings.remove_config('spm', 'global',
# getattr(c, cif))
#
# # settings.new_config('spm', 'global',
# # {'config_id': 'spm',
# # 'standalone': False,
# # 'directory': config.get_spm_standalone_path()})
#
# # for c in settings.configs('matlab', 'global'):
# # settings.remove_config('matlab', 'global',
# # getattr(c, cif))
#
# # try:
# # del c_c['engine']['global'][
# # 'capsul.engine.module.matlab'][
# # 'executable']
# #
# # except KeyError:
# # pass
#
# # SPM
# elif not config.get_use_spm():
#
# try:
# c_c['engine']['global'][
# 'capsul.engine.module.spm']['spm'[
# 'directory'] = config.get_spm_path()
#
# except KeyError:
# pass
#
# cif = c_e.settings.config_id_field
#
# with c_e.settings as settings:
#
# for c in settings.configs('spm', 'global'):
# settings.remove_config('spm', 'global',
# getattr(c, cif))
#
# # settings.new_config('spm', 'global',
# # {'config_id': 'spm',
# # 'directory': config.get_spm_path(),
# # 'standalone': False})
#
# # for c in settings.configs('matlab', 'global'):
# # settings.remove_config('matlab', 'global',
# # getattr(c, cif))
# #
# # settings.new_config('matlab', 'global',
# # {'config_id': 'matlab',
# # 'executable': config.get_matlab_path()})
# # try:
# # c_c['engine']['global'][
# # 'capsul.engine.module.matlab'][
# # 'executable'] = config.get_matlab_path()
# #
# # except KeyError:
# # pass
#
# # no SPM at all
# else:
# cif = c_e.settings.config_id_field
#
# with c_e.settings as settings:
#
# for c in settings.configs('spm', 'global'):
# settings.remove_config('spm', 'global',
# getattr(c, cif))
#
# try:
# del c_c['engine']['global'][
# 'capsul.engine.module.spm'][
# 'directory']
#
# except KeyError:
# pass
#
# # no MATLAB at all
# if (not config.get_use_matlab() and
# not config.get_use_matlab_standalone()):
# cif = c_e.settings.config_id_field
#
# with c_e.settings as settings:
#
# for c in settings.configs('matlab', 'global'):
# settings.remove_config('matlab', 'global',
# getattr(c, cif))
#
# try:
# del c_c['engine']['global'][
# 'capsul.engine.module.matlab'][
# 'executable']
#
# except KeyError:
# pass
#
# # only MATLAB
# if config.get_use_matlab() and not config.get_use_spm():
#
# try:
# c_c['engine']['global'][
# 'capsul.engine.module.matlab'][
# 'executable'] = config.get_matlab_path()
#
# except KeyError:
# pass
#
# cif = c_e.settings.config_id_field
#
# with c_e.settings as settings:
#
# for c in settings.configs('matlab', 'global'):
# settings.remove_config('matlab', 'global',
# getattr(c, cif))
#
# settings.new_config('matlab', 'global',
# {'config_id': 'matlab',
# 'executable': config.get_matlab_path()})
#
# for c in settings.configs('spm', 'global'):
# settings.remove_config('spm', 'global',
# getattr(c, cif))
config.get_capsul_config(sync_from_engine=False)
config.saveConfig()
return True
[docs]
def ok_clicked(self):
"""Blabla"""
if self.validate_and_save(OK_clicked=True):
self.accept()
self.close()
[docs]
def use_afni_changed(self):
"""Called when the use_afni checkbox is changed."""
if not self.use_afni_checkbox.isChecked():
self.afni_choice.setDisabled(True)
self.afni_label.setDisabled(True)
else:
self.afni_choice.setDisabled(False)
self.afni_label.setDisabled(False)
[docs]
def use_ants_changed(self):
"""Called when the use_ants checkbox is changed."""
if not self.use_ants_checkbox.isChecked():
self.ants_choice.setDisabled(True)
self.ants_label.setDisabled(True)
else:
self.ants_choice.setDisabled(False)
self.ants_label.setDisabled(False)
[docs]
def use_freesurfer_changed(self):
"""Called when the use_freesurfer checkbox is changed."""
if not self.use_freesurfer_checkbox.isChecked():
self.freesurfer_choice.setDisabled(True)
self.freesurfer_label.setDisabled(True)
else:
self.freesurfer_choice.setDisabled(False)
self.freesurfer_label.setDisabled(False)
[docs]
def use_fsl_changed(self):
"""Called when the use_fsl checkbox is changed."""
if not self.use_fsl_checkbox.isChecked():
self.fsl_choice.setDisabled(True)
self.fsl_label.setDisabled(True)
else:
self.fsl_choice.setDisabled(False)
self.fsl_label.setDisabled(False)
[docs]
def use_matlab_changed(self):
"""Called when the use_matlab checkbox is changed."""
if not self.use_matlab_checkbox.isChecked():
self.matlab_choice.setDisabled(True)
self.spm_choice.setDisabled(True)
self.matlab_label.setDisabled(True)
self.spm_label.setDisabled(True)
self.spm_browse.setDisabled(True)
self.matlab_browse.setDisabled(True)
self.use_spm_checkbox.setChecked(False)
else:
self.matlab_choice.setDisabled(False)
self.matlab_label.setDisabled(False)
self.matlab_browse.setDisabled(False)
self.use_matlab_standalone_checkbox.setChecked(False)
[docs]
def use_matlab_standalone_changed(self):
"""Called when the use_matlab_standalone checkbox is changed."""
if not self.use_matlab_standalone_checkbox.isChecked():
archi = platform.architecture()
if "Windows" not in archi[1]:
self.spm_standalone_choice.setDisabled(True)
self.use_spm_standalone_checkbox.setChecked(False)
self.spm_standalone_label.setDisabled(True)
self.spm_standalone_browse.setDisabled(True)
self.matlab_standalone_choice.setDisabled(True)
self.matlab_standalone_label.setDisabled(True)
self.matlab_standalone_browse.setDisabled(True)
else:
self.matlab_standalone_choice.setDisabled(False)
self.matlab_standalone_label.setDisabled(False)
self.matlab_standalone_browse.setDisabled(False)
self.use_matlab_checkbox.setChecked(False)
[docs]
def use_mrtrix_changed(self):
"""Called when the use_mrtrix checkbox is changed."""
if not self.use_mrtrix_checkbox.isChecked():
self.mrtrix_choice.setDisabled(True)
self.mrtrix_label.setDisabled(True)
else:
self.mrtrix_choice.setDisabled(False)
self.mrtrix_label.setDisabled(False)
[docs]
def use_spm_changed(self):
"""Called when the use_spm checkbox is changed."""
if not self.use_spm_checkbox.isChecked():
self.spm_choice.setDisabled(True)
self.spm_label.setDisabled(True)
self.spm_browse.setDisabled(True)
else:
self.use_matlab_checkbox.setChecked(True)
self.spm_choice.setDisabled(False)
self.spm_label.setDisabled(False)
self.spm_browse.setDisabled(False)
self.spm_standalone_choice.setDisabled(True)
self.spm_standalone_label.setDisabled(True)
self.spm_standalone_browse.setDisabled(True)
self.use_spm_standalone_checkbox.setChecked(False)
self.use_matlab_standalone_checkbox.setChecked(False)
[docs]
def use_spm_standalone_changed(self):
"""Called when the use_spm_standalone checkbox is changed."""
if not self.use_spm_standalone_checkbox.isChecked():
self.spm_standalone_choice.setDisabled(True)
self.spm_standalone_label.setDisabled(True)
self.spm_standalone_browse.setDisabled(True)
else:
archi = platform.architecture()
if "Windows" not in archi[1]:
self.use_matlab_standalone_checkbox.setChecked(True)
self.spm_standalone_choice.setDisabled(False)
self.spm_standalone_label.setDisabled(False)
self.spm_standalone_browse.setDisabled(False)
self.spm_choice.setDisabled(True)
self.spm_label.setDisabled(True)
self.spm_browse.setDisabled(True)
self.use_spm_checkbox.setChecked(False)
self.use_matlab_checkbox.setChecked(False)
[docs]
def admin_mode_switch(self):
"""Called when the admin mode checkbox is clicked."""
config = Config()
if self.admin_mode_checkbox.isChecked():
psswd, ok = QInputDialog.getText(
self,
"Password Input Dialog",
"Enter the admin password:",
QLineEdit.Password,
)
if ok:
salt_psswd = self.salt + psswd
hash_psswd = hashlib.sha256(salt_psswd.encode()).hexdigest()
if hash_psswd != config.get_admin_hash():
self.admin_mode_checkbox.setChecked(False)
self.status_label.setText(
"<i style='color:red'>Wrong password.</i>"
)
else:
self.change_psswd.setVisible(True)
self.edit_config.setVisible(True)
self.status_label.clear()
else:
self.admin_mode_checkbox.setChecked(False)
else:
self.change_psswd.setVisible(False)
self.edit_config.setVisible(False)
[docs]
def wrong_path(self, path, tool, extra_mess=""):
"""Blabla"""
QApplication.restoreOverrideCursor()
self.status_label.setText("")
self.msg = QMessageBox()
self.msg.setIcon(QMessageBox.Critical)
self.msg.setText("Invalid " + tool + " path")
if extra_mess:
extra_mess = " " + extra_mess
self.msg.setInformativeText(
"The {0}{1} path entered {2} is invalid.".format(
tool, extra_mess, path
)
)
self.msg.setWindowTitle("Error")
self.msg.setStandardButtons(QMessageBox.Ok)
self.msg.buttonClicked.connect(self.msg.close)
self.msg.show()
[docs]
def use_current_mainwindow_size(self, main_window):
"""Blabla"""
self.mainwindow_size_x_spinbox.setValue(main_window.width())
self.mainwindow_size_y_spinbox.setValue(main_window.height())
[docs]
class PopUpProperties(QDialog):
"""Is called when the user wants to change the current project's
properties (File > properties).
.. Methods:
- ok_clicked: saves the modifications and updates the data browser
"""
# Signal that will be emitted at the end to tell that the project has
# been created
signal_settings_change = pyqtSignal()
[docs]
def __init__(self, project, databrowser, old_tags):
"""Initialization
:param project: current project in the software
:param databrowser: data browser instance of the software
:param old_tags: visualized tags before opening this dialog
"""
super().__init__()
self.setModal(True)
self.project = project
self.databrowser = databrowser
self.old_tags = old_tags
_translate = QtCore.QCoreApplication.translate
self.setObjectName("Dialog")
self.setWindowTitle("project properties")
self.tab_widget = QtWidgets.QTabWidget(self)
self.tab_widget.setEnabled(True)
# The 'Visualized tags" tab
self.tab_tags = PopUpVisualizedTags(
self.project, self.project.session.get_shown_tags()
)
self.tab_tags.setObjectName("tab_tags")
self.tab_widget.addTab(
self.tab_tags, _translate("Dialog", "Visualized tags")
)
# The 'Informations" tab
self.tab_infos = PopUpInformation(self.project)
self.tab_infos.setObjectName("tab_infos")
self.tab_widget.addTab(
self.tab_infos, _translate("Dialog", "Informations")
)
# The 'OK' push button
self.push_button_ok = QtWidgets.QPushButton("OK")
self.push_button_ok.setObjectName("pushButton_ok")
self.push_button_ok.clicked.connect(self.ok_clicked)
# The 'Cancel' push button
self.push_button_cancel = QtWidgets.QPushButton("Cancel")
self.push_button_cancel.setObjectName("pushButton_cancel")
self.push_button_cancel.clicked.connect(self.close)
hbox_buttons = QHBoxLayout()
hbox_buttons.addStretch(1)
hbox_buttons.addWidget(self.push_button_ok)
hbox_buttons.addWidget(self.push_button_cancel)
vbox = QVBoxLayout()
vbox.addWidget(self.tab_widget)
vbox.addLayout(hbox_buttons)
self.setLayout(vbox)
[docs]
def ok_clicked(self):
"""Saves the modifications and updates the data browser."""
history_maker = ["modified_visibilities", self.old_tags]
new_visibilities = []
for x in range(self.tab_tags.list_widget_selected_tags.count()):
visible_tag = self.tab_tags.list_widget_selected_tags.item(
x
).text()
new_visibilities.append(visible_tag)
new_visibilities.append(TAG_FILENAME)
self.project.session.set_shown_tags(new_visibilities)
history_maker.append(new_visibilities)
self.project.undos.append(history_maker)
self.project.redos.clear()
self.project.unsavedModifications = True
# Columns updated
self.databrowser.table_data.update_visualized_columns(
self.old_tags, self.project.session.get_shown_tags()
)
self.accept()
self.close()
[docs]
class PopUpQuit(QDialog):
"""Is called when the user closes the software and the current project has
been modified.
.. Methods:
- can_exit: returns the value of bool_exit
- cancel_clicked: makes the actions to cancel the action
- do_not_save_clicked: makes the actions not to save the project
- save_as_clicked: makes the actions to save the project
"""
save_as_signal = pyqtSignal()
do_not_save_signal = pyqtSignal()
cancel_signal = pyqtSignal()
[docs]
def __init__(self, database):
"""Initialization.
:param database: current database in the project
"""
super().__init__()
self.database = database
self.bool_exit = False
self.setWindowTitle("Confirm exit")
label = QLabel(self)
label.setText(
"Do you want to exit without saving "
+ self.database.getName()
+ "?"
)
push_button_save_as = QPushButton("Save", self)
push_button_do_not_save = QPushButton("Do not save", self)
push_button_cancel = QPushButton("Cancel", self)
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(push_button_save_as)
hbox.addWidget(push_button_do_not_save)
hbox.addWidget(push_button_cancel)
hbox.addStretch(1)
vbox = QVBoxLayout()
vbox.addWidget(label)
vbox.addLayout(hbox)
self.setLayout(vbox)
push_button_save_as.clicked.connect(self.save_as_clicked)
push_button_do_not_save.clicked.connect(self.do_not_save_clicked)
push_button_cancel.clicked.connect(self.cancel_clicked)
[docs]
def can_exit(self):
"""Return the value of bool_exit.
:return: bool_exit value
"""
return self.bool_exit
[docs]
def cancel_clicked(self):
"""Makes the actions to cancel the action."""
self.bool_exit = False
self.close()
[docs]
def do_not_save_clicked(self):
"""Makes the actions not to save the project."""
self.bool_exit = True
self.close()
[docs]
def save_as_clicked(self):
"""Makes the actions to save the project."""
self.save_as_signal.emit()
self.bool_exit = True
self.close()
[docs]
class PopUpRemoveScan(QDialog):
"""Is called when the user wants to remove a scan that was previously sent
to the pipeline manager.
:param scan: The scan that may be removed
:param size: The number of scan the user wants to remove
.. Methods:
- cancel_clicked:
- no_all_clicked:
- yes_all_clicked:
- yes_clicked:
"""
[docs]
def __init__(self, scan, size):
super().__init__()
self.setWindowTitle("The document exists in the pipeline manager")
self.stop = False
self.repeat = False
label = QLabel(self)
label.setText(
"The document " + scan + "\nwas previously sent to the "
"pipeline manager, do you "
"really want to delete it ?"
)
push_button_yes = QPushButton("Ok", self)
push_button_cancel = QPushButton("No", self)
if size > 1:
push_button_yes_all = QPushButton("Ok to all", self)
push_button_no_all = QPushButton("No to all", self)
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(push_button_yes)
hbox.addWidget(push_button_cancel)
if size > 1:
hbox.addWidget(push_button_yes_all)
hbox.addWidget(push_button_no_all)
hbox.addStretch(1)
vbox = QVBoxLayout()
vbox.addWidget(label)
vbox.addLayout(hbox)
self.setLayout(vbox)
push_button_yes.clicked.connect(self.yes_clicked)
push_button_cancel.clicked.connect(self.cancel_clicked)
if size > 1:
push_button_yes_all.clicked.connect(self.yes_all_clicked)
push_button_no_all.clicked.connect(self.no_all_clicked)
[docs]
class PopUpRemoveTag(QDialog):
"""Is called when the user wants to remove a user tag from
populse_mia project.
.. Methods:
- ok_action: verifies the selected tags and send the information to
the data browser
- search_str: matches the searched pattern with the tags of the project
"""
# Signal that will be emitted at the end to tell that
# the project has been created
signal_remove_tag = pyqtSignal()
[docs]
def __init__(self, databrowser, project):
"""Initialization
:param databrowser: current project in the software
:param project: data browser instance of the software
"""
super().__init__()
self.databrowser = databrowser
self.project = project
self.setWindowTitle("Remove a tag")
self.setModal(True)
_translate = QtCore.QCoreApplication.translate
self.setObjectName("Remove a tag")
# The 'OK' push button
self.push_button_ok = QtWidgets.QPushButton(self)
self.push_button_ok.setObjectName("push_button_ok")
self.push_button_ok.setText(_translate("Remove a tag", "OK"))
hbox_buttons = QHBoxLayout()
hbox_buttons.addStretch(1)
hbox_buttons.addWidget(self.push_button_ok)
# The "Tag list" label
self.label_tag_list = QtWidgets.QLabel(self)
self.label_tag_list.setTextFormat(QtCore.Qt.AutoText)
self.label_tag_list.setObjectName("label_tag_list")
self.label_tag_list.setText(
_translate("Remove a tag", "Available tags:")
)
self.search_bar = QtWidgets.QLineEdit(self)
self.search_bar.setObjectName("lineEdit_search_bar")
self.search_bar.setPlaceholderText("Search")
self.search_bar.textChanged.connect(self.search_str)
hbox_top = QHBoxLayout()
hbox_top.addWidget(self.label_tag_list)
hbox_top.addStretch(1)
hbox_top.addWidget(self.search_bar)
# The list of tags
self.list_widget_tags = QtWidgets.QListWidget(self)
self.list_widget_tags.setObjectName("listWidget_tags")
self.list_widget_tags.setSelectionMode(
QtWidgets.QAbstractItemView.MultiSelection
)
vbox = QVBoxLayout()
vbox.addLayout(hbox_top)
vbox.addWidget(self.list_widget_tags)
vbox.addLayout(hbox_buttons)
self.setLayout(vbox)
for tag in self.project.session.get_fields(COLLECTION_CURRENT):
if tag.origin == TAG_ORIGIN_USER:
item = QtWidgets.QListWidgetItem()
self.list_widget_tags.addItem(item)
item.setText(_translate("Dialog", tag.field_name))
self.setLayout(vbox)
# Connecting the OK push buttone
self.push_button_ok.clicked.connect(self.ok_action)
[docs]
def ok_action(self):
"""Verifies the selected tags and send the information to the data
browser.
"""
self.accept()
self.tag_names_to_remove = []
for item in self.list_widget_tags.selectedItems():
self.tag_names_to_remove.append(item.text())
self.databrowser.remove_tag_infos(self.tag_names_to_remove)
self.close()
[docs]
def search_str(self, str_search):
"""
Matches the searched pattern with the tags of the project
:param str_search: string pattern to search
"""
_translate = QtCore.QCoreApplication.translate
if str_search != "":
return_list = []
for tag in self.project.session.get_fields(COLLECTION_CURRENT):
if tag.origin == TAG_ORIGIN_USER:
if str_search.upper() in tag.name.upper():
return_list.append(tag.name)
else:
return_list = []
for tag in self.project.session.get_fields(COLLECTION_CURRENT):
if tag.origin == TAG_ORIGIN_USER:
return_list.append(tag.name)
self.list_widget_tags.clear()
for tag_name in return_list:
item = QtWidgets.QListWidgetItem()
self.list_widget_tags.addItem(item)
item.setText(_translate("Dialog", tag_name))
[docs]
class PopUpSaveProjectAs(QDialog):
"""Is called when the user wants to save a project under another name.
.. Method:
- fill_input: fills the input field when a project is clicked on
- return_value: sets the widget's attributes depending on the
selected file name
"""
# Signal that will be emitted at the end to tell
# that the new file name has been chosen
signal_saved_project = pyqtSignal()
[docs]
def __init__(self):
super().__init__()
self.setWindowTitle("Save project as")
self.validate = False
self.new_project = QLineEdit()
self.new_project_label = QLabel("New project name")
self.config = Config()
self.project_path = self.config.get_projects_save_path()
project_list = os.listdir(self.project_path)
self.v_box = QVBoxLayout()
# Label
self.label = QLabel("Projects list:")
project_list.sort()
for i in range(0, len(project_list)):
if os.path.isdir(os.path.join(self.project_path, project_list[i])):
label = QLabel_clickable(project_list[i])
label.clicked.connect(
partial(self.fill_input, project_list[i])
)
self.v_box.addWidget(label)
# The text input
self.h_box_text = QHBoxLayout()
self.h_box_text.addWidget(self.new_project_label)
self.h_box_text.addWidget(self.new_project)
self.h_box_bottom = QHBoxLayout()
self.h_box_bottom.addStretch(1)
# The 'OK' push button
self.push_button_ok = QtWidgets.QPushButton("Save as")
self.push_button_ok.setObjectName("pushButton_ok")
self.push_button_ok.clicked.connect(self.return_value)
self.h_box_bottom.addWidget(self.push_button_ok)
# The 'Cancel' push button
self.push_button_cancel = QtWidgets.QPushButton("Cancel")
self.push_button_cancel.setObjectName("pushButton_cancel")
self.push_button_cancel.clicked.connect(self.close)
self.h_box_bottom.addWidget(self.push_button_cancel)
self.scroll = QScrollArea()
self.scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.scroll.setMaximumHeight(250)
self.widget = QWidget()
self.widget.setLayout(self.v_box)
self.scroll.setWidget(self.widget)
self.final = QVBoxLayout()
self.final.addWidget(self.scroll)
self.final_layout = QVBoxLayout()
self.final_layout.addWidget(self.label)
self.final_layout.addLayout(self.final)
self.final_layout.addLayout(self.h_box_text)
self.final_layout.addLayout(self.h_box_bottom)
self.setLayout(self.final_layout)
[docs]
def fill_input(self, name):
"""Fills the input field with the name of a project."""
self.new_project.setText(name)
[docs]
def return_value(self):
"""Sets the widget's attributes depending on the selected file name.
:return: new project's file name
"""
# import message_already_exists only here to prevent circular
# import issue
from populse_mia.utils import message_already_exists
file_name_tuple = self.new_project.text()
if len(file_name_tuple) > 0:
file_name = file_name_tuple
projects_folder = self.project_path
if file_name:
entire_path = os.path.abspath(
os.path.join(projects_folder, file_name)
)
self.path, self.name = os.path.split(entire_path)
self.total_path = entire_path
self.relative_path = os.path.relpath(entire_path)
self.relative_subpath = os.path.relpath(self.path)
if not os.path.exists(self.relative_path) and self.name != "":
self.signal_saved_project.emit()
self.validate = True
self.close()
# A signal is emitted to tell that the project
# has been created
elif file_name == "":
return
else:
if self.config.get_user_mode():
message_already_exists()
return
else:
msgtext = (
"Do you really want to overwrite the "
+ file_name
+ " project ?\nThis action "
"delete all contents inside this folder!"
)
msg = QMessageBox()
msg.setIcon(QMessageBox.Warning)
title = "populse_mia - Warning: Overwriting project"
reply = msg.question(
self,
title,
msgtext,
QMessageBox.Yes | QMessageBox.No,
)
if reply == QMessageBox.Yes:
self.validate = True
self.close()
else:
return
return entire_path
[docs]
class PopUpSeeAllProjects(QDialog):
"""Is called when the user wants to create a see all the projects.
.. Methods:
- checkState: checks if the project still exists and returns the
corresponding icon
- item_to_path: returns the path of the first selected item
- open_project: switches to the selected project
"""
[docs]
def __init__(self, saved_projects, main_window):
"""Initialization.
:param saved_projects: List of saved projects
:param main_window: Main window
"""
super().__init__()
self.mainWindow = main_window
self.setWindowTitle("See all saved projects")
self.setMinimumWidth(500)
# Tree widget
self.label = QLabel()
self.label.setText("List of saved projects")
self.treeWidget = QTreeWidget()
self.treeWidget.setColumnCount(3)
self.treeWidget.setHeaderLabels(["Name", "Path", "State"])
i = -1
for path in saved_projects.pathsList:
i += 1
text = os.path.basename(path)
wdg = QTreeWidgetItem()
wdg.setText(0, text)
wdg.setText(1, os.path.abspath(path))
wdg.setIcon(2, self.checkState(path))
self.treeWidget.addTopLevelItem(wdg)
hd = self.treeWidget.header()
hd.setSectionResizeMode(QHeaderView.ResizeToContents)
# Buttons
# The 'Open project' push button
self.pushButtonOpenProject = QPushButton("Open project")
self.pushButtonOpenProject.setObjectName("pushButton_ok")
self.pushButtonOpenProject.clicked.connect(self.open_project)
# The 'Cancel' push button
self.pushButtonCancel = QPushButton("Cancel")
self.pushButtonCancel.setObjectName("pushButton_cancel")
self.pushButtonCancel.clicked.connect(self.close)
# Layouts
self.hBoxButtons = QHBoxLayout()
self.hBoxButtons.addStretch(1)
self.hBoxButtons.addWidget(self.pushButtonOpenProject)
self.hBoxButtons.addWidget(self.pushButtonCancel)
self.vBox = QVBoxLayout()
self.vBox.addWidget(self.label)
self.vBox.addWidget(self.treeWidget)
self.vBox.addLayout(self.hBoxButtons)
self.setLayout(self.vBox)
[docs]
def checkState(self, path):
"""Checks if the project still exists and returns the corresponding
icon.
:param path: path of the project
:return: either a green "v" or a red cross depending on
the existence of the project
"""
sources_images_dir = Config().getSourceImageDir()
if os.path.exists(os.path.join(path)):
icon = QIcon(os.path.join(sources_images_dir, "green_v.png"))
else:
icon = QIcon(os.path.join(sources_images_dir, "red_cross.png"))
return icon
[docs]
def item_to_path(self):
"""Returns the path of the first selected item.
:return: the path of the first selected item
"""
nb_items = len(self.treeWidget.selectedItems())
if nb_items == 0:
msg = QMessageBox()
msg.setIcon(QMessageBox.Warning)
msg.setText("Please select a project to open")
msg.setWindowTitle("Warning")
msg.setStandardButtons(QMessageBox.Ok)
msg.buttonClicked.connect(msg.close)
msg.exec()
return ""
else:
item = self.treeWidget.selectedItems()[0]
text = item.text(1)
return text
[docs]
def open_project(self):
"""Switches to the selected project."""
file_name = self.item_to_path()
if file_name != "":
entire_path = os.path.abspath(file_name)
self.path, self.name = os.path.split(entire_path)
self.relative_path = os.path.relpath(file_name)
project_switched = self.mainWindow.switch_project(
self.relative_path, self.name
)
if project_switched:
self.accept()
self.close()
[docs]
class PopUpSelectFilter(PopUpFilterSelection):
"""Is called when the user wants to open a filter that has already been
saved.
.. Methods:
- ok_clicked: saves the modifications and updates the data browser
"""
[docs]
def __init__(self, project, databrowser):
"""Initialization.
:param project: current project in the software
:param databrowser: data browser instance of the software
"""
super(PopUpSelectFilter, self).__init__(project)
self.project = project
self.databrowser = databrowser
self.config = Config()
self.setWindowTitle("Open a filter")
# Filling the filter list
for filter in self.project.filters:
item = QtWidgets.QListWidgetItem()
self.list_widget_filters.addItem(item)
item.setText(filter.name)
[docs]
def ok_clicked(self):
"""Saves the modifications and updates the data browser."""
for item in self.list_widget_filters.selectedItems():
# Current filter updated
filter_name = item.text()
filter_object = self.project.getFilter(filter_name)
self.project.setCurrentFilter(filter_object)
break
self.databrowser.open_filter_infos()
self.accept()
self.close()
[docs]
class PopUpSelectIteration(QDialog):
"""Is called when the user wants to run an iterated pipeline.
.. Methods:
- ok_clicked: sends the selected values to the pipeline manager
"""
[docs]
def __init__(self, iterated_tag, tag_values):
"""Initialization.
:param iterated_tag: name of the iterated tag
:param tag_values: values that can take the iterated tag
"""
super().__init__()
self.iterated_tag = iterated_tag
self.tag_values = tag_values
self.final_values = []
self.setWindowTitle(
"Iterate pipeline run over tag {0}".format(self.iterated_tag)
)
self.v_box = QVBoxLayout()
# Label
self.label = QLabel("Select values to iterate over:")
self.v_box.addWidget(self.label)
self.check_boxes = []
for tag_value in self.tag_values:
check_box = QCheckBox(tag_value)
check_box.setCheckState(QtCore.Qt.Checked)
self.check_boxes.append(check_box)
self.v_box.addWidget(check_box)
self.h_box_bottom = QHBoxLayout()
self.h_box_bottom.addStretch(1)
# The 'OK' push button
self.push_button_ok = QtWidgets.QPushButton("OK")
self.push_button_ok.setObjectName("pushButton_ok")
self.push_button_ok.clicked.connect(self.ok_clicked)
self.h_box_bottom.addWidget(self.push_button_ok)
# The 'Cancel' push button
self.push_button_cancel = QtWidgets.QPushButton("Cancel")
self.push_button_cancel.setObjectName("pushButton_cancel")
self.push_button_cancel.clicked.connect(self.close)
self.h_box_bottom.addWidget(self.push_button_cancel)
self.scroll = QScrollArea()
self.widget = QWidget()
self.widget.setLayout(self.v_box)
self.scroll.setWidget(self.widget)
self.final = QVBoxLayout()
self.final.addWidget(self.scroll)
self.final_layout = QVBoxLayout()
self.final_layout.addLayout(self.final)
self.final_layout.addLayout(self.h_box_bottom)
self.setLayout(self.final_layout)
[docs]
def ok_clicked(self):
"""Sends the selected values to the pipeline manager."""
final_values = []
for check_box in self.check_boxes:
if check_box.isChecked():
final_values.append(check_box.text())
self.final_values = final_values
self.accept()
self.close()
[docs]
class PopUpTagSelection(QDialog):
"""Is called when the user wants to update the tags that are visualized in
the data browser.
.. Methods:
- cancel_clicked: closes the pop-up
- item_clicked: checks the checkbox of an item when the latter
is clicked
- ok_clicked: actions when the "OK" button is clicked
- search_str: matches the searched pattern with the tags of the project
"""
[docs]
def __init__(self, project):
"""Initialization.
:param project: current project in the software
"""
super().__init__()
self.project = project
_translate = QtCore.QCoreApplication.translate
# The "Tag list" label
self.label_tag_list = QtWidgets.QLabel(self)
self.label_tag_list.setTextFormat(QtCore.Qt.AutoText)
self.label_tag_list.setObjectName("label_tag_list")
self.label_tag_list.setText(
_translate("main_window", "Available tags:")
)
# The search bar to search in the list of tags
self.search_bar = QtWidgets.QLineEdit(self)
self.search_bar.setObjectName("lineEdit_search_bar")
self.search_bar.setPlaceholderText("Search")
self.search_bar.textChanged.connect(self.search_str)
# The list of tags
self.list_widget_tags = QtWidgets.QListWidget(self)
self.list_widget_tags.setObjectName("listWidget_tags")
self.list_widget_tags.setSelectionMode(
QtWidgets.QAbstractItemView.SingleSelection
)
self.list_widget_tags.itemClicked.connect(self.item_clicked)
self.push_button_ok = QtWidgets.QPushButton(self)
self.push_button_ok.setObjectName("pushButton_ok")
self.push_button_ok.setText("OK")
self.push_button_ok.clicked.connect(self.ok_clicked)
self.push_button_cancel = QtWidgets.QPushButton(self)
self.push_button_cancel.setObjectName("pushButton_cancel")
self.push_button_cancel.setText("Cancel")
self.push_button_cancel.clicked.connect(self.cancel_clicked)
hbox_top_left = QHBoxLayout()
hbox_top_left.addWidget(self.label_tag_list)
hbox_top_left.addWidget(self.search_bar)
vbox_top_left = QVBoxLayout()
vbox_top_left.addLayout(hbox_top_left)
vbox_top_left.addWidget(self.list_widget_tags)
hbox_buttons = QHBoxLayout()
hbox_buttons.addStretch(1)
hbox_buttons.addWidget(self.push_button_ok)
hbox_buttons.addWidget(self.push_button_cancel)
vbox_final = QVBoxLayout()
vbox_final.addLayout(vbox_top_left)
vbox_final.addLayout(hbox_buttons)
self.setLayout(vbox_final)
[docs]
def item_clicked(self, item):
"""Checks the checkbox of an item when the latter is clicked.
:param item: clicked item
"""
for idx in range(self.list_widget_tags.count()):
itm = self.list_widget_tags.item(idx)
if itm == item:
itm.setCheckState(QtCore.Qt.Checked)
else:
itm.setCheckState(QtCore.Qt.Unchecked)
[docs]
def ok_clicked(self):
"""Actions when the "OK" button is clicked."""
# Has to be override in the PopUpSelectTag* classes
pass
[docs]
def search_str(self, str_search):
"""Matches the searched pattern with the tags of the project.
:param str_search: string pattern to search
"""
return_list = []
if str_search != "":
for tag in self.project.session.get_fields_names(
COLLECTION_CURRENT
):
if tag != TAG_CHECKSUM and tag != TAG_HISTORY:
if str_search.upper() in tag.upper():
return_list.append(tag)
else:
for tag in self.project.session.get_fields_names(
COLLECTION_CURRENT
):
if tag != TAG_CHECKSUM and tag != TAG_HISTORY:
return_list.append(tag)
for idx in range(self.list_widget_tags.count()):
item = self.list_widget_tags.item(idx)
if item.text() in return_list:
item.setHidden(False)
else:
item.setHidden(True)
[docs]
class PopUpSelectTag(PopUpTagSelection):
"""Is called when the user wants to update the tag to display in the mini
viewer.
.. Methods:
- ok_clicked: saves the modifications and updates the mini viewer
"""
[docs]
def __init__(self, project):
"""Initialization.
:param project: current project in the software
"""
super(PopUpSelectTag, self).__init__(project)
self.project = project
self.config = Config()
# Filling the list and checking the thumbnail tag
for tag in self.project.session.get_fields_names(COLLECTION_CURRENT):
if tag != TAG_CHECKSUM and tag != TAG_HISTORY:
item = QtWidgets.QListWidgetItem()
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
if tag == self.config.getThumbnailTag():
item.setCheckState(QtCore.Qt.Checked)
else:
item.setCheckState(QtCore.Qt.Unchecked)
self.list_widget_tags.addItem(item)
item.setText(tag)
self.list_widget_tags.sortItems()
[docs]
def ok_clicked(self):
"""
Saves the modifications and updates the mini viewer
"""
for idx in range(self.list_widget_tags.count()):
item = self.list_widget_tags.item(idx)
if item.checkState() == QtCore.Qt.Checked:
self.config.setThumbnailTag(item.text())
break
self.accept()
self.close()
[docs]
class PopUpSelectTagCountTable(PopUpTagSelection):
"""Is called when the user wants to update a visualized tag of the count
table.
.. Methods:
- ok_clicked: updates the selected tag and closes the pop-up
"""
[docs]
def __init__(self, project, tags_to_display, tag_name_checked=None):
"""Initialization.
:param project: current project in the software
:param tags_to_display: the tags to display
:param tag_name_checked: the checked tags
"""
super(PopUpSelectTagCountTable, self).__init__(project)
self.selected_tag = None
for tag in tags_to_display:
if tag != TAG_CHECKSUM and tag != TAG_HISTORY:
item = QtWidgets.QListWidgetItem()
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
if tag == tag_name_checked:
item.setCheckState(QtCore.Qt.Checked)
else:
item.setCheckState(QtCore.Qt.Unchecked)
self.list_widget_tags.addItem(item)
item.setText(tag)
self.list_widget_tags.sortItems()
[docs]
def ok_clicked(self):
"""Updates the selected tag and closes the pop-up."""
for idx in range(self.list_widget_tags.count()):
item = self.list_widget_tags.item(idx)
if item.checkState() == QtCore.Qt.Checked:
self.selected_tag = item.text()
break
self.accept()
self.close()
[docs]
class PopUpShowHistory(QDialog):
"""Class to display the history of a document.
.. Methods:
- file_clicked: close the history window and select the file in the
data browser
- find_associated_bricks:
- find_process_from_plug:
- io_value_is_scan: checks if the I/O value is a scan
- node_selected: called when a pipeline node is clicked
- _updateio_table: fill in the input and output sections of the table
- update_table: update the brick row at the bottom
"""
[docs]
def __init__(self, project, brick_uuid, scan, databrowser, main_window):
"""Prepares the brick history popup.
:param project: current project in the software
:param scan: filename of the scan
:param databrowser: data browser instance of the software
:param main_window: main window of the software
"""
super().__init__()
# We do not want few parameters in the outputs parameters display
self.banished_param = ["notInDb", "dict4runtime"]
self.setModal(False)
self.setWindowFlags(
self.windowFlags() & QtCore.Qt.WindowStaysOnBottomHint
)
self.databrowser = databrowser
self.main_window = main_window
self.project = project
self.setWindowTitle("History of " + scan)
brick_row = project.session.get_document(COLLECTION_BRICK, brick_uuid)
full_brick_name = project.session.get_value(
COLLECTION_BRICK, brick_uuid, BRICK_NAME
).split(".")
layout = QVBoxLayout()
self.splitter = QSplitter(Qt.Qt.Vertical)
self.table = QTableWidget()
# self.table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.table.setVerticalScrollMode(
QtWidgets.QAbstractItemView.ScrollPerPixel
)
self.table.setHorizontalScrollMode(
QtWidgets.QAbstractItemView.ScrollPerPixel
)
history_uuid = self.project.session.get_value(
COLLECTION_CURRENT, scan, TAG_HISTORY
)
self.unitary_pipeline = False
self.uuid_idx = 0
if history_uuid is not None:
self.pipeline_xml = self.project.session.get_value(
COLLECTION_HISTORY, history_uuid, HISTORY_PIPELINE
)
if self.pipeline_xml is not None:
self.brick_list = self.project.session.get_value(
COLLECTION_HISTORY, history_uuid, HISTORY_BRICKS
)
engine = Config.get_capsul_engine()
try:
pipeline = engine.get_process_instance(self.pipeline_xml)
except Exception:
pipeline = None
if pipeline is not None:
# handle case of pipeline node alone --> exploded view
# (e.g. a pipeline alone and plug exported)
if len(pipeline.nodes) == 2:
for key in pipeline.nodes.keys():
if key != "":
if isinstance(
pipeline.nodes[key], PipelineNode
):
pipeline = pipeline.nodes[key].process
full_brick_name.pop(0)
self.unitary_pipeline = True
# handle cases of named pipeline/brick without being a
# single Pipeline node (e.g. a pipeline alone without
# exporting plugs)
if (
not self.unitary_pipeline
and pipeline.name != "CustomPipeline"
) or (
len(full_brick_name) == 2
# FIXME: We have "main" when ?
and full_brick_name[1] == "main"
):
full_brick_name.pop(0)
self.unitary_pipeline = True
self.pipeline_view = PipelineDeveloperView(
pipeline, allow_open_controller=False
)
self.pipeline_view.auto_dot_node_positions()
self.splitter.addWidget(self.pipeline_view)
self.pipeline_view.node_clicked.connect(self.node_selected)
(self.pipeline_view.process_clicked.connect)(
self.node_selected
)
bricks = self.find_associated_bricks(full_brick_name[0])
for bricks_uuids in bricks.values():
for i in range(0, len(bricks_uuids)):
if bricks_uuids[i] == brick_uuid:
self.uuid_idx = i
break
else:
continue
break
selected_name = full_brick_name[0]
try:
self.node_selected(
selected_name, pipeline.nodes[selected_name]
)
except Exception:
print(
"\nerror in naming association brick\\pipeline, "
"cannot select node ..."
)
pass
inputs = getattr(brick_row, BRICK_INPUTS)
outputs = getattr(brick_row, BRICK_OUTPUTS)
for k in self.banished_param:
outputs.pop(k, None)
inputs.pop(k, None)
brick_name = getattr(brick_row, BRICK_NAME)
init = getattr(brick_row, BRICK_INIT)
init_time = getattr(brick_row, BRICK_INIT_TIME)
exec = getattr(brick_row, BRICK_INIT)
exec_time = getattr(brick_row, BRICK_INIT_TIME)
self.update_table(
inputs, outputs, brick_name, init, init_time, exec, exec_time
)
self.splitter.addWidget(self.table)
layout.addWidget(self.splitter)
self.setLayout(layout)
screen_resolution = QApplication.instance().desktop().screenGeometry()
width, height = screen_resolution.width(), screen_resolution.height()
self.setGeometry(300, 200, round(0.6 * width), round(0.4 * height))
[docs]
def file_clicked(self):
"""
Close the history window and select the file in the data browser.
"""
file = self.sender().text()
self.databrowser.table_data.clearSelection()
row_to_select = self.databrowser.table_data.get_scan_row(file)
self.databrowser.table_data.selectRow(row_to_select)
item_to_scroll_to = self.databrowser.table_data.item(row_to_select, 0)
self.databrowser.table_data.scrollToItem(item_to_scroll_to)
self.close()
[docs]
def find_associated_bricks(self, node_name):
"""Blabla
:param node_name: blabla
:return: blabla
"""
bricks = {}
for uuid in self.brick_list:
full_brick_name = self.project.session.get_value(
COLLECTION_BRICK, uuid, BRICK_NAME
)
list_full_brick_name = full_brick_name.split(".")
if self.unitary_pipeline:
list_full_brick_name.pop(0)
if list_full_brick_name[0] == node_name:
if full_brick_name not in bricks:
bricks[full_brick_name] = [uuid]
else:
bricks[full_brick_name].append(uuid)
return bricks
[docs]
def find_process_from_plug(self, plug):
"""Blabla
:param plug: blabla
:return: blabla
"""
process_name = ""
plug_name = ""
if plug.output:
link_done = False
for link in plug.links_from:
if not link_done:
link_done = True
process_name += "." + link[2].name
plug_name = link[1]
if isinstance(link[2], PipelineNode):
(
sub_process_name,
plug_name,
) = self.find_process_from_plug(link[2].plugs[link[1]])
process_name += sub_process_name
else:
link_done = False
for link in plug.links_to:
if not link_done:
link_done = True
process_name += "." + link[2].name
plug_name = link[1]
if isinstance(link[2], PipelineNode):
(
sub_process_name,
plug_name,
) = self.find_process_from_plug(link[2].plugs[link[1]])
process_name += sub_process_name
return process_name, plug_name
[docs]
def io_value_is_scan(self, value):
"""Checks if the I/O value is a scan.
:param value: I/O value
:return: The scan corresponding to the value if it exists,
None otherwise
"""
value_scan = None
for scan in self.project.session.get_documents_names(
COLLECTION_CURRENT
):
if scan in str(value):
value_scan = scan
return value_scan
[docs]
def node_selected(self, node_name, process):
"""Emit a signal when a node is clicked.
:param node_name: node name
:param process: process of the corresponding node
"""
if hasattr(process, "pipeline_node"):
process = process.pipeline_node
bricks = self.find_associated_bricks(node_name)
if hasattr(process, "full_name") and node_name in process.full_name:
full_node_name = process.full_name
if bricks:
if len(bricks) == 1:
brick_row = self.project.session.get_document(
COLLECTION_BRICK,
next(iter(bricks.values()))[self.uuid_idx],
)
inputs = getattr(brick_row, BRICK_INPUTS)
outputs = getattr(brick_row, BRICK_OUTPUTS)
for k in self.banished_param:
outputs.pop(k, None)
inputs.pop(k, None)
brick_name = getattr(brick_row, BRICK_NAME)
init = getattr(brick_row, BRICK_INIT)
init_time = getattr(brick_row, BRICK_INIT_TIME)
exec = getattr(brick_row, BRICK_INIT)
exec_time = getattr(brick_row, BRICK_INIT_TIME)
self.update_table(
inputs,
outputs,
brick_name,
init,
init_time,
exec,
exec_time,
)
else:
# subpipeline case
inputs_dict = {}
outputs_dict = {}
if isinstance(process, PipelineNode):
for plug_name, plug in process.plugs.items():
if plug.activated:
(
process_name,
inner_plug_name,
) = self.find_process_from_plug(plug)
for uuid in bricks.values():
full_brick_name = (
self.project.session.get_value(
COLLECTION_BRICK, uuid[0], BRICK_NAME
)
)
if (
full_brick_name
== full_node_name + process_name
):
if plug.output:
plugs = self.project.session.get_value(
COLLECTION_BRICK,
uuid[self.uuid_idx],
BRICK_OUTPUTS,
)
outputs_dict[plug_name] = plugs[
inner_plug_name
]
else:
plugs = self.project.session.get_value(
COLLECTION_BRICK,
uuid[self.uuid_idx],
BRICK_INPUTS,
)
inputs_dict[plug_name] = plugs[
inner_plug_name
]
for k in self.banished_param:
outputs_dict.pop(k, None)
inputs_dict.pop(k, None)
self.update_table(inputs_dict, outputs_dict, full_node_name)
for name, gnode in self.pipeline_view.scene.gnodes.items():
if name == node_name:
gnode.fonced_viewer(False)
else:
gnode.fonced_viewer(True)
def _updateio_table(self, io_dict, item_idx):
"""Fill in the input and output sections of the table.
:param io_dict: inputs / outputs dictionary
:param item_idx: current column element index
:return: new current column element index
"""
for key, value in sorted(io_dict.items()):
item = QTableWidgetItem()
item.setText(key)
self.table.setHorizontalHeaderItem(item_idx, item)
if isinstance(value, list) and value:
widget = QWidget()
v_layout = QVBoxLayout()
v_layout.setAlignment(QtCore.Qt.AlignTop)
label = QLabel("[")
v_layout.addWidget(label)
for sub_value in value:
if isinstance(sub_value, list) and sub_value:
label = QLabel("[")
v_layout.addWidget(label)
for sub_sub_value in sub_value:
sub_sub_value = str(sub_sub_value)
value_scan = self.io_value_is_scan(sub_sub_value)
if value_scan is None:
del v_layout
del label
v_layout = QVBoxLayout()
# v_layout.setAlignment(QtCore.Qt.AlignTop)
v_layout.setAlignment(
QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop
)
label = QLabel(str(value))
v_layout.addWidget(label)
break
else:
h_layout = QHBoxLayout()
h_layout.setAlignment(QtCore.Qt.AlignLeft)
button = QPushButton(value_scan)
button.clicked.connect(self.file_clicked)
h_layout.addWidget(button)
label = QLabel(",")
h_layout.addWidget(label)
v_layout.addLayout(h_layout)
else:
label = QLabel("],")
v_layout.addWidget(label)
continue
break
else:
sub_value = str(sub_value)
value_scan = self.io_value_is_scan(sub_value)
if value_scan is None:
del v_layout
del label
v_layout = QVBoxLayout()
# v_layout.setAlignment(QtCore.Qt.AlignTop)
v_layout.setAlignment(
QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop
)
label = QLabel(str(value))
v_layout.addWidget(label)
break
else:
h_layout = QHBoxLayout()
button = QPushButton(value_scan)
button.clicked.connect(self.file_clicked)
h_layout.addWidget(button)
label = QLabel(",")
h_layout.addWidget(label)
v_layout.addLayout(h_layout)
else:
label = QLabel("]")
v_layout.addWidget(label)
widget.setLayout(v_layout)
self.table.setCellWidget(0, item_idx, widget)
else:
value_scan = self.io_value_is_scan(str(value))
if value_scan is not None:
widget = QWidget()
v_layout = QVBoxLayout()
button = QPushButton(value_scan)
button.clicked.connect(self.file_clicked)
v_layout.addWidget(button)
# v_layout.setAlignment(QtCore.Qt.AlignTop)
# v_layout.setAlignment(QtCore.Qt.AlignCenter)
v_layout.setAlignment(
QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop
)
widget.setLayout(v_layout)
self.table.setCellWidget(0, item_idx, widget)
else:
widget = QWidget()
v_layout = QVBoxLayout()
# v_layout.setAlignment(QtCore.Qt.AlignTop)
v_layout.setAlignment(
QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop
)
label = QLabel(str(value))
v_layout.addWidget(label)
widget.setLayout(v_layout)
self.table.setCellWidget(0, item_idx, widget)
item_idx += 1
return item_idx
[docs]
def update_table(
self,
inputs,
outputs,
brick_name,
init="",
init_time=None,
exec="",
exec_time=None,
):
"""Filling the table.
:param inputs: inputs dictionary
:param outputs: outputs dictionary
:param brick_name: name of the brick
:param init: initialisation status
:param init_time: init date / time
:param exec: execution status
:param exec_time: execution date / time
"""
self.table.removeRow(0)
self.table.setRowCount(1)
nbColumn = 1
if init != "":
nbColumn += 2
if exec != "":
nbColumn += 2
self.table.setColumnCount(nbColumn + len(inputs) + len(outputs))
# Brick name
item_idx = 0
item = QTableWidgetItem()
item.setText(BRICK_NAME)
self.table.setHorizontalHeaderItem(item_idx, item)
widget = QWidget()
v_layout = QVBoxLayout()
v_layout.setAlignment(QtCore.Qt.AlignTop)
label = QLabel(brick_name)
v_layout.addWidget(label)
widget.setLayout(v_layout)
self.table.setCellWidget(0, item_idx, widget)
item_idx += 1
# Brick init
if init != "":
item = QTableWidgetItem()
item.setText(BRICK_INIT)
self.table.setHorizontalHeaderItem(item_idx, item)
widget = QWidget()
v_layout = QVBoxLayout()
v_layout.setAlignment(QtCore.Qt.AlignTop)
label = QLabel(init)
v_layout.addWidget(label)
widget.setLayout(v_layout)
self.table.setCellWidget(0, item_idx, widget)
item_idx += 1
# Brick init time
item = QTableWidgetItem()
item.setText(BRICK_INIT_TIME)
self.table.setHorizontalHeaderItem(item_idx, item)
widget = QWidget()
v_layout = QVBoxLayout()
v_layout.setAlignment(QtCore.Qt.AlignTop)
if init_time is not None:
label = QLabel(str(init_time))
v_layout.addWidget(label)
widget.setLayout(v_layout)
self.table.setCellWidget(0, item_idx, widget)
item_idx += 1
# Brick execution
if exec != "":
item = QTableWidgetItem()
item.setText(BRICK_EXEC)
self.table.setHorizontalHeaderItem(item_idx, item)
widget = QWidget()
v_layout = QVBoxLayout()
v_layout.setAlignment(QtCore.Qt.AlignTop)
label = QLabel(exec)
v_layout.addWidget(label)
widget.setLayout(v_layout)
self.table.setCellWidget(0, item_idx, widget)
item_idx += 1
# Brick execution time
item = QTableWidgetItem()
item.setText(BRICK_EXEC_TIME)
self.table.setHorizontalHeaderItem(item_idx, item)
widget = QWidget()
v_layout = QVBoxLayout()
v_layout.setAlignment(QtCore.Qt.AlignTop)
if exec_time is not None:
label = QLabel(str(exec_time))
v_layout.addWidget(label)
widget.setLayout(v_layout)
self.table.setCellWidget(0, item_idx, widget)
item_idx += 1
item_idx = self._updateio_table(inputs, item_idx)
_ = self._updateio_table(outputs, item_idx)
self.table.verticalHeader().setMinimumSectionSize(30)
self.table.resizeColumnsToContents()
self.table.resizeRowsToContents()
[docs]
class PopUpVisualizedTags(QWidget):
"""
Is called when the user wants to update the tags that are visualized.
.. Methods:
- search_str: matches the searched pattern with the tags of the project
- click_select_tag: puts the selected tags in the "selected tag" table
- click_unselect_tag: removes the unselected tags from populse_mia
"selected tag" table
"""
# Signal that will be emitted at the end to tell that the preferences
# have been changed
signal_preferences_change = pyqtSignal()
[docs]
def __init__(self, project, visualized_tags):
"""Initialization.
:param project: current project in the software
:param visualized_tags: project's visualized tags before opening
this widget
"""
super().__init__()
self.project = project
self.visualized_tags = visualized_tags
_translate = QtCore.QCoreApplication.translate
# Two buttons to select or unselect tags
self.push_button_select_tag = QtWidgets.QPushButton(self)
self.push_button_select_tag.setObjectName("pushButton_select_tag")
self.push_button_select_tag.clicked.connect(self.click_select_tag)
self.push_button_unselect_tag = QtWidgets.QPushButton(self)
self.push_button_unselect_tag.setObjectName("pushButton_unselect_tag")
self.push_button_unselect_tag.clicked.connect(self.click_unselect_tag)
self.push_button_select_tag.setText(_translate("main_window", "-->"))
self.push_button_unselect_tag.setText(_translate("main_window", "<--"))
vbox_tag_buttons = QVBoxLayout()
vbox_tag_buttons.addWidget(self.push_button_select_tag)
vbox_tag_buttons.addWidget(self.push_button_unselect_tag)
# The "Tag list" label
self.label_tag_list = QtWidgets.QLabel(self)
self.label_tag_list.setTextFormat(QtCore.Qt.AutoText)
self.label_tag_list.setObjectName("label_tag_list")
self.label_tag_list.setText(
_translate("main_window", "Available tags:")
)
# The search bar to search in the list of tags
self.search_bar = QtWidgets.QLineEdit(self)
self.search_bar.setObjectName("lineEdit_search_bar")
self.search_bar.setPlaceholderText("Search")
self.search_bar.textChanged.connect(self.search_str)
# The list of tags
self.list_widget_tags = QtWidgets.QListWidget(self)
self.list_widget_tags.setObjectName("listWidget_tags")
(
self.list_widget_tags.setSelectionMode(
QtWidgets.QAbstractItemView.MultiSelection
)
)
hbox_top_left = QHBoxLayout()
hbox_top_left.addWidget(self.label_tag_list)
hbox_top_left.addWidget(self.search_bar)
vbox_top_left = QVBoxLayout()
vbox_top_left.addLayout(hbox_top_left)
vbox_top_left.addWidget(self.list_widget_tags)
# List of the tags selected by the user
self.label_visualized_tags = QtWidgets.QLabel(self)
self.label_visualized_tags.setTextFormat(QtCore.Qt.AutoText)
self.label_visualized_tags.setObjectName("label_visualized_tags")
self.label_visualized_tags.setText("Visualized tags:")
self.list_widget_selected_tags = QtWidgets.QListWidget(self)
(
self.list_widget_selected_tags.setObjectName(
"listWidget_visualized_tags"
)
)
(
self.list_widget_selected_tags.setSelectionMode(
QtWidgets.QAbstractItemView.MultiSelection
)
)
v_box_top_right = QVBoxLayout()
v_box_top_right.addWidget(self.label_visualized_tags)
v_box_top_right.addWidget(self.list_widget_selected_tags)
hbox_tags = QHBoxLayout()
hbox_tags.addLayout(vbox_top_left)
hbox_tags.addLayout(vbox_tag_buttons)
hbox_tags.addLayout(v_box_top_right)
self.setLayout(hbox_tags)
self.left_tags = [] # List that will keep track on
# the tags on the left (invisible tags)
for tag in project.session.get_fields_names(COLLECTION_CURRENT):
if (
tag != TAG_CHECKSUM
and tag != TAG_FILENAME
and tag != TAG_HISTORY
):
item = QtWidgets.QListWidgetItem()
if tag not in self.visualized_tags:
# Tag not visible: left side
self.list_widget_tags.addItem(item)
self.left_tags.append(tag)
else:
# Tag visible: right side
self.list_widget_selected_tags.addItem(item)
item.setText(tag)
self.list_widget_tags.sortItems()
[docs]
def search_str(self, str_search):
"""Matches the searched pattern with the tags of the project.
:param str_search: string pattern to search
"""
return_list = []
if str_search != "":
for tag in self.left_tags:
if str_search.upper() in tag.upper():
return_list.append(tag)
else:
for tag in self.left_tags:
return_list.append(tag)
# Selection updated
self.list_widget_tags.clear()
for tag_name in return_list:
item = QtWidgets.QListWidgetItem()
self.list_widget_tags.addItem(item)
item.setText(tag_name)
self.list_widget_tags.sortItems()
[docs]
def click_select_tag(self):
"""Puts the selected tags in the "selected tag" table."""
rows = sorted(
[index.row() for index in self.list_widget_tags.selectedIndexes()],
reverse=True,
)
for row in rows:
# assuming the other listWidget is called listWidget_2
self.left_tags.remove(self.list_widget_tags.item(row).text())
(
self.list_widget_selected_tags.addItem(
self.list_widget_tags.takeItem(row)
)
)
[docs]
def click_unselect_tag(self):
"""Removes the unselected tags from populse_mia table."""
rows = sorted(
[
index.row()
for index in self.list_widget_selected_tags.selectedIndexes()
],
reverse=True,
)
for row in rows:
(
self.left_tags.append(
self.list_widget_selected_tags.item(row).text()
)
)
(
self.list_widget_tags.addItem(
self.list_widget_selected_tags.takeItem(row)
)
)
self.list_widget_tags.sortItems()