Threaded Cypher App Source Code:
Here's the source code for the application. I've done my best to comment
heavily.
# -*- coding: utf-8 -*-
"""
Created on Fri Oct 29 19:44:44 2021
@author: ANG
"""
#IMPORTS
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
from ang_cyphers import aes_sim_v_2_1 as advc2
class diverted_traffic_signals(QObject):
'''
Class allows signals to be assigned and emitted to the main thread from whatever
worker. To allow data to be passed from a worker thread they have to have declared
signals here.
Where: someSignalVar = pyqtSignal(dataTypeHere)
'''
result = pyqtSignal(object)
class diverted_traffic_worker_thread(QRunnable):
'''
This class is passed the args vals, key, and enc to its __init__, from WindowClassOne
on_execute(). It then uses these passed attributes in run(). Last, it emits
the result from run() to return_cypher(self, thread_out_result)—where thread_out_result
is "result" emitted from diverted_traffic_worker_thread run().
'''
def __init__(self, vals, key, enc, *args, **kwargs):
super(diverted_traffic_worker_thread, self).__init__()
#Constructor arguments (re-used for processing)
self.vals = vals
self.key = key
self.enc = enc
self.args = args
self.kwargs = kwargs
self.signals = diverted_traffic_signals()
@pyqtSlot()
def run(self):
'''
Process the threaded function. All heavy lifting here.
'''
if self.enc == 0:
self.result = advc2.sim_aes_1(vals=self.vals, key=self.key, enc=bool(self.enc))
self.result = ' '.join(map(str, self.result))
self.signals.result.emit(self.result)
elif self.enc == 2:
try:
self.vals = self.vals.split()
self.result = advc2.sim_aes_1(vals=self.vals, key=self.key, enc=bool(self.enc))
self.signals.result.emit(self.result)
except:
self.result = 'wrong pass'
self.signals.result.emit(self.result)
class WindowClassOne(QWidget):
'''
This application cyphers a string that is encoded in utf-8 or ascii. Before
it only cyphered and decyphered ascii characters. So, to solve this problem
I added to statements that did the same for ascii but for utf-8 as well. I
wanted to do this because most of the web is encoded with utf-8. You can also
apply the same encoding and decoding methods by adding more ways of encoding
decoding. By adding seven lines of code I have incorporated both types of
encoding in the function advc2.sim_aes_1().
'''
def __init__(self):
super(WindowClassOne,self).__init__()
#SET THE DEFAULT FONT FOR APP
self.setFont(QFont('consolas',11))
#SET DEFUALT MINIMUM SIZE OF WINDOW
self.setMinimumSize(900,900)
#INSTANTIATE AN OUTER LAYOUT (master layout)
self.outerLayout = QVBoxLayout()
#ADD THE MAIN WINDOWS OUTER LAYOUT (MASTER LAYOUT)
self.setLayout(self.outerLayout)
#INSTANTIATE LEFT AND RIGHT LAYOUTS (sub layouts)
self.upperLayout = QGridLayout()
self.lowerLayout = QGridLayout()
self.lowerLayoutWidgs = QGridLayout()
self.subformlayout_1 = QFormLayout()
#CALL THE INNER LAYOUTS INTO THE OUTER LAYOUT (MASTER LAYOUT)
self.outerLayout.addLayout(self.upperLayout, 2) #the integer indicates respective stretch
self.outerLayout.addLayout(self.lowerLayout, 2) #the integer indicates respective stretch
self.outerLayout.addLayout(self.lowerLayoutWidgs, 0)
#INSTANTIATE UPPER WIDGETS (added in order)
self.cypher_decypher_label = QLabel('Cypher-Decypher Output:')
self.upper_layout_textedit = QTextEdit('', readOnly=True, )
self.export_ct_button = QPushButton('Save Output', self, maximumWidth=125)
#INSTANTIATE LOWER WIDGETS (added in order)
self.lower_label = QLabel('Cypher-Decypher Input:')
self.lower_layout_textedit = QTextEdit('', readOnly=False, acceptRichText=False)
self.cypher_type_label = QLabel('')
self.cypher_key_label = QLabel('Key Input')
self.aes_sub_cypher_button = QPushButton('Execute', maximumWidth=150)
self.aes_pass_textinput = QLineEdit('', maxLength=16, maximumWidth=150)
self.aes_pass_textinput.setEchoMode(QLineEdit.Password)
self.aes_decode_label = QLabel('Decypher')
self.decode_check_box = QCheckBox()
self.load_txt_button = QPushButton('Load Text', maximumWidth=100)
self.load_txt_label = QLabel('*load .txt to Cypher/Decypher')
self.progress_bar = QProgressBar(self) #I have included the progress bar here but I do not do anything with it
#create threadpool
self.threadpool = QThreadPool()
#CALL THE WIDGETS TO THE LEFT AND RIGHT LAYOUTS
#you have to declare where the widget goes in the gridlayout here
#so syntax; SomeLayout.addWidget(SomeWidget, gridCoord_y, gridCoord_x)
#span multi syntax; SomeLayout.addWidget(SomeWidget, gridCoord_y, gridCoord_x, gridSpanCoord_y2, gridSpanCoord_x2)
self.upperLayout.addWidget(self.cypher_decypher_label, 0, 0, 1, 0)
self.upperLayout.addWidget(self.upper_layout_textedit, 1, 0, 1, 0)
self.upperLayout.addWidget(self.export_ct_button, 2, 0, 1, 0)
self.lowerLayout.addWidget(self.lower_label, 0, 0, 1, 1)
self.lowerLayout.addWidget(self.progress_bar, 0, 1, 1, 1)
self.lowerLayout.addWidget(self.lower_layout_textedit, 1, 0, 2, 2)
self.lowerLayoutWidgs.addLayout(self.subformlayout_1, 2, 0, 1, 1)
#layout.addWidget(self.progressBar)
#INSTANTIATE THE SUB FORM LAYOUTS
self.subformlayout_1.addRow(self.load_txt_button, self.load_txt_label)
self.subformlayout_1.addRow(self.cypher_key_label, self.aes_pass_textinput)
self.subformlayout_1.addRow(self.aes_decode_label, self.decode_check_box)
self.subformlayout_1.addRow(self.cypher_type_label, self.aes_sub_cypher_button)
#SET STYLES
self.setStyleSheet("height:30px ; font: consolas ")
self.export_ct_button.setStyleSheet("background-color : #ADC6C5")
self.load_txt_button.setStyleSheet("background-color : #ADC6C5")
self.aes_sub_cypher_button.setStyleSheet("background-color : #ADC6C5")
self.progress_bar.setStyleSheet("background-color : #C6C6C3")
def key_alert_diag(self):
'''
Function to generate key length alert message.
'''
self.load_fasta_alert = QMessageBox()
self.load_fasta_alert.setWindowTitle('Key size!')
self.load_fasta_alert.setText('The key must be 16 characters...')
self.load_fasta_alert.exec_()
def wrong_key_alert_diag(self):
'''
Function to generate wrong key alert message.
'''
self.load_fasta_alert = QMessageBox()
self.load_fasta_alert.setIconPixmap(QPixmap("systemlockscreen.ico"))
self.load_fasta_alert.setWindowTitle('Wrong Key!')
self.load_fasta_alert.setText('nice try... key is definitely wrong...')
self.load_fasta_alert.exec_()
def return_cypher(self, thread_out_result):
'''
Function that gets passed the result of the threaded run() from diverted_traffic_worker_thread
as thread_out_result. When the thread emits result, it goes here as
arg thread_out_result.
'''
if self.decode_check_box.checkState() == 0:
self.upper_layout_textedit.clear()
self.upper_layout_textedit.insertPlainText(thread_out_result)
self.lower_layout_textedit.clear()
self.aes_pass_textinput.clear()
elif thread_out_result != 'wrong pass':
self.upper_layout_textedit.clear()
self.upper_layout_textedit.insertPlainText(thread_out_result)
self.lower_layout_textedit.clear()
self.aes_pass_textinput.clear()
elif thread_out_result == 'wrong pass':
dlg = wrong_key_alert_diag(self)
self.aes_pass_textinput.clear()
def on_execute(self):
'''
This function is the initial driver of the thread. First it checks
the integrity of the key length. Then, assigns the three variables
needed for the encryption/decryption. Then, assigns the assn_work
(assn_work) to diverted_traffic_worker_thread and passes the three
assignments as args for diverted_traffic_worker_thread (the threaded class)—
this applies the args as class attibutes to diverted_traffic_worker_thread.
Lastly, it signals diverted_traffic_worker_thread to run and connect
the result to return_cypher().
In the diverted_traffic_worker_thread class, it emits "result" to the
thread_out_result arg in return_cypher(). That is how "result" is passed
from diverted_traffic_worker_thread to return_cypher() — but the
function return_cypher() is called here in the second to last line
of this function.
Basic steps:
1. checks key
2. assigns key, enc, vals
3. applies key, enc, vals as attributes of diverted_traffic_worker_thread
4. calls a signal to run() in diverted_traffic_worker_thread and
passes its emmited work to return_cypher() as return_cypher
argument thread_out_result
5. calls the thread to start
'''
if len(self.aes_pass_textinput.text()) < 16:
dlg = key_alert_diag(self)
else:
key = self.aes_pass_textinput.text()
enc = self.decode_check_box.checkState() #0 or 1
vals = self.lower_layout_textedit.toPlainText()
assn_work = diverted_traffic_worker_thread(vals, key, enc)
assn_work.signals.result.connect(lambda checked: return_cypher(self, assn_work.result))
self.threadpool.start(assn_work)
def on_export_ct(self):
'''
Function to export a file as .txt
'''
#GET DIRECTORY EXTENTION TO SAVE (EXPORT)
save_file_var = QFileDialog.getSaveFileName(None,'SaveTextFile','./', "Text Files (*.txt)")
#APPLY QTEXTEDIT AS PLAIN TEXT TO SAVE
Text = self.upper_layout_textedit.toPlainText()
#PREVENT ERROR ON CANCEL
if save_file_var[0]:
# Finally this will Save your file to the path selected.
with open(save_file_var[0], 'w') as file:
file.write(Text)
self.upper_layout_textedit.clear()
def load_txt(self):
'''
function that loads a .txt file -- encrypted or not encrypted.
'''
filename = QFileDialog.getOpenFileName(None, 'Open file', './', 'txt Files (*.txt)')
#PREVENT ERROR ON CANCEL
if filename[0]:
txt = open(filename[0], 'r')
with txt:
self.data = txt.read()
self.filename = filename[0].split('/')
self.lower_layout_textedit.insertPlainText(self.data)
#CONNECT THE BUTTONS TO RESPECTIVE FUNCTIONS
self.aes_sub_cypher_button.clicked.connect(lambda checked: on_execute(self))
self.export_ct_button.clicked.connect(lambda checked: on_export_ct(self))
self.load_txt_button.clicked.connect(lambda checked: load_txt(self))
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setStyle('Fusion')
window = WindowClassOne()
window.show()
sys.exit(app.exec_())
With the above, to apply the thread it looks like the following visual—which
I thought gives a good visual aid on what is going on.