-
Notifications
You must be signed in to change notification settings - Fork 0
/
component.py
255 lines (196 loc) · 9.14 KB
/
component.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
import warnings
from dataclasses import dataclass, field
from unittest.mock import Mock
from _anvil_designer.common_structures import ClassDict
# from anvil import Boolean, Color, Html, String, Themerole
def default_val(val):
return lambda: val
String = str
Number = float
Integer = int
Color = str
Boolean = bool
Themerole = str
Object = object
Seconds = float
Items = list
Datagridcolumns = list
Pixels = int
Uri = str
Html = str
Icon = str
Form = object
@dataclass
class Component:
_events: dict = field(default_factory=dict)
def add_event_handler(self, event_name, handler_func):
"""Add an event handler function to be called when the event happens on this component. Event handlers will be
called in the order they are added. Adding the same event handler multiple times will mean it gets called
multiple times. """
ev = self._events.get(event_name, [])
ev.append(handler_func)
self._events.update({event_name: ev})
def get_event_handlers(self, event_name):
"""Get the current event_handlers for a given event_name """
return self._events.get(event_name, [])
def raise_event(self, event_name, **event_args):
"""Trigger the event on this component. Any keyword arguments are passed to the handler function. """
for ev in self._events.get(event_name, []):
ev(**event_args)
def remove_event_handler(self, event_name, handler_func=None):
"""Remove a specific event handler function for a given event. Calling remove_event_handler with just the
event name will remove all the handlers for this event """
if handler_func is None:
self._events.pop(event_name, None)
else:
self._events[event_name].remove(handler_func)
def remove_from_parent(self):
"""Remove this component from its parent container. """
pass
def scroll_into_view(self, smooth=False):
"""Scroll the window to make sure this component is in view. """
pass
def set_event_handler(self, event_name, handler_func):
"""Set a function to call when the ‘event_name’ event happens on this component. Using set_event_handler
removes all other handlers. Setting the handler function to None removes all handlers. """
self._events[event_name] = [handler_func]
@dataclass
class Container(Component):
_components: list = field(default_factory=list)
def add_component(self, component, **kwargs):
"""Add a component to this container. """
if kwargs.get('index', None):
self._components.insert(kwargs["index"], component)
else:
self._components.append(component)
pass
def clear(self):
"""Remove all components from this container """
self._components = []
def get_components(self):
"""Get a list of components in this container """
return self._components
def raise_event_on_children(self, event_name, **event_args):
"""Trigger the ‘event_name’ event on all children of this component. Any keyword arguments are passed to the
handler function. """
pass
def refresh_data_bindings(self):
"""All bound properties for a particular Form are updated whenever:
self.refresh_data_bindings() is called, or
self.init_components(**properties) is called, or
the self.item for the Form is set."""
pass
class Media:
"""Media object subclassed by BlobMedia and UrlMedia.
Attributes
----------
content_type : str
the MIME type of this media. This is a string with values like "text/plain" or "image/png".
content : bytes
a binary string of the image or file
name : str
optional filename
length:
length in bytes of `content`
url:
Gives an URL where this media can be downloaded, if this Media is “permanent” (e.g. if it is stored in
Data Tables, or a Google Drive file). If a Media object is not permanent (e.g. it is an anonymous
BlobMedia object), its url property will be None. However, you can still download non-permanent Media
objects using Link components, or display them with Image components.
Methods
-------
get_bytes
returns the content as a string
"""
def __init__(self, url: str = None, content_type: str = None, content: bytes = None, name: str = None):
self.url = url
self.content_type = content_type
if content_type == "text/plain" and not isinstance(content, bytes):
raise TypeError("`content` should be type bytes")
self.content = content
self.name = name
@property
def length(self):
"""length in bytes of `content`"""
return len(self.content)
def get_bytes(self) -> str:
"""Get a binary string of the data represented by this Media object """
return self.content.decode()
def alert(content, title="", buttons=None, large=False, dismissible=True, role=None):
"""Pop up an alert box. By default, it will have a single “OK” button which will return True when clicked."""
if buttons:
return buttons[0][1]
return True
def confirm(content, title="", buttons=None, large=False, dismissible=False, role=None):
"""Pop up a confirmation box. By default, it will have “Yes” and “No” buttons which will return True and False
respectively when clicked."""
return True
def download(media: Media):
"""Download the given Media Object immediately in the user’s browser."""
return Mock()
def get_focused_component():
"""Get the currently focused Anvil component, or None if focus is not in a component."""
return Mock()
def get_open_form():
"""Returns the form most recently opened with open_form()."""
return RunTimeSettings.get_open_form()
def get_url_hash():
"""Get the decoded hash (the part after the ‘#’ character) of the URL used to open this app. If the first
character of the hash is a question mark (eg ‘#?a=foo&b=bar’), it will be interpreted as query-string-type
parameters and returned as a dictionary (eg {‘a’: ‘foo’, ‘b’: ‘bar’})."""
return Mock()
def open_form(form, *args, **kwargs):
"""Open the specified form as a new page.If ‘form’ is a string, a new form will be created (extra arguments will
be passed to its constructor).If ‘form’ is a Form object, it will be opened directly."""
return Mock()
def set_default_error_handling(handler_fn):
"""Set a function to be called when an uncaught exception occurs. If set to None, a pop-up will appear letting
the user know that an error has occurred."""
return Mock()
def set_url_hash(*args, **kwargs):
"""This is added for `anvil_extras`. for some reason it is not in the anvil documentation."""
return Mock()
@dataclass
class RunTimeSettings:
_open_form: str = None # used for get_open_form()
@classmethod
def get_open_form(cls):
"""Returns the form instance of the form that is currently open.
This is used by get_open_form() to return the form instance and
is set by a form dependent on HtmlTemplate (when it is opened)."""
if cls._open_form is None:
# no form dependent on HtmlTemplate has been opened yet
warnings.warn("`get_open_form` points to the form with HtmlTemplate.\n"
"This form has not been instantiated.")
return Mock()
return cls._open_form
@classmethod
def set_open_form(cls, form_instance):
cls._open_form = form_instance
return
@dataclass
class HtmlTemplate(Container):
background: Color = field(default_factory=Color) # The background colour of this component.
border: String = field(default_factory=String) # The border of this component. Can take any valid CSS border value.
foreground: Color = field(default_factory=Color) # The foreground colour of this component.
html: Html = field(default_factory=Html) # The HTML from which this panel is defined
parent: Container = field(default_factory=Container) #
role: Themerole = field(
default_factory=Themerole) # Choose how this component can appear, based on your app’s visual theme.
tag: ClassDict = field(
default_factory=ClassDict) # Use this property to store any extra information about this component
tooltip: String = field(default_factory=String) # Text to display when you hover the mouse over this component
visible: Boolean = field(default_factory=Boolean) # Should this component be displayed?
def __post_init__(self):
RunTimeSettings.set_open_form(self)
def add_component(self, component, slot="default"):
"""Add a component to the named slot of this HTML templated panel. If no slot is specified, the ‘default’ slot
will be used. """
super().add_component(component)
def call_js(self, js_function_name, *args):
"""Call a Javascript function """
pass
def clear(self, slot="default"):
"""clear the HTML template of all components or clear a specific slot of components. """
super().clear()
pass