измени дизайн приложения и темы чтобы было красивое приложение со стильным минималистичным современным дизайном



import sys
import hashlib
import secrets
import string
import asyncio
import aiohttp
from zxcvbn import zxcvbn
from PyQt5.QtWidgets import (
QApplication, QWidget, QLabel, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout,
QCheckBox, QSpinBox, QTabWidget, QGroupBox, QMessageBox, QMenuBar, QAction,
QTextEdit
)
from PyQt5.QtCore import Qt, QEvent, QThread, pyqtSignal

class Password:
def init(self):
pass

def generate(self, length=12, use_uppercase=True, use_digits=True, use_symbols=True):
characters = string.ascii_lowercase
if use_uppercase:
characters += string.ascii_uppercase
if use_digits:
characters += string.digits
if use_symbols:
characters += string.punctuation

password = ''.join(secrets.choice(characters) for _ in range(length))
return password

def check_strength(self, password):
result = zxcvbn(password)
score = result['score'] # от 0 до 4
feedback = result['feedback']
return score, feedback

async def check_leak(self, password):
sha1pwd = hashlib.sha1(password.encode('utf-8')).hexdigest().upper()
prefix, suffix = sha1pwd[:5], sha1pwd[5:]
url = f'https://api.pwnedpasswords.com/range/{prefix}'

async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
text = await resp.text()

hashes = (line.split(':') for line in text.splitlines())
for h, count in hashes:
if h == suffix:
return int(count)
return 0

class PasswordManagerApp(QWidget):
def init(self):
super().init()
self.password = Password()
self.initUI()

def initUI(self):
self.setWindowTitle('Password Manager')
self.setFixedSize(800, 450) # Соотношение сторон 16:9

self.initMenuBar()

self.tabs = QTabWidget()
self.tabs.addTab(self.createGeneratorTab(), "Генератор паролей")
self.tabs.addTab(self.createCheckerTab(), "Проверка пароля")

mainLayout = QVBoxLayout()
mainLayout.setMenuBar(self.menuBar)
mainLayout.addWidget(self.tabs)

self.setLayout(mainLayout)
self.applyStyle('Fusion')

def initMenuBar(self):
self.menuBar = QMenuBar(self)

themeMenu = self.menuBar.addMenu('Темы')

themes = ['Fusion', 'Windows', 'WindowsVista', 'Macintosh', 'Dark', 'Light']

for theme in themes:
action = QAction(theme, self)
action.triggered.connect(lambda checked, t=theme: self.applyStyle(t))
themeMenu.addAction(action)

def applyStyle(self, themeName):
if themeName == 'Fusion':
QApplication.setStyle(themeName)
self.setStyleSheet('')
elif themeName == 'Dark':
self.setStyleSheet(self.darkTheme())
elif themeName == 'Light':
self.setStyleSheet(self.lightTheme())
else:
QApplication.setStyle(themeName)
self.setStyleSheet('')

def darkTheme(self):
return """
QWidget {
background-color: #2e2e2e;
color: #ffffff;
font-size: 14px;
}
QPushButton {
background-color: #3c3c3c;
border: none;
padding: 5px;
}
QPushButton:hover {
background-color: #555555;
}
QLineEdit, QTextEdit {
background-color: #3c3c3c;
border: 1px solid #5e5e5e;
color: #ffffff;
}
"""

def lightTheme(self):
return """
QWidget {
background-color: #f6f6f6;
color: #000000;
font-size: 14px;
}
QPushButton {
background-color: #e0e0e0;
border: none;
padding: 5px;
}
QPushButton:hover {
background-color: #c8c8c8;
}
QLineEdit, QTextEdit {
background-color: #ffffff;
border: 1px solid #c0c0c0;
color: #000000;
}
"""

def createGeneratorTab(self):
generatorTab = QWidget()
layout = QVBoxLayout()

optionsGroup = QGroupBox("Опции пароля")
optionsLayout = QHBoxLayout()

self.lengthLabel = QLabel("Длина:")
self.lengthSpinBox = QSpinBox()
self.lengthSpinBox.setRange(6, 128)
self.lengthSpinBox.setValue(12)

self.uppercaseCheck = QCheckBox("Включать Заглавные")
self.uppercaseCheck.setChecked(True)
self.digitsCheck = QCheckBox("Включать Цифры")
self.digitsCheck.setChecked(True)
self.symbolsCheck = QCheckBox("Включать Символы")
self.symbolsCheck.setChecked(True)

optionsLayout.addWidget(self.lengthLabel)
optionsLayout.addWidget(self.lengthSpinBox)
optionsLayout.addWidget(self.uppercaseCheck)
optionsLayout.addWidget(self.digitsCheck)
optionsLayout.addWidget(self.symbolsCheck)
optionsGroup.setLayout(optionsLayout)

self.generatedPasswordLabel = QLabel("Сгенерированный пароль:")
self.generatedPasswordLine = QLineEdit()
self.generatedPasswordLine.setReadOnly(True)
self.generatedPasswordLine.setEchoMode(QLineEdit.Password)
self.generatedPasswordLine.installEventFilter(self)

self.generateButton = QPushButton("Сгенерировать")
self.generateButton.clicked.connect(self.generatePassword)

self.copyButton = QPushButton("Скопировать в буфер")
self.copyButton.clicked.connect(lambda: self.copyToClipboard(self.generatedPasswordLine.text()))

layout.addWidget(optionsGroup)
layout.addWidget(self.generatedPasswordLabel)
layout.addWidget(self.generatedPasswordLine)
layout.addWidget(self.generateButton)
layout.addWidget(self.copyButton)
layout.addStretch()

generatorTab.setLayout(layout)
return generatorTab

def createCheckerTab(self):
checkerTab = QWidget()
layout = QVBoxLayout()

self.passwordLabel = QLabel("Введите пароль для проверки:")
self.passwordInput = QLineEdit()
self.passwordInput.setEchoMode(QLineEdit.Password)
self.passwordInput.installEventFilter(self)

self.checkButton = QPushButton("Проверить пароль")
self.checkButton.clicked.connect(self.checkPassword)

self.copyInputButton = QPushButton("Скопировать пароль в буфер")
self.copyInputButton.clicked.connect(lambda: self.copyToClipboard(self.passwordInput.text()))

self.strengthLabel = QLabel("Сложность пароля:")
self.strengthResult = QLabel("")

self.feedbackLabel = QLabel("Рекомендации:")
self.feedbackText = QTextEdit()
self.feedbackText.setReadOnly(True)

self.leakLabel = QLabel("Проверка утечек:")
self.leakResult = QLabel("")
self.leakButton = QPushButton("Проверить утечки")
self.leakButton.clicked.connect(self.checkLeaksAsync)
self.leakButton.setEnabled(False)

layout.addWidget(self.passwordLabel)
layout.addWidget(self.passwordInput)
layout.addWidget(self.copyInputButton)
layout.addWidget(self.checkButton)
layout.addWidget(self.strengthLabel)
layout.addWidget(self.strengthResult)
layout.addWidget(self.feedbackLabel)
layout.addWidget(self.feedbackText)
layout.addWidget(self.leakLabel)
layout.addWidget(self.leakResult)
layout.addWidget(self.leakButton)
layout.addStretch()

checkerTab.setLayout(layout)
return checkerTab

def eventFilter(self, source, event):
if event.type() == QEvent.Enter:
if isinstance(source, QLineEdit):
source.setEchoMode(QLineEdit.Normal)
elif event.type() == QEvent.Leave:
if isinstance(source, QLineEdit):
source.setEchoMode(QLineEdit.Password)
return super(PasswordManagerApp, self).eventFilter(source, event)

def generatePassword(self):
length = self.lengthSpinBox.value()
use_uppercase = self.uppercaseCheck.isChecked()
use_digits = self.digitsCheck.isChecked()
use_symbols = self.symbolsCheck.isChecked()

password = self.password.generate(length=length, use_uppercase=use_uppercase,
use_digits=use_digits, use_symbols=use_symbols)
self.generatedPasswordLine.setText(password)

def copyToClipboard(self, text):
clipboard = QApplication.clipboard()
clipboard.setText(text)
QMessageBox.information(self, "Скопировано", "Пароль скопирован в буфер обмена.")

def checkPassword(self):
password = self.passwordInput.text()
if password == '':
QMessageBox.warning(self, "Ошибка", "Введите пароль для проверки.")
return

score, feedback = self.password.check_strength(password)
self.strengthResult.setText(f"{score}/4")
feedback_text = '\n'.join(feedback.get('suggestions', []))
self.feedbackText.setPlainText(feedback_text)
self.leakButton.setEnabled(True)
self.leakResult.setText("")

def checkLeaksAsync(self):
password = self.passwordInput.text()
if password == '':
QMessageBox.warning(self, "Ошибка", "Введите пароль для проверки.")
return

self.leakButton.setEnabled(False)
self.leakResult.setText("Проверка...")

self.thread = LeakCheckThread(password)
self.thread.leak_found.connect(self.handleLeakResult)
self.thread.error_occurred.connect(self.handleLeakError)
self.thread.start()

def handleLeakResult(self, count):
if count == 0:
self.leakResult.setText("Утечек не обнаружено.")
else:
self.leakResult.setText(f"Пароль найден {count} раз в утечках!")
self.leakButton.setEnabled(True)

def handleLeakError(self, error):
QMessageBox.warning(self, "Ошибка", f"Произошла ошибка при проверке утечек: {error}")
self.leakButton.setEnabled(True)
self.leakResult.setText("")

def closeEvent(self, event):
try:
if self.thread.isRunning():
self.thread.quit()
self.thread.wait()
except AttributeError:
pass
event.accept()

class LeakCheckThread(QThread):
leak_found = pyqtSignal(int)
error_occurred = pyqtSignal(str)

def init(self, password):
super().init()
self.password = password

def run(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
count = loop.run_until_complete(self.checkPasswordLeak(self.password))
self.leak_found.emit(count)
except Exception as e:
self.error_occurred.emit(str(e))

async def checkPasswordLeak(self, password):
sha1pwd = hashlib.sha1(password.encode('utf-8')).hexdigest().upper()
prefix, suffix = sha1pwd[:5], sha1pwd[5:]
url = f'https://api.pwnedpasswords.com/range/{prefix}'

async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
if resp.status != 200:
raise Exception("Не удалось получить данные от сервера.")
text = await resp.text()

hashes = (line.split(':') for line in text.splitlines())
for h, count in hashes:
if h == suffix:
return int(count)
return 0

if name == 'main':
app = QApplication(sys.argv)
main_app = PasswordManagerApp()
main_app.show()
sys.exit(app.exec_())