/
nightflash
/
USBypass
Обзор
Документация
Войти
/
nightflash
/
USBypass
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/GUI_PC_client/main.py
589 строк
22 KB
Дмитрий Гладышев
Две строки в названии ячейки
11 авг 2026, 17:47
11 авг 2026, 17:47
888bf5b
Код
Авторство
О чём код?
import sys import os import json import serial from PySide6.QtCore import Qt, QModelIndex, Signal, QTimer from PySide6.QtGui import QDropEvent from serial.tools import list_ports import time import hashlib from PySide6 import QtWidgets, QtGui, QtCore from PySide6.QtWidgets import QMessageBox, QTableWidgetItem, QDialog, QLineEdit, QTableWidget, QAbstractItemView, \ QTableView import design import editForm import passwordForm DEBUG = True def log(message : str): if DEBUG: print(message) def isWindows(): """Проверяет, под какой ОС запущено приложение. True, если Windows.""" if os.name == "nt": return True else: return False def hashStr(message : str) -> str: """ Хеширование строки с использованием алгоритма SHA-256 :param message: строка для хеширования :return: хеш строки """ hash_obj = hashlib.sha256() hash_obj.update(message.encode('utf-8')) sha256_hash = hash_obj.hexdigest().upper() return sha256_hash class USBypassGUIApp(QtWidgets.QMainWindow, design.Ui_MainWindow): def __init__(self): super().__init__() self.setupUi(self) self.setWindowTitle("USBypass GUI") # Настройки по-умолчанию self.settings = { "savepassword": 0, "port": "COM1", "password": "" } if isWindows(): self.datapath = os.getenv('APPDATA') + "\\USBypass\\" if not os.path.exists(self.datapath): os.mkdir(self.datapath) print("DATAPATH: " + self.datapath) k = 0 self.path = __file__ for i in range(0, len(self.path)): if self.path[i] == "\\" or self.path[i] == "/": k = i self.path = self.path[:k + 1] # Если находимся в папке _internal, то переходим на уровень выше if self.path[-10:] == "_internal\\": self.path = self.path[:-10] print("PATH: " + self.path) self.selectPort.clear() # Получаем список всех доступных портов available_ports = list_ports.comports() if available_ports: print("Найденные COM-порты:") for port in available_ports: # Выводим имя порта и его описание print(f"Порт: {port.device:<15} | Описание: {port.description} {port.hwid}") self.selectPort.addItem(port.device) else: print("COM-порты не найдены.") self.loadSettings() self.connected = False self.ser = None self.slots = [] self.tableWidget = TableWidgetDragRows() self.verticalLayout.addWidget(self.tableWidget) self.tableWidget.setColumnCount(2) self.tableWidget.setHorizontalHeaderLabels(["ID", "Название"]) self.tableWidget.verticalHeader().setVisible(False) self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tableWidget.rowsMoved.connect(self.on_rows_moved) self.connectButton.clicked.connect(self.connectButtonClicked) self.tableWidget.cellDoubleClicked.connect(self.rowDblClicked) self.addButton.clicked.connect(self.addButtonClicked) self.deleteButton.clicked.connect(self.delButtonClicked) self.disconnectButton.clicked.connect(self.disconnectButtonClick) self.changePinButton.clicked.connect(self.changePinButtonClicked) self.changePasswordButton.clicked.connect(self.changePasswordButtonClicked) self.framebar.setVisible(False) self.tableframe.setVisible(False) def __del__(self): self.settings["savepassword"] = 1 if self.keepPassword.isChecked() else 0 self.settings["password"] = self.passwordEdit.text() if self.keepPassword.isChecked() else "" self.settings["port"] = self.selectPort.currentText() self.saveSettings() def saveSettings(self): """Сохранение настроек в файл""" log("Сохранение настроек.") try: with open(self.datapath + 'settings.json', 'w') as f: json.dump(self.settings, f) except Exception as e: log("ОШИБКА: Не удалось сохранить настройки.") QMessageBox.critical(self, "Критическая ошибка", f"Ошибка сохранения файла настроек. {e}.") def loadSettings(self): """Загрузка настроек из файла""" try: with open(self.datapath + 'settings.json') as f: self.settings = json.load(f) except FileNotFoundError: pass except Exception as e: log("Ошибка чтения файла настроек. Возможно нет прав доступа на чтение.") QMessageBox.critical(self, "Критическая ошибка", f"Ошибка чтения файла настроек. {e}.") for index in range(self.selectPort.count()): if self.selectPort.itemText(index) == self.settings["port"]: self.selectPort.setCurrentIndex(index) break if self.settings["savepassword"] == 1: self.keepPassword.setChecked(True) self.passwordEdit.setText(self.settings["password"]) def readDevice(self): self.slots = [] listSlots = self.send_at_command("AT+LIST?") if "+OK" in listSlots[0:3]: listSlots = listSlots[4:] listSlots = list(map(int, listSlots.split(","))) self.tableWidget.setRowCount(len(listSlots)) for i in range(len(listSlots)): ind = listSlots[i] pwd = self.send_at_command(f"AT+NAME{ind}?") if "+OK" in pwd[0:3]: self.slots.append([ind, pwd[4:]]) row = 0 for item in self.slots: #self.tableWidget.setItem(row, 0, QTableWidgetItem(str(row + 1))) self.tableWidget.setItem(row, 0, QTableWidgetItem(str(item[0]))) sname = item[1] if '\t' in sname: k = sname.split('\t') sname=f"{k[0]} | {k[1]}" self.tableWidget.setItem(row, 1, QTableWidgetItem(sname)) row += 1 def on_rows_moved(self): newslots = [] for i in range(self.tableWidget.rowCount()): id = int(self.tableWidget.item(i, 0).text()) for item in self.slots: if item[0] == id: newslots.append(item) break #print(newslots) self.slots = newslots[:] ids = [] for item in self.slots: ids.append(item[0]) idlist = ','.join(map(str, ids)) self.send_at_command(f"AT+LIST={idlist}") def addButtonClicked(self): wnd = editFormApp() wnd.editName.setText("") wnd.editLogin.setText("") wnd.editPassword.setText("") if wnd.exec(): sname = wnd.editName.text() + '\t' + wnd.editName_2.text() slogin = wnd.editLogin.text() spassword = wnd.editPassword.text() if slogin != "": spassword = slogin + '\t' + spassword ids = [] for item in self.slots: ids.append(item[0]) # Ищем первый свободный слот for i in range(1, 64): if i not in ids: self.slots.append((i, sname)) ids.append(i) idlist = ','.join(map(str, ids)) self.send_at_command(f"AT+NAME{i}={sname}") self.send_at_command(f"AT+SLOT{i}={spassword}") self.send_at_command(f"AT+LIST={idlist}") if '\t' in sname: k = sname.split('\t') sname = f"{k[0]} | {k[1]}" self.tableWidget.setRowCount(self.tableWidget.rowCount() + 1) #self.tableWidget.setItem(self.tableWidget.rowCount() - 1, 0, QTableWidgetItem(f"{self.tableWidget.rowCount()}")) self.tableWidget.setItem(self.tableWidget.rowCount() - 1, 0, QTableWidgetItem(f"{i}")) self.tableWidget.setItem(self.tableWidget.rowCount() - 1, 1, QTableWidgetItem(f"{sname}")) break def delButtonClicked(self): msg = QMessageBox() msg.setIcon(QMessageBox.Question) msg.setText("Удалить выделенный пароль?") msg.setInformativeText("Вы уверены?") msg.setWindowTitle("Вопрос") msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) if msg.exec() != QMessageBox.Ok: return row = None if self.tableWidget.selectedItems(): for item in self.tableWidget.selectedItems(): row = item.row() break if row is not None: item = self.slots[row] id = item[0] self.slots.pop(row) ids = [] for item in self.slots: ids.append(item[0]) idlist = ','.join(map(str, ids)) self.send_at_command(f"AT+NAME{id}=") self.send_at_command(f"AT+SLOT{id}=") self.send_at_command(f"AT+LIST={idlist}") def rowDblClicked(self, row): item = self.slots[row] id = item[0] wnd = editFormApp() if '\t' in item[1]: sname = item[1].split('\t') wnd.editName.setText(sname[0]) wnd.editName_2.setText(sname[1]) else: wnd.editName.setText(item[1]) wnd.editName_2.setText("") s = self.send_at_command(f"AT+SLOT{item[0]}?") if "+OK" in s[0:4]: s = s[4:] if '\t' in s: slogin, spassword = s.split('\t') wnd.editLogin.setText(slogin) wnd.editPassword.setText(spassword) else: wnd.editLogin.setText("") wnd.editPassword.setText(s) if wnd.exec(): sname = wnd.editName.text() + '\t' + wnd.editName_2.text() slogin = wnd.editLogin.text() spassword = wnd.editPassword.text() if slogin != "": spassword = slogin + '\t' + spassword self.slots[row][1] = sname self.send_at_command(f"AT+NAME{id}={sname}") self.send_at_command(f"AT+SLOT{id}={spassword}") if '\t' in sname: k = sname.split('\t') sname=f"{k[0]} | {k[1]}" self.tableWidget.setItem(row, 1, QTableWidgetItem(sname)) def connectButtonClicked(self): if self.selectPort.currentText(): try: self.ser = serial.Serial(self.selectPort.currentText(), 115200, timeout=1) except Exception as e: QMessageBox.critical(self, "Ошибка", f"Возникла ошибка: {e}") return time.sleep(1) hashp = hashStr(self.passwordEdit.text()) if self.ser.is_open: if self.send_at_command(f"AT+AUTH={hashp}") == "+OK": self.connected = True self.framebar.setVisible(True) self.tableframe.setVisible(True) self.groupBox.setVisible(False) else: self.disconnect() QMessageBox.critical(self, "Ошибка", f"Ошибка атентификации. Неверный пароль доступа.") return self.readDevice() def disconnectButtonClick(self): self.connected = False self.framebar.setVisible(False) self.tableframe.setVisible(False) self.groupBox.setVisible(True) self.disconnect() def changePinButtonClicked(self): wnd = passwordFormApp(1) wnd.editPassword.setText("") wnd.editPassword_2.setText("") if wnd.exec(): pwd = wnd.editPassword.text() self.send_at_command(f"AT+PIN={pwd}") def changePasswordButtonClicked(self): wnd = passwordFormApp(0) wnd.editPassword.setText("") wnd.editPassword_2.setText("") if wnd.exec(): pwd = wnd.editPassword.text() hashp = hashStr(pwd) self.send_at_command(f"AT+PWD={hashp}") def send_at_command(self,cmd): log(f"-> {cmd}") self.ser.write((cmd + "\r\n").encode()) response = self.ser.readline().decode().strip() log(f"<- {response}") return response def disconnect(self): self.connected = False try: self.ser.close() except: pass class editFormApp(QDialog, editForm.Ui_Dialog): def __init__(self): super().__init__() self.setupUi(self) self.buttonBox.accepted.disconnect(self.accept) self.buttonBox.accepted.connect(self.formaccept) self.hideButton.clicked.connect(self.hideButtonClicked) self.iconHiden = QtGui.QIcon() self.iconUnhiden = QtGui.QIcon() self.iconHiden.addPixmap(QtGui.QPixmap("images/eye.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.iconUnhiden.addPixmap(QtGui.QPixmap("images/eye-slash.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.hideButton.setIcon(self.iconHiden) self.hideButton.setIconSize(QtCore.QSize(20, 20)) def formaccept(self): if self.editName.text() == "": QMessageBox.warning(self, "Warning", "Название не может быть пустым") return if len(self.editName.text()) > 10: QMessageBox.warning(self, "Warning", "Название не может быть больше 10 символов") return if len(self.editName_2.text()) > 10: QMessageBox.warning(self, "Warning", "Вторая строка не может быть больше 10 символов") return self.accept() def hideButtonClicked(self): if self.editPassword.echoMode() == QLineEdit.Password: self.editPassword.setEchoMode(QLineEdit.Normal) self.hideButton.setIcon(self.iconUnhiden) else: self.editPassword.setEchoMode(QLineEdit.Password) self.hideButton.setIcon(self.iconHiden) class passwordFormApp(QDialog, passwordForm.Ui_Dialog): def __init__(self, mode): """ :param mode: 0 - режим пароля, 1 - режим пин-кода """ super().__init__() self.setupUi(self) self.buttonBox.accepted.disconnect(self.accept) self.buttonBox.accepted.connect(self.formaccept) self.mode = mode def formaccept(self): if self.editPassword.text() == "": QMessageBox.warning(self, "Warning", "Пароль не может быть пустым") return if self.editPassword.text() != self.editPassword_2.text(): QMessageBox.warning(self, "Warning", "Пароли не совпадают") return if self.mode == 1: if len(self.editPassword.text()) != 4: QMessageBox.warning(self, "Warning", "Длина пин-кода должна составлять 4 символа") return s = self.editPassword.text() try: v = int(s) except ValueError: QMessageBox.warning(self, "Warning", "Пин-код должен состоять только из 4-х цифр") return else: if v < 0: QMessageBox.warning(self, "Warning", "Пин-код должен состоять только из 4-х цифр") return self.accept() class TableWidgetDragRows(QTableWidget): """ Подкласс QTableWidget, позволяющий перетаскивать строки для изменения их порядка. https://stackoverflow.com/questions/26227885/drag-and-drop-rows-within-qtablewidget """ rowsMoved = Signal() def __init__(self, *args, **kwargs): QTableWidget.__init__(self, *args, **kwargs) self.setDragEnabled(True) self.setAcceptDrops(True) self.viewport().setAcceptDrops(True) self.setDragDropOverwriteMode(False) self.setDropIndicatorShown(True) self.setSelectionMode(QAbstractItemView.SingleSelection) self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setDragDropMode(QAbstractItemView.InternalMove) def dropEvent(self, event): if event.source() == self and (event.dropAction() == Qt.MoveAction or self.dragDropMode() == QAbstractItemView.InternalMove): success, row, col, topIndex = self.dropOn(event) if success: selRows = self.getSelectedRowsFast() top = selRows[0] # print 'top is %d'%top dropRow = row if dropRow == -1: dropRow = self.rowCount() # print 'dropRow is %d'%dropRow offset = dropRow - top # print 'offset is %d'%offset for i, row in enumerate(selRows): r = row + offset if r > self.rowCount() or r < 0: r = 0 self.insertRow(r) # print 'inserting row at %d'%r selRows = self.getSelectedRowsFast() # print 'selected rows: %s'%selRows top = selRows[0] # print 'top is %d'%top offset = dropRow - top # print 'offset is %d'%offset for i, row in enumerate(selRows): r = row + offset if r > self.rowCount() or r < 0: r = 0 for j in range(self.columnCount()): # print 'source is (%d, %d)'%(row, j) # print 'item text: %s'%self.item(row,j).text() source = QTableWidgetItem(self.item(row, j)) # print 'dest is (%d, %d)'%(r,j) self.setItem(r, j, source) # Why does this NOT need to be here? # for row in reversed(selRows): # self.removeRow(row) event.accept() QTimer.singleShot(0, lambda: self.rowsMoved.emit()) else: QTableView.dropEvent(event) def getSelectedRowsFast(self): selRows = [] for item in self.selectedItems(): if item.row() not in selRows: selRows.append(item.row()) return selRows def droppingOnItself(self, event, index): dropAction = event.dropAction() if self.dragDropMode() == QAbstractItemView.InternalMove: dropAction = Qt.MoveAction if event.source() == self and event.possibleActions() & Qt.MoveAction and dropAction == Qt.MoveAction: selectedIndexes = self.selectedIndexes() child = index while child.isValid() and child != self.rootIndex(): if child in selectedIndexes: return True child = child.parent() return False def dropOn(self, event): if event.isAccepted(): return False, None, None, None index = QModelIndex() row = -1 col = -1 if self.viewport().rect().contains(event.position().toPoint()): index = self.indexAt(event.position().toPoint()) if not index.isValid() or not self.visualRect(index).contains(event.position().toPoint()): index = self.rootIndex() if self.model().supportedDropActions() & event.dropAction(): if index != self.rootIndex(): dropIndicatorPosition = self.position(event.position().toPoint(), self.visualRect(index), index) if dropIndicatorPosition == QAbstractItemView.AboveItem: row = index.row() col = index.column() # index = index.parent() elif dropIndicatorPosition == QAbstractItemView.BelowItem: row = index.row() + 1 col = index.column() # index = index.parent() else: row = index.row() col = index.column() if not self.droppingOnItself(event, index): # print 'row is %d'%row # print 'col is %d'%col return True, row, col, index return False, None, None, None def position(self, pos, rect, index): r = QAbstractItemView.OnViewport margin = 2 if pos.y() - rect.top() < margin: r = QAbstractItemView.AboveItem elif rect.bottom() - pos.y() < margin: r = QAbstractItemView.BelowItem elif rect.contains(pos, True): r = QAbstractItemView.OnItem if r == QAbstractItemView.OnItem and not (self.model().flags(index) & Qt.ItemIsDropEnabled): r = QAbstractItemView.AboveItem if pos.y() < rect.center().y() else QAbstractItemView.BelowItem return r def main(): app = QtWidgets.QApplication(sys.argv) # Новый экземпляр QApplication window = USBypassGUIApp() # Создаём объект класса ExampleApp window.show() # Показываем окно app.exec() # и запускаем приложение if __name__ == '__main__': main()