Как написать пароль на питоне

Yet another one-liner:

python -c "import random;print(''.join([random.choice(random.choice([['a','e','f','g','h','m','n','t','y'],['A','B','E','F','G','H','J','K','L','M','N','Q','R','T','X','Y'],['2','3','4','5','6','7','8','9'],['/','*','+','~','@','#','%','^','&','//']])) for i in range(16)]));"

Samples:

L+f##Q~H88NBe6Ny

&7@M7gt4J^///gH3e

5e2455hgn2h^//ffh

Advantage:

Change char list explicitly, exclude similar chars(etc. i 1 l I, 0 o O).(Just add/del in the list)

Change password length from 16 to whatever you want.

Disadvantage:

Readability.

PS. readable version:

python -c "import random;
print(''.join([random.choice(random.choice(
[
 ['a','e','f','g','h','m','n','t','y'],
 ['A','B','E','F','G','H','J','K','L','M','N','Q','R','T','X','Y'],
 ['2','3','4','5','6','7','8','9'],
 ['/','*','+','~','@','#','%','^','&','//']
])) 
for i in range(16)]));"

Are you getting started with Python? Learning how to create a password generator can be the perfect project to learn or review basic Python concepts.

To create a password generator in Python you can use a for loop that randomly selects alphanumeric characters, digits, and punctuation characters to generate the password string. You can set the password length that defines the number of loop iterations. Also, by using a nested for loop you can improve the password generator to generate multiple passwords.

We will start with a simple password generator and then we will refactor the code to make it more flexible.

Are you ready?

We will start by generating a random string of 12 characters. To do that we will use the function random.choice() that returns a random character from a sequence.

We will do the following:

  1. Import the random module.
  2. Set the password length to 12.
  3. Define a list of characters that we will use to generate the random password. In this first version of the program, we will use just a few letters and numbers.
  4. Create an empty string called password.
  5. Write a for loop that executes 12 iterations and that at every iteration selects a random character from the string characters and appends it to the password string.
  6. Print the password we have generated.
import random

password_length = 12

characters = "abcde12345"

password = ""   

for index in range(password_length):
    password = password + random.choice(characters)

print("Password generated: {}".format(password))

And here is an example of a password generated with this code:

Password generated: dcb4a2c4aac5

As you can see the password is not strong considering that we have used a limited number of characters and numbers.

In the next section, we will make it more secure.

How to Generate a Password Using All Alphanumeric Characters

Let’s improve the complexity of the password by using all alphanumeric characters.

To do that we could simply update the value of the characters string to include all letters and numbers but it would be time-consuming and prone to errors.

What can we do instead?

We can import the Python string module that provides a few constants that we can use in the generation of a password.

Here are a few examples:

>>> import string
>>> string.ascii_lowercase
'abcdefghijklmnopqrstuvwxyz'
>>> string.ascii_uppercase
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> string.ascii_letters
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> string.digits
'0123456789'
>>> string.punctuation
'!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~'

We won’t use string.ascii_lowercase or string.ascii_uppercase considering that they are both included instring.ascii_letters.

In our password generator we will use the following three sets of characters:

  1. string.ascii_letters
  2. string.digits
  3. string.punctuation

We will use the + symbol to concatenate the three sets of characters to create a single string that we will assign to the characters variable.

This is what you get if you concatenate the three sets of characters in the Python shell:

>>> string.ascii_letters + string.digits + string.punctuation
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~'

Let’s update our program…

import random, string

password_length = 12

characters = string.ascii_letters + string.digits + string.punctuation

password = ""   

for index in range(password_length):
    password = password + random.choice(characters)

print("Password generated: {}".format(password))

Here are three examples of passwords generated with the updated program:

Password generated: iE%g.JqurkB0
Password generated: |>J+qbZ<Vl7$
Password generated: c94,JRgshz#g

Update the Password Generator to Receive the Password Length as User Input

Let’s make our password generator a bit more flexible.

We will allow the user to provide the password length instead of hardcoding it in our program. To do that we will use the input function.

import random, string

password_length = int(input("Provide the password length: "))

characters = string.ascii_letters + string.digits + string.punctuation

password = ""   

for index in range(password_length):
    password = password + random.choice(characters)

print("Password generated: {}".format(password))

Notice that we have converted the value returned by the input function into an integer considering that when using Python 3 the input function returns a string.

The program works fine.

Confirm it works on your machine too before continuing with this tutorial.

Provide the password length: 12
Password generated: ]"c_ga%M^iOd

If you don’t convert the output of the input function into an integer you get the following error when you use the variable password_length in the for loop.

Provide the password length: 12
Traceback (most recent call last):
  File "password_generator.py", line 9, in <module>
    for index in range(password_length):
TypeError: 'str' object cannot be interpreted as an integer

How to Create a Python Password Generator that Generates Multiple Passwords

In this section, we will enhance our password generator to generate a custom number of passwords.

The approach will be very similar to the one we have used to get the password length from the user with the input function.

We will do the following:

  1. Get the number_of passwords using the input function.
  2. Add a for loop to generate multiple passwords based on the value of the variable number_of passwords provided by the user.
import random, string

number_of_passwords = int(input("How many passwords do you want to generate? "))
password_length = int(input("Provide the password length: "))

characters = string.ascii_letters + string.digits + string.punctuation

for password_index in range(number_of_passwords):
    password = ""   

    for index in range(password_length):
        password = password + random.choice(characters)

    print("Password {} generated: {}".format(password_index, password))

Note: make sure you use the correct indentation as shown in the code above.

Let’s test our code:

How many passwords do you want to generate? 3
Provide the password length: 8
Password 0 generated: 2B.1&=~k
Password 1 generated: Wt$@1vi'
Password 2 generated: ,aOXN_@$

It works, nice!

How to Generate Strong Passwords in Python

Before completing this tutorial let’s find out how we can enforce a set of rules to make sure the passwords we generate are strong enough.

We will make sure passwords contain at least:

  • three digits
  • two punctuation characters

To do that we will use two integers that define the number of digits and punctuation characters. Then we will use multiple nested for loops.

import random, string

number_of_digits = 3
number_of_punctuation_characters = 2
characters = string.ascii_letters + string.digits + string.punctuation

number_of_passwords = int(input("How many passwords do you want to generate? "))
password_length = int(input("Provide the password length: "))

for password_index in range(number_of_passwords):
    password = ""

    for digits_index in range(number_of_digits):
        password = password + random.choice(string.digits)

    for punctuation_index in range(number_of_punctuation_characters):
        password = password + random.choice(string.punctuation)

    for index in range(password_length - number_of_digits - number_of_punctuation_characters):
        password = password + random.choice(string.ascii_letters)

    print("Password {} generated: {}".format(password_index, password))

Let’s have a look at a few passwords generated with this program:

How many passwords do you want to generate? 3
Provide the password length: 10
Password 0 generated: 738<>ZKwMA
Password 1 generated: 332|(SlZDT
Password 2 generated: 756%#NFWHs

They look fine but to make them stronger we have to avoid having digits and punctuation characters always in the same position of the password string.

To shuffle characters in the password string we will use the random.shuffle() function.

We will create a specific function that does the shuffling.

def randomize_password(password):
    password_list = list(password)
    random.shuffle(password_list)
    return "".join(password_list)

This function converts the password string into a list before applying random.shuffle. Then it returns a string using the string join method.

Then update the last print statement to call therandomize_password function.

print("Password {} generated: {}".format(password_index, randomize_password(password)))

And the output is…

How many passwords do you want to generate? 3
Provide the password length: 10
Password 0 generated: X1+dT)4U1q
Password 1 generated: 2T7g+OQ-B4
Password 2 generated: g3n0<3O>if

The password is a lot stronger now that digits and punctuation characters are in random positions.

In the video below I cover what we went through in this tutorial:

Conclusion

In this tutorial, you have learned how to create a password generator in Python and how to update its logic to increase the strength of the passwords.

We have covered a few Python core concepts:

  • importing modules.
  • defining functions.
  • using the input function to read user input.
  • nested for loops.
  • selecting random characters from a string and shuffling a string.
  • using the string format method.

Well done for completing this tutorial!

Related posts:

I’m a Tech Lead, Software Engineer and Programming Coach. I want to help you in your journey to become a Super Developer!

При получении доступа к информационным ресурсам, особенно в условиях удаленной работы нам нередко приходится сталкиваться с придумыванием надежного пароля. А если ресурсов много (корпоративные, публичные, частные и т.п.), и по требованиям кибербезопасности пароли к разным информационным ресурсам тоже должны быть разными, как избежать их повторяемости и избавиться от муки креативности в данном вопросе?

Предлагаем поручить задачу генерации надежного пароля программе на языке Python (пусть у питона голова болит))).

Условия, предъявляемые различными ресурсами, к надежности пароля чаще всего следующие:

  • Пароль содержит не менее 12 символов
  • Пароль содержит хотя бы одну заглавную букву
  • Пароль содержит хотя бы одну строчную букву
  • Пароль содержит хотя бы одну цифру
  • Пароль содержит хотя бы один спецсимвол

Вот как это можно реализовать с помощью Python:

# Импорт модуля random
import random
# Создаем функцию pass_gen
def pass_gen(length):
digits=’1234567890′
leters=’ABCDEFGHIJKLMNOPQRSTUVWXYZ’
leters_2=’abcdefghijklmnopqrstuvwxyz’
symbols='[email protected]#$%^&*()-+’
password=»
var=[digits,leters,leters_2,symbols]
# Генерация пароля
if length<12:
return print(‘Ошибка! Пароль должен иметь не менее 12 символов’)
else:
password+=random.choice(digits)
password+=random.choice(leters)
password+=random.choice(leters_2)
password+=random.choice(symbols)
while len(password)<length:
password+=random.choice(var[random.randint(0,3)])
print(password)
pass_gen(12)

И при каждом запуске кода, вы будете получать уникальный пароль, соответствующий всем необходимым требованиям.

Время на прочтение
16 мин

Количество просмотров 21K

Штош. В этой статье я научу вас делать кроссплатформенное приложение генератор паролей с графическим интерфейсом. Мы будем использовать язык Python и библиотеку PySide6 — привязку к инструментарию фреймворка Qt.

Приложение умеет генерировать пароли, скрывать и копировать их в буфер обмена. Длина задается с помощью слайдера и счетчика. Пул допустимых символов меняется кнопками. Сила пароля рассчитывается по информационной энтропии.

В статье я постарался затронуть все моменты создания и сборки приложения. Ознакомиться с проектом можно на GitHub.

Устанавливаем все необходимое

Скачайте и установите Python последней версии (желательно). Приложение должно работать с Python версии 3.7+

Создадим директорию проекта password-generator и виртуальное окружение, в моем случае venvPasswordGenerator

Установим библиотеку PySide6.

pip install pyside6

Документация Qt for Python.

Создаем интерфейс

Переходим к созданию интерфейса. Для этого нам понадобится программа Qt Designer. Её можно найти в папке установленной ранее библиотеки.

venv*/Lib/site-packages/PySide6/designer

Окно новой формы в программе Qt Designer

Окно новой формы в программе Qt Designer

Создаем MainWindow. Убираем menubar и statusbar

Сразу сохраняем файл интерфейса. Я называю его main.ui, потому что в будущем я могу захотеть сделать отдельную форму для настроек или еще чего-то.

Сначала закинем 4 Horizontal Layout для компоновки элементов. Скопировать элемент можно перетаскиванием с зажатой клавишей Ctrl.

Выберем вертикальное расположение для центрального виджета. centralwidget -> Lay Out Vertically

Компоновка выбора символов

Закинем одну кнопку выбора символов в нижнюю горизонтальную компоновку.

Чтобы кнопка имела 2 состояния, нужно поставить свойство checkable

Скопируем кнопку 3 раза. Поставим текст. Я думаю, так должно быть понятно, за какой набор символов отвечает каждая отдельная кнопка.

Дадим элементам осмысленные имена.

Проставим свойство checked на всех символах, кроме специальных.

Компоновка длины пароля

Здесь будет указываться длина пароля с помощью слайдера и счетчика. Компоновку я назову layout_length, слайдер — slider_length, счетчик — spinbox_length

Компоновка энтропии и сложности пароля

Закидываем лейбл.

Ставим горизонтальное выравнивание по центру.

Копируем элемент и вводим примерный текст.

Компоновка пароля

Перед нами встает интересная задача. Как поместить кнопку видимости пароля в элемент Line Edit? К сожалению, встроенных в Qt Designer методов для такого действия я не нашел. Лучшим решением я посчитал поместить поле и кнопку рядом во фрейме. Если вы знаете способ лучше, поделитесь в комментариях.

Widget Box -> Containers -> Frame

Поставим горизонтальное выравнивание для фрейма.

Добавим 2 кнопки в горизонтальную компоновку, а не во фрейм. Перетаскивать элемент нужно в самый правый край компоновки.

Еще я захотел поставить сверху изображение замка. На удивление, в Qt Designer это тоже реализовано через одно место, в котором никто не хочет побывать. Есть способ с помощью лейбла и его свойства pixmap, но в таком случае нельзя изменять размер изображения. Поэтому я решил сделать через иконку заблокированной кнопки.

Иконки Material Icons

Возьмем иконки для приложения. Я буду использовать бесплатные Material Icons.

Скачаем белую векторную иконку замка. Я выберу первый вид — Outlined. Вы можете взять другой, как вам приятнее.

Для повторной генерации возьмем Refresh. Копирование в буфер обмена — Content Copy. Видимость пароля — Visibility и Visibility Off. Для иконки приложения возьмем черную иконку ключа в формате png и сконвертируем её в формат для иконок ico

Иконки в Windows File Explorer

Иконки в Windows File Explorer

Создаем файл ресурсов

Создадим файл ресурсов, который будет хранить иконки. Позже мы сконвертируем его в Python код. Resource Browser -> Edit Resources -> New Resource File -> resources.qrc

Добавляем префикс icons

Добавляем все файлы иконок.

Проставляем иконки

В заблокированную кнопку ставим иконку замка с помощью свойства icon -> Disabled On. Выбираем иконку с помощью Choose Resource

Для кнопки видимости выбираем свойство checkable, так как она тоже имеет 2 состояния. Сразу проставим checked по умолчанию.

В Normal Off берем иконку невидимости, в Normal On — видимости.

Остальные иконки проставляем в Normal On

Стилизуем интерфейс

Для стилизации приложения я буду использовать урезанный язык CSS. Писать советую в каком-нибудь редакторе. Я пишу в Visual Studio Code, конечно же.

Главное окно

Создадим файл QMainWindow.css. Для главного виджета поставим цвет фона #121212, белый цвет текста, шрифт Verdana с размером 16 поинтов и внешний отступ 10 пикселей.

QWidget {
    background-color: #121212;
    color: white;
    font-family: Verdana;
    font-size: 16pt;
    margin: 10px;
}

Для кнопок ставим сплошную серую границу 2 пикселя с радиусом границы 5 пикселей.

QPushButton {
    border: 2px solid gray;
    border-radius: 5px;
}

Вставим код в элемент MainWindow с помощью опции Change styleSheet

Отдельно для кнопок символов я поставлю внутренний отступ 10 пикселей, текст располагается слишком близко к границам.

QPushButton#btn_lower,
#btn_upper,
#btn_digits,
#btn_special {
    padding: 10px;
}

При наведении на кнопку цвет границ будет меняться на зеленый #090.

QPushButton:hover {
    border-color: #090;
}

При нажатии граница будет увеличиваться до 4 пикселей.

QPushButton:pressed {
    border: 4px solid #090;
    border-radius: 5px;
}

Для отмеченного состояния кнопки поставим на фон темно-зеленый цвет #006300.

QPushButton:checked {
    background-color: #006300;
    border-color: #090;
}

QMainWindow.css

QWidget {
    background-color: #121212;
    color: white;
    font-family: Verdana;
    font-size: 16pt;
    margin: 10px;
}

QPushButton {
    border: 2px solid gray;
    border-radius: 5px;
}

QPushButton#btn_lower,
#btn_upper,
#btn_digits,
#btn_special {
    padding: 10px;
}

QPushButton:hover {
    border-color: #090;
}

QPushButton:pressed {
    border: 4px solid #090;
    border-radius: 5px;
}

QPushButton:checked {
    background-color: #006300;
    border-color: #090;
}

Чтобы посмотреть превью интерфейса, нужно нажать сочетание клавиш Ctrl + R

Иконка замка

Уберем границы с помощью стиля border: none;

Поставим размер 100 на 100 пикселей.

Фрейм пароля

Поставим для фрейма пароля такую же границу, как и для кнопок. Уберем отступ до правого элемента.

QFrame {
    border: 2px solid gray;
    border-radius: 5px;
    margin-right: 0;
}

QFrame:hover {
    border-color: #090;
}

Поставим вертикальную политику Maximum

Поле пароля

Для поля пароля уберем границы и внешние отступы. Поставим размер шрифта 20 поинтов.

QLineEdit {
    border: none;
    margin: 0;
    font-size: 20pt;
}

Кнопка видимости

Для кнопки видимости пароля так же уберем границы и внешние отступы. Еще поставим прозрачный фон и размер 30 на 30 пикселей.

QPushButton {
    border: none;
    margin: 0;
    background-color: transparent;
}

Кнопка генерации пароля

Для кнопки генерации пароля поставим размер иконки 52 на 52 пикселя. Так границы кнопки будут идти ровно по границам фрейма. Уберем правый и левый внешний отступ.

QPushButton {
    margin-right: 0;
    margin-left: 0;
}

Кнопка копирования пароля в буфер обмена

Почему-то иконка копирования очень плотно прилегает к границам. Я нашел размер 42 на 42 пикселя и внутренний отступ 5 пикселей. Уберем отступ до левого элемента.

QPushButton {
    padding: 5px;
    margin-left: 0;
}

Слайдер

Для псевдоэлемента groove уберем цвет и поставим высоту 5 пикселей. Грув — это линия слайдера, или «желобок», «канавка», «борозда», если верить гугл переводчику.

QSlider::groove:horizontal {
    background-color: transparent;
    height: 5px;
}

Слева от ручки слайдера будет зеленый цвет. Для этого используется селектор псевдоэлемента sub-page

QSlider::sub-page:horizontal {
    background-color: #090;
}

Справа от ручки будет серый цвет. Псевдоэлемент add-page

QSlider::add-page:horizontal {
    background-color: gray;
}

Ручку слайдера я сделаю белой с шириной 22 пикселя, радиусом 10 пикселей и внешними отступами снизу и сверху по -8 пикселей.

QSlider::handle:horizontal {
    background-color: white;
    width: 22px;
    border-radius: 10px;
    margin-top: -8px;
    margin-bottom: -8px;
}

Поставим максимум 100 пикселей и значение по умолчанию 12.

Счетчик

Счетчик похож на кнопки.

QSpinBox {
    border: 2px solid gray;
    border-radius: 5px;
    background: transparent;
    padding: 2px;
}

QSpinBox:hover {
    border-color: #009900;
}

Поставим значения, как у слайдера. Сделаем горизонтальное выравнивание по центру и уберем эти отвратительные стрелочки.

Лейблы информации

Если растягивать окно интерфейса по вертикали, большую часть будут занимать лейблы информации. Мы этого не хотим, и поэтому ставим у двух лейблов вертикальную политику Maximum

Делаем последние штрихи

Поставим размер приложения по умолчанию. Мне нравится 542 на 418 пикселей. Можете сделать круглые числа, как вам угодно.

Напишем название приложения в свойстве windowTitle. Поставим иконку в windowIcon

Поставим курсор Pointing Hand для кнопок и слайдера.

Уберем демонстрационный текст из лейблов. Он будет генерироваться программой.

Штош. Интерфейс готов, переходим к следующему этапу.

Конвертируем файл ресурсов и интерфейса

Для того, чтобы сконвертировать файл ресурсов, нужно написать в терминал pyside6-rcc, название файла ресурсов, флаг -o и название файла на выходе.

pyside6-rcc resources.qrc -o resources.py

Файл интерфейса конвертируется таким же образом, только с помощью приложения pyside6-uic

pyside6-uic main.ui -o ui_main.py

Этот файл хочет, чтобы я добавлял в конце _rc к файлу ресурсов, а я не хочу. Поменяем import resources_rc на import resources

Пишем код

Рекомендую писать код в среде разработки PyCharm или Visual Studio Code. Vim-еры, не бейте.

Для начала создадим модуль приложения — app.py. Я вставлю готовый сниппет для запуска приложения с файлом дизайна.

Сниппет

import sys

from PySide6.QtWidgets import QApplication, QMainWindow

from ui_main import Ui_MainWindow


class App(QMainWindow):
    def __init__(self):
        super(App, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)


if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = App()
    window.show()

    sys.exit(app.exec())

Поменяю название класса App на PasswordGenerator

Модуль кнопок

Давайте сделаем отдельный модуль с привязкой сущностей к кнопкам — buttons.py. В стандартной библиотеке string есть строковые переменные для пула символов. Возьмем ascii_lowercase, ascii_uppercase, digits и punctuation.

from string import ascii_lowercase, ascii_uppercase, digits, punctuation

Вот так они выглядят.

ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz'
ascii_uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
digits = '0123456789'
punctuation = r"""!"#$%&'()*+,-./:;<=>?@[]^_`{|}~"""

Давайте сделаем перечисление, которое привязывает кнопки выбора символов к этим строковым переменным.

from enum import Enum

class Characters(Enum):
    btn_upper = ascii_uppercase
    btn_lower = ascii_lowercase
    btn_digits = digits
    btn_special = punctuation

Сразу создадим словарь с количеством символов на каждый пул. Нам это пригодится для расчета энтропии.

CHARACTER_NUMBER = {
    'btn_lower': len(Characters.btn_lower.value),
    'btn_upper': len(Characters.btn_upper.value),
    'btn_digits': len(Characters.btn_digits.value),
    'btn_special': len(Characters.btn_special.value)
}

Модуль генерации пароля

Давайте сделаем модуль генерации паролей без привязки к интерфейсу — password.py. Напишем функцию создания нового пароля. Аргументами будут длина пароля и все используемые символы в виде строки.

Для генерации безопасного пароля мы будем использовать библиотеку secrets. Сделаем генератор из случайно выбранных символов и соединим их в строку с помощью метода join

import secrets

def create_new(length: int, characters: str) -> str:
    return "".join(secrets.choice(characters) for _ in range(length))

Получаем энтропию пароля

Теперь сделаем функцию для получения энтропии. В нее нужно будет передавать длину пароля и количество символов. Формула энтропии пароля выглядит следующим образом:

H=log_2N^L=L times log_2N

где N — это количество возможных символов, а L — количество символов в пароле. H измеряется в битах.

Давайте импортируем логарифм по основанию 2 из библиотеки math.

from math import log2

Возвращаем значение, округленное до 2 знаков после запятой.

def get_entropy(length: int, character_number: int) -> float:
    entropy = length * log2(character_number)
    return round(entropy, 2)

Перечисление для сложности пароля

Создадим целочисленное перечисление сложности пароля к его энтропии по нижней границе. Я не нашел единого стандарта, в разных генераторах сила пароля считается по-разному, поэтому сделаю так: ничтожный пароль от 0 до 30 бит, слабый после 30, хороший после 50, сильный после 70 и замечательный после 120.

from enum import IntEnum

class StrengthToEntropy(IntEnum):
    Pathetic = 0
    Weak = 30
    Good = 50
    Strong = 70
    Excellent = 120

Связываем значения слайдера и счетчика

Давайте сделаем метод класса приложения, который будет связывать значения слайдера и счетчика. Для этого соединим сигнал изменения значения valueChanged с установкой значения другого элемента.

  def connect_slider_to_spinbox(self) -> None:
      self.ui.slider_length.valueChanged.connect(self.ui.spinbox_length.setValue)
      self.ui.spinbox_length.valueChanged.connect(self.ui.slider_length.setValue)

Не забудьте прописать метод в конструкторе класса.

class PasswordGenerator(QMainWindow):
    def __init__(self):
        ...
        self.connect_slider_to_spinbox()

Получаем символы для пароля

Создадим новый метод для получения символов отмеченных кнопок. Объявим пустую строку chars. Для каждой кнопки в перечислении добавляем её символы в строку, если она отмечена.

def get_characters(self) -> str:
    chars = ""

    for btn in buttons.Characters:
        if getattr(self.ui, btn.name).isChecked():
            chars += btn.value

    return chars

Ставим пароль

Ну и наконец-то, метод установки пароля. Ставим в элемент line_password новый пароль с помощью метода setText. Длину берем из слайдера или счетчика, не имеет значения.

def set_password(self) -> None:
    self.ui.line_password.setText(
        password.create_new(
            length=self.ui.slider_length.value(),
            characters=self.get_characters())
    )

Добавим метод в конструктор. Приложение должно сгенерировать пароль при запуске.

Соединим изменение слайдера или счетчика с установкой пароля.

def connect_slider_to_spinbox(self) -> None:
    ...
    self.ui.spinbox_length.valueChanged.connect(self.set_password)

Позалипаем на быстрое изменение паролей :3

Когда не нажата ни одна кнопка символов, происходит IndexError. Обработаем этот случай, просто очищая поле пароля.

def set_password(self) -> None:
    try:
        self.ui.line_password.setText(
            password.create_new(
                length=self.ui.slider_length.value(),
                characters=self.get_characters())
        )
    except IndexError:
        self.ui.line_password.clear()

Получаем количество символов

Принцип тот же, что и у получения строки символов, только в этот раз мы работаем со словарем, а не с перечислением.

def get_character_number(self) -> int:
    num = 0

    for btn in buttons.CHARACTER_NUMBER.items():
        if getattr(self.ui, btn[0]).isChecked():
            num += btn[1]

    return num

Ставим энтропию пароля

Делаем метод для лейбла энтропии. Берем длину из текста пароля, а не из слайдера или счетчика, так нужно. Ставим в лейбл выражение с помощью f-строки.

def set_entropy(self) -> None:
    length = len(self.ui.line_password.text())
    char_num = self.get_character_number()

    self.ui.label_entropy.setText(
        f"Entropy: {password.get_entropy(length, char_num)} bit"
    )

Добавим установку энтропии в конце метода установки пароля.

def set_password(self) -> None:
    ...

    self.set_entropy()

Без отмеченных кнопок символов получается ValueError. Обработаем эту ошибку в функции get_entropy модуля password. В таком случае она будет возвращать 0.0.

def get_entropy(length: int, character_number: int) -> float:
    try:
        entropy = length * log2(character_number)
    except ValueError:
        return 0.0

    return round(entropy, 2)

Ставим сообщение о сложности пароля

Теперь сделаем метод для лейбла сложности пароля. Для каждой сложности из перечисления сравниваем энтропию со значением.

def set_strength(self) -> None:
    length = len(self.ui.line_password.text())
    char_num = self.get_character_number()

    for strength in password.StrengthToEntropy:
        if password.get_entropy(length, char_num) >= strength.value:
            self.ui.label_strength.setText(f"Strength: {strength.name}")

Добавим в конец метода установки пароля.

Ставим генерацию пароля на кнопки

Запишу в модуль buttons кортеж с именами кнопок, при нажатии на которые будет генерироваться новый пароль.

GENERATE_PASSWORD = (
    'btn_refresh', 'btn_lower', 'btn_upper', 'btn_digits', 'btn_special'
)

Теперь соединим кнопки с методом в конструкторе класса.

for btn in buttons.GENERATE_PASSWORD:
    getattr(self.ui, btn).clicked.connect(self.set_password)

Изменяем видимость пароля

Напишем метод для изменения видимости пароля. Если кнопка отмечена, ставим нормальный эхо мод, иначе ставим эхо мод пароля.

from PySide6.QtWidgets import QApplication, QMainWindow, QLineEdit

...

def change_password_visibility(self) -> None:
    if self.ui.btn_visibility.isChecked():
        self.ui.line_password.setEchoMode(QLineEdit.Normal)
    else:
        self.ui.line_password.setEchoMode(QLineEdit.Password)

Соединим нажатие кнопки с методом в конструкторе класса.

class PasswordGenerator(QMainWindow):
    def __init__(self):
        ...
        self.ui.btn_visibility.clicked.connect(self.change_password_visibility)

Копируем в буфер обмена

Чтобы скопировать текст в буфер обмена, нужно вызвать метод clipboard класса QApplication и поставить в него текст.

def copy_to_clipboard(self) -> None:
    QApplication.clipboard().setText(self.ui.line_password.text())

Так же соединим нажатие кнопки с методом в конструкторе класса. Я думаю, вы справитесь.

Очищаем буфер обмена при закрытии приложения

Для того, чтобы делать что-либо при закрытии приложения, нам нужна функция этого события. В Qt это событие называется QCloseEvent. В PySide6 пишем closeEvent

Событие не нужно ни с чем соединять в конструкторе. Оно работает сразу при объявлении соответствующего метода в классе приложения.

from PySide6.QtGui import QCloseEvent

def closeEvent(self, event: QCloseEvent) -> None:
    QApplication.clipboard().clear()

Меняем лейблы при ручном изменении пароля

Остался последний штрих. При ручном изменении пароля энтропия не меняется. Решить проблему можно с помощью сигнала редактирования текста textEdited

def do_when_password_edit(self) -> None:
    self.ui.line_password.textEdited.connect(self.set_entropy)
    self.ui.line_password.textEdited.connect(self.set_strength)

Фишка в том, что это будет не совсем правильная энтропия, потому что количество допустимых символов берется по прежнему из отмеченных кнопок. Предлагаю вам самим решить эту проблему.

buttons.py

from string import ascii_lowercase, ascii_uppercase, digits, punctuation
from enum import Enum


class Characters(Enum):
    btn_lower = ascii_lowercase
    btn_upper = ascii_uppercase
    btn_digits = digits
    btn_special = punctuation


CHARACTER_NUMBER = {
    'btn_lower': len(Characters.btn_lower.value),
    'btn_upper': len(Characters.btn_upper.value),
    'btn_digits': len(Characters.btn_digits.value),
    'btn_special': len(Characters.btn_special.value)
}

GENERATE_PASSWORD = (
    'btn_refresh', 'btn_lower', 'btn_upper', 'btn_digits', 'btn_special'
)

password.py

from enum import IntEnum
from math import log2
import secrets


class StrengthToEntropy(IntEnum):
    Pathetic = 0
    Weak = 30
    Good = 50
    Strong = 70
    Excellent = 120


def create_new(length: int, characters: str) -> str:
    return ''.join(secrets.choice(characters) for _ in range(length))


def get_entropy(length: int, character_number: int) -> float:
    try:
        entropy = length * log2(character_number)
    except ValueError:
        return 0.0

    return round(entropy, 2)

Меняем структуру проекта

Штош. Давайте поменяем структуру проекта. Поместим все файлы в папку password-generator. Здесь создадим папку ui, в которую поместим все файлы, связанные с интерфейсом. Поменяем импортирование модулей, чтобы все работало. Из ui_main.py я убрал импортирование ресурсов и добавил его в главный модуль приложения.

from ui.ui_main import Ui_MainWindow
import ui.resources

app.py

import sys

from PySide6.QtWidgets import QApplication, QMainWindow, QLineEdit
from PySide6.QtGui import QCloseEvent

import buttons
import password
from ui.ui_main import Ui_MainWindow
import ui.resources


class PasswordGenerator(QMainWindow):
    def __init__(self):
        super(PasswordGenerator, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.connect_slider_to_spinbox()
        self.set_password()
        self.do_when_password_edit()

        for btn in buttons.GENERATE_PASSWORD:
            getattr(self.ui, btn).clicked.connect(self.set_password)

        self.ui.btn_visibility.clicked.connect(self.change_password_visibility)
        self.ui.btn_copy.clicked.connect(self.copy_to_clipboard)

    def connect_slider_to_spinbox(self) -> None:
        self.ui.slider_length.valueChanged.connect(self.ui.spinbox_length.setValue)
        self.ui.spinbox_length.valueChanged.connect(self.ui.slider_length.setValue)
        self.ui.spinbox_length.valueChanged.connect(self.set_password)

    def do_when_password_edit(self) -> None:
        self.ui.line_password.textEdited.connect(self.set_entropy)
        self.ui.line_password.textEdited.connect(self.set_strength)

    def get_characters(self) -> str:
        chars = ''

        for btn in buttons.Characters:
            if getattr(self.ui, btn.name).isChecked():
                chars += btn.value

        return chars

    def set_password(self) -> None:
        try:
            self.ui.line_password.setText(
                password.create_new(
                    length=self.ui.slider_length.value(),
                    characters=self.get_characters())
            )
        except IndexError:
            self.ui.line_password.clear()

        self.set_entropy()
        self.set_strength()

    def get_character_number(self) -> int:
        num = 0

        for btn in buttons.CHARACTER_NUMBER.items():
            if getattr(self.ui, btn[0]).isChecked():
                num += btn[1]

        return num

    def set_entropy(self) -> None:
        length = len(self.ui.line_password.text())
        char_num = self.get_character_number()

        self.ui.label_entropy.setText(
            f'Entropy: {password.get_entropy(length, char_num)} bit'
        )

    def set_strength(self) -> None:
        length = len(self.ui.line_password.text())
        char_num = self.get_character_number()

        for strength in password.StrengthToEntropy:
            if password.get_entropy(length, char_num) >= strength.value:
                self.ui.label_strength.setText(f'Strength: {strength.name}')

    def change_password_visibility(self) -> None:
        if self.ui.btn_visibility.isChecked():
            self.ui.line_password.setEchoMode(QLineEdit.Normal)
        else:
            self.ui.line_password.setEchoMode(QLineEdit.Password)

    def copy_to_clipboard(self) -> None:
        QApplication.clipboard().setText(self.ui.line_password.text())

    def closeEvent(self, event: QCloseEvent) -> None:
        QApplication.clipboard().clear()


if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = PasswordGenerator()
    window.show()

    sys.exit(app.exec())

Создаем файл зависимостей

Создадим файл зависимостей с помощью команды pip freeze > requirements.txt

PySide6==6.3.1
PySide6-Addons==6.3.1
PySide6-Essentials==6.3.1
shiboken6==6.3.1

Система контроля версий

Инициализируем систему контроля версий, конечно же Git. Установите систему, если вы еще этого не сделали. Пропишите в терминал git init из корневого каталога или используйте удобный графический интерфейс в PyCharm.

Создадим файл .gitignore. Будем игнорировать любую папку виртуального окружения, папку PyCharm в моем случае и питонячий кэш. Для VS Code можно прописать .vscode

venv*
.idea
.vscode
__pycache__

Теперь можно сделать первый коммит. Опять же, в PyCharm это делается очень удобно, но если вы любите работать в терминале, то пропишите команды:

git add .

git commit -m "Initial commit"

Собираем приложение для Windows

Переходим к созданию исполняемых файлов. У Qt есть понятная документация, в которой собраны разные способы дистрибуции приложения. Самый простой способ — это PyInstaller. Нет, вру, самый простой способ — это PyInstaller с интерфейсом, auto-py-to-exe. Все эти библиотеки хороши своей понятностью и легкостью, но обычно на выходе получаются небезопасные и тяжелые бинарники.

Я решил показать вам инструмент получше — компилятор Nuitka. Он доступен для всех версий Qt, работает на всех платформах и распространяется по свободной лицензии MIT.

Установим библиотеку с помощью команды pip install nuitka. Для сборки приложения в один файл понадобится библиотека zstandard, добавим ее в команду через пробел.

Пишем команду компиляции. Собираем приложение в один файл с помощью флага --onefile. Флаг --follow-imports нужен для соблюдения всех импортов. Добавляем плагин PySide6 с помощью флага --enable-plugin=pyside6. Чтобы убрать консольное окно в системе Windows добавим --windows-disable-console. Добавим иконку с помощью флага --windows-icon-from-ico

Чтобы убрать все генерируемые нуиткой папки сборки добавим флаг --remove-output

Можно указать название файла через флаг -o, но это вообще необязательно, всегда можно просто переименовать файл. В конце напишем путь к файлу скрипта, который мы собираемся собирать.

nuitka --onefile --follow-imports --enable-plugin=pyside6 --windows-disable-console --windows-icon-from-ico=uiiconsapp-icon.ico --remove-output -o password-generator.exe app.py

На выходе получилось приложение с размером 16.7 мегабайт.

Собираем приложение для Linux

Я использовал дистрибутив Ubuntu версии 22.04. С нуиткой вышло громадное приложение в 145 мегабайт. Если вы знаете, как сделать меньше — пишите в комментарии.

python -m nuitka --onefile --follow-imports --enable-plugin=pyside6 --remove-output app.py

Я попробовал собрать приложение с PyInstaller, получилось уже получше, 57,6 мегабайт. С иконкой в докбаре и проводнике вообще беда, она просто не хотела ставиться.

pyinstaller --name="Password Generator" --windowed app.py

Собираем приложение для macOS

Я использовал версию 10.15 Catalina. Для сборки приложения под macOS нужно прописать специальный параметр --macos-create-app-bundle

python3 -m nuitka --onefile --follow-imports --enable-plugin=pyside6 --macos-create-app-bundle --remove-output -o password-generator.app app.py

Вышло почти так же компактно, как и на винде — 18 мегабайт. С иконкой тоже возникли проблемы, поэтому собирал без нее.

Штош

Надеюсь, вам понравился такой комплексный туториал. Генерируйте сильные пароли, дегенерируйте слабые, и будет вам счастье. До встречи.


GitHub репозиторий

import random

import array

MAX_LEN = 12

DIGITS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'

LOCASE_CHARACTERS = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',

                     'i', 'j', 'k', 'm', 'n', 'o', 'p', 'q',

                     'r', 's', 't', 'u', 'v', 'w', 'x', 'y',

                     'z']

UPCASE_CHARACTERS = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',

                     'I', 'J', 'K', 'M', 'N', 'O', 'P', 'Q',

                     'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',

                     'Z']

SYMBOLS = ['@', '#', '$', '%', '=', ':', '?', '.', '/', '|', '~', '>',

           '*', '(', ')', '<']

COMBINED_LIST = DIGITS + UPCASE_CHARACTERS + LOCASE_CHARACTERS + SYMBOLS

rand_digit = random.choice(DIGITS)

rand_upper = random.choice(UPCASE_CHARACTERS)

rand_lower = random.choice(LOCASE_CHARACTERS)

rand_symbol = random.choice(SYMBOLS)

temp_pass = rand_digit + rand_upper + rand_lower + rand_symbol

for x in range(MAX_LEN - 4):

    temp_pass = temp_pass + random.choice(COMBINED_LIST)

    temp_pass_list = array.array('u', temp_pass)

    random.shuffle(temp_pass_list)

password = ""

for x in temp_pass_list:

        password = password + x

print(password)

import random

import array

MAX_LEN = 12

DIGITS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'

LOCASE_CHARACTERS = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',

                     'i', 'j', 'k', 'm', 'n', 'o', 'p', 'q',

                     'r', 's', 't', 'u', 'v', 'w', 'x', 'y',

                     'z']

UPCASE_CHARACTERS = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',

                     'I', 'J', 'K', 'M', 'N', 'O', 'P', 'Q',

                     'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',

                     'Z']

SYMBOLS = ['@', '#', '$', '%', '=', ':', '?', '.', '/', '|', '~', '>',

           '*', '(', ')', '<']

COMBINED_LIST = DIGITS + UPCASE_CHARACTERS + LOCASE_CHARACTERS + SYMBOLS

rand_digit = random.choice(DIGITS)

rand_upper = random.choice(UPCASE_CHARACTERS)

rand_lower = random.choice(LOCASE_CHARACTERS)

rand_symbol = random.choice(SYMBOLS)

temp_pass = rand_digit + rand_upper + rand_lower + rand_symbol

for x in range(MAX_LEN - 4):

    temp_pass = temp_pass + random.choice(COMBINED_LIST)

    temp_pass_list = array.array('u', temp_pass)

    random.shuffle(temp_pass_list)

password = ""

for x in temp_pass_list:

        password = password + x

print(password)


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters

Show hidden characters

import random
num = input(‘login ‘)
pas = »
for x in range(16): #Количество символов (16)
pas = pas + random.choice(list(‘1234567890abcdefghigklmnopqrstuvyxwzABCDEFGHIGKLMNOPQRSTUVYXWZ’)) #Символы, из которых будет составлен пароль
print(‘Hello, ‘, num, ‘your password is: ‘, pas)

Понравилась статья? Поделить с друзьями:
  • Как написать пароль для аккаунта
  • Как написать пароль apple id
  • Как написать пародию на стих
  • Как написать парню якобы случайно
  • Как написать парню я тебя люблю красиво своими словами