diff --git a/huffman_py/gui/InputDialog.py b/huffman_py/gui/InputDialog.py
new file mode 100644
index 0000000..aead471
--- /dev/null
+++ b/huffman_py/gui/InputDialog.py
@@ -0,0 +1,48 @@
+#
+# Sam Hadow - Huffman-py
+# Copyright (C) 2023
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+from PyQt5 import QtCore, QtGui, QtWidgets
+from PyQt5.QtWidgets import *
+class InputDialog(QDialog):
+ def __init__(self, texte1, texte2, thirdBox = False, texte3=None, parent=None):
+ super().__init__(parent)
+
+ self.setWindowTitle("données")
+
+ self.box1 = QLineEdit(self)
+ self.box2 = QLineEdit(self)
+ buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self);
+
+ layout = QFormLayout(self)
+ layout.addRow(texte1, self.box1)
+ layout.addRow(texte2, self.box2)
+
+ self.thirdBox = thirdBox
+ if self.thirdBox:
+ self.box3 = QLineEdit(self)
+ layout.addRow(texte3, self.box3)
+
+ layout.addWidget(buttonBox)
+
+ buttonBox.accepted.connect(self.accept)
+ buttonBox.rejected.connect(self.reject)
+
+ def getInputs(self):
+ if self.thirdBox:
+ return (self.box1.text(), self.box2.text(), self.box3.text())
+ else:
+ return (self.box1.text(), self.box2.text())
diff --git a/huffman_py/gui/Ui_MainWindow.py b/huffman_py/gui/Ui_MainWindow.py
new file mode 100644
index 0000000..88051d2
--- /dev/null
+++ b/huffman_py/gui/Ui_MainWindow.py
@@ -0,0 +1,270 @@
+#
+# Sam Hadow - Huffman-py
+# Copyright (C) 2023
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+from PyQt5 import QtCore, QtGui, QtWidgets, QtSvg
+from PyQt5.QtWidgets import *
+from PyQt5.QtGui import *
+from PyQt5.QtCore import *
+
+import os, json, sys
+
+from huffman_py.gui.generate_graph import *
+from huffman_py.fonctions.decode import *
+from huffman_py.fonctions.encode import *
+from huffman_py.gui.InputDialog import *
+from huffman_py.fonctions.ifFileGetContent import *
+from huffman_py.gui.messageBox import *
+
+
+
+class Ui_MainWindow(QWidget):
+ def setupUi(self, MainWindow):
+ if not MainWindow.objectName():
+ MainWindow.setObjectName(u"MainWindow")
+ MainWindow.resize(800, 800)
+ self.centralwidget = QWidget(MainWindow)
+ self.centralwidget.setObjectName(u"centralwidget")
+ self.gridLayout = QGridLayout(self.centralwidget)
+ self.gridLayout.setObjectName(u"gridLayout")
+
+ # widget pour attacher le graphe/texte
+ self.widget = QWidget(self.centralwidget)
+ self.widget.setObjectName(u"widget")
+ self.widget.setMinimumSize(QSize(10, 10))
+
+ self.gridLayout.addWidget(self.widget, 1, 0, 1, 1)
+
+ # widget_2 pour attacher les boutons
+ self.widget_2 = QWidget(self.centralwidget)
+ self.widget_2.setObjectName(u"widget_2")
+ self.widget_2.setMaximumSize(QSize(4000, 50))
+
+ self.gridLayout.addWidget(self.widget_2, 0, 0, 1, 1)
+
+ ##
+ MainWindow.setCentralWidget(self.centralwidget)
+
+ self.retranslateUi(MainWindow)
+
+
+ # grille pour les widget texte ou svg
+ self.gridLayout_main = QGridLayout(self.widget)
+ self.gridLayout_main.setObjectName(u"gridLayout_main")
+
+ # les widgets en question
+ #####
+ # graphe de l'arbre
+ self.viewer = QtSvg.QSvgWidget()
+ self.gridLayout_main.addWidget(self.viewer,0, 0, 1, 1)
+
+ #####
+ # texte
+ self.texte = QTextEdit()
+ self.texte.setReadOnly(True)
+ self.gridLayout_main.addWidget(self.texte,1, 0, 1, 1)
+
+ # on cache pour le moment les widget, on les affichera quand on en aura besoin
+ self.viewer.setVisible(False)
+ self.texte.setVisible(False)
+
+
+ # éléments de grid 2
+ # boutons
+ ######################
+ self.gridLayout_2 = QGridLayout(self.widget_2)
+ self.gridLayout_2.setObjectName(u"gridLayout_2")
+
+ #####
+ # push button encodage
+ self.pushButton_encode = QPushButton(self.widget_2)
+ self.pushButton_encode.setObjectName(u"pushButton")
+
+ self.gridLayout_2.addWidget(self.pushButton_encode, 1, 0, 1, 1)
+
+ self.pushButton_encode.setText(QCoreApplication.translate("MainWindow", u"encoder", None))
+
+ # fonction du bouton
+ self.pushButton_encode.clicked.connect(self.showDialogEncoder)
+
+
+ #####
+ # push button décodage
+ self.pushButton_decode = QPushButton(self.widget_2)
+ self.pushButton_decode.setObjectName(u"pushButton_decode")
+
+ self.gridLayout_2.addWidget(self.pushButton_decode, 1, 1, 1, 1)
+
+ self.pushButton_decode.setText(QCoreApplication.translate("MainWindow", u"décoder", None))
+
+ # fonction du bouton
+ self.pushButton_decode.clicked.connect(self.showDialogDecoder)
+
+
+ #####
+ # push button encode to file
+ self.pushButton_encodeToFile = QPushButton(self.widget_2)
+ self.pushButton_encodeToFile.setObjectName(u"pushButton_encodeToFile")
+
+ self.gridLayout_2.addWidget(self.pushButton_encodeToFile, 1, 2, 1, 1)
+
+ self.pushButton_encodeToFile.setText(QCoreApplication.translate("MainWindow", u"encoder vers fichier", None))
+
+ # fonction du bouton
+ self.pushButton_encodeToFile.clicked.connect(self.showDialogEncoderFichier)
+
+
+ #####
+ #push button decode to file
+ self.pushButton_decodeToFile = QPushButton(self.widget_2)
+ self.pushButton_decodeToFile.setObjectName(u"pushButton_decodeToFile")
+
+ self.gridLayout_2.addWidget(self.pushButton_decodeToFile, 1, 3, 1, 1)
+
+ self.pushButton_decodeToFile.setText(QCoreApplication.translate("MainWindow", u"décoder vers fichier", None))
+
+ # fonction du bouton
+ self.pushButton_decodeToFile.clicked.connect(self.showDialogDecoderFichier)
+
+ ##### fin boutons
+
+
+ # lancement d'une fonction avant la fermeture
+ qApp.aboutToQuit.connect(self.closeEvent)
+
+
+ ######
+ # fonctions des boutons
+ def showDialogEncoder(self):
+ '''affiche le dialogue pour encoder un texte (chemin vers fichier accepté) et affiche l'arbre correspondant'''
+ text, ok = QInputDialog.getText(self, 'données', 'entrez le texte')
+ text = ifFileGetContent(text)
+ if ok and len(text) > 0:
+ the_data = (str(text))
+
+ print(the_data)
+ encoding, racine, _ = huffman_encode(the_data)
+ print("Encoded output", encoding)
+ print("Decoded Output", huffman_decode(encoding, racine))
+
+ #générer l'arbre à afficher (le .svg)
+ generate_graph(racine)
+
+ # rafraichir l'affichage de l'arbre
+ self.viewer.load('tree.svg')
+ self.viewer.update()
+
+ # on affiche le widget contenant l'arbre (et on affiche le texte encodé, dans un petit espace de préférence)
+ self.viewer.setVisible(True)
+ self.texte.setText(encoding)
+ self.texte.setMaximumSize(QSize(4000, 50))
+ self.texte.setVisible(True)
+
+
+ def showDialogDecoder(self):
+ '''affiche le dialogue pour décoder un texte encodé avec ses codes (chemins vers fichiers acceptés)'''
+ dialog = InputDialog("binaire","Dict {lettre:code} JSON-formatted")
+ if dialog.exec():
+ data = dialog.getInputs()
+ binaire = ifFileGetContent(data[0])
+
+ if len(binaire)>0 and len(data[1])>0:
+ try:
+ dico = json.loads(ifFileGetContent(data[1]))
+ except ValueError:
+ create_msg_box("Impossible de lire le dictionnaire fourni, est-ce bien un dictionnaire python formatté en JSON?","Dictionnaire invalide")
+ return 0
+
+ try:
+ text = decode_from_dico(binaire, dico)
+ except ValueError:
+ create_msg_box("Impossible de décoder le texte, est-ce bien le dictionnaire correspondant à ce texte encodé?","Erreur décodage")
+ return 0
+ except TypeError:
+ create_msg_box("Impossible d'utiliser le texte fourni, est-ce bien un texte encodé en binaire?","Erreur entrée")
+ return 0
+
+ # on remplace le contenu du widget texte par le texte obtenu en décodant
+ self.texte.setText(text)
+ self.texte.setMaximumSize(QSize(4000, 4000))
+
+ # on affiche le widget contenant le texte (et on cache l'arbre)
+ self.viewer.setVisible(False)
+ self.texte.setVisible(True)
+
+ def showDialogEncoderFichier(self):
+ '''affiche le dialogue pour encoder un texte (chemin vers fichier accepté) et crée 2 fichiers au chemin spécifié (un raw avec le texte encode et un json avec le dictionnaire des codes)'''
+ dialog = InputDialog("texte","chemin vers fichier")
+ if dialog.exec():
+ data = dialog.getInputs()
+ texte = ifFileGetContent(data[0])
+ if len(data[1])>0 and len(texte)>0:
+ try:
+ with open(data[1]+'.raw','w') as fd_bin, open(data[1]+'.json','w') as fd_dic:
+ #encoding, _, dico = huffman_encode(data[0])
+ encoding, _, dico = huffman_encode(texte)
+ json.dump(dico,fd_dic)
+ fd_bin.write(encoding)
+ except FileNotFoundError:
+ create_msg_box("Le chemin est invalide, est-ce que le dossier dans lequel créer le fichier existe?","Erreur entrée")
+
+ def showDialogDecoderFichier(self):
+ '''affiche le dialogue pour décoder un texte encodé avec ses codes (chemins vers fichiers acceptés), écrit le résultat dans le fichier donné en argument'''
+ dialog = InputDialog("texte","Dict {lettre:code} JSON-formatted", texte3 = "chemin vers fichier", thirdBox = True)
+ if dialog.exec():
+ data = dialog.getInputs()
+ binaire = ifFileGetContent(data[0])
+
+ if len(binaire)>0 and len(data[1])>0:
+ try:
+ dico = json.loads(ifFileGetContent(data[1]))
+ except ValueError:
+ create_msg_box("Impossible de lire le dictionnaire fourni, est-ce bien un dictionnaire python formatté en JSON?","Dictionnaire invalide")
+ return 0
+
+ try:
+ texte = decode_from_dico(binaire, dico)
+ except ValueError:
+ create_msg_box("Impossible de décoder le texte, est-ce bien le dictionnaire correspondant à ce texte encodé?","Erreur décodage")
+ return 0
+ except TypeError:
+ create_msg_box("Impossible d'utiliser le texte fourni, est-ce bien un texte encodé en binaire?","Erreur entrée")
+ return 0
+ try:
+ with open(data[2],'w') as fd:
+ fd.write(texte)
+ except FileNotFoundError:
+ create_msg_box("Le chemin est invalide, est-ce que le dossier dans lequel créer le fichier existe?","Erreur entrée")
+
+ ##### fin fonctions des boutons
+
+
+ def retranslateUi(self, MainWindow):
+ MainWindow.setWindowTitle(QCoreApplication.translate("Huffman.py", u"Huffman.py", None))
+
+ def closeEvent(self):
+ # supprimer le fichier du graphe avant de fermer le programme
+ try:
+ os.unlink('./tree.gv')
+ os.unlink('./tree.svg')
+ except:
+ # si l'utilisateur avait ouvert le programme mais pas généré d'arbre
+ pass
+ print('exit')
+ sys.exit(0)
+
+
+
diff --git a/huffman_py/gui/generate_graph.py b/huffman_py/gui/generate_graph.py
new file mode 100644
index 0000000..6fb9ebe
--- /dev/null
+++ b/huffman_py/gui/generate_graph.py
@@ -0,0 +1,25 @@
+#
+# Sam Hadow - Huffman-py
+# Copyright (C) 2023
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+from huffman_py.gui.generate_node import *
+#le fichier generate_node.py
+
+def generate_graph(racine):
+ G = graphviz.Digraph(format='svg')
+ generate_node(G, racine)
+ G.render(outfile="tree.svg",overwrite_source=True)
+ #on peut ensuite intégrer l'arbre dans QT
diff --git a/huffman_py/gui/generate_node.py b/huffman_py/gui/generate_node.py
new file mode 100644
index 0000000..0c52ef7
--- /dev/null
+++ b/huffman_py/gui/generate_node.py
@@ -0,0 +1,36 @@
+#
+# Sam Hadow - Huffman-py
+# Copyright (C) 2023
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+import graphviz
+
+def generate_node(graph, sommet, father = None):
+ if sommet.left and sommet.right:
+ #pas une feuille
+ graph.node(str(sommet.identifiant), label = str(sommet.occurrence) , style = "filled", fillcolor = "red", shape="circle")
+ else:
+ #une feuille
+ graph.node(str(sommet.identifiant), label = graphviz.escape(str(sommet.occurrence)+'\n'+str(sommet.lettres)), style = "filled", fillcolor = "green")
+ # https://graphviz.readthedocs.io/en/stable/api.html#graphviz.escape
+
+ if father != None:
+ graph.edge(str(father), str(sommet.identifiant), label=str(sommet.code))
+
+ if sommet.left:
+ generate_node(graph, sommet.left, sommet.identifiant)
+ if sommet.right:
+ generate_node(graph, sommet.right, sommet.identifiant)
+
diff --git a/huffman_py/gui/messageBox.py b/huffman_py/gui/messageBox.py
new file mode 100644
index 0000000..f8348bd
--- /dev/null
+++ b/huffman_py/gui/messageBox.py
@@ -0,0 +1,27 @@
+#
+# Sam Hadow - Huffman-py
+# Copyright (C) 2023
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+from PyQt5 import QtCore, QtGui, QtWidgets
+from PyQt5.QtWidgets import *
+
+def create_msg_box(message,titre):
+ msgBox = QMessageBox()
+ msgBox.setIcon(QMessageBox.Information)
+ msgBox.setText(message)
+ msgBox.setWindowTitle(titre)
+ msgBox.setStandardButtons(QMessageBox.Ok)
+ msgBox.exec_()