diff --git a/.gitignore b/.gitignore index df6a042edc325672a96a87c19a3c7e81b638ea7d..9557404f665af8420c551a8a38c5c6491091a972 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ coverage.xml .hypothesis/ .pytest_cache/ cover/ +config.yml # Translations *.mo diff --git a/requirement.txt b/requirement.txt index 239f8828713c028c29fcf705bbfebfa77d49d119..ce6e2a33d96911eb8f9897ad814da96b9f259dda 100644 --- a/requirement.txt +++ b/requirement.txt @@ -1,3 +1,4 @@ vispy==0.12.2 PyQt5==5.15.9 -numpy==1.24.2 \ No newline at end of file +numpy==1.24.2 +PyYAML==6.0 \ No newline at end of file diff --git a/utils/data_processing/data_processing.py b/utils/data_processing/data_processing.py index 8f6c009dd5ebe76e3a59d72f8083d63f425d199f..56ce97889ade2325092e41dc2cb87e6e35d7a076 100644 --- a/utils/data_processing/data_processing.py +++ b/utils/data_processing/data_processing.py @@ -1,5 +1,6 @@ from utils.math import data_extraction from utils.files.input import ScannedObject +from utils.settings.SettingManager import SettingManager def progressbar_placeholder(percent:int): """ @@ -25,7 +26,13 @@ def get_raw_data(obj:ScannedObject, ndigits:int,delta_z:float=1,update_progress_ data = {} for colone in colones: data[colone] = [] - discrete_vertices = obj.get_discrete_vertices(delta_z) + + if SettingManager.get_instance().get_setting("discretisation_method") == "Z0-Zi < DeltaZ": + get_discrete_vertices = obj.get_discrete_vertices + else: + get_discrete_vertices = obj.get_discrete_vertices3 + + discrete_vertices = get_discrete_vertices(delta_z) progress = 0 for discrete_values in discrete_vertices: mean_x ,mean_y, mean_z = data_extraction.get_x_y_z_mean(discrete_values) @@ -58,7 +65,12 @@ def get_discrete_data(obj:ScannedObject, ndigits:int, delta_z:float=1, update_pr data = {} for colone in colones: data[colone] = [] - discrete_vertices = obj.get_discrete_vertices(delta_z) + if SettingManager.get_instance().get_setting("discretisation_method") == "Z0-Zi < DeltaZ": + get_discrete_vertices = obj.get_discrete_vertices + else: + get_discrete_vertices = obj.get_discrete_vertices3 + + discrete_vertices = get_discrete_vertices(delta_z) progress = 0 for discrete_values in discrete_vertices: x,y,z = data_extraction.get_x_y_z_mean(discrete_values) diff --git a/utils/files/input.py b/utils/files/input.py index b29c8770f87c640543f4b0666079c8174ba8ffc0..46a340e9e0f895f4f41fadf1c97a9f0b8d41fb75 100644 --- a/utils/files/input.py +++ b/utils/files/input.py @@ -14,7 +14,7 @@ class ResultFileNotGiven(Exception): """ Exception raised when no faces was given. """ - + class ScannedObject: """ This class is used to manage the data of the 3D object. @@ -230,7 +230,7 @@ class ScannedObject: for index in range(len(sorted)): L[-1].append(sorted[index]) if sorted[index][2] - z > step: - z = sorted[index][2] + z = sorted[index+1][2] L.append([]) return L diff --git a/utils/files/output.py b/utils/files/output.py index 9f0ca90f81671b41210ec6323561938085fc5535..b228d1cf41c90b87c4153862685146ff86a6deb5 100644 --- a/utils/files/output.py +++ b/utils/files/output.py @@ -2,6 +2,9 @@ This module is used to manage the output files of the program. """ +from utils.settings.SettingManager import SettingManager + + def format_data(data:dict, separator:str, selected_columns:list = None) -> str: """ Format the data to be saved in the output file. @@ -24,15 +27,22 @@ def format_data(data:dict, separator:str, selected_columns:list = None) -> str: 3 ;6 ;9 ' """ + isPrettier = SettingManager.get_instance().get_setting('pretiffy_output_file') output = '' if selected_columns is None: selected_columns = list(data.keys()) for column_name in selected_columns: - output += column_name.ljust(len(column_name) if len(column_name) > 8 else 9 ) + separator + if isPrettier: + output += column_name.ljust(len(column_name) if len(column_name) > 8 else 9 ) + separator + else: + output += column_name + separator output += '\n' for i in range(len(data[selected_columns[0]])): for column in selected_columns: - output += str(data[column][i]).ljust(len(column) if len(column) > 8 else 9 ) + separator + if isPrettier: + output += str(data[column][i]).ljust(len(column) if len(column) > 8 else 9 ) + separator + else: + output += str(data[column][i]) + separator output += '\n' return output diff --git a/utils/gui/pyqt/main_window/MainWindow.py b/utils/gui/pyqt/main_window/MainWindow.py index c987da5246f5d381336abe663ef6d9384aa2f2cb..299e946979aed84d75ea0dc816012377750bb68e 100644 --- a/utils/gui/pyqt/main_window/MainWindow.py +++ b/utils/gui/pyqt/main_window/MainWindow.py @@ -3,6 +3,8 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QThread from PyQt5.QtWidgets import QFileDialog, QWidget from utils.files.input import ScannedObject +from utils.gui.pyqt.settings.Settings import Settings +from utils.settings.SettingManager import SettingManager from utils.graph2D.visplot_render import cross_section, render2D from utils.graph3D.visplot_render import render3D from utils.gui.pyqt.main_window.UI_MainWindow import Ui_MainWindow @@ -23,6 +25,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): self.input_file_choose_btn.clicked.connect(self.select_file) self.output_folder_choose_btn.clicked.connect(self.select_folder) self.show_graph_checkbox.stateChanged.connect(self.toggle_graphs) + self.actionPr_f_rennces.triggered.connect(self.show_settings) self.graphType = [ @@ -58,19 +61,25 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): cb.addItems(self.graphType) self.slots = [ - [self.slot0,None], - [self.slot1,None], - [self.slot2,None], - [self.slot3,None], - [self.slot4,None], - [self.slot5,None], - [self.slot6,None], - [self.slot7,None], - [self.slot8,None], - [self.slot9,None], - [self.slot10,None] + [self.slot0,"Aucun"], + [self.slot1,"Aucun"], + [self.slot2,"Aucun"], + [self.slot3,"Aucun"], + [self.slot4,"Aucun"], + [self.slot5,"Aucun"], + [self.slot6,"Aucun"], + [self.slot7,"Aucun"], + [self.slot8,"Aucun"], + [self.slot9,"Aucun"], + [self.slot10,"Aucun"] ] - + + for slot_nb,slot in enumerate(self.slots): + slot[1] = SettingManager.get_instance().get_last_graph(slot_nb) + self.comboBoxes[slot_nb].setCurrentText(slot[1]) + + self.settings_window = Settings() + self.threads = [] ############################################################################### @@ -127,8 +136,10 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): self.output_folder_path.setPlainText("Invalid folder path") return + settings = SettingManager.get_instance() for count,_ in enumerate(self.slots): self.slots[count][1] = self.comboBoxes[count].currentText() + settings.set_last_graph(count,self.slots[count][1]) self.clear_graphs() self.completed = 0 @@ -285,3 +296,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): Set the status of the analyse """ self.status_text.setText(status) + + def show_settings(self): + """ + Show the settings window + """ + self.settings_window.show() \ No newline at end of file diff --git a/utils/gui/pyqt/main_window/Workers/DiscreteDataWorker.py b/utils/gui/pyqt/main_window/Workers/DiscreteDataWorker.py index 39ef1ae2dc6bd2cb19f4a92902ddadc9390290a9..eed5594ebb9c8cf3ed68b2b70a1e09d38f884001 100644 --- a/utils/gui/pyqt/main_window/Workers/DiscreteDataWorker.py +++ b/utils/gui/pyqt/main_window/Workers/DiscreteDataWorker.py @@ -3,6 +3,7 @@ from utils.files.input import ScannedObject from utils.files.output import save_output_file, format_data from utils.gui.pyqt.main_window.Workers.Worker import Worker from utils.data_processing.data_processing import get_discrete_data +from utils.settings.SettingManager import SettingManager class DiscreteDataProcessWorker(Worker): """ @@ -28,14 +29,17 @@ class DiscreteDataProcessWorker(Worker): self.set_weight(100) self.set_status("Saving data...") self.update_progress(10) - save_output_file(f'{self.output_path}/{self.output_file_prefix}_delta_{self.delta_z}_analyse_rayon.txt', + suffix = SettingManager.get_instance().get_setting('discrete_data_suffix').replace('{delta_z}',str(self.delta_z)) + extension = SettingManager.get_instance().get_setting('output_file_extension') + separator = SettingManager.get_instance().get_setting('output_file_separator') + save_output_file(f'{self.output_path}/{self.output_file_prefix}{suffix}{extension}', format_data(discrete_data, - '\t', + separator, ["X moy (en mm)", "Y moy (en mm)", "Z moy (en mm)", "Discretisation(en mm)", "Rayon moyen (en mm)", - "Rayon ecart type (en mm)"] )) + "Rayon ecart type (en mm)"])) self.set_status("Done !") self.finished.emit() \ No newline at end of file diff --git a/utils/gui/pyqt/main_window/Workers/RawDataWorker.py b/utils/gui/pyqt/main_window/Workers/RawDataWorker.py index ecbb40ac3558090e6d9b58758722825c9af09e5a..76efa5a486682beeaf0e5c2f7923cc1bbc2accce 100644 --- a/utils/gui/pyqt/main_window/Workers/RawDataWorker.py +++ b/utils/gui/pyqt/main_window/Workers/RawDataWorker.py @@ -3,6 +3,7 @@ from utils.files.input import ScannedObject from utils.files.output import save_output_file, format_data from utils.gui.pyqt.main_window.Workers.Worker import Worker from utils.data_processing.data_processing import get_raw_data +from utils.settings.SettingManager import SettingManager class RawDataProcessWorker(Worker): """ @@ -26,9 +27,12 @@ class RawDataProcessWorker(Worker): self.processedData.emit(raw_data) self.set_weight(100) self.set_status("Saving data...") - save_output_file(f'{self.output_path}/{self.output_file_prefix}_delta_{self.delta_z}_analyse_brute.txt', + suffix = SettingManager.get_instance().get_setting('raw_data_suffix').replace('{delta_z}',str(self.delta_z)) + extension = SettingManager.get_instance().get_setting('output_file_extension') + separator = SettingManager.get_instance().get_setting('output_file_separator') + save_output_file(f'{self.output_path}/{self.output_file_prefix}{suffix}{extension}', format_data(raw_data, - '\t', + separator, ["X (en mm)", "Y (en mm)", "Z (en mm)", diff --git a/utils/gui/pyqt/settings/Settings.py b/utils/gui/pyqt/settings/Settings.py new file mode 100644 index 0000000000000000000000000000000000000000..0501f698d4c3b32c408cc66a01b00cfa82d072d0 --- /dev/null +++ b/utils/gui/pyqt/settings/Settings.py @@ -0,0 +1,40 @@ +import os +from PyQt5 import QtWidgets +from PyQt5.QtWidgets import QWidget +from utils.files.input import ScannedObject +from utils.gui.pyqt.settings.UI_Settings import Ui_Settings +from utils.settings.SettingManager import SettingManager + +class Settings(QtWidgets.QMainWindow,Ui_Settings): + + def __init__(self, parent=None): + super().__init__(parent) + # Retrieve the UI + self.setupUi(self) + # Setup buttons listeners + settings = SettingManager.get_instance() + self.discretisation_method.addItems(settings.get_setting('discretisation_methods')) + self.discretisation_method.setCurrentText(settings.get_setting('discretisation_method')) + self.raw_data_suffix.setText(settings.get_setting('raw_data_suffix')) + self.discrete_data_suffix.setText(settings.get_setting('discrete_data_suffix')) + self.extention.setText(settings.get_setting('output_file_extension')) + self.separator.setText(settings.get_setting('output_file_separator')) + self.prettify.setChecked(settings.get_setting('pretiffy_output_file')) + + #Connect to onchanged actions + self.discretisation_method.currentTextChanged.connect(self.accept) + self.raw_data_suffix.textChanged.connect(self.accept) + self.discrete_data_suffix.textChanged.connect(self.accept) + self.extention.textChanged.connect(self.accept) + self.separator.textChanged.connect(self.accept) + self.prettify.stateChanged.connect(self.accept) + + + def accept(self): + settings = SettingManager.get_instance() + settings.set_setting('discretisation_method', self.discretisation_method.currentText()) + settings.set_setting('raw_data_suffix', self.raw_data_suffix.text()) + settings.set_setting('discrete_data_suffix', self.discrete_data_suffix.text()) + settings.set_setting('output_file_extension', self.extention.text()) + settings.set_setting('output_file_separator', self.separator.text()) + settings.set_setting('pretiffy_output_file', self.prettify.isChecked()) \ No newline at end of file diff --git a/utils/gui/pyqt/settings/UI_Settings.py b/utils/gui/pyqt/settings/UI_Settings.py new file mode 100644 index 0000000000000000000000000000000000000000..4fb3a552bd3a528e3befaab84fbac9db85a1458f --- /dev/null +++ b/utils/gui/pyqt/settings/UI_Settings.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'settings.ui' +# +# Created by: PyQt5 UI code generator 5.15.9 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_Settings(object): + def setupUi(self, Settings): + Settings.setObjectName("Settings") + Settings.resize(712, 213) + self.centralwidget = QtWidgets.QWidget(Settings) + self.centralwidget.setObjectName("centralwidget") + self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) + self.gridLayout.setObjectName("gridLayout") + self.gridLayout_2 = QtWidgets.QGridLayout() + self.gridLayout_2.setObjectName("gridLayout_2") + self.discretisation_method_label = QtWidgets.QLabel(self.centralwidget) + self.discretisation_method_label.setObjectName("discretisation_method_label") + self.gridLayout_2.addWidget(self.discretisation_method_label, 0, 0, 1, 1) + self.discretisation_method = QtWidgets.QComboBox(self.centralwidget) + self.discretisation_method.setObjectName("discretisation_method") + self.gridLayout_2.addWidget(self.discretisation_method, 0, 1, 1, 1) + self.raw_data_suffix_label = QtWidgets.QLabel(self.centralwidget) + self.raw_data_suffix_label.setObjectName("raw_data_suffix_label") + self.gridLayout_2.addWidget(self.raw_data_suffix_label, 1, 0, 1, 1) + self.raw_data_suffix = QtWidgets.QLineEdit(self.centralwidget) + self.raw_data_suffix.setObjectName("raw_data_suffix") + self.gridLayout_2.addWidget(self.raw_data_suffix, 1, 1, 1, 1) + self.discrete_data_suffix_label = QtWidgets.QLabel(self.centralwidget) + self.discrete_data_suffix_label.setObjectName("discrete_data_suffix_label") + self.gridLayout_2.addWidget(self.discrete_data_suffix_label, 2, 0, 1, 1) + self.discrete_data_suffix = QtWidgets.QLineEdit(self.centralwidget) + self.discrete_data_suffix.setObjectName("discrete_data_suffix") + self.gridLayout_2.addWidget(self.discrete_data_suffix, 2, 1, 1, 1) + self.extention_label = QtWidgets.QLabel(self.centralwidget) + self.extention_label.setObjectName("extention_label") + self.gridLayout_2.addWidget(self.extention_label, 3, 0, 1, 1) + self.extention = QtWidgets.QLineEdit(self.centralwidget) + self.extention.setObjectName("extention") + self.gridLayout_2.addWidget(self.extention, 3, 1, 1, 1) + self.separator_label = QtWidgets.QLabel(self.centralwidget) + self.separator_label.setObjectName("separator_label") + self.gridLayout_2.addWidget(self.separator_label, 4, 0, 1, 1) + self.separator = QtWidgets.QLineEdit(self.centralwidget) + self.separator.setObjectName("separator") + self.gridLayout_2.addWidget(self.separator, 4, 1, 1, 1) + self.prettify_label = QtWidgets.QLabel(self.centralwidget) + self.prettify_label.setObjectName("prettify_label") + self.gridLayout_2.addWidget(self.prettify_label, 5, 0, 1, 1) + self.prettify = QtWidgets.QCheckBox(self.centralwidget) + self.prettify.setText("") + self.prettify.setObjectName("prettify") + self.gridLayout_2.addWidget(self.prettify, 5, 1, 1, 1) + self.gridLayout.addLayout(self.gridLayout_2, 0, 0, 1, 1) + Settings.setCentralWidget(self.centralwidget) + + self.retranslateUi(Settings) + QtCore.QMetaObject.connectSlotsByName(Settings) + + def retranslateUi(self, Settings): + _translate = QtCore.QCoreApplication.translate + Settings.setWindowTitle(_translate("Settings", "Préférences")) + self.discretisation_method_label.setText(_translate("Settings", "Methode de discretisation")) + self.raw_data_suffix_label.setText(_translate("Settings", "Suffixe des fichiers de données brutes")) + self.discrete_data_suffix_label.setText(_translate("Settings", "Suffixe des fichiers de données discretisées")) + self.extention_label.setText(_translate("Settings", "Extensions des fichiers de sorties")) + self.separator_label.setText(_translate("Settings", "Separateur de colone")) + self.prettify_label.setText(_translate("Settings", "Aligner les colones")) diff --git a/utils/gui/pyqt/settings/settings.ui b/utils/gui/pyqt/settings/settings.ui new file mode 100644 index 0000000000000000000000000000000000000000..488368d1a5857f23ff8070c82e4a213dc02b548a --- /dev/null +++ b/utils/gui/pyqt/settings/settings.ui @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Settings</class> + <widget class="QMainWindow" name="Settings"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>712</width> + <height>213</height> + </rect> + </property> + <property name="windowTitle"> + <string>Préférences</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <widget class="QLabel" name="discretisation_method_label"> + <property name="text"> + <string>Methode de discretisation</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="discretisation_method"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="raw_data_suffix_label"> + <property name="text"> + <string>Suffixe des fichiers de données brutes</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="raw_data_suffix"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="discrete_data_suffix_label"> + <property name="text"> + <string>Suffixe des fichiers de données discretisées</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="discrete_data_suffix"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="extention_label"> + <property name="text"> + <string>Extensions des fichiers de sorties</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="extention"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="separator_label"> + <property name="text"> + <string>Separateur de colone</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="separator"/> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="prettify_label"> + <property name="text"> + <string>Aligner les colones</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QCheckBox" name="prettify"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + <resources/> + <connections/> +</ui> diff --git a/utils/settings/SettingManager.py b/utils/settings/SettingManager.py new file mode 100644 index 0000000000000000000000000000000000000000..ac27298741dbde42e711e42f32a3b17373362aa9 --- /dev/null +++ b/utils/settings/SettingManager.py @@ -0,0 +1,60 @@ +from yaml import load, dump +try: + from yaml import CLoader as Loader, CDumper as Dumper +except ImportError: + from yaml import Loader, Dumper + +class SettingManager: + instance = None + + def __init__(self): + try: + with open('config.yml', 'r') as f: + self.settings = load(f.read(), Loader=Loader) + except FileNotFoundError: + self.settings = {} + self.createInitialSettings() + + @staticmethod + def get_instance(): + if SettingManager.instance is None: + SettingManager.instance = SettingManager() + return SettingManager.instance + + + def save(self): + with open('config.yml', 'w') as f: + f.write(dump(self.settings, Dumper=Dumper)) + + def createInitialSettings(self): + self.settings['version'] = "1.0.0" + self.settings['name'] = 'Analyse Morphologique' + self.settings['authors'] = 'Alexis Doghmane <alexis@doghmane.fr>, Djalim Simaila <djalim.simaila@inrae.fr>' + self.settings['description'] = 'Analyse Morphologique' + self.settings['repo'] = 'https://forgemia.inra.fr/scanner3d/analysemorphologique' + self.settings['lastGraph'] = ["Aucun" for i in range(11)] + self.settings['discretisation_method'] = 'Z0-Zi >= DeltaZ' # 'Z0-Zi < DeltaZ' + self.settings['discretisation_methods'] = ['Z0-Zi >= DeltaZ', 'Z0-Zi < DeltaZ'] + self.settings['raw_data_suffix'] = '_delta_{delta_z}_analyse_brute' + self.settings['discrete_data_suffix'] = '_delta_{delta_z}_analyse_rayon' + self.settings['output_file_extension'] = '.txt' + self.settings['output_file_separator'] = '\t' + self.settings['pretiffy_output_file'] = True + self.save() + + def get_last_graph(self, graph_number): + return self.settings['lastGraph'][graph_number] + + def set_last_graph(self, graph_number, graph_name): + self.settings['lastGraph'][graph_number] = graph_name + self.save() + + def get_setting(self, setting_name): + return self.settings[setting_name] + + def get_settings(self): + return self.settings + + def set_setting(self, setting_name, setting_value): + self.settings[setting_name] = setting_value + self.save() \ No newline at end of file