import os 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 from utils.gui.pyqt.main_window.Workers.DiscreteDataWorker import DiscreteDataProcessWorker from utils.gui.pyqt.main_window.Workers.PreProcessWorker import PreProcessWorker from utils.gui.pyqt.main_window.Workers.RawDataWorker import RawDataProcessWorker from utils.gui.pyqt.error_popup.ErrorPopup import ErrorPopup class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): """ Main window of the application """ def __init__(self, parent=None): super(MainWindow, self).__init__(parent) # Retrieve the UI self.setupUi(self) # Setup buttons listeners self.start_analyse_button.clicked.connect(self.start_preprocess) 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.actionSauvegarder_le_model_redress.triggered.connect(self.save_model) self.actionPr_f_rennces.triggered.connect(self.show_settings) self.graphType = [ "Aucun", "Mesh3D", "Coupe XZ", "Coupe YZ", "Evolution du rayon moyen", ] self.obj = None self.raw_data= None self.discrete_data = None self.completed = 0 self.total = 2 self.comboBoxes = [ self.slot0ComboBox, self.slot1ComboBox, self.slot2ComboBox, self.slot3ComboBox, self.slot4ComboBox, self.slot5ComboBox, self.slot6ComboBox, self.slot7ComboBox, self.slot8ComboBox, self.slot9ComboBox, self.slot10ComboBox ] for cb in self.comboBoxes: cb.addItems(self.graphType) self.slots = [ [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 = [] ############################################################################### # # # # # Input/Setting Management # # # # # ############################################################################### def select_file(self): """ Open a file dialog to select the input file """ file = QFileDialog.getOpenFileName()[0] self.input_file_path.setPlainText(file) self.output_file_prefix.setText(os.path.splitext(os.path.basename(file))[0]) def select_folder(self): """ Open a file dialog to select the output folder """ self.output_folder_path.setPlainText(QFileDialog.getExistingDirectory()) def check_input_file(self): """ Check if the input file is valid """ if not os.path.isfile(self.input_file_path.toPlainText()): ErrorPopup("Fichier d'entrée invalide",button_label="Choisir un fichier d'entrée",button_callback=self.select_file).show_popup() return False return True def check_output_folder(self): """ Check if the output folder is valid """ if not os.path.isdir(self.output_folder_path.toPlainText()): ErrorPopup("Dossier de sortie invalide",button_label="Choisir un dossier de sortie",button_callback=self.select_folder).show_popup() return False return True ############################################################################### # # # # # Data Processing # # # # # ############################################################################### def start_preprocess(self): """ Start the analyse """ if not self.check_input_file(): return if not self.check_output_folder(): 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 # Create the thread to run the analyse self.preprocess_thread = QThread() self.preprocess_worker = PreProcessWorker("PreProcessWorker",self.input_file_path.toPlainText()) self.preprocess_worker.moveToThread(self.preprocess_thread) # Connect the signals # Start self.preprocess_thread.started.connect(self.preprocess_worker.run) # Progress self.preprocess_worker.status.connect(self.set_status) self.preprocess_worker.progress.connect(self.update_progress_bar) self.preprocess_worker.processed_obj.connect(self.set_obj) self.preprocess_worker.processed_obj.connect(self.process_raw_data) self.preprocess_worker.processed_obj.connect(self.process_discrete_data) # Finished self.preprocess_worker.finished.connect(self.preprocess_thread.quit) self.preprocess_worker.finished.connect(self.preprocess_worker.deleteLater) self.preprocess_thread.finished.connect(self.preprocess_thread.deleteLater) # Start the thread self.preprocess_thread.start() self.start_analyse_button.setEnabled(False) def process_raw_data(self, obj:ScannedObject): self.processrawdata_thread = QThread() self.processraw_worker = RawDataProcessWorker("RawDataProcessWorker", obj, self.output_folder_path.toPlainText(), self.output_file_prefix.text(), self.discretisation_value_selector.value()) self.processraw_worker.moveToThread(self.processrawdata_thread) # Connect the signals # Start self.processrawdata_thread.started.connect(self.processraw_worker.run) # Progress self.processraw_worker.status.connect(self.set_status) self.processraw_worker.progress.connect(self.update_progress_bar) self.processraw_worker.processedData.connect(self.set_raw_data) # Finished self.processraw_worker.finished.connect(self.finish_analyse) self.processraw_worker.finished.connect(self.processrawdata_thread.quit) self.processraw_worker.finished.connect(self.processraw_worker.deleteLater) self.processrawdata_thread.finished.connect(self.processrawdata_thread.deleteLater) # Start the thread self.processrawdata_thread.start() def process_discrete_data(self, obj:ScannedObject): self.processdiscrete_thread = QThread() self.processdiscrete_worker = DiscreteDataProcessWorker("DiscreteDataProcessWorker", obj, self.output_folder_path.toPlainText(), self.output_file_prefix.text(), self.discretisation_value_selector.value()) self.processdiscrete_worker.moveToThread(self.processdiscrete_thread) # Connect the signals # Start self.processdiscrete_thread.started.connect(self.processdiscrete_worker.run) # Progress self.processdiscrete_worker.status.connect(self.set_status) self.processdiscrete_worker.progress.connect(self.update_progress_bar) self.processdiscrete_worker.processedData.connect(self.set_discrete_data) # Finished self.processdiscrete_worker.finished.connect(self.finish_analyse) self.processdiscrete_worker.finished.connect(self.processdiscrete_thread.quit) self.processdiscrete_worker.finished.connect(self.processdiscrete_worker.deleteLater) self.processdiscrete_thread.finished.connect(self.processdiscrete_thread.deleteLater) # Start the thread self.processdiscrete_thread.start() def set_obj(self,obj:ScannedObject): self.obj = obj def set_discrete_data(self,discrete_data:dict): self.discrete_data = discrete_data def set_raw_data(self,raw_data:dict): self.raw_data = raw_data def save_model(self): if self.obj is None: ErrorPopup("Aucune analyse effectuée. Aucun modèle à sauvegarder").show_popup() return file_path = QFileDialog.getSaveFileName(self, "Sauvegarder le modèle", "./", "Fichier OBJ (*.obj)") self.obj.export_obj(file_path[0]) ############################################################################### # # # # # Graphs management # # # # # ############################################################################### def toggle_graphs(self): """ Show or hide the graphs """ if self.show_graph_checkbox.isChecked(): self.Graphs.show() else: self.Graphs.hide() def renderGraphs(self,obj:ScannedObject,raw_data:dict,discrete_data:dict): if not self.show_graph_checkbox.isChecked(): return for slot in self.slots: current_slot = slot[0] graph_type = slot[1] if graph_type == "Mesh3D": current_slot.addWidget(render3D(obj,False).native) if graph_type == "Coupe XZ": current_slot.addWidget(cross_section(obj.get_x(), obj.get_z(), "Coupe XZ", "X (en mm)", "Z (en mm)", False).native) if graph_type == "Coupe YZ": current_slot.addWidget(cross_section(obj.get_y(), obj.get_z(), "Coupe YZ", "Y (en mm)", "Z (en mm)", False).native) if graph_type == "Evolution du rayon moyen": current_slot.addWidget(render2D(list(zip(discrete_data['Z moy (en mm)'],discrete_data['Rayon moyen (en mm)'])), "Evolution du rayon moyen en fonction de Z", "Z (en mm)", "Rayon moyen (en mm)\n", False).native) def clear_graphs(self): """ Clear the graphs """ if not self.show_graph_checkbox.isChecked(): return for slot,_ in self.slots: for i in reversed(range(slot.count())): slot.itemAt(i).widget().setParent(None) ############################################################################### # # # # # User interface updates # # # # # ############################################################################### def finish_analyse(self): """ Finish the analyse """ self.completed += 1 if self.completed == self.total: self.status_text.setText("Done") self.analyse_progress_bar.setValue(100) self.renderGraphs(self.obj,self.raw_data,self.discrete_data) self.start_analyse_button.setEnabled(True) def update_progress_bar(self, value): """ Update the progress bar """ self.analyse_progress_bar.setValue(value) def set_status(self, status:str): """ Set the status of the analyse """ self.status_text.setText(status) def show_settings(self): """ Show the settings window """ self.settings_window.show()