├── .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 | [](#download)
4 | 
5 | [](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 | 
14 |
15 | - Ubuntu 22.04
16 |
17 | 
18 |
19 | - macOS 10.15 Catalina
20 |
21 | 
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>\
149 | svg>\
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 |
--------------------------------------------------------------------------------