Skip to content

Commit c871ceb

Browse files
committed
Update 1.3
1 parent 13629cc commit c871ceb

File tree

4 files changed

+146
-99
lines changed

4 files changed

+146
-99
lines changed

changelog.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,10 @@ First public version. The following features are introduced:
1717
—— Categorization is now implemented into the cipher selection combo box;
1818
—— Selected cipher's properties are now shown in the cipher selection combo box;
1919
—— It is now possible to select text fields for opening a file and saving into a file;
20-
—— Automatic updates are implemented
20+
—— Automatic updates are implemented
21+
22+
1.3
23+
—— It's now possible to switch the layout without rebooting;
24+
—— Fixed paste function failing due to an old variable name;
25+
—— Added support for latest Python versions and decreased the bundle size for installation.
26+
—— Added loading progress bar dialog on ciphers loading.

config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"open": false,
1010
"save": true,
1111
"font": [
12-
"13pt \"Cascadia Code\"",
13-
"13pt \"Cascadia Code\""
12+
"13pt \"Segoe UI Semilight\"",
13+
"13pt \"Segoe UI Semilight\""
1414
],
1515
"wrap": [
1616
true,

requirements.txt

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,7 @@
1-
PyQt5==5.15.2
2-
nltk==3.6.2
3-
pyperclip==1.8.1
4-
pywin32==301
5-
docutils==0.16
6-
numpy==1.19.4
7-
brotli==1.0.9
8-
Cython==0.29.24
9-
importlib_metadata==4.6.1
10-
keyring==23.0.1
11-
lockfile==0.12.2
12-
lxml==4.6.3
13-
monotonic==1.6
14-
ordereddict==1.1
15-
protobuf==3.17.3
16-
pyOpenSSL==20.0.1
17-
tornado==6.1
18-
wincertstore==0.2.1
19-
zipp==3.5.0
1+
PyQt5
2+
nltk
3+
pyperclip
4+
pywin32
5+
docutils
6+
requests
7+
cryptography

sicrypt.py

Lines changed: 130 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
1-
import sys, os, pyperclip, glob, json, typing
1+
import sys, os, pyperclip, glob, json
2+
from PyQt5.QtWidgets import QWidget
23
import traceback, importlib, requests, subprocess
3-
import nltk, base64, hashlib, cryptography
44

5-
from PyQt5 import QtCore, QtGui, QtWidgets
5+
from PyQt5 import QtCore
66
from PyQt5.QtCore import *
77
from PyQt5.QtGui import *
88
from PyQt5.QtWidgets import *
9-
from typing import (List as list,
10-
Tuple as tuple,
11-
Dict as dict,
12-
Callable)
9+
from typing import Callable, Iterator, Tuple
1310
from uibld import *
1411
from uibld.settings import *
1512
from uibld.style import *
1613

1714

1815
#Global variables declaration
1916
gitHubLink = 'https://github.com/StarterCraft/sicrypt'
20-
version = 'v1.2'
17+
version = 'v1.3'
18+
ciphers = []
19+
categories = []
2120

2221

2322
class Window(QMainWindow):
@@ -27,7 +26,7 @@ class Window(QMainWindow):
2726
#Translate function to simplify translation
2827
translate = QApplication.translate
2928

30-
def __init__(self, UIClass: (Ui_MainWindowVertical, Ui_MainWindowHorizontal), config):
29+
def __init__(self, UIClass: Ui_MainWindowVertical | Ui_MainWindowHorizontal, config):
3130
QMainWindow.__init__(self)
3231
self.ui = UIClass()
3332
self.ui.setupUi(self)
@@ -108,6 +107,7 @@ class PlainTextEdit(QPlainTextEdit):
108107
def __init__(self, *args, **kwargs):
109108
super().__init__(*args, **kwargs)
110109

110+
111111
def setTabPolicy(self, policy: int) -> None:
112112
'''
113113
Set the text field's tab policy. Auxilary method.
@@ -331,10 +331,12 @@ def applyConfig(self, root: Window, onInit: bool = False) -> None:
331331

332332
#False for default vertical position,
333333
#True for horizontal position
334-
if (type(self.ui) == Ui_MainWindowHorizontal) != self.config['textFields']['position'] and not onInit:
335-
messageBox(root, QMessageBox.Information,
336-
root.translate('TextFieldsPositionChangedDialog', 'Text fields` position has been changed'),
337-
root.translate('TextFieldsPositionChangedDialog', 'Text fields` position has been changed, changes`ll be applied after restart'))
334+
if (not onInit):
335+
print(('Гориз сейчас' if type(root.ui) == Ui_MainWindowHorizontal else'Верт сейчас'), "хочу", ('гориз' if self.config['textFields']['position'] else 'верт'))
336+
if (type(root.ui) == Ui_MainWindowHorizontal) != self.config['textFields']['position']:
337+
root.ui = (Ui_MainWindowHorizontal if self.config['textFields']['position'] else Ui_MainWindowVertical)()
338+
root.ui.setupUi(root)
339+
initUi(root, self)
338340

339341
#Setting font
340342
root.ui.ptx_SourceText.setStyleSheet(f'font: {config["font"][0]}')
@@ -394,6 +396,18 @@ def handleDialogAcception(self, root: Window) -> None:
394396
self.applyConfig(root)
395397

396398

399+
class CipherLoadingDialog(QDialog):
400+
def __init__(self):
401+
QDialog.__init__()
402+
403+
self.setStyleSheet(styleSheet)
404+
405+
self.label = QLabel(self.tr('Загрузка шифров... Пожалуйста, подождите.'))
406+
self.pgbar = QProgressBar()
407+
408+
self.show()
409+
410+
397411
class Cipher:
398412
'''
399413
A representation of a cipher, allowing to add custom ciphers to the
@@ -479,6 +493,48 @@ def getInformation(self, root: Window) -> str:
479493

480494

481495
#Major functions are defined below
496+
def initUi(root: Window, settings: Settings):
497+
root.ui.cbb_Cipher.setToolTip(ciphers[0].getInformation(root))
498+
499+
checkForUpdates(root)
500+
501+
#Bind functions to actions and buttons
502+
root.ui.tbt_Encrypt.clicked.connect( lambda: encrypt(root, 'utf-8') )
503+
root.ui.tbt_Decrypt.clicked.connect( lambda: decrypt(root, 'utf-8') )
504+
root.ui.btn_Paste.clicked.connect( lambda: pasteSourceText(root) )
505+
root.ui.btn_Copy.clicked.connect( lambda: copyResultText(root) )
506+
root.ui.btn_TransferResToSrc.clicked.connect( lambda: transferText(root, True) )
507+
root.ui.btn_TransferSrcToRes.clicked.connect( lambda: transferText(root, False) )
508+
root.ui.btn_About.clicked.connect( lambda: info(root) )
509+
root.ui.btn_Settings.clicked.connect( settings.open )
510+
root.ui.tbt_OpenFile.clicked.connect( lambda: openFileDialog(root, 'utf-8', True, root.openFileActionGroup.actions()[0].isChecked()) )
511+
root.ui.tbt_SaveToFile.clicked.connect( lambda: openFileDialog(root, 'utf-8', False, root.saveToFileActionGroup.actions()[0].isChecked()) )
512+
root.ui.cbb_Cipher.currentTextChanged.connect( lambda: switchCurrentCipher(root) )
513+
514+
for action in root.openFileActionGroup.actions():
515+
if action.text()[1:7] == 'Source': action.setChecked(True)
516+
517+
for action in root.saveToFileActionGroup.actions():
518+
if action.text()[1:7] == 'Result': action.setChecked(True)
519+
520+
for i in range(0, 4):
521+
for action in root.encodingActions[i]:
522+
if i in range(0, 2): action.triggered.connect( lambda: openFileDialog(root, action.text().lower(), action.data()) )
523+
elif i == 2: action.triggered.connect( lambda: encrypt(root, action.text().lower()) )
524+
elif i == 3: action.triggered.connect( lambda: decrypt(root, action.text().lower()) )
525+
526+
for customEncodingAction in root.customEncodingActions: customEncodingAction.triggered.connect(
527+
lambda: (openFileDialog(root,
528+
inputDialog(root, root.tr('Custom encoding'), root.tr('Specify an encoding...')).lower(), customEncodingAction.data())
529+
if customEncodingAction.data() in range(0, 2) else (customEncodingAction.triggered.connect(
530+
lambda: encrypt(root, inputDialog(root, root.tr('Custom encoding'), root.tr('Specify an encoding...')).lower())
531+
if action.data() == 2 else customEncodingAction.triggered.connect(
532+
lambda: decrypt(root, inputDialog(root, root.tr('Custom encoding'), root.tr('Specify an encoding...')).lower())
533+
)))))
534+
535+
addCiphersToCbbCipher(root)
536+
537+
482538
def checkForUpdates(root: Window):
483539
'''
484540
Check for updates, and if there is one, ask the user
@@ -489,25 +545,28 @@ def checkForUpdates(root: Window):
489545
490546
:returns: None
491547
'''
492-
response = getRequest(root, 'https://api.github.com/repos/StarterCraft/sicrypt/releases/latest').json()
493-
tag = response['tag_name']
494-
if int(tag[1:2]) > int(version[1:2]) or int(tag[3:4]) > int(version[3:4]):
495-
if messageBox(root, QMessageBox.Information,
496-
root.translate('UpdateDialog', 'Update available'),
497-
root.translate('UpdateDialog', f'Update {tag} is available, would you like to download and install it?'),
498-
details = getRequest(root, 'https://raw.githubusercontent.com/StarterCraft/sicrypt/master/changelog.txt').text,
499-
buttons = [QMessageBox.Yes, QMessageBox.No]):
500-
installerURL = getRequest(root, response['assets_url']).json()['browser_download_url']
501-
502-
with requests.get(installerURL, stream = True) as request:
503-
request.raise_for_status()
504-
with open('installer.exe', 'wb') as f:
505-
for chunk in request.iter_content(chunk_size = None):
506-
if chunk: f.write(chunk)
507-
508-
subprocess.Popen('installer.exe', creationflags = subprocess.CREATE_NEW_CONSOLE)
509-
exit(1)
510-
else: return
548+
try:
549+
response = getRequest(root, 'https://api.github.com/repos/StarterCraft/sicrypt/releases/latest').json()
550+
tag = response['tag_name']
551+
if int(tag[1:2]) > int(version[1:2]) or int(tag[3:4]) > int(version[3:4]):
552+
if messageBox(root, QMessageBox.Information,
553+
root.translate('UpdateDialog', 'Update available'),
554+
root.translate('UpdateDialog', f'Update {tag} is available, would you like to download and install it?'),
555+
details = getRequest(root, 'https://raw.githubusercontent.com/StarterCraft/sicrypt/master/changelog.txt').text,
556+
buttons = [QMessageBox.Yes, QMessageBox.No]):
557+
installerURL = getRequest(root, response['assets_url']).json()['browser_download_url']
558+
559+
with requests.get(installerURL, stream = True) as request:
560+
request.raise_for_status()
561+
with open('installer.exe', 'wb') as f:
562+
for chunk in request.iter_content(chunk_size = None):
563+
if chunk: f.write(chunk)
564+
565+
subprocess.Popen('installer.exe', creationflags = subprocess.CREATE_NEW_CONSOLE)
566+
exit(1)
567+
else: return
568+
except KeyError: #github api does not return a valid message
569+
pass
511570

512571

513572
def loadCiphers(root: Window, allowDownloadingNew: bool, allowDownloadingUpdates: bool) -> None:
@@ -527,7 +586,7 @@ def loadCiphers(root: Window, allowDownloadingNew: bool, allowDownloadingUpdates
527586
528587
:returns: None
529588
'''
530-
global ciphers
589+
global ciphers, categories
531590

532591
ciphers, categories, classNames = [], [], {}
533592
available = glob.glob('ciphers/*')
@@ -542,11 +601,15 @@ def loadCiphers(root: Window, allowDownloadingNew: bool, allowDownloadingUpdates
542601
#Find now ciphers on GitHub
543602
if allowDownloadingNew:
544603
availableOnGitHub = []
545-
for file in getRequest(root, 'https://api.github.com/repos/StarterCraft/sicrypt/git/trees/master?recursive=1').json()['tree']:
546-
try:
547-
if (file['path'][:file['path'].index('/')] == 'ciphers' and file['path'].endswith('.py')):
548-
availableOnGitHub.append(file['path'])
549-
except ValueError: continue
604+
605+
try:
606+
for file in getRequest(root, 'https://api.github.com/repos/StarterCraft/sicrypt/git/trees/master?recursive=1').json()['tree']:
607+
try:
608+
if (file['path'][:file['path'].index('/')] == 'ciphers' and file['path'].endswith('.py')):
609+
availableOnGitHub.append(file['path'])
610+
except ValueError: continue
611+
except KeyError:
612+
pass
550613

551614
for file in availableOnGitHub:
552615
if file not in available[:]:
@@ -567,7 +630,17 @@ def loadCiphers(root: Window, allowDownloadingNew: bool, allowDownloadingUpdates
567630
savedNames.append(className)
568631
classNames.update({fileName.replace('/', '.'): savedNames})
569632

633+
loading = QProgressDialog('Downloading ciphers...', 'Abort', 0, len(classNames.items()))
634+
loading.setWindowTitle('Downloading ciphers...')
635+
loading.setWindowModality(Qt.WindowModality.WindowModal)
636+
loading.setValue(0)
637+
loading.show()
638+
print(638)
639+
cix = 0
640+
570641
for classFile, classNames in classNames.items():
642+
loading.setValue(cix)
643+
571644
for className in classNames:
572645
_class = getattr(importlib.import_module(classFile[:-3]), className)
573646
try:
@@ -583,14 +656,15 @@ def loadCiphers(root: Window, allowDownloadingNew: bool, allowDownloadingUpdates
583656
response = getRequest(root, f'https://raw.githubusercontent.com/StarterCraft/sicrypt/master/{classFile.replace(".", "/")}')
584657
if response == 200:
585658
file.write(response.text)
659+
586660
_compareClass = getattr(importlib.import_module('ciphers.dl'), className)
587661
if _compareClass.version == _class.version: pass
588662
elif _compareClass.version[0] > _class.version[0] or _compareClass.version[1] > _class.version[1]:
589663
with open(classFile, 'w') as originalFile: originalFile.write(response.text)
590664
ciphers.append(_compareClass())
591665

592666
ciphers.append(_class())
593-
os.system('del ciphers\\dl.cache')
667+
os.remove('ciphers/dl.cache')
594668

595669
else: messageBox(root, QMessageBox.Critical,
596670
root.translate('CipherLoadingErrorMessagebox', f'Failed to load cipher {className}'),
@@ -600,8 +674,17 @@ def loadCiphers(root: Window, allowDownloadingNew: bool, allowDownloadingUpdates
600674
messageBox(root, QMessageBox.Critical, root.translate('CipherLoadingErrorMessagebox', f'Failed to load cipher {className}'),
601675
root.translate('CipherLoadingErrorMessagebox',
602676
f'Cipher {className}, declared in {classFile}, failed to load due to {type(exception).__name__}. See the details below'),
603-
details = f'Our GitHub:{gitHubLink}\n{traceback.format_exc()}')
677+
details = f'Our GitHub: {gitHubLink}\n{traceback.format_exc()}')
678+
679+
cix += 1
680+
681+
if (loading.wasCanceled()):
682+
return
683+
684+
loading.hide()
604685

686+
687+
def addCiphersToCbbCipher(root: Window):
605688
for cipher in ciphers:
606689
if cipher.category not in categories: categories.append(cipher.category)
607690

@@ -749,7 +832,7 @@ def pasteSourceText(root: Window) -> None:
749832
750833
:returns: None
751834
'''
752-
root.ui.plainTextEdit.setPlainText(str(pyperclip.paste()))
835+
root.ui.ptx_SourceText.setPlainText(str(pyperclip.paste()))
753836

754837

755838
def info(root: Window) -> None:
@@ -950,7 +1033,7 @@ def getRequest(root: Window, url: str):
9501033
details = f'Our GitHub:{gitHubLink}\n\n{traceback.format_exc()}')
9511034

9521035

953-
if __name__ == '__main__':
1036+
def main():
9541037
app = QApplication(sys.argv)
9551038
app.setStyleSheet(appStyleSheet)
9561039

@@ -965,43 +1048,10 @@ def getRequest(root: Window, url: str):
9651048
try:
9661049
#Load ciphers
9671050
loadCiphers(root, settings.config['encryption']['dlnew'], settings.config['encryption']['dlupd'])
968-
root.ui.cbb_Cipher.setToolTip(ciphers[0].getInformation(root))
969-
1051+
9701052
checkForUpdates(root)
9711053

972-
#Bind functions to actions and buttons
973-
root.ui.tbt_Encrypt.clicked.connect( lambda: encrypt(root, 'utf-8') )
974-
root.ui.tbt_Decrypt.clicked.connect( lambda: decrypt(root, 'utf-8') )
975-
root.ui.btn_Paste.clicked.connect( lambda: pasteSourceText(root) )
976-
root.ui.btn_Copy.clicked.connect( lambda: copyResultText(root) )
977-
root.ui.btn_TransferResToSrc.clicked.connect( lambda: transferText(root, True) )
978-
root.ui.btn_TransferSrcToRes.clicked.connect( lambda: transferText(root, False) )
979-
root.ui.btn_About.clicked.connect( lambda: info(root) )
980-
root.ui.btn_Settings.clicked.connect( settings.open )
981-
root.ui.tbt_OpenFile.clicked.connect( lambda: openFileDialog(root, 'utf-8', True, root.openFileActionGroup.actions()[0].isChecked()) )
982-
root.ui.tbt_SaveToFile.clicked.connect( lambda: openFileDialog(root, 'utf-8', False, root.saveToFileActionGroup.actions()[0].isChecked()) )
983-
root.ui.cbb_Cipher.currentTextChanged.connect( lambda: switchCurrentCipher(root) )
984-
985-
for action in root.openFileActionGroup.actions():
986-
if action.text()[1:7] == 'Source': action.setChecked(True)
987-
988-
for action in root.saveToFileActionGroup.actions():
989-
if action.text()[1:7] == 'Result': action.setChecked(True)
990-
991-
for i in range(0, 4):
992-
for action in root.encodingActions[i]:
993-
if i in range(0, 2): action.triggered.connect( lambda: openFileDialog(root, action.text().lower(), action.data()) )
994-
elif i == 2: action.triggered.connect( lambda: encrypt(root, action.text().lower()) )
995-
elif i == 3: action.triggered.connect( lambda: decrypt(root, action.text().lower()) )
996-
997-
for customEncodingAction in root.customEncodingActions: customEncodingAction.triggered.connect(
998-
lambda: (openFileDialog(root,
999-
inputDialog(root, root.tr('Custom encoding'), root.tr('Specify an encoding...')).lower(), customEncodingAction.data())
1000-
if customEncodingAction.data() in range(0, 2) else (customEncodingAction.triggered.connect(
1001-
lambda: encrypt(root, inputDialog(root, root.tr('Custom encoding'), root.tr('Specify an encoding...')).lower())
1002-
if action.data() == 2 else customEncodingAction.triggered.connect(
1003-
lambda: decrypt(root, inputDialog(root, root.tr('Custom encoding'), root.tr('Specify an encoding...')).lower())
1004-
)))))
1054+
initUi(root, settings)
10051055

10061056
#Settings dialog
10071057
settings.accepted.connect( lambda: settings.handleDialogAcception(root) )
@@ -1022,3 +1072,6 @@ def getRequest(root: Window, url: str):
10221072
'have fixed this problem yourself, huge thanks to you, please submit a pull request with the code files '
10231073
'you have changed.'),
10241074
details = f'Our GitHub: {gitHubLink}\n\n{traceback.format_exc()}')
1075+
1076+
if __name__ == '__main__':
1077+
main()

0 commit comments

Comments
 (0)