270 lines
9.6 KiB
Python
270 lines
9.6 KiB
Python
#
|
|
# 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 <http://www.gnu.org/licenses/>.
|
|
#
|
|
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.functions.decode import *
|
|
from huffman_py.functions.encode import *
|
|
from huffman_py.gui.InputDialog import *
|
|
from huffman_py.functions.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 to attach graph/text
|
|
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 to attach buttons
|
|
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)
|
|
|
|
|
|
# text/svg widget grid
|
|
self.gridLayout_main = QGridLayout(self.widget)
|
|
self.gridLayout_main.setObjectName(u"gridLayout_main")
|
|
|
|
# text/svg widgets
|
|
#####
|
|
# svg (tree graph)
|
|
self.viewer = QtSvg.QSvgWidget()
|
|
self.gridLayout_main.addWidget(self.viewer,0, 0, 1, 1)
|
|
|
|
#####
|
|
# text
|
|
self.text = QTextEdit()
|
|
self.text.setReadOnly(True)
|
|
self.gridLayout_main.addWidget(self.text,1, 0, 1, 1)
|
|
|
|
# hide widgets for now
|
|
self.viewer.setVisible(False)
|
|
self.text.setVisible(False)
|
|
|
|
|
|
# grid2
|
|
# buttons
|
|
######################
|
|
self.gridLayout_2 = QGridLayout(self.widget_2)
|
|
self.gridLayout_2.setObjectName(u"gridLayout_2")
|
|
|
|
#####
|
|
# push button encoding
|
|
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"encode", None))
|
|
|
|
# button function
|
|
self.pushButton_encode.clicked.connect(self.showDialogEncoder)
|
|
|
|
|
|
#####
|
|
# push button decoding
|
|
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"decode", None))
|
|
|
|
# button function
|
|
self.pushButton_decode.clicked.connect(self.showDialogDecode)
|
|
|
|
|
|
#####
|
|
# 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"encode to file", None))
|
|
|
|
# button function
|
|
self.pushButton_encodeToFile.clicked.connect(self.showDialogEncodeToFile)
|
|
|
|
|
|
#####
|
|
#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"decode to file", None))
|
|
|
|
# button function
|
|
self.pushButton_decodeToFile.clicked.connect(self.showDialogDecodeToFile)
|
|
|
|
##### end buttons
|
|
|
|
|
|
# function on exit
|
|
qApp.aboutToQuit.connect(self.closeEvent)
|
|
|
|
|
|
######
|
|
# button functions
|
|
def showDialogEncoder(self):
|
|
'''dialog to encode a text, path to file allowed, will show a tree'''
|
|
text_input, ok = QInputDialog.getText(self, 'Input', 'Text to encode')
|
|
text = ifFileGetContent(text_input)
|
|
if ok and len(text) > 0:
|
|
the_data = (str(text))
|
|
|
|
print(the_data)
|
|
encoding, root, _ = huffman_encode(the_data)
|
|
print("Encoded output", encoding)
|
|
print("Decoded Output", huffman_decode(encoding, root))
|
|
|
|
# generate tree svg
|
|
generate_graph(root)
|
|
|
|
# refresh output
|
|
self.viewer.load('tree.svg')
|
|
self.viewer.update()
|
|
|
|
# show tree (and encoded text in a small box)
|
|
self.viewer.setVisible(True)
|
|
self.text.setText(encoding)
|
|
self.text.setMaximumSize(QSize(4000, 50))
|
|
self.text.setVisible(True)
|
|
|
|
|
|
def showDialogDecode(self):
|
|
'''dialog to decode a text with its dictionary, will show decoded text, path to files allowed'''
|
|
dialog = InputDialog("binary","Dict {char:code} JSON-formatted")
|
|
if dialog.exec():
|
|
data = dialog.getInputs()
|
|
binary = ifFileGetContent(data[0])
|
|
|
|
if len(binary)>0 and len(data[1])>0:
|
|
try:
|
|
dict = json.loads(ifFileGetContent(data[1]))
|
|
except ValueError:
|
|
create_msg_box("Can't read dictionary, is it JSON-formatted?","Invalid dictionary")
|
|
return 0
|
|
|
|
try:
|
|
text = decode_from_dict(binary, dict)
|
|
except ValueError:
|
|
create_msg_box("Can't decode text, is it the right dictionary?","decoding error")
|
|
return 0
|
|
except TypeError:
|
|
create_msg_box("Can't use text in input, is it a binary?","Input error")
|
|
return 0
|
|
|
|
# replace text widget content with decoded text
|
|
self.text.setText(text)
|
|
self.text.setMaximumSize(QSize(4000, 4000))
|
|
|
|
# show text widget (and hide tree)
|
|
self.viewer.setVisible(False)
|
|
self.text.setVisible(True)
|
|
|
|
def showDialogEncodeToFile(self):
|
|
'''Dialog to encode a text (path allowed), create a path.raw file with encoded text and a path.json file with corresponding dictionary'''
|
|
dialog = InputDialog("text","path to file")
|
|
if dialog.exec():
|
|
data = dialog.getInputs()
|
|
text = ifFileGetContent(data[0])
|
|
if len(data[1])>0 and len(text)>0:
|
|
try:
|
|
with open(data[1]+'.raw','w') as fd_bin, open(data[1]+'.json','w') as fd_dic:
|
|
encoding, _, dict = huffman_encode(text)
|
|
json.dump(dict,fd_dic)
|
|
fd_bin.write(encoding)
|
|
except FileNotFoundError:
|
|
create_msg_box("Invalid path, does this directory exist?","Input error")
|
|
|
|
def showDialogDecodeToFile(self):
|
|
'''Dialog to decode a binary (paths accepted for both dictionary and binary) and create a file with decoded text'''
|
|
dialog = InputDialog("text","Dict {lettre:code} JSON-formatted", text3 = "path to file", thirdBox = True)
|
|
if dialog.exec():
|
|
data = dialog.getInputs()
|
|
binary = ifFileGetContent(data[0])
|
|
|
|
if len(binary)>0 and len(data[1])>0:
|
|
try:
|
|
dict = json.loads(ifFileGetContent(data[1]))
|
|
except ValueError:
|
|
create_msg_box("Can't read dictionary, is it JSON-formatted?","Invalid dictionary")
|
|
return 0
|
|
|
|
try:
|
|
text = decode_from_dict(binary, dict)
|
|
except ValueError:
|
|
create_msg_box("Can't decode text, is it the right dictionary?","decoding error")
|
|
return 0
|
|
except TypeError:
|
|
create_msg_box("Can't use text in input, is it a binary?","Input error")
|
|
return 0
|
|
try:
|
|
with open(data[2],'w') as fd:
|
|
fd.write(text)
|
|
except FileNotFoundError:
|
|
create_msg_box("Invalid path, does this directory exist?","Input error")
|
|
|
|
##### end button functions
|
|
|
|
|
|
def retranslateUi(self, MainWindow):
|
|
MainWindow.setWindowTitle(QCoreApplication.translate("Huffman.py", u"Huffman.py", None))
|
|
|
|
def closeEvent(self):
|
|
# delete graph files before exiting
|
|
try:
|
|
os.unlink('./tree.gv')
|
|
os.unlink('./tree.svg')
|
|
except:
|
|
# if user didn't generate a tree
|
|
pass
|
|
print('exit')
|
|
sys.exit(0)
|
|
|
|
|
|
|