From ac4f4410a243b5b650d17810c1f36db3a2b55401 Mon Sep 17 00:00:00 2001
From: Djalim Simaila <DjalimS.pro@outlook.fr>
Date: Mon, 24 Apr 2023 15:55:40 +0200
Subject: [PATCH] first functionnal gui

---
 app.py                                      |  10 +
 main.py                                     |  63 +-----
 utils/create_xyz.py                         |  41 ++++
 utils/data_processing/data_processing.py    |  76 +++++++
 utils/gui/pyqt/main_window/MainWindow.py    | 210 ++++++++++++++++++++
 utils/gui/pyqt/main_window/MainWindow.ui    | 129 ++++++++++++
 utils/gui/pyqt/main_window/UI_MainWindow.py |  98 +++++++++
 7 files changed, 565 insertions(+), 62 deletions(-)
 create mode 100644 app.py
 create mode 100644 utils/create_xyz.py
 create mode 100644 utils/data_processing/data_processing.py
 create mode 100644 utils/gui/pyqt/main_window/MainWindow.py
 create mode 100644 utils/gui/pyqt/main_window/MainWindow.ui
 create mode 100644 utils/gui/pyqt/main_window/UI_MainWindow.py

diff --git a/app.py b/app.py
new file mode 100644
index 0000000..507135b
--- /dev/null
+++ b/app.py
@@ -0,0 +1,10 @@
+import sys
+from PyQt5 import uic
+from PyQt5.QtWidgets import QApplication
+from utils.gui.pyqt.main_window.MainWindow import MainWindow
+
+if __name__ == "__main__":
+    app = QApplication(sys.argv)
+    window = MainWindow()
+    window.show()
+    app.exec()
\ No newline at end of file
diff --git a/main.py b/main.py
index 68097d3..0d7a8dd 100644
--- a/main.py
+++ b/main.py
@@ -1,70 +1,9 @@
-from utils.math import data_extraction
+from utils.data_processing.data_processing import get_discrete_data, get_raw_data
 from utils.files import output
 from utils.files.input import ScannedObject
 from utils.math.position_manipulation import verticalise
-from utils.graph3D.visplot_render import render3D
 import time
 
-def get_raw_data(obj:ScannedObject, ndigits:int, delta_z:int = 1)->dict:
-    """
-    Calculates data from the given object
-
-    :param obj: Object to analyse
-    :param ndigits: Number of digits to keep after the comma
-    :return: dict(str:list) with the following keys:
-        - X (en mm)     : list of x values
-        - Y (en mm)     : list of y values
-        - Z (en mm)     : list of z values
-        - teta (en rad) : list of teta values
-        - rayon (en mm) : list of radius values
-        - Xi-Xmoy       : list of Xi-Xmoy values
-        - Yi-Ymoy       : list of Yi-Ymoy values
-    """
-    columns = ["X (en mm)", "Y (en mm)", "Z (en mm)", "teta (en rad)", "rayon (en mm)","Xi-Xmoy","Yi-Ymoy"]
-    data = {}
-    for column in columns:
-        data[column] = []
-    for discrete_values in obj.get_discrete_vertices(delta_z):
-        mean_x ,mean_y, mean_z = data_extraction.get_x_y_z_mean(discrete_values)
-        for x,y,z in discrete_values:
-            data["X (en mm)"].append(round(x, ndigits))
-            data["Y (en mm)"].append(round(y, ndigits))
-            data["Z (en mm)"].append(round(z, ndigits))
-            data["teta (en rad)"].append(round(data_extraction.get_teta_from_x_y(x,y,mean_x,mean_y), ndigits))
-            data["rayon (en mm)"].append(round(data_extraction.get_radius_from_x_y(x,y,mean_x,mean_y), ndigits))
-            data["Xi-Xmoy"].append(round(x-mean_x, ndigits))
-            data["Yi-Ymoy"].append(round(y-mean_y, ndigits))
-    return data
-
-def get_discrete_data(obj:ScannedObject, ndigits:int,delta_z:int= 1)->dict:
-    """
-    Calculates data from the given object
-
-    :param obj: Object to analyse
-    :param ndigits: Number of digits to keep after the comma
-    :return: dict(str:list) with the following keys:
-        - X moy (en mm)             : list of x mean values
-        - Y moy (en mm)             : list of y mean values
-        - Z moy (en mm)             : list of z mean values
-        - Rayon moyen (en mm)       : list of mean radius values
-        - Rayon ecart type (en mm)  : list of radius standard deviation values
-    """
-    columns = ["X moy (en mm)", "Y moy (en mm)", "Z moy (en mm)","Delta z(en mm)","Rayon moyen (en mm)","Rayon ecart type (en mm)"]
-    data = {}
-    for column in columns:
-        data[column] = []
-    for discrete_values in obj.get_discrete_vertices(delta_z):
-        x,y,z = data_extraction.get_x_y_z_mean(discrete_values)
-        data["X moy (en mm)"].append(round(x, ndigits))
-        data["Y moy (en mm)"].append(round(y, ndigits))
-        data["Z moy (en mm)"].append(round(z, ndigits))
-        first = discrete_values[0]
-        last = discrete_values[-1]
-        data["Delta z(en mm)"].append(round(last[2]-first[2],ndigits))
-        data["Rayon moyen (en mm)"].append(round(data_extraction.get_mean_radius(discrete_values), ndigits))
-        data["Rayon ecart type (en mm)"].append(round(data_extraction.get_radius_std(discrete_values), ndigits))
-    return data
-
 def main():
     # Create an object from the given file
     total_time = time.time()
diff --git a/utils/create_xyz.py b/utils/create_xyz.py
new file mode 100644
index 0000000..c149271
--- /dev/null
+++ b/utils/create_xyz.py
@@ -0,0 +1,41 @@
+import random
+import math
+
+def create_circle(radius:float, center_x:float, center_y:float,points_number:int=100):
+    """
+    Create a circle with the given radius and center
+    :param radius: Radius of the circle
+    :param center_x: X coordinate of the center
+    :param center_y: Y coordinate of the center
+    :param points_number: Number of points to create the circle
+    :return: list of points
+    """
+    cpt = 0
+    angles = []
+    while cpt < 2*math.pi:
+        angles.append(cpt)
+        cpt += 2*math.pi/points_number
+    return [[round(radius * math.cos(angle) + center_x,6), round(radius * math.sin(angle) + center_y,6)] for angle in angles]
+
+def create_cylinder(radius:float, center_x:float, center_y:float, height:float, points_number:int=100):
+    """
+    Create a cylinder with the given radius, center and height
+    :param radius: Radius of the cylinder
+    :param center_x: X coordinate of the center
+    :param center_y: Y coordinate of the center
+    :param height: Height of the cylinder
+    :param points_number: Number of points to create the circle
+    :return: list of points
+    """
+    circle = create_circle(radius,center_x,center_y,points_number)
+    cylinder = []
+    for z in range(int(height)):
+        cylinder.append([[x,y,z] for x,y in circle])
+    return cylinder
+
+if __name__ == "__main__":
+    with open("test_cylinder.xyz",'w') as f:
+        for couches in create_cylinder(10,0,0,10):
+            for points in couches:
+                x,y,z = points[0],points[1],points[2]
+                f.write(f"{x} {y} {z}\n")
diff --git a/utils/data_processing/data_processing.py b/utils/data_processing/data_processing.py
new file mode 100644
index 0000000..4502f80
--- /dev/null
+++ b/utils/data_processing/data_processing.py
@@ -0,0 +1,76 @@
+from utils.math import data_extraction
+from utils.files.input import ScannedObject
+
+def progressbar_placeholder(percent:int):
+    """
+    This function is a placeholder for a progressbar function
+    """
+
+def get_raw_data(obj:ScannedObject, ndigits:int,delta_z:float=1,update_progress_bar = progressbar_placeholder)->dict:
+    """
+    Calculates data from the given object
+
+    :param obj: Object to analyse
+    :param ndigits: Number of digits to keep after the comma
+    :return: dict(str:list) with the following keys:
+        - X (en mm)     : list of x values
+        - Y (en mm)     : list of y values
+        - Z (en mm)     : list of z values
+        - teta (en rad) : list of teta values
+        - rayon (en mm) : list of radius values
+        - Xi-Xmoy       : list of Xi-Xmoy values
+        - Yi-Ymoy       : list of Yi-Ymoy values
+    """
+    colones = ["X (en mm)", "Y (en mm)", "Z (en mm)", "teta (en rad)", "rayon (en mm)","Xi-Xmoy","Yi-Ymoy"]
+    data = {}
+    for colone in colones:
+        data[colone] = []
+    discrete_vertices = obj.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)
+        for x,y,z in discrete_values:
+            data["X (en mm)"].append(round(x, ndigits))
+            data["Y (en mm)"].append(round(y, ndigits))
+            data["Z (en mm)"].append(round(z, ndigits))
+            data["teta (en rad)"].append(round(data_extraction.get_teta_from_x_y(x,y,mean_x,mean_y), ndigits))
+            data["rayon (en mm)"].append(round(data_extraction.get_radius_from_x_y(x,y,mean_x,mean_y), ndigits))
+            data["Xi-Xmoy"].append(round(x-mean_x, ndigits))
+            data["Yi-Ymoy"].append(round(y-mean_y, ndigits))
+        update_progress_bar(int(progress/len(discrete_vertices)*100))
+        progress += 1
+    return data
+
+def get_discrete_data(obj:ScannedObject, ndigits:int, delta_z:float=1, update_progress_bar= progressbar_placeholder)->dict:
+    """
+    Calculates data from the given object
+
+    :param obj: Object to analyse
+    :param ndigits: Number of digits to keep after the comma
+    :return: dict(str:list) with the following keys:
+        - X moy (en mm)             : list of x mean values
+        - Y moy (en mm)             : list of y mean values
+        - Z moy (en mm)             : list of z mean values
+        - Rayon moyen (en mm)       : list of mean radius values
+        - Rayon ecart type (en mm)  : list of radius standard deviation values
+    """
+    colones = ["X moy (en mm)", "Y moy (en mm)", "Z moy (en mm)","Delta z(en mm)","Rayon moyen (en mm)","Rayon ecart type (en mm)"]
+    data = {}
+    for colone in colones:
+        data[colone] = []
+    discrete_vertices = obj.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)
+        data["X moy (en mm)"].append(round(x, ndigits))
+        data["Y moy (en mm)"].append(round(y, ndigits))
+        data["Z moy (en mm)"].append(round(z, ndigits))
+        first = discrete_values[0]
+        last = discrete_values[-1]
+        data["Delta z(en mm)"].append(round(last[2]-first[2],ndigits))
+        data["Rayon moyen (en mm)"].append(round(data_extraction.get_mean_radius(discrete_values), ndigits))
+        data["Rayon ecart type (en mm)"].append(round(data_extraction.get_radius_std(discrete_values), ndigits))
+        update_progress_bar(int(progress/len(discrete_vertices)*100))
+        progress += 1
+    return data
+
diff --git a/utils/gui/pyqt/main_window/MainWindow.py b/utils/gui/pyqt/main_window/MainWindow.py
new file mode 100644
index 0000000..96a1c6b
--- /dev/null
+++ b/utils/gui/pyqt/main_window/MainWindow.py
@@ -0,0 +1,210 @@
+import time
+import os
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QThread, pyqtSignal, QObject
+from PyQt5.QtWidgets import QFileDialog
+from main import get_discrete_data, get_raw_data
+from utils.files.output import save_output_file, format_data
+from utils.files.input import ScannedObject
+from utils.math.position_manipulation import verticalise
+from utils.gui.pyqt.main_window.UI_MainWindow import Ui_MainWindow
+
+def analyse(file_path:str,output_path:str, output_file_prefix:str, delta_z:float,set_status,update_ui,set_weight):
+    """
+    Run the analyse
+
+    Args:
+        file_path (str): Path to the file to analyse
+        output_path (str): Path to the output folder
+        delta_z (float): Delta z to use
+        set_status (function): Function to set the status
+        update_ui (function): Function to update the ui
+        set_weight (function): Function to set the weight   
+
+    """
+    set_status("Loading file...")
+    obj = ScannedObject.from_obj_file(file_path)
+    update_ui(5)
+    set_status("Verticalising object...")
+    verticalise(obj)
+    update_ui(5)
+    set_status("Normalising object...")
+    obj.normalise()
+    update_ui(5)
+    set_weight(70)
+    set_status("Calculating raw data...")
+    raw_data = get_raw_data(obj, 6,delta_z,update_ui)
+    set_status("Calculating discrete data...")
+    discrete_data = get_discrete_data(obj, 6,delta_z,update_ui)
+    set_weight(100)
+    set_status("Saving data...")
+    save_output_file(f'{output_path}/{output_file_prefix}_delta_{delta_z}_analyse_brute.txt',
+                                format_data(raw_data,
+                                            '\t',
+                                            ["X (en mm)",
+                                            "Y (en mm)",
+                                            "Z (en mm)",
+                                            "teta (en rad)",
+                                            "rayon (en mm)",
+                                            "Xi-Xmoy",
+                                            "Yi-Ymoy"] ))
+    update_ui(10)
+    save_output_file(f'{output_path}/{output_file_prefix}_delta_{delta_z}_analyse_rayon.txt',
+                                format_data(discrete_data,
+                                        '\t',
+                                        ["X moy (en mm)",
+                                        "Y moy (en mm)",
+                                        "Z moy (en mm)",
+                                        "Delta z(en mm)",
+                                        "Rayon moyen (en mm)",
+                                        "Rayon ecart type (en mm)"] ))
+    update_ui(100)
+    set_status("Done !")
+
+class Worker(QObject):
+    """
+    Worker to run the analyse in a thread
+    """
+    finished = pyqtSignal()
+    progress = pyqtSignal(int)
+    status = pyqtSignal(str)
+
+    def __init__(self, objpath,output_path,output_file_prefix,delta_z):
+        super().__init__()
+        self.objpath = objpath
+        self.delta_z = delta_z
+        self.output_path = output_path
+        self.output_file_prefix = output_file_prefix
+        self.progress_value = 0
+        self.progress_weight = 100
+
+    def run(self):
+        """
+        Run the analyse
+        """
+        analyse(self.objpath,
+                self.output_path,
+                self.output_file_prefix,
+                self.delta_z,
+                self.set_status,
+                self.update_progress,
+                self.set_weight)
+        
+        self.finished.emit()
+
+    def set_status(self, status:str):
+        """
+        Set the weight of the progress bar
+        """
+        self.status.emit(status)
+
+    def set_weight(self, weight):
+        """
+        Set the weight of the progress bar
+        """
+        self.progress_weight = weight
+
+    def update_progress(self, percent):
+        """
+        Update the progress bar
+        """
+        self.progress_value += int(percent/100*self.progress_weight)
+        self.progress.emit(self.progress_value)
+
+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_analyse)
+        self.input_file_choose_btn.clicked.connect(self.select_file)
+        self.output_folder_choose_btn.clicked.connect(self.select_folder)
+
+        self.completed = 0
+
+    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 start_analyse(self):
+        """
+        Start the analyse
+        """
+        if not self.check_input_file():
+            self.input_file_path.setPlainText("Invalid file path")
+            return
+        if not self.check_output_folder():
+            self.output_folder_path.setPlainText("Invalid folder path")
+            return
+
+        # Create the thread to run the analyse
+        self.thread = QThread()
+        self.worker = Worker(self.input_file_path.toPlainText(),
+                             self.output_folder_path.toPlainText(),
+                             self.output_file_prefix.text(),
+                             self.discretisation_value_selector.value())
+        self.worker.moveToThread(self.thread)
+
+        # Connect the signals
+        # Start
+        self.thread.started.connect(self.worker.run)
+        # Progress
+        self.worker.progress.connect(self.update_progress_bar)
+        self.worker.status.connect(self.set_status)
+        # Finished
+        self.worker.finished.connect(self.finish_analyse)
+        self.worker.finished.connect(self.thread.quit)
+        self.worker.finished.connect(self.worker.deleteLater)
+        self.thread.finished.connect(self.thread.deleteLater)
+
+        # Start the thread
+        self.thread.start()
+        self.start_analyse_button.setEnabled(False)
+
+    def set_status(self, status:str):
+        """
+        Set the status of the analyse
+        """
+        self.status_text.setText(status)
+
+    def finish_analyse(self):
+        """
+        Finish the analyse
+        """
+        self.start_analyse_button.setEnabled(True)
+
+    def check_input_file(self):
+        """
+        Check if the input file is valid
+        """
+        if os.path.isfile(self.input_file_path.toPlainText()):
+            return True
+
+    def check_output_folder(self):
+        """
+        Check if the output folder is valid
+        """
+        if os.path.isdir(self.output_folder_path.toPlainText()):
+            return True
+
+    def update_progress_bar(self, value):
+        """
+        Update the progress bar
+        """
+        self.analyse_progress_bar.setValue(value)
\ No newline at end of file
diff --git a/utils/gui/pyqt/main_window/MainWindow.ui b/utils/gui/pyqt/main_window/MainWindow.ui
new file mode 100644
index 0000000..79d0239
--- /dev/null
+++ b/utils/gui/pyqt/main_window/MainWindow.ui
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>529</width>
+    <height>567</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QGridLayout" name="gridLayout">
+    <item row="0" column="0">
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QLabel" name="input_file_label">
+          <property name="text">
+           <string>Chemin du fichier .obj</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="input_file_choose_btn">
+          <property name="text">
+           <string>Choisir le fichier</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QPlainTextEdit" name="input_file_path"/>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_2">
+        <item>
+         <widget class="QLabel" name="output_folder_label">
+          <property name="text">
+           <string>Repertoire de sortie</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="output_folder_choose_btn">
+          <property name="text">
+           <string>Choisir le repertoire</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QPlainTextEdit" name="output_folder_path"/>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_3">
+        <item>
+         <widget class="QLabel" name="output_file_prefix_label">
+          <property name="text">
+           <string>Préfix du fichier de sortie</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="output_file_prefix"/>
+        </item>
+       </layout>
+      </item>
+      <item alignment="Qt::AlignHCenter">
+       <widget class="QLabel" name="discretisation_label">
+        <property name="text">
+         <string>Discretisation (en mm)</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QDoubleSpinBox" name="discretisation_value_selector">
+        <property name="minimum">
+         <double>0.000000000000000</double>
+        </property>
+        <property name="value">
+         <double>1.000000000000000</double>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QProgressBar" name="analyse_progress_bar">
+        <property name="value">
+         <number>0</number>
+        </property>
+       </widget>
+      </item>
+      <item alignment="Qt::AlignHCenter">
+       <widget class="QCheckBox" name="show_graph_checkbox">
+        <property name="text">
+         <string>afficher les graphes</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="start_analyse_button">
+        <property name="text">
+         <string>Analyser le fichier</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="status_text">
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/utils/gui/pyqt/main_window/UI_MainWindow.py b/utils/gui/pyqt/main_window/UI_MainWindow.py
new file mode 100644
index 0000000..8f91a42
--- /dev/null
+++ b/utils/gui/pyqt/main_window/UI_MainWindow.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'MainWindow.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_MainWindow(object):
+    def setupUi(self, MainWindow):
+        MainWindow.setObjectName("MainWindow")
+        MainWindow.resize(529, 567)
+        self.centralwidget = QtWidgets.QWidget(MainWindow)
+        self.centralwidget.setObjectName("centralwidget")
+        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
+        self.gridLayout.setObjectName("gridLayout")
+        self.verticalLayout = QtWidgets.QVBoxLayout()
+        self.verticalLayout.setObjectName("verticalLayout")
+        self.horizontalLayout = QtWidgets.QHBoxLayout()
+        self.horizontalLayout.setObjectName("horizontalLayout")
+        self.input_file_label = QtWidgets.QLabel(self.centralwidget)
+        self.input_file_label.setObjectName("input_file_label")
+        self.horizontalLayout.addWidget(self.input_file_label)
+        self.input_file_choose_btn = QtWidgets.QPushButton(self.centralwidget)
+        self.input_file_choose_btn.setObjectName("input_file_choose_btn")
+        self.horizontalLayout.addWidget(self.input_file_choose_btn)
+        self.verticalLayout.addLayout(self.horizontalLayout)
+        self.input_file_path = QtWidgets.QPlainTextEdit(self.centralwidget)
+        self.input_file_path.setObjectName("input_file_path")
+        self.verticalLayout.addWidget(self.input_file_path)
+        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+        self.output_folder_label = QtWidgets.QLabel(self.centralwidget)
+        self.output_folder_label.setObjectName("output_folder_label")
+        self.horizontalLayout_2.addWidget(self.output_folder_label)
+        self.output_folder_choose_btn = QtWidgets.QPushButton(self.centralwidget)
+        self.output_folder_choose_btn.setObjectName("output_folder_choose_btn")
+        self.horizontalLayout_2.addWidget(self.output_folder_choose_btn)
+        self.verticalLayout.addLayout(self.horizontalLayout_2)
+        self.output_folder_path = QtWidgets.QPlainTextEdit(self.centralwidget)
+        self.output_folder_path.setObjectName("output_folder_path")
+        self.verticalLayout.addWidget(self.output_folder_path)
+        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
+        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+        self.output_file_prefix_label = QtWidgets.QLabel(self.centralwidget)
+        self.output_file_prefix_label.setObjectName("output_file_prefix_label")
+        self.horizontalLayout_3.addWidget(self.output_file_prefix_label)
+        self.output_file_prefix = QtWidgets.QLineEdit(self.centralwidget)
+        self.output_file_prefix.setObjectName("output_file_prefix")
+        self.horizontalLayout_3.addWidget(self.output_file_prefix)
+        self.verticalLayout.addLayout(self.horizontalLayout_3)
+        self.discretisation_label = QtWidgets.QLabel(self.centralwidget)
+        self.discretisation_label.setObjectName("discretisation_label")
+        self.verticalLayout.addWidget(self.discretisation_label, 0, QtCore.Qt.AlignHCenter)
+        self.discretisation_value_selector = QtWidgets.QDoubleSpinBox(self.centralwidget)
+        self.discretisation_value_selector.setMinimum(0.0)
+        self.discretisation_value_selector.setProperty("value", 1.0)
+        self.discretisation_value_selector.setObjectName("discretisation_value_selector")
+        self.verticalLayout.addWidget(self.discretisation_value_selector)
+        self.analyse_progress_bar = QtWidgets.QProgressBar(self.centralwidget)
+        self.analyse_progress_bar.setProperty("value", 0)
+        self.analyse_progress_bar.setObjectName("analyse_progress_bar")
+        self.verticalLayout.addWidget(self.analyse_progress_bar)
+        self.show_graph_checkbox = QtWidgets.QCheckBox(self.centralwidget)
+        self.show_graph_checkbox.setObjectName("show_graph_checkbox")
+        self.verticalLayout.addWidget(self.show_graph_checkbox, 0, QtCore.Qt.AlignHCenter)
+        self.start_analyse_button = QtWidgets.QPushButton(self.centralwidget)
+        self.start_analyse_button.setObjectName("start_analyse_button")
+        self.verticalLayout.addWidget(self.start_analyse_button)
+        self.status_text = QtWidgets.QLineEdit(self.centralwidget)
+        self.status_text.setReadOnly(True)
+        self.status_text.setObjectName("status_text")
+        self.verticalLayout.addWidget(self.status_text)
+        self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
+        MainWindow.setCentralWidget(self.centralwidget)
+        self.statusbar = QtWidgets.QStatusBar(MainWindow)
+        self.statusbar.setObjectName("statusbar")
+        MainWindow.setStatusBar(self.statusbar)
+
+        self.retranslateUi(MainWindow)
+        QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+    def retranslateUi(self, MainWindow):
+        _translate = QtCore.QCoreApplication.translate
+        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
+        self.input_file_label.setText(_translate("MainWindow", "Chemin du fichier .obj"))
+        self.input_file_choose_btn.setText(_translate("MainWindow", "Choisir le fichier"))
+        self.output_folder_label.setText(_translate("MainWindow", "Repertoire de sortie"))
+        self.output_folder_choose_btn.setText(_translate("MainWindow", "Choisir le repertoire"))
+        self.output_file_prefix_label.setText(_translate("MainWindow", "Préfix du fichier de sortie"))
+        self.discretisation_label.setText(_translate("MainWindow", "Discretisation (en mm)"))
+        self.show_graph_checkbox.setText(_translate("MainWindow", "afficher les graphes"))
+        self.start_analyse_button.setText(_translate("MainWindow", "Analyser le fichier"))
-- 
GitLab