1#!/usr/bin/env python3
2
3import re
4
5from asciimatics.effects import Julia, Clock
6from asciimatics.widgets import Frame, TextBox, Layout, Label, Divider, Text, \
7    CheckBox, RadioButtons, Button, PopUpDialog
8from asciimatics.scene import Scene
9from asciimatics.screen import Screen
10from asciimatics.exceptions import ResizeScreenError, NextScene, StopApplication, \
11    InvalidFields
12import sys
13
14# Initial data for the form
15form_data = {
16    "TA": ["Hello world!", "How are you?"],
17    "TB": "alphabet",
18    "TC": "123",
19    "TD": "a@b.com",
20    "Things": 2,
21    "CA": False,
22    "CB": True,
23    "CC": False,
24}
25
26
27class DemoFrame(Frame):
28    def __init__(self, screen):
29        super(DemoFrame, self).__init__(screen,
30                                        int(screen.height * 2 // 3),
31                                        int(screen.width * 2 // 3),
32                                        data=form_data,
33                                        has_shadow=True,
34                                        name="My Form")
35        layout = Layout([1, 18, 1])
36        self.add_layout(layout)
37        self._reset_button = Button("Reset", self._reset)
38        layout.add_widget(Label("Group 1:"), 1)
39        layout.add_widget(TextBox(5,
40                                  label="My First Box:",
41                                  name="TA",
42                                  on_change=self._on_change), 1)
43        layout.add_widget(
44            Text(label="Alpha:",
45                 name="TB",
46                 on_change=self._on_change,
47                 validator="^[a-zA-Z]*$"), 1)
48        layout.add_widget(
49            Text(label="Number:",
50                 name="TC",
51                 on_change=self._on_change,
52                 validator="^[0-9]*$"), 1)
53        layout.add_widget(
54            Text(label="Email:",
55                 name="TD",
56                 on_change=self._on_change,
57                 validator=self._check_email), 1)
58        layout.add_widget(Divider(height=2), 1)
59        layout.add_widget(Label("Group 2:"), 1)
60        layout.add_widget(RadioButtons([("Option 1", 1),
61                                        ("Option 2", 2),
62                                        ("Option 3", 3)],
63                                       label="A Longer Selection:",
64                                       name="Things",
65                                       on_change=self._on_change), 1)
66        layout.add_widget(CheckBox("Field 1",
67                                   label="A very silly long name for fields:",
68                                   name="CA",
69                                   on_change=self._on_change), 1)
70        layout.add_widget(
71            CheckBox("Field 2", name="CB", on_change=self._on_change), 1)
72        layout.add_widget(
73            CheckBox("Field 3", name="CC", on_change=self._on_change), 1)
74        layout.add_widget(Divider(height=3), 1)
75        layout2 = Layout([1, 1, 1])
76        self.add_layout(layout2)
77        layout2.add_widget(self._reset_button, 0)
78        layout2.add_widget(Button("View Data", self._view), 1)
79        layout2.add_widget(Button("Quit", self._quit), 2)
80        self.fix()
81
82    def _on_change(self):
83        changed = False
84        self.save()
85        for key, value in self.data.items():
86            if key not in form_data or form_data[key] != value:
87                changed = True
88                break
89        self._reset_button.disabled = not changed
90
91    def _reset(self):
92        self.reset()
93        raise NextScene()
94
95    def _view(self):
96        # Build result of this form and display it.
97        try:
98            self.save(validate=True)
99            message = "Values entered are:\n\n"
100            for key, value in self.data.items():
101                message += "- {}: {}\n".format(key, value)
102        except InvalidFields as exc:
103            message = "The following fields are invalid:\n\n"
104            for field in exc.fields:
105                message += "- {}\n".format(field)
106        self._scene.add_effect(
107            PopUpDialog(self._screen, message, ["OK"]))
108
109    def _quit(self):
110        self._scene.add_effect(
111            PopUpDialog(self._screen,
112                        "Are you sure?",
113                        ["Yes", "No"],
114                        on_close=self._quit_on_yes))
115
116    @staticmethod
117    def _check_email(value):
118        m = re.match(r"^[a-zA-Z0-9_\-.]+@[a-zA-Z0-9_\-.]+\.[a-zA-Z0-9_\-.]+$",
119                     value)
120        return len(value) == 0 or m is not None
121
122    @staticmethod
123    def _quit_on_yes(selected):
124        # Yes is the first button
125        if selected == 0:
126            raise StopApplication("User requested exit")
127
128
129class ClockFrame(Frame):
130    def __init__(self, screen, x, y):
131        super(ClockFrame, self).__init__(screen, 13, 26,
132                                         name="Clock",
133                                         has_shadow=True,
134                                         x=x, y=y)
135        self.add_effect(Clock(self._canvas, 13, 7, 7, Screen.COLOUR_BLUE))
136        self.fix()
137
138
139def demo(screen, scene):
140    scenes = []
141    effects = [
142        Julia(screen),
143        ClockFrame(screen, 0, 0),
144        ClockFrame(screen, screen.width - 26, 0),
145        ClockFrame(screen, 0, screen.height - 13),
146        ClockFrame(screen, screen.width - 26, screen.height - 13),
147        DemoFrame(screen),
148    ]
149    scenes.append(Scene(effects, -1))
150
151    screen.play(scenes, stop_on_resize=True, start_scene=scene)
152
153
154last_scene = None
155while True:
156    try:
157        Screen.wrapper(demo, catch_interrupt=False, arguments=[last_scene])
158        sys.exit(0)
159    except ResizeScreenError as e:
160        last_scene = e.scene
161