8000 gui: add restricted input · pythonarcade/arcade@61806a6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 61806a6

Browse files
committed
gui: add restricted input
1 parent dab1e62 commit 61806a6

File tree

3 files changed

+177
-0
lines changed

3 files changed

+177
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""Example of using experimental UIRestrictedInputText.
2+
3+
If Arcade and Python are properly installed, you can run this example with:
4+
python -m arcade.examples.gui.own_widgets
5+
"""
6+
7+
from __future__ import annotations
8+
9+
import arcade
10+
from arcade.gui import UIAnchorLayout, UIBoxLayout, UIView
11+
from arcade.gui.experimental.restricted_input import UIIntInput
12+
13+
14+
class MyView(UIView):
15+
def __init__(self):
16+
super().__init__()
17+
self.background_color = arcade.uicolor.BLUE_BELIZE_HOLE
18+
19+
root = self.ui.add(UIAnchorLayout())
20+
bars = root.add(UIBoxLayout(space_between=10))
21+
22+
# UIWidget based progress bar
23+
self.input_field = UIIntInput(width=300, height=40, font_size=22)
24+
bars.add(self.input_field)
25+
26+
27+
def main():
28+
window = arcade.Window(antialiasing=False)
29+
window.show_view(MyView())
30+
arcade.run()
31+
32+
33+
if __name__ == "__main__":
34+
main()
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""
2+
This is an experimental implementation of a restricted input field.
3+
If the implementation is successful, the feature will be merged into the existing UIInputText class.
4+
"""
5+
6+
from typing import Optional
7+
8+
9+
from arcade.gui import UIEvent, UIInputText
10+
11+
12+
class UIRestrictedInput(UIInputText):
13+
"""
14+
A text input field that restricts the input to a certain type.
15+
16+
This class is meant to be subclassed to create custom input fields
17+
that restrict the input by providing a custom validation method.
18+
19+
Invalid inputs are dropped.
20+
"""
21+
22+
@property
23+
def text(self):
24+
"""Text of the input field."""
25+
return self.doc.text
26+
27+
@text.setter
28+
def text(self, text: str):
29+
if not self.validate(text):
30+
# if the text is invalid, do not update the text
31+
return
32+
33+
# we can not call super().text = text here: https://bugs.python.org/issue14965
34+
UIInputText.text.__set__(self, text) # type: ignore
35+
36+
def on_event(self, event: UIEvent) -> Optional[bool]:
37+
# check if text changed during event handling,
38+
# if so we need to validate the new text
39+
old_text = self.text
40+
pos = self.caret.position
41+
42+
result = super().on_event(event)
43+
if not self.validate(self.text):
44+
self.text = old_text
45+
self.caret.position = pos
46+
47+
return result
48+
49+
def validate(self, text) -> bool:
50+
"""Override this method to add custom validation logic.
51+
52+
Be aware that an empty string should always be valid.
53+
"""
54+
return True
55+
56+
57+
class UIIntInput(UIRestrictedInput):
58+
def validate(self, text) -> bool:
59+
if text == "":
60+
return True
61+
62+
try:
63+
int(text)
64+
return True
65+
except ValueError:
66+
return False
67+
68+
69+
class UIFloatInput(UIRestrictedInput):
70+
def validate(self, text) -> bool:
71+
if text == "":
72+
return True
73+
74+
try:
75+
float(text)
76+
return True
77+
except ValueError:
78+
return False
79+
80+
81+
class UIRegexInput(UIRestrictedInput):
82+
def __init__(self, *args, pattern: str = r".*", **kwargs):
83+
super().__init__()
84+
self.pattern = pattern
85+
86+
def validate(self, text: str) -> bool:
87+
if text == "":
88+
return True
89+
90+
import re
91+
92+
return re.match(self.pattern, text) is not None
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from arcade.gui.experimental import UIPasswordInput
2+
from arcade.gui.experimental.restricted_input import UIRestrictedInput, UIIntInput, UIRegexInput
3+
4+
5+
def test_restricted_input_ignore_invalid_input(ui):
6+
class FailingInput(UIRestrictedInput):
7+
def validate(self, text) -> bool:
8+
return text == ""
9+
10+
fi = ui.add(FailingInput())
11+
12+
# WHEN
13+
ui.click(fi.center_x, fi.center_y)
14+
for l in "abcdef-.,1234567890":
15+
ui.type_text(l)
16+
17+
assert fi.text == ""
18+
19+
20+
def test_int_input_accepts_only_digits(ui):
21+
fi = ui.add(UIIntInput())
22+
23+
# WHEN
24+
ui.click(fi.center_x, fi.center_y)
25+
for l in "abcdef-.,1234567890":
26+
ui.type_text(l)
27+
28+
assert fi.text == "1234567890"
29+
30+
31+
def test_float_input_accepts_only_float(ui):
32+
fi = ui.add(UIIntInput())
33+
34+
# WHEN
35+
ui.click(fi.center_x, fi.center_y)
36+
for l in "abcdef-.,1234567890":
37+
ui.type_text(l)
38+
39+
assert fi.text == "1234567890"
40+
41+
42+
def test_regex_input_accepts_only_matching_patterns(ui):
43+
fi = ui.add(UIRegexInput(pattern="^[0-9]+$"))
44+
45+
# WHEN
46+
ui.click(fi.center_x, fi.center_y)
47+
for l in "abcdef-.,1234567890":
48+
ui.type_text(l)
49+
50+
assert fi.text == "1234567890"
51+

0 commit comments

Comments
 (0)
0