Skip to content

Commit bd608a0

Browse files
authored
Merge pull request #1 from marcohenning/develop
Version 1.0.0
2 parents 6375adb + a2837e1 commit bd608a0

2,475 files changed

Lines changed: 5285 additions & 3 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.coveragerc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[run]
2+
omit = tests/*

README.md

Lines changed: 222 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,222 @@
1-
# pyqtcaptcha
2-
A modern and fully customizable CAPTCHA library for PyQt and PySide
1+
# PyQt CAPTCHA
2+
3+
[![PyPI](https://img.shields.io/badge/pypi-v1.0.0-blue)](https://pypi.org/project/pyqtcaptcha)
4+
[![Python](https://img.shields.io/badge/python-3.7+-blue)](https://github.com/marcohenning/pyqtcaptcha)
5+
[![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/marcohenning/pyqtcaptcha/blob/master/LICENSE)
6+
[![Coverage](https://img.shields.io/badge/coverage-96%25-neon)](https://github.com/marcohenning/pyqtcaptcha)
7+
[![Build](https://img.shields.io/badge/build-passing-neon)](https://github.com/marcohenning/pyqtcaptcha)
8+
9+
A modern and fully customizable CAPTCHA library for PyQt and PySide.
10+
11+
![main](https://github.com/user-attachments/assets/14af04a6-c953-4038-8121-0c7c91b92f9f)
12+
13+
## About
14+
15+
This library was made to bring CAPTCHA support to PyQt desktop applications. While the measure is mostly used in web applications due to their ease of automation, desktop applications can also be automated using programs like AutoHotkey. This CAPTCHA widget is fully customizable from everything related to visuals to the difficulty of the tasks and helps protect your application from spam and abuse through automation.
16+
17+
> All images have been sourced from public domain video material and are not subject to copyright restrictions.
18+
19+
## Installation
20+
21+
```
22+
pip install pyqtcaptcha
23+
```
24+
25+
## Example
26+
27+
```python
28+
from PyQt6.QtWidgets import QMainWindow
29+
from pyqtcaptcha import Captcha, CaptchaDifficulty
30+
31+
32+
class Window(QMainWindow):
33+
def __init__(self):
34+
super().__init__(parent=None)
35+
36+
self.captcha = Captcha(self)
37+
self.captcha.setDifficulty(CaptchaDifficulty.HARD)
38+
self.captcha.setFixedSize(130, 40)
39+
```
40+
41+
## Documentation
42+
43+
> **IMPORTANT:** <br>Styling of the widget must not be done by setting the stylesheet manually as the widget calculates the stylesheet itself and overrides it. Use the provided methods such as `setBackgroundColor()` instead.
44+
45+
* **Setting the difficulty:**
46+
```python
47+
captcha.setDifficulty(CaptchaDifficulty.HARD)
48+
```
49+
50+
> On easy difficulty the user will have to complete image selection tasks (i.e. "Select all images containing cars"). On hard difficulty the user will have to select image squares (i.e. "Select all squares containing traffic lights"). Medium difficulty alternates between these two options randomly.
51+
52+
* **Checking the state:**
53+
```python
54+
captcha.isPassed()
55+
```
56+
57+
* **Resetting the state:**
58+
```python
59+
captcha.reset()
60+
```
61+
62+
* **Setting the button text:**
63+
```python
64+
captcha.setText('Text')
65+
```
66+
67+
* **Setting the button foreground color:**
68+
```python
69+
captcha.setButtonForegroundColor(QColor(255, 0, 0))
70+
```
71+
72+
* **Setting the button background color:**
73+
```python
74+
captcha.setButtonBackgroundColor(QColor(0, 255, 0))
75+
```
76+
77+
* **Setting the button border color:**
78+
```python
79+
captcha.setButtonBorderColor(QColor(0, 0, 255))
80+
```
81+
82+
* **Setting the button border width:**
83+
```python
84+
captcha.setButtonBorderWidth(1)
85+
```
86+
87+
* **Setting the button border radius:**
88+
```python
89+
captcha.setButtonBorderRadius(5)
90+
```
91+
92+
* **Setting the checkbox color:**
93+
```python
94+
captcha.setCheckboxColor(QColor(255, 255, 0))
95+
```
96+
97+
* **Setting the checkbox width:**
98+
```python
99+
captcha.setCheckboxWidth(2)
100+
```
101+
102+
> The checkbox width is also used for the width of the checkmark.
103+
104+
* **Setting the checkmark color:**
105+
```python
106+
captcha.setCheckmarkColor(QColor(0, 255, 255))
107+
```
108+
109+
* **Setting the CAPTCHA popup foreground color:**
110+
```python
111+
captcha.setCaptchaForegroundColor(QColor(255, 255, 255))
112+
```
113+
114+
> The foreground color (white by default) is used for text elements like the captcha prompt and submit button text as well as for the checkmark on selected images.
115+
116+
* **Setting the CAPTCHA popup background color:**
117+
```python
118+
captcha.setCaptchaBackgroundColor(QColor(255, 0, 255))
119+
```
120+
121+
> The background color (white by default) is used for the background of the popup window.
122+
123+
* **Setting the CAPTCHA popup border color:**
124+
```python
125+
captcha.setCaptchaBorderColor(QColor(100, 255, 0))
126+
```
127+
128+
* **Setting the CAPTCHA popup border radius:**
129+
```python
130+
captcha.setCaptchaBorderRadius(10)
131+
```
132+
133+
* **Setting the CAPTCHA popup primary color:**
134+
```python
135+
captcha.setCaptchaPrimaryColor(QColor(0, 100, 255))
136+
```
137+
138+
> The primary color (green by default) is used for the background of the captcha prompt and the background of the submit button.
139+
140+
* **Setting the CAPTCHA popup primary color (hovered):**
141+
```python
142+
captcha.setCaptchaPrimaryColorHovered(QColor(255, 100, 0))
143+
```
144+
145+
> The primary hovered color (light-green by default) is used for the hovered background of the submit button.
146+
147+
* **Setting the CAPTCHA popup secondary color:**
148+
```python
149+
captcha.setCaptchaSecondaryColor(QColor(255, 255, 100))
150+
```
151+
152+
> The secondary color (gray by default) is used for elements like the refresh button, visual button, audio button and play button.
153+
154+
* **Setting the CAPTCHA popup secondary color (hovered):**
155+
```python
156+
captcha.setCaptchaSecondaryColorHovered(QColor(0, 255, 50))
157+
```
158+
159+
> The secondary hovered color (dark-gray by default) is used as the hover color for elements like the refresh button, visual button, audio button and play button.
160+
161+
* **Connecting methods to the provided events:**
162+
```python
163+
captcha.started.connect(self.some_function)
164+
captcha.aborted.connect(self.some_function)
165+
captcha.passed.connect(self.some_function)
166+
captcha.failed.connect(self.some_function)
167+
```
168+
169+
> The `started` signal is emitted every time the popup window gets opened, while the `aborted` signal is emitted every time the popup window gets closed without submitting an answer. When the submit button is pressed either the `passed` signal or the `failed` signal is emitted depending on the correctness of the answer.
170+
171+
**<br>All available methods:**
172+
173+
| Method | Description |
174+
|--------------------------------------------------------|-------------------------------------------------|
175+
| `text(self)` | Get the current button text |
176+
| `setText(self, text: str)` | Set the button text |
177+
| `getButtonForegroundColor(self)` | Get the current button foreground color |
178+
| `setButtonForegroundColor(self, color: QColor)` | Set the button foreground color |
179+
| `getButtonBackgroundColor(self)` | Get the current button background color |
180+
| `setButtonBackgroundColor(self, color: QColor)` | Set the button background color |
181+
| `getButtonBorderColor(self)` | Get the current button border color |
182+
| `setButtonBorderColor(self, color: QColor)` | Set the button border color |
183+
| `getButtonBorderWidth(self)` | Get the current button border width |
184+
| `setButtonBorderWidth(self, width: int)` | Set the button border width |
185+
| `getButtonBorderRadius(self)` | Get the current button border radius |
186+
| `setButtonBorderRadius(self, radius: int)` | Set the button border radius |
187+
| `getCheckboxColor(self)` | Get the current checkbox color |
188+
| `setCheckboxColor(self, color: QColor)` | Set the checkbox color |
189+
| `getCheckboxWidth(self)` | Get the current checkbox width |
190+
| `setCheckboxWidth(self, width: int)` | Set the checkbox width |
191+
| `getCheckmarkColor(self)` | Get the current checkmark color |
192+
| `setCheckmarkColor(self, color: QColor)` | Set the checkmark color |
193+
| `getCaptchaForegroundColor(self)` | Get the current captcha foreground color |
194+
| `setCaptchaForegroundColor(self, color: QColor)` | Set the captcha foreground color |
195+
| `getCaptchaBackgroundColor(self)` | Get the current captcha background color |
196+
| `setCaptchaBackgroundColor(self, color: QColor)` | Set the captcha background color |
197+
| `getCaptchaBorderColor(self)` | Get the current captcha border color |
198+
| `setCaptchaBorderColor(self, color: QColor)` | Set the captcha border color |
199+
| `getCaptchaBorderRadius(self)` | Get the current captcha border radius |
200+
| `setCaptchaBorderRadius(self, radius: int)` | Set the captcha border radius |
201+
| `getCaptchaPrimaryColor(self)` | Get the current captcha primary color |
202+
| `setCaptchaPrimaryColor(self, color: QColor)` | Set the captcha primary color |
203+
| `getCaptchaPrimaryColorHovered(self)` | Get the current captcha primary hovered color |
204+
| `setCaptchaPrimaryColorHovered(self, color: QColor)` | Set the captcha primary hovered color |
205+
| `getCaptchaSecondaryColor(self)` | Get the current captcha secondary color |
206+
| `setCaptchaSecondaryColor(self, color: QColor)` | Set the captcha secondary color |
207+
| `getCaptchaSecondaryColorHovered(self)` | Get the current captcha secondary hovered color |
208+
| `setCaptchaSecondaryColorHovered(self, color: QColor)` | Set the captcha secondary hovered color |
209+
| `getDifficulty(self)` | Get the current captcha difficulty |
210+
| `setDifficulty(self, difficulty: CaptchaDifficulty)` | Set the captcha difficulty |
211+
| `isPassed(self)` | Has the captcha been completed? |
212+
| `setPassed(self, passed: bool)` | Set the completion status of the captcha |
213+
| `reset(self)` | Reset the captcha |
214+
| `getPopup(self)` | Get the current captcha popup |
215+
216+
## Showcase
217+
218+
https://github.com/user-attachments/assets/4a9245ea-3cee-40fd-9457-123a4dc0c58a
219+
220+
## License
221+
222+
This software is licensed under the [MIT license](https://github.com/marcohenning/pyqtcaptcha/blob/master/LICENSE).

examples/pyqt6/example.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import sys
2+
from PyQt6.QtWidgets import QMainWindow, QApplication, QWidget, QGridLayout
3+
from src.pyqtcaptcha import Captcha
4+
5+
6+
class Window(QMainWindow):
7+
8+
def __init__(self):
9+
super().__init__(parent=None)
10+
11+
# Window settings
12+
self.setWindowTitle('Example')
13+
self.resize(500, 300)
14+
15+
# Create layout
16+
grid_layout = QGridLayout()
17+
captcha = Captcha()
18+
captcha.setFixedSize(130, 39)
19+
grid_layout.addWidget(captcha)
20+
21+
# Apply layout
22+
central_widget = QWidget()
23+
central_widget.setLayout(grid_layout)
24+
self.setCentralWidget(central_widget)
25+
self.setFocus()
26+
27+
28+
# Run the example
29+
if __name__ == '__main__':
30+
app = QApplication(sys.argv)
31+
window = Window()
32+
window.show()
33+
app.exec()

examples/pyside6/example.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import sys
2+
from PySide6.QtWidgets import QMainWindow, QApplication, QWidget, QGridLayout
3+
from src.pyqtcaptcha import Captcha
4+
5+
6+
class Window(QMainWindow):
7+
8+
def __init__(self):
9+
super().__init__(parent=None)
10+
11+
# Window settings
12+
self.setWindowTitle('Example')
13+
self.resize(500, 300)
14+
15+
# Create layout
16+
grid_layout = QGridLayout()
17+
captcha = Captcha()
18+
captcha.setFixedSize(130, 39)
19+
grid_layout.addWidget(captcha)
20+
21+
# Apply layout
22+
central_widget = QWidget()
23+
central_widget.setLayout(grid_layout)
24+
self.setCentralWidget(central_widget)
25+
self.setFocus()
26+
27+
28+
# Run the example
29+
if __name__ == '__main__':
30+
app = QApplication(sys.argv)
31+
window = Window()
32+
window.show()
33+
app.exec()

pytest.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[pytest]
2+
qt_api=pyqt6

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
QtPy>=2.4.1
2+
pygame>=2.6.0

setup.cfg

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
[options.entry_points]
3+
pyinstaller40 =
4+
hook-dirs = pyqtcaptcha.hooks:get_hook_dirs

setup.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from setuptools import setup, find_namespace_packages
2+
3+
4+
with open('README.md', 'r') as fh:
5+
readme = "\n" + fh.read()
6+
7+
setup(
8+
name='pyqtcaptcha',
9+
version='1.0.0',
10+
author='Marco Henning',
11+
license='MIT',
12+
packages=find_namespace_packages(where="src"),
13+
package_dir={"": "src"},
14+
package_data={
15+
'pyqtcaptcha.files': ['**/*.png', '**/*.mp3'],
16+
'pyqtcaptcha.hooks': ['*.py']
17+
},
18+
install_requires=[
19+
'QtPy>=2.4.1',
20+
'pygame>=2.6.0'
21+
],
22+
python_requires='>=3.7',
23+
description='A modern and fully customizable CAPTCHA library for PyQt and PySide',
24+
long_description=readme,
25+
long_description_content_type='text/markdown',
26+
url='https://github.com/marcohenning/pyqtcaptcha',
27+
keywords=['python', 'pyqt', 'qt', 'captcha'],
28+
classifiers=[
29+
'Programming Language :: Python :: 3',
30+
'Operating System :: OS Independent',
31+
'License :: OSI Approved :: MIT License'
32+
]
33+
)

src/pyqtcaptcha/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from .captcha import Captcha
1+
from .captcha import Captcha, CaptchaType, CaptchaTask, CaptchaDifficulty

0 commit comments

Comments
 (0)