├── .gitignore ├── LICENSE ├── README.MD ├── requirements.txt ├── screenshots ├── macos.png ├── ubuntu.png └── windows.png └── src ├── __init__.py ├── app.py ├── buttons.py ├── password.py └── ui ├── __init__.py ├── css ├── MainWindow.css ├── btn_copy.css ├── btn_refresh.css ├── btn_visibility.css ├── frame_password.css ├── line_password.css ├── slider_length.css └── spinbox_length.css ├── icons ├── app-icon.ico ├── copy.svg ├── invisible.svg ├── lock.svg ├── refresh.svg └── visible.svg ├── main.ui ├── resources.py ├── resources.qrc └── ui_main.py /.gitignore: -------------------------------------------------------------------------------- 1 | venv* 2 | .idea 3 | .vscode 4 | __pycache__ 5 | *.exe -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 shtosh python 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Password Generator 2 | 3 | [![GitHub all releases](https://img.shields.io/github/downloads/lesskop/password-generator/total)](#download) 4 | ![GitHub repo size](https://img.shields.io/github/repo-size/lesskop/password-generator) 5 | [![YouTube Views](https://img.shields.io/youtube/views/WHSqprSMjnQ?style=social)](https://www.youtube.com/watch?v=WHSqprSMjnQ) 6 | 7 | Cross-platform GUI password generator written in Python 8 | 9 | ## Screenshots 10 | 11 | - Windows 10 12 | 13 | ![Windows](screenshots/windows.png) 14 | 15 | - Ubuntu 22.04 16 | 17 | ![Ubuntu](screenshots/ubuntu.png) 18 | 19 | - macOS 10.15 Catalina 20 | 21 | ![macOS](screenshots/macos.png) 22 | 23 | ## Download 24 | 25 | [Last release](https://github.com/lesskop/password-generator/releases/tag/v1.0) 26 | 27 | - [Windows](https://github.com/lesskop/password-generator/releases/download/v1.0/password-generator-v1.0-win64.zip) 28 | - [Linux](https://github.com/lesskop/password-generator/releases/download/v1.0/password-generator-v1.0-linux64.tar.gz) 29 | - [macOS](https://github.com/lesskop/password-generator/releases/download/v1.0/password-generator-v1.0-macos.tar.gz) 30 | 31 | ## Installation 32 | 33 | 1. You should have [Python 3.7+](https://www.python.org/downloads/) and [Git](https://git-scm.com/downloads) installed on your local machine. 34 | 35 | 2. Clone the repository: 36 | 37 | ```bash 38 | git clone https://github.com/lesskop/password-generator.git 39 | ``` 40 | 41 | 3. Change the directory: 42 | 43 | ```bash 44 | cd password-generator 45 | ``` 46 | 47 | 4. Create a virtual environment (recommended): 48 | 49 | ```bash 50 | python -m venv venv 51 | ``` 52 | 53 | 5. Activate it: 54 | 55 | - Windows: 56 | 57 | ```bash 58 | venv\Scripts\activate 59 | ``` 60 | 61 | - Linux/macOS: 62 | 63 | ```bash 64 | source venv/bin/activate 65 | ``` 66 | 67 | 6. Install dependencies: 68 | 69 | ```bash 70 | pip install -r requirements.txt 71 | ``` 72 | 73 | ## Usage 74 | 75 | Run application with command: 76 | 77 | ```bash 78 | python src/app.py 79 | ``` 80 | 81 | or 82 | 83 | ```bash 84 | cd src 85 | python app.py 86 | ``` 87 | 88 | ## Tutorials 89 | 90 | [YouTube video](https://youtu.be/WHSqprSMjnQ) 91 | 92 | [Статья на Хабре](https://habr.com/p/689536) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesskop/password-generator/dc84179304e1e216ca3be2eb20928fb1eaebd266/requirements.txt -------------------------------------------------------------------------------- /screenshots/macos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesskop/password-generator/dc84179304e1e216ca3be2eb20928fb1eaebd266/screenshots/macos.png -------------------------------------------------------------------------------- /screenshots/ubuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesskop/password-generator/dc84179304e1e216ca3be2eb20928fb1eaebd266/screenshots/ubuntu.png -------------------------------------------------------------------------------- /screenshots/windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesskop/password-generator/dc84179304e1e216ca3be2eb20928fb1eaebd266/screenshots/windows.png -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesskop/password-generator/dc84179304e1e216ca3be2eb20928fb1eaebd266/src/__init__.py -------------------------------------------------------------------------------- /src/app.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from PySide6.QtWidgets import QApplication, QMainWindow, QLineEdit 4 | from PySide6.QtGui import QCloseEvent 5 | 6 | import buttons 7 | import password 8 | from ui.ui_main import Ui_MainWindow 9 | import ui.resources 10 | 11 | 12 | class PasswordGenerator(QMainWindow): 13 | def __init__(self): 14 | super(PasswordGenerator, self).__init__() 15 | self.ui = Ui_MainWindow() 16 | self.ui.setupUi(self) 17 | 18 | self.connect_slider_to_spinbox() 19 | self.set_password() 20 | self.do_when_password_edit() 21 | 22 | for btn in buttons.GENERATE_PASSWORD: 23 | getattr(self.ui, btn).clicked.connect(self.set_password) 24 | 25 | self.ui.btn_visibility.clicked.connect(self.change_password_visibility) 26 | self.ui.btn_copy.clicked.connect(self.copy_to_clipboard) 27 | 28 | def connect_slider_to_spinbox(self) -> None: 29 | self.ui.slider_length.valueChanged.connect(self.ui.spinbox_length.setValue) 30 | self.ui.spinbox_length.valueChanged.connect(self.ui.slider_length.setValue) 31 | self.ui.spinbox_length.valueChanged.connect(self.set_password) 32 | 33 | def do_when_password_edit(self) -> None: 34 | self.ui.line_password.textEdited.connect(self.set_entropy) 35 | self.ui.line_password.textEdited.connect(self.set_strength) 36 | 37 | def get_characters(self) -> str: 38 | chars = '' 39 | 40 | for btn in buttons.Characters: 41 | if getattr(self.ui, btn.name).isChecked(): 42 | chars += btn.value 43 | 44 | return chars 45 | 46 | def set_password(self) -> None: 47 | try: 48 | self.ui.line_password.setText( 49 | password.create_new( 50 | length=self.ui.slider_length.value(), 51 | characters=self.get_characters()) 52 | ) 53 | except IndexError: 54 | self.ui.line_password.clear() 55 | 56 | self.set_entropy() 57 | self.set_strength() 58 | 59 | def get_character_number(self) -> int: 60 | num = 0 61 | 62 | for btn in buttons.CHARACTER_NUMBER.items(): 63 | if getattr(self.ui, btn[0]).isChecked(): 64 | num += btn[1] 65 | 66 | return num 67 | 68 | def set_entropy(self) -> None: 69 | length = len(self.ui.line_password.text()) 70 | char_num = self.get_character_number() 71 | 72 | self.ui.label_entropy.setText( 73 | f'Entropy: {password.get_entropy(length, char_num)} bit' 74 | ) 75 | 76 | def set_strength(self) -> None: 77 | length = len(self.ui.line_password.text()) 78 | char_num = self.get_character_number() 79 | 80 | for strength in password.StrengthToEntropy: 81 | if password.get_entropy(length, char_num) >= strength.value: 82 | self.ui.label_strength.setText(f'Strength: {strength.name}') 83 | 84 | def change_password_visibility(self) -> None: 85 | if self.ui.btn_visibility.isChecked(): 86 | self.ui.line_password.setEchoMode(QLineEdit.Normal) 87 | else: 88 | self.ui.line_password.setEchoMode(QLineEdit.Password) 89 | 90 | def copy_to_clipboard(self) -> None: 91 | QApplication.clipboard().setText(self.ui.line_password.text()) 92 | 93 | def closeEvent(self, event: QCloseEvent) -> None: 94 | QApplication.clipboard().clear() 95 | 96 | 97 | if __name__ == "__main__": 98 | app = QApplication(sys.argv) 99 | 100 | window = PasswordGenerator() 101 | window.show() 102 | 103 | sys.exit(app.exec()) 104 | -------------------------------------------------------------------------------- /src/buttons.py: -------------------------------------------------------------------------------- 1 | from string import ascii_lowercase, ascii_uppercase, digits, punctuation 2 | from enum import Enum 3 | 4 | 5 | class Characters(Enum): 6 | btn_lower = ascii_lowercase 7 | btn_upper = ascii_uppercase 8 | btn_digits = digits 9 | btn_special = punctuation 10 | 11 | 12 | CHARACTER_NUMBER = { 13 | 'btn_lower': len(Characters.btn_lower.value), 14 | 'btn_upper': len(Characters.btn_upper.value), 15 | 'btn_digits': len(Characters.btn_digits.value), 16 | 'btn_special': len(Characters.btn_special.value) 17 | } 18 | 19 | GENERATE_PASSWORD = ( 20 | 'btn_refresh', 'btn_lower', 'btn_upper', 'btn_digits', 'btn_special' 21 | ) 22 | -------------------------------------------------------------------------------- /src/password.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum 2 | from math import log2 3 | import secrets 4 | 5 | 6 | class StrengthToEntropy(IntEnum): 7 | Pathetic = 0 8 | Weak = 30 9 | Good = 50 10 | Strong = 70 11 | Excellent = 120 12 | 13 | 14 | def create_new(length: int, characters: str) -> str: 15 | return ''.join(secrets.choice(characters) for _ in range(length)) 16 | 17 | 18 | def get_entropy(length: int, character_number: int) -> float: 19 | try: 20 | entropy = length * log2(character_number) 21 | except ValueError: 22 | return 0.0 23 | 24 | return round(entropy, 2) 25 | -------------------------------------------------------------------------------- /src/ui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesskop/password-generator/dc84179304e1e216ca3be2eb20928fb1eaebd266/src/ui/__init__.py -------------------------------------------------------------------------------- /src/ui/css/MainWindow.css: -------------------------------------------------------------------------------- 1 | QWidget { 2 | background-color: #121212; 3 | color: white; 4 | font-family: Verdana; 5 | font-size: 16pt; 6 | margin: 10px; 7 | } 8 | 9 | QPushButton { 10 | border: 2px solid gray; 11 | border-radius: 5px; 12 | } 13 | 14 | QPushButton#btn_lower, 15 | #btn_upper, 16 | #btn_digits, 17 | #btn_special { 18 | padding: 10px; 19 | } 20 | 21 | QPushButton:hover { 22 | border-color: #090; 23 | } 24 | 25 | QPushButton:pressed { 26 | border: 4px solid #090; 27 | border-radius: 5px; 28 | } 29 | 30 | QPushButton:checked { 31 | background-color: #006300; 32 | border-color: #090; 33 | } 34 | -------------------------------------------------------------------------------- /src/ui/css/btn_copy.css: -------------------------------------------------------------------------------- 1 | QPushButton { 2 | padding: 5px; 3 | margin-left: 0; 4 | } 5 | -------------------------------------------------------------------------------- /src/ui/css/btn_refresh.css: -------------------------------------------------------------------------------- 1 | QPushButton { 2 | margin-right: 0; 3 | margin-left: 0; 4 | } 5 | -------------------------------------------------------------------------------- /src/ui/css/btn_visibility.css: -------------------------------------------------------------------------------- 1 | QPushButton { 2 | border: none; 3 | margin: 0; 4 | background-color: transparent; 5 | } 6 | -------------------------------------------------------------------------------- /src/ui/css/frame_password.css: -------------------------------------------------------------------------------- 1 | QFrame { 2 | border: 2px solid gray; 3 | border-radius: 5px; 4 | margin-right: 0; 5 | } 6 | 7 | QFrame:hover { 8 | border-color: #090; 9 | } 10 | -------------------------------------------------------------------------------- /src/ui/css/line_password.css: -------------------------------------------------------------------------------- 1 | QLineEdit { 2 | border: none; 3 | margin: 0; 4 | font-size: 20pt; 5 | } 6 | -------------------------------------------------------------------------------- /src/ui/css/slider_length.css: -------------------------------------------------------------------------------- 1 | QSlider::groove:horizontal { 2 | background-color: transparent; 3 | height: 5px; 4 | } 5 | 6 | QSlider::sub-page:horizontal { 7 | background-color: #090; 8 | } 9 | 10 | QSlider::add-page:horizontal { 11 | background-color: gray; 12 | } 13 | 14 | QSlider::handle:horizontal { 15 | background-color: white; 16 | width: 22px; 17 | border-radius: 10px; 18 | margin-top: -8px; 19 | margin-bottom: -8px; 20 | } 21 | -------------------------------------------------------------------------------- /src/ui/css/spinbox_length.css: -------------------------------------------------------------------------------- 1 | QSpinBox { 2 | border: 2px solid gray; 3 | border-radius: 5px; 4 | background: transparent; 5 | padding: 2px; 6 | } 7 | 8 | QSpinBox:hover { 9 | border-color: #009900; 10 | } 11 | -------------------------------------------------------------------------------- /src/ui/icons/app-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesskop/password-generator/dc84179304e1e216ca3be2eb20928fb1eaebd266/src/ui/icons/app-icon.ico -------------------------------------------------------------------------------- /src/ui/icons/copy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ui/icons/invisible.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ui/icons/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ui/icons/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ui/icons/visible.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ui/main.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 542 10 | 418 11 | 12 | 13 | 14 | PointingHandCursor 15 | 16 | 17 | Password Generator 18 | 19 | 20 | 21 | :/icons/icons/app-icon.ico:/icons/icons/app-icon.ico 22 | 23 | 24 | QWidget { 25 | background-color: #121212; 26 | color: white; 27 | font-family: Verdana; 28 | font-size: 16pt; 29 | margin: 10px; 30 | } 31 | 32 | QPushButton { 33 | border: 2px solid gray; 34 | border-radius: 5px; 35 | } 36 | 37 | QPushButton#btn_lower, 38 | #btn_upper, 39 | #btn_digits, 40 | #btn_special { 41 | padding: 10px; 42 | } 43 | 44 | QPushButton:hover { 45 | border-color: #090; 46 | } 47 | 48 | QPushButton:pressed { 49 | border: 4px solid #090; 50 | border-radius: 5px; 51 | } 52 | 53 | QPushButton:checked { 54 | background-color: #006300; 55 | border-color: #090; 56 | } 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | false 68 | 69 | 70 | 71 | 0 72 | 0 73 | 74 | 75 | 76 | border: none; 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | :/icons/icons/lock.svg:/icons/icons/lock.svg 85 | 86 | 87 | 88 | 100 89 | 100 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 0 101 | 0 102 | 103 | 104 | 105 | QFrame { 106 | border: 2px solid gray; 107 | border-radius: 5px; 108 | margin-right: 0; 109 | } 110 | 111 | QFrame:hover { 112 | border-color: #090; 113 | } 114 | 115 | 116 | 117 | QFrame::StyledPanel 118 | 119 | 120 | QFrame::Raised 121 | 122 | 123 | 124 | 125 | 126 | QLineEdit { 127 | border: none; 128 | margin: 0; 129 | font-size: 20pt; 130 | } 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | PointingHandCursor 139 | 140 | 141 | QPushButton { 142 | border: none; 143 | margin: 0; 144 | background-color: transparent; 145 | } 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | :/icons/icons/invisible.svg 154 | :/icons/icons/visible.svg:/icons/icons/invisible.svg 155 | 156 | 157 | 158 | 30 159 | 30 160 | 161 | 162 | 163 | true 164 | 165 | 166 | true 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | PointingHandCursor 177 | 178 | 179 | QPushButton { 180 | margin-right: 0; 181 | margin-left: 0; 182 | } 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | :/icons/icons/refresh.svg 191 | 192 | 193 | 194 | 195 | 52 196 | 52 197 | 198 | 199 | 200 | Ctrl+R 201 | 202 | 203 | 204 | 205 | 206 | 207 | PointingHandCursor 208 | 209 | 210 | QPushButton { 211 | padding: 5px; 212 | margin-left: 0; 213 | } 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | :/icons/icons/copy.svg 222 | 223 | 224 | 225 | 226 | 42 227 | 42 228 | 229 | 230 | 231 | Ctrl+C 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 0 244 | 0 245 | 246 | 247 | 248 | 249 | 250 | 251 | Qt::AlignCenter 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 0 260 | 0 261 | 262 | 263 | 264 | 265 | 266 | 267 | Qt::AlignCenter 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | PointingHandCursor 279 | 280 | 281 | QSlider::groove:horizontal { 282 | background-color: transparent; 283 | height: 5px; 284 | } 285 | 286 | QSlider::sub-page:horizontal { 287 | background-color: #090; 288 | } 289 | 290 | QSlider::add-page:horizontal { 291 | background-color: gray; 292 | } 293 | 294 | QSlider::handle:horizontal { 295 | background-color: white; 296 | width: 22px; 297 | border-radius: 10px; 298 | margin-top: -8px; 299 | margin-bottom: -8px; 300 | } 301 | 302 | 303 | 304 | 100 305 | 306 | 307 | 12 308 | 309 | 310 | Qt::Horizontal 311 | 312 | 313 | 314 | 315 | 316 | 317 | QSpinBox { 318 | border: 2px solid gray; 319 | border-radius: 5px; 320 | background: transparent; 321 | padding: 2px; 322 | } 323 | 324 | QSpinBox:hover { 325 | border-color: #009900; 326 | } 327 | 328 | 329 | 330 | Qt::AlignCenter 331 | 332 | 333 | QAbstractSpinBox::NoButtons 334 | 335 | 336 | 100 337 | 338 | 339 | 12 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | PointingHandCursor 351 | 352 | 353 | a-z 354 | 355 | 356 | true 357 | 358 | 359 | true 360 | 361 | 362 | 363 | 364 | 365 | 366 | PointingHandCursor 367 | 368 | 369 | A-Z 370 | 371 | 372 | true 373 | 374 | 375 | true 376 | 377 | 378 | 379 | 380 | 381 | 382 | PointingHandCursor 383 | 384 | 385 | 0-9 386 | 387 | 388 | true 389 | 390 | 391 | true 392 | 393 | 394 | 395 | 396 | 397 | 398 | PointingHandCursor 399 | 400 | 401 | #$% 402 | 403 | 404 | true 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | -------------------------------------------------------------------------------- /src/ui/resources.py: -------------------------------------------------------------------------------- 1 | # Resource object code (Python 3) 2 | # Created by: object code 3 | # Created by: The Resource Compiler for Qt version 6.3.1 4 | # WARNING! All changes made in this file will be lost! 5 | 6 | from PySide6 import QtCore 7 | 8 | qt_resource_data = b"\ 9 | \x00\x00\x01\xed\ 10 | <\ 11 | svg xmlns=\x22http:\ 12 | //www.w3.org/200\ 13 | 0/svg\x22 height=\x222\ 14 | 4px\x22 viewBox=\x220 \ 15 | 0 24 24\x22 width=\x22\ 16 | 24px\x22 fill=\x22#FFF\ 17 | FFF\x22>\ 42 | \x00\x00\x01\xaa\ 43 | <\ 44 | svg xmlns=\x22http:\ 45 | //www.w3.org/200\ 46 | 0/svg\x22 height=\x222\ 47 | 4px\x22 viewBox=\x220 \ 48 | 0 24 24\x22 width=\x22\ 49 | 24px\x22 fill=\x22#FFF\ 50 | FFF\x22><\ 53 | path d=\x22M0 0h24v\ 54 | 24H0V0z\x22 opacity\ 55 | =\x22.87\x22/>\ 71 | \x00\x00\x01\x1e\ 72 | <\ 73 | svg xmlns=\x22http:\ 74 | //www.w3.org/200\ 75 | 0/svg\x22 height=\x222\ 76 | 4px\x22 viewBox=\x220 \ 77 | 0 24 24\x22 width=\x22\ 78 | 24px\x22 fill=\x22#FFF\ 79 | FFF\x22>\ 91 | \x00\x00\x03\x85\ 92 | <\ 93 | svg xmlns=\x22http:\ 94 | //www.w3.org/200\ 95 | 0/svg\x22 height=\x222\ 96 | 4px\x22 viewBox=\x220 \ 97 | 0 24 24\x22 width=\x22\ 98 | 24px\x22 fill=\x22#FFF\ 99 | FFF\x22>\ 150 | \x00\x00\x01i\ 151 | <\ 152 | svg xmlns=\x22http:\ 153 | //www.w3.org/200\ 154 | 0/svg\x22 height=\x222\ 155 | 4px\x22 viewBox=\x220 \ 156 | 0 24 24\x22 width=\x22\ 157 | 24px\x22 fill=\x22#FFF\ 158 | FFF\x22>\ 175 | \x00\x00\x01\xcf\ 176 | \x00\ 177 | \x00%\xbex\x9c\xed\x98\xbbJ\x03A\x14\x86OH\xb1\ 178 | \x8d\xc1N4\x08k\xa1`\x99t\xdb\xe5\x09R\xe9\x03\ 179 | \xa4\x0e\x8a\x0aA\x10\xc1\x0d66\x16B\xbaT\x82\x8d\ 180 | \xbd\xd8\xe71\xc4F\x1bk\x13m\x12o\xf1\xcc\xec\x0c\ 181 | \x0eagv\xd78\x9b\x8c\x9c/|9\xb0\x93\xc9\xf9\ 182 | '\xd9\xec%\x00\x05|T*\x80\xcfkp\xbd\x01\xb0\ 183 | \x04\x00\x9b(n\x82\x06D\xdb9\xeb@\x10\x04A\x10\ 184 | \x04A\xa4c\x15m\xa27\xe8=\xfa\x8a\x0e\xd1\x07\xb1\ 185 | \xad\x85\x96g\x96N\xcf\x0a\xdaE\xdf\xd1q\x82o\xe8\ 186 | \x15\xcc\xcf:\xea\xe8\x0b$\xe7\x9et\x80n\xcd \xaf\ 187 | \xca\x0e\xfa\x09\xd9\xb3K\xd9\xdc\xdd\xdcSG\xd4a\xba\ 188 | \xec\xea\x1a\xb65=B\xa1\x0e6v\xf2\x8b\xecl\xdf\ 189 | \x1d\xfcAv\xe93\xba\x1c\x93M\x8e\xc7\xadA\x1d?\ 190 | \xce\x98\xbfk\xc82B;h\x80.\x08\x03\xb1md\ 191 | \x98w\xa9\xc9\x16\xb7\x86\xb8\xf1\xa3\x94\xd9\xd91Rw\ 192 | \x9cyB\xab\x86\xb9U\xf1\x1a\xdd\xba\xcb\x9al\xea\x1a\ 193 | L\xe3\x87)\xf27\x0d\xfdM\xd9\xd55\x98\xbe\x87i\ 194 | m%\xf4\xbf\xd5\xcc\xeb\xa4\xc8.\xe9X\xcc\xcf<0\ 195 | \xf4\xbe\xd3\xcc\x092\xe4\x0f,\xe7g\xeeiz\xeb\xce\ 196 | U\xa5\x0c\xf9K9\xe4g\xc6\x9d[\x5c\xca\x7f\x11\xd3\ 197 | \xdb\x95\xfd\xe7\x5c\xd3\xdb\x85\xdf\xef\x99\xa1\xf7\xbc\x1f?\ 198 | O\x13\xfa\xdb>\x7f\xe5\x81\xed\xeb\x07\xdb\xe4q\xfdf\ 199 | \x9b<\xae\x9fm\xe3\xf2\xfd\x8b\xc4\xe5\xfbG\x89\xcb\xf7\ 200 | \xef*\xae\xfe\x7fB\x10\x04A\x10\x04\xf1\xef\x18s,\ 201 | \xd6\xb6\xcf\xeb\x17x\xbc~@\x91\xd7!\x14x\xed\xe3\ 202 | \xbd\xe7\x08\xeb#\xc0~?\xaa\x8dF\x08\xd0\xf3\xdb\x00\ 203 | 5\xac\xb5\x1e\x80\x8f5\xc4\x91EQ=Q\x8b\x13\xd5\ 204 | S^'\xe7\xf9\xed\xda8z\xdfP\xad}\x1e\xf1'\ 205 | \x87\xcc%s\xca\xdc\xb6?\x9foq#\xde0\ 206 | " 207 | 208 | qt_resource_name = b"\ 209 | \x00\x05\ 210 | \x00o\xa6S\ 211 | \x00i\ 212 | \x00c\x00o\x00n\x00s\ 213 | \x00\x0b\ 214 | \x09\xdeI'\ 215 | \x00v\ 216 | \x00i\x00s\x00i\x00b\x00l\x00e\x00.\x00s\x00v\x00g\ 217 | \x00\x08\ 218 | \x05\x9eT\xa7\ 219 | \x00l\ 220 | \x00o\x00c\x00k\x00.\x00s\x00v\x00g\ 221 | \x00\x08\ 222 | \x06|W\x87\ 223 | \x00c\ 224 | \x00o\x00p\x00y\x00.\x00s\x00v\x00g\ 225 | \x00\x0d\ 226 | \x09\x9eH\xe7\ 227 | \x00i\ 228 | \x00n\x00v\x00i\x00s\x00i\x00b\x00l\x00e\x00.\x00s\x00v\x00g\ 229 | \x00\x0b\ 230 | \x0cj!\xc7\ 231 | \x00r\ 232 | \x00e\x00f\x00r\x00e\x00s\x00h\x00.\x00s\x00v\x00g\ 233 | \x00\x0c\ 234 | \x07o(\x7f\ 235 | \x00a\ 236 | \x00p\x00p\x00-\x00i\x00c\x00o\x00n\x00.\x00i\x00c\x00o\ 237 | " 238 | 239 | qt_resource_struct = b"\ 240 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ 241 | \x00\x00\x00\x00\x00\x00\x00\x00\ 242 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ 243 | \x00\x00\x00\x00\x00\x00\x00\x00\ 244 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00\x03\ 245 | \x00\x00\x00\x00\x00\x00\x00\x00\ 246 | \x00\x00\x00,\x00\x00\x00\x00\x00\x01\x00\x00\x01\xf1\ 247 | \x00\x00\x01\x83\x03/4\xc1\ 248 | \x00\x00\x00B\x00\x00\x00\x00\x00\x01\x00\x00\x03\x9f\ 249 | \x00\x00\x01\x83\x03/z1\ 250 | \x00\x00\x00\x94\x00\x01\x00\x00\x00\x01\x00\x00\x09\xb7\ 251 | \x00\x00\x01\x83\x032O\xaa\ 252 | \x00\x00\x00X\x00\x00\x00\x00\x00\x01\x00\x00\x04\xc1\ 253 | \x00\x00\x01\x83\x03/\x9e\x8e\ 254 | \x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ 255 | \x00\x00\x01\x83\x03/\x93q\ 256 | \x00\x00\x00x\x00\x00\x00\x00\x00\x01\x00\x00\x08J\ 257 | \x00\x00\x01\x83\x03/YE\ 258 | " 259 | 260 | def qInitResources(): 261 | QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data) 262 | 263 | def qCleanupResources(): 264 | QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data) 265 | 266 | qInitResources() 267 | -------------------------------------------------------------------------------- /src/ui/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/app-icon.ico 4 | icons/copy.svg 5 | icons/invisible.svg 6 | icons/lock.svg 7 | icons/refresh.svg 8 | icons/visible.svg 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/ui/ui_main.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtCore import QCoreApplication, QMetaObject, QSize, Qt 2 | from PySide6.QtGui import QCursor, QIcon 3 | from PySide6.QtWidgets import (QAbstractSpinBox, QFrame, QHBoxLayout, QLabel, 4 | QLineEdit, QPushButton, QSizePolicy, 5 | QSlider, QSpinBox, QVBoxLayout, QWidget) 6 | 7 | 8 | class Ui_MainWindow(object): 9 | def setupUi(self, MainWindow): 10 | if not MainWindow.objectName(): 11 | MainWindow.setObjectName(u"MainWindow") 12 | MainWindow.resize(542, 418) 13 | MainWindow.setCursor(QCursor(Qt.PointingHandCursor)) 14 | icon = QIcon() 15 | icon.addFile(u":/icons/icons/app-icon.ico", QSize(), QIcon.Normal, QIcon.Off) 16 | MainWindow.setWindowIcon(icon) 17 | MainWindow.setStyleSheet(u"QWidget {\n" 18 | " background-color: #121212;\n" 19 | " color: white;\n" 20 | " font-family: Verdana;\n" 21 | " font-size: 16pt;\n" 22 | " margin: 10px;\n" 23 | "}\n" 24 | "\n" 25 | "QPushButton {\n" 26 | " border: 2px solid gray;\n" 27 | " border-radius: 5px;\n" 28 | "}\n" 29 | "\n" 30 | "QPushButton#btn_lower,\n" 31 | "#btn_upper,\n" 32 | "#btn_digits,\n" 33 | "#btn_special {\n" 34 | " padding: 10px;\n" 35 | "}\n" 36 | "\n" 37 | "QPushButton:hover {\n" 38 | " border-color: #090;\n" 39 | "}\n" 40 | "\n" 41 | "QPushButton:pressed {\n" 42 | " border: 4px solid #090;\n" 43 | " border-radius: 5px;\n" 44 | "}\n" 45 | "\n" 46 | "QPushButton:checked {\n" 47 | " background-color: #006300;\n" 48 | " border-color: #090;\n" 49 | "}\n" 50 | "") 51 | self.centralwidget = QWidget(MainWindow) 52 | self.centralwidget.setObjectName(u"centralwidget") 53 | self.centralwidget.setStyleSheet(u"") 54 | self.verticalLayout = QVBoxLayout(self.centralwidget) 55 | self.verticalLayout.setObjectName(u"verticalLayout") 56 | self.icon_lock = QPushButton(self.centralwidget) 57 | self.icon_lock.setObjectName(u"icon_lock") 58 | self.icon_lock.setEnabled(False) 59 | sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) 60 | sizePolicy.setHorizontalStretch(0) 61 | sizePolicy.setVerticalStretch(0) 62 | sizePolicy.setHeightForWidth(self.icon_lock.sizePolicy().hasHeightForWidth()) 63 | self.icon_lock.setSizePolicy(sizePolicy) 64 | self.icon_lock.setStyleSheet(u"border: none;\n" 65 | "") 66 | icon1 = QIcon() 67 | icon1.addFile(u":/icons/icons/lock.svg", QSize(), QIcon.Normal, QIcon.Off) 68 | self.icon_lock.setIcon(icon1) 69 | self.icon_lock.setIconSize(QSize(100, 100)) 70 | 71 | self.verticalLayout.addWidget(self.icon_lock) 72 | 73 | self.layout_password = QHBoxLayout() 74 | self.layout_password.setObjectName(u"layout_password") 75 | self.frame_password = QFrame(self.centralwidget) 76 | self.frame_password.setObjectName(u"frame_password") 77 | sizePolicy1 = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum) 78 | sizePolicy1.setHorizontalStretch(0) 79 | sizePolicy1.setVerticalStretch(0) 80 | sizePolicy1.setHeightForWidth(self.frame_password.sizePolicy().hasHeightForWidth()) 81 | self.frame_password.setSizePolicy(sizePolicy1) 82 | self.frame_password.setStyleSheet(u"QFrame {\n" 83 | " border: 2px solid gray;\n" 84 | " border-radius: 5px;\n" 85 | " margin-right: 0;\n" 86 | "}\n" 87 | "\n" 88 | "QFrame:hover {\n" 89 | " border-color: #090;\n" 90 | "}\n" 91 | "") 92 | self.frame_password.setFrameShape(QFrame.StyledPanel) 93 | self.frame_password.setFrameShadow(QFrame.Raised) 94 | self.horizontalLayout_10 = QHBoxLayout(self.frame_password) 95 | self.horizontalLayout_10.setObjectName(u"horizontalLayout_10") 96 | self.line_password = QLineEdit(self.frame_password) 97 | self.line_password.setObjectName(u"line_password") 98 | self.line_password.setStyleSheet(u"QLineEdit {\n" 99 | " border: none;\n" 100 | " margin: 0;\n" 101 | " font-size: 20pt;\n" 102 | "}\n" 103 | "") 104 | 105 | self.horizontalLayout_10.addWidget(self.line_password) 106 | 107 | self.btn_visibility = QPushButton(self.frame_password) 108 | self.btn_visibility.setObjectName(u"btn_visibility") 109 | self.btn_visibility.setCursor(QCursor(Qt.PointingHandCursor)) 110 | self.btn_visibility.setStyleSheet(u"QPushButton {\n" 111 | " border: none;\n" 112 | " margin: 0;\n" 113 | " background-color: transparent;\n" 114 | "}\n" 115 | "") 116 | icon2 = QIcon() 117 | icon2.addFile(u":/icons/icons/invisible.svg", QSize(), QIcon.Normal, QIcon.Off) 118 | icon2.addFile(u":/icons/icons/visible.svg", QSize(), QIcon.Normal, QIcon.On) 119 | self.btn_visibility.setIcon(icon2) 120 | self.btn_visibility.setIconSize(QSize(30, 30)) 121 | self.btn_visibility.setCheckable(True) 122 | self.btn_visibility.setChecked(True) 123 | 124 | self.horizontalLayout_10.addWidget(self.btn_visibility) 125 | 126 | self.layout_password.addWidget(self.frame_password) 127 | 128 | self.btn_refresh = QPushButton(self.centralwidget) 129 | self.btn_refresh.setObjectName(u"btn_refresh") 130 | self.btn_refresh.setCursor(QCursor(Qt.PointingHandCursor)) 131 | self.btn_refresh.setStyleSheet(u"QPushButton {\n" 132 | " margin-right: 0;\n" 133 | " margin-left: 0;\n" 134 | "}\n" 135 | "") 136 | icon3 = QIcon() 137 | icon3.addFile(u":/icons/icons/refresh.svg", QSize(), QIcon.Normal, QIcon.On) 138 | self.btn_refresh.setIcon(icon3) 139 | self.btn_refresh.setIconSize(QSize(52, 52)) 140 | 141 | self.layout_password.addWidget(self.btn_refresh) 142 | 143 | self.btn_copy = QPushButton(self.centralwidget) 144 | self.btn_copy.setObjectName(u"btn_copy") 145 | self.btn_copy.setCursor(QCursor(Qt.PointingHandCursor)) 146 | self.btn_copy.setStyleSheet(u"QPushButton {\n" 147 | " padding: 5px;\n" 148 | " margin-left: 0;\n" 149 | "}\n" 150 | "") 151 | icon4 = QIcon() 152 | icon4.addFile(u":/icons/icons/copy.svg", QSize(), QIcon.Normal, QIcon.On) 153 | self.btn_copy.setIcon(icon4) 154 | self.btn_copy.setIconSize(QSize(42, 42)) 155 | 156 | self.layout_password.addWidget(self.btn_copy) 157 | 158 | self.verticalLayout.addLayout(self.layout_password) 159 | 160 | self.layout_info = QHBoxLayout() 161 | self.layout_info.setObjectName(u"layout_info") 162 | self.label_strength = QLabel(self.centralwidget) 163 | self.label_strength.setObjectName(u"label_strength") 164 | sizePolicy2 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) 165 | sizePolicy2.setHorizontalStretch(0) 166 | sizePolicy2.setVerticalStretch(0) 167 | sizePolicy2.setHeightForWidth(self.label_strength.sizePolicy().hasHeightForWidth()) 168 | self.label_strength.setSizePolicy(sizePolicy2) 169 | self.label_strength.setAlignment(Qt.AlignCenter) 170 | 171 | self.layout_info.addWidget(self.label_strength) 172 | 173 | self.label_entropy = QLabel(self.centralwidget) 174 | self.label_entropy.setObjectName(u"label_entropy") 175 | sizePolicy2.setHeightForWidth(self.label_entropy.sizePolicy().hasHeightForWidth()) 176 | self.label_entropy.setSizePolicy(sizePolicy2) 177 | self.label_entropy.setAlignment(Qt.AlignCenter) 178 | 179 | self.layout_info.addWidget(self.label_entropy) 180 | 181 | self.verticalLayout.addLayout(self.layout_info) 182 | 183 | self.layout_length = QHBoxLayout() 184 | self.layout_length.setObjectName(u"layout_length") 185 | self.slider_length = QSlider(self.centralwidget) 186 | self.slider_length.setObjectName(u"slider_length") 187 | self.slider_length.setCursor(QCursor(Qt.PointingHandCursor)) 188 | self.slider_length.setStyleSheet(u"QSlider::groove:horizontal {\n" 189 | " background-color: transparent;\n" 190 | " height: 5px;\n" 191 | "}\n" 192 | "\n" 193 | "QSlider::sub-page:horizontal {\n" 194 | " background-color: #090;\n" 195 | "}\n" 196 | "\n" 197 | "QSlider::add-page:horizontal {\n" 198 | " background-color: gray;\n" 199 | "}\n" 200 | "\n" 201 | "QSlider::handle:horizontal {\n" 202 | " background-color: white;\n" 203 | " width: 22px;\n" 204 | " border-radius: 10px;\n" 205 | " margin-top: -8px;\n" 206 | " margin-bottom: -8px;\n" 207 | "}\n" 208 | "") 209 | self.slider_length.setMaximum(100) 210 | self.slider_length.setValue(12) 211 | self.slider_length.setOrientation(Qt.Horizontal) 212 | 213 | self.layout_length.addWidget(self.slider_length) 214 | 215 | self.spinbox_length = QSpinBox(self.centralwidget) 216 | self.spinbox_length.setObjectName(u"spinbox_length") 217 | self.spinbox_length.setStyleSheet(u"QSpinBox {\n" 218 | " border: 2px solid gray;\n" 219 | " border-radius: 5px;\n" 220 | " background: transparent;\n" 221 | " padding: 2px;\n" 222 | "}\n" 223 | "\n" 224 | "QSpinBox:hover {\n" 225 | " border-color: #009900;\n" 226 | "}\n" 227 | "") 228 | self.spinbox_length.setAlignment(Qt.AlignCenter) 229 | self.spinbox_length.setButtonSymbols(QAbstractSpinBox.NoButtons) 230 | self.spinbox_length.setMaximum(100) 231 | self.spinbox_length.setValue(12) 232 | 233 | self.layout_length.addWidget(self.spinbox_length) 234 | 235 | self.verticalLayout.addLayout(self.layout_length) 236 | 237 | self.layout_characters = QHBoxLayout() 238 | self.layout_characters.setObjectName(u"layout_characters") 239 | self.btn_lower = QPushButton(self.centralwidget) 240 | self.btn_lower.setObjectName(u"btn_lower") 241 | self.btn_lower.setCursor(QCursor(Qt.PointingHandCursor)) 242 | self.btn_lower.setCheckable(True) 243 | self.btn_lower.setChecked(True) 244 | 245 | self.layout_characters.addWidget(self.btn_lower) 246 | 247 | self.btn_upper = QPushButton(self.centralwidget) 248 | self.btn_upper.setObjectName(u"btn_upper") 249 | self.btn_upper.setCursor(QCursor(Qt.PointingHandCursor)) 250 | self.btn_upper.setCheckable(True) 251 | self.btn_upper.setChecked(True) 252 | 253 | self.layout_characters.addWidget(self.btn_upper) 254 | 255 | self.btn_digits = QPushButton(self.centralwidget) 256 | self.btn_digits.setObjectName(u"btn_digits") 257 | self.btn_digits.setCursor(QCursor(Qt.PointingHandCursor)) 258 | self.btn_digits.setCheckable(True) 259 | self.btn_digits.setChecked(True) 260 | 261 | self.layout_characters.addWidget(self.btn_digits) 262 | 263 | self.btn_special = QPushButton(self.centralwidget) 264 | self.btn_special.setObjectName(u"btn_special") 265 | self.btn_special.setCursor(QCursor(Qt.PointingHandCursor)) 266 | self.btn_special.setCheckable(True) 267 | 268 | self.layout_characters.addWidget(self.btn_special) 269 | 270 | self.verticalLayout.addLayout(self.layout_characters) 271 | 272 | MainWindow.setCentralWidget(self.centralwidget) 273 | 274 | self.retranslateUi(MainWindow) 275 | 276 | QMetaObject.connectSlotsByName(MainWindow) 277 | 278 | def retranslateUi(self, MainWindow): 279 | MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"Password Generator", None)) 280 | self.icon_lock.setText("") 281 | self.btn_visibility.setText("") 282 | self.btn_refresh.setText("") 283 | self.btn_refresh.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+R", None)) 284 | self.btn_copy.setText("") 285 | self.btn_copy.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+C", None)) 286 | self.label_strength.setText("") 287 | self.label_entropy.setText("") 288 | self.btn_lower.setText(QCoreApplication.translate("MainWindow", u"a-z", None)) 289 | self.btn_upper.setText(QCoreApplication.translate("MainWindow", u"A-Z", None)) 290 | self.btn_digits.setText(QCoreApplication.translate("MainWindow", u"0-9", None)) 291 | self.btn_special.setText(QCoreApplication.translate("MainWindow", u"#$%", None)) 292 | --------------------------------------------------------------------------------