nova-trame 0.17.3__py3-none-any.whl → 0.18.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nova/trame/view/components/__init__.py +2 -1
- nova/trame/view/components/file_upload.py +82 -0
- nova/trame/view/components/remote_file_input.py +31 -4
- nova/trame/view/theme/theme.py +1 -1
- nova/trame/view_model/remote_file_input.py +2 -0
- {nova_trame-0.17.3.dist-info → nova_trame-0.18.0.dist-info}/METADATA +2 -1
- {nova_trame-0.17.3.dist-info → nova_trame-0.18.0.dist-info}/RECORD +10 -9
- {nova_trame-0.17.3.dist-info → nova_trame-0.18.0.dist-info}/LICENSE +0 -0
- {nova_trame-0.17.3.dist-info → nova_trame-0.18.0.dist-info}/WHEEL +0 -0
- {nova_trame-0.17.3.dist-info → nova_trame-0.18.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
"""View implementation for FileUpload."""
|
2
|
+
|
3
|
+
from typing import Any, List, Optional
|
4
|
+
|
5
|
+
from trame.widgets import vuetify3 as vuetify
|
6
|
+
|
7
|
+
from .remote_file_input import RemoteFileInput
|
8
|
+
|
9
|
+
|
10
|
+
class FileUpload(vuetify.VBtn):
|
11
|
+
"""Component for uploading a file from either the user's filesystem or the server filesystem."""
|
12
|
+
|
13
|
+
def __init__(self, v_model: str, base_paths: Optional[List[str]] = None, label: str = "", **kwargs: Any) -> None:
|
14
|
+
"""Constructor for FileUpload.
|
15
|
+
|
16
|
+
Parameters
|
17
|
+
----------
|
18
|
+
v_model : str
|
19
|
+
The state variable to set when the user uploads their file. The state variable will contain a latin1-decoded
|
20
|
+
version of the file contents. If your file is binary or requires a different string encoding, then you can
|
21
|
+
call `encode('latin1')` on the file contents to get the underlying bytes.
|
22
|
+
base_paths: list[str], optional
|
23
|
+
Passed to :ref:`RemoteFileInput <api_remotefileinput>`.
|
24
|
+
label : str, optional
|
25
|
+
The text to display on the upload button.
|
26
|
+
**kwargs
|
27
|
+
All other arguments will be passed to the underlying
|
28
|
+
`Button component <https://trame.readthedocs.io/en/latest/trame.widgets.vuetify3.html#trame.widgets.vuetify3.VBtn>`_.
|
29
|
+
|
30
|
+
Returns
|
31
|
+
-------
|
32
|
+
None
|
33
|
+
"""
|
34
|
+
self._v_model = v_model
|
35
|
+
if base_paths:
|
36
|
+
self._base_paths = base_paths
|
37
|
+
else:
|
38
|
+
self._base_paths = ["/"]
|
39
|
+
self._ref_name = f"nova__fileupload_{self._next_id}"
|
40
|
+
|
41
|
+
super().__init__(label, **kwargs)
|
42
|
+
self.create_ui()
|
43
|
+
|
44
|
+
def create_ui(self) -> None:
|
45
|
+
self.local_file_input = vuetify.VFileInput(
|
46
|
+
v_model=(self._v_model, None),
|
47
|
+
classes="d-none",
|
48
|
+
ref=self._ref_name,
|
49
|
+
# Serialize the content in a way that will work with nova-mvvm and then push it to the server.
|
50
|
+
update_modelValue=(
|
51
|
+
f"{self._v_model}.arrayBuffer().then((contents) => {{"
|
52
|
+
f"trigger('decode_blob_{self._id}', [contents]); "
|
53
|
+
"});"
|
54
|
+
),
|
55
|
+
)
|
56
|
+
self.remote_file_input = RemoteFileInput(
|
57
|
+
v_model=self._v_model, base_paths=self._base_paths, input_props={"classes": "d-none"}, return_contents=True
|
58
|
+
)
|
59
|
+
|
60
|
+
with self:
|
61
|
+
with vuetify.VMenu(activator="parent"):
|
62
|
+
with vuetify.VList():
|
63
|
+
vuetify.VListItem("From Local Machine", click=f"trame.refs.{self._ref_name}.click()")
|
64
|
+
vuetify.VListItem("From Analysis Cluster", click=self.remote_file_input.open_dialog)
|
65
|
+
|
66
|
+
@self.server.controller.trigger(f"decode_blob_{self._id}")
|
67
|
+
def _decode_blob(contents: bytes) -> None:
|
68
|
+
self.remote_file_input.decode_file(contents)
|
69
|
+
|
70
|
+
def select_file(self, value: str) -> None:
|
71
|
+
"""Programmatically set the RemoteFileInput path.
|
72
|
+
|
73
|
+
Parameters
|
74
|
+
----------
|
75
|
+
value: str
|
76
|
+
The new value for the RemoteFileInput.
|
77
|
+
|
78
|
+
Returns
|
79
|
+
-------
|
80
|
+
None
|
81
|
+
"""
|
82
|
+
self.remote_file_input.select_file(value)
|
@@ -10,9 +10,10 @@ from trame_client.widgets.core import AbstractElement
|
|
10
10
|
|
11
11
|
from nova.mvvm.trame_binding import TrameBinding
|
12
12
|
from nova.trame.model.remote_file_input import RemoteFileInputModel
|
13
|
-
from nova.trame.view.components import InputField
|
14
13
|
from nova.trame.view_model.remote_file_input import RemoteFileInputViewModel
|
15
14
|
|
15
|
+
from .input_field import InputField
|
16
|
+
|
16
17
|
|
17
18
|
class RemoteFileInput:
|
18
19
|
"""Generates a file selection dialog for picking files off of the server.
|
@@ -30,6 +31,7 @@ class RemoteFileInput:
|
|
30
31
|
dialog_props: Optional[dict[str, Any]] = None,
|
31
32
|
extensions: Optional[list[str]] = None,
|
32
33
|
input_props: Optional[dict[str, Any]] = None,
|
34
|
+
return_contents: bool = False,
|
33
35
|
) -> None:
|
34
36
|
"""Constructor for RemoteFileInput.
|
35
37
|
|
@@ -52,6 +54,9 @@ class RemoteFileInput:
|
|
52
54
|
Only files with these extensions will be shown by default. The user can still choose to view all files.
|
53
55
|
input_props : dict[str, typing.Any], optional
|
54
56
|
Props to be passed to InputField.
|
57
|
+
return_contents : bool
|
58
|
+
If true, then the v_model will contain the contents of the file. If false, then the v_model will contain the
|
59
|
+
path of the file.
|
55
60
|
|
56
61
|
Raises
|
57
62
|
------
|
@@ -73,6 +78,7 @@ class RemoteFileInput:
|
|
73
78
|
self.dialog_props = dict(dialog_props) if dialog_props else {}
|
74
79
|
self.extensions = extensions if extensions else []
|
75
80
|
self.input_props = dict(input_props) if input_props else {}
|
81
|
+
self.return_contents = return_contents
|
76
82
|
|
77
83
|
if "__events" not in self.input_props:
|
78
84
|
self.input_props["__events"] = []
|
@@ -178,14 +184,35 @@ class RemoteFileInput:
|
|
178
184
|
else:
|
179
185
|
model_name = self.v_model
|
180
186
|
|
187
|
+
self.set_v_model = client.JSEval(
|
188
|
+
exec=f"{model_name} = $event; flushState('{model_name.split('.')[0].split('[')[0]}');"
|
189
|
+
).exec
|
190
|
+
|
181
191
|
self.vm = RemoteFileInputViewModel(self.model, binding)
|
182
192
|
|
183
193
|
self.vm.dialog_bind.connect(self.vm.get_dialog_state_name())
|
184
194
|
self.vm.file_list_bind.connect(self.vm.get_file_list_state_name())
|
185
195
|
self.vm.filter_bind.connect(self.vm.get_filter_state_name())
|
186
196
|
self.vm.on_close_bind.connect(client.JSEval(exec=f"{self.vm.get_dialog_state_name()} = false;").exec)
|
187
|
-
self.
|
188
|
-
|
189
|
-
|
197
|
+
if self.return_contents:
|
198
|
+
self.vm.on_update_bind.connect(self.read_file)
|
199
|
+
else:
|
200
|
+
self.vm.on_update_bind.connect(self.set_v_model)
|
190
201
|
self.vm.showing_all_bind.connect(self.vm.get_showing_all_state_name())
|
191
202
|
self.vm.valid_selection_bind.connect(self.vm.get_valid_selection_state_name())
|
203
|
+
|
204
|
+
def read_file(self, file_path: str) -> None:
|
205
|
+
with open(file_path, mode="rb") as file:
|
206
|
+
self.decode_file(file.read())
|
207
|
+
|
208
|
+
def decode_file(self, bytestream: bytes) -> None:
|
209
|
+
decoded_content = bytestream.decode("latin1")
|
210
|
+
self.set_v_model(decoded_content)
|
211
|
+
|
212
|
+
def select_file(self, value: str) -> None:
|
213
|
+
"""Programmatically set the v_model value."""
|
214
|
+
self.vm.select_file(value)
|
215
|
+
|
216
|
+
def open_dialog(self) -> None:
|
217
|
+
"""Programmatically opens the dialog for selecting a file."""
|
218
|
+
self.vm.open_dialog()
|
nova/trame/view/theme/theme.py
CHANGED
@@ -233,7 +233,7 @@ class ThemedApp:
|
|
233
233
|
# [slot override example]
|
234
234
|
layout.pre_content = vuetify.VSheet(classes="bg-background ")
|
235
235
|
# [slot override example complete]
|
236
|
-
with vuetify.VContainer(classes="flex-
|
236
|
+
with vuetify.VContainer(classes="flex-0-1 overflow-hidden pt-0 pb-0", fluid=True):
|
237
237
|
layout.content = html.Div(classes="h-100 overflow-y-auto pb-1 ")
|
238
238
|
layout.post_content = vuetify.VSheet(classes="bg-background ")
|
239
239
|
|
@@ -35,6 +35,8 @@ class RemoteFileInputViewModel:
|
|
35
35
|
self.previous_value = self.value
|
36
36
|
self.populate_file_list()
|
37
37
|
|
38
|
+
self.dialog_bind.update_in_view(True)
|
39
|
+
|
38
40
|
def close_dialog(self, cancel: bool = False) -> None:
|
39
41
|
if not cancel:
|
40
42
|
self.on_update_bind.update_in_view(self.value)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: nova-trame
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.18.0
|
4
4
|
Summary: A Python Package for injecting curated themes and custom components into Trame applications
|
5
5
|
License: MIT
|
6
6
|
Keywords: NDIP,Python,Trame,Vuetify
|
@@ -25,6 +25,7 @@ Requires-Dist: trame-matplotlib
|
|
25
25
|
Requires-Dist: trame-plotly
|
26
26
|
Requires-Dist: trame-vega
|
27
27
|
Requires-Dist: trame-vuetify
|
28
|
+
Project-URL: Changelog, https://code.ornl.gov/ndip/public-packages/nova-trame/blob/main/CHANGELOG.md
|
28
29
|
Description-Content-Type: text/markdown
|
29
30
|
|
30
31
|
nova-trame
|
@@ -1,9 +1,10 @@
|
|
1
1
|
nova/__init__.py,sha256=ED6jHcYiuYpr_0vjGz0zx2lrrmJT9sDJCzIljoDfmlM,65
|
2
2
|
nova/trame/__init__.py,sha256=gFrAg1qva5PIqR5TjvPzAxLx103IKipJLqp3XXvrQL8,59
|
3
3
|
nova/trame/model/remote_file_input.py,sha256=9KAf31ZHzpsh_aXUrNcF81Q5jvUZDWCzW1QATKls-Jk,3675
|
4
|
-
nova/trame/view/components/__init__.py,sha256=
|
4
|
+
nova/trame/view/components/__init__.py,sha256=pzMkVyeHL9DjL8w5jN5n2FJbigo1lYIqgUSgjoQz5UA,178
|
5
|
+
nova/trame/view/components/file_upload.py,sha256=7VcpfA6zmiqMDLkwVPlb35Tf0IUTBN1xsHpoUFnSr1w,3111
|
5
6
|
nova/trame/view/components/input_field.py,sha256=Wf-WuYm9SmbHXYy1cvYy8zDWe5rhvLcUHmwqmBvqVxk,16074
|
6
|
-
nova/trame/view/components/remote_file_input.py,sha256=
|
7
|
+
nova/trame/view/components/remote_file_input.py,sha256=MnsUttZaCy8vEpOmRqXMsWa6PiC-RTRb-hJnnH0rBZc,9644
|
7
8
|
nova/trame/view/components/visualization/__init__.py,sha256=reqkkbhD5uSksHHlhVMy1qNUCwSekS5HlXk6wCREYxU,152
|
8
9
|
nova/trame/view/components/visualization/interactive_2d_plot.py,sha256=foZCMoqbuahT5dtqIQvm8C4ZJcY9P211eJEcpQJltmM,3421
|
9
10
|
nova/trame/view/components/visualization/matplotlib_figure.py,sha256=xxgEAE2boc-sIFEIsbJxQZQ5biBMBXT0U8Il5299VNc,11974
|
@@ -18,11 +19,11 @@ nova/trame/view/theme/assets/js/delay_manager.js,sha256=vmb34DZ5YCQIlRW9Tf2M_uvJ
|
|
18
19
|
nova/trame/view/theme/assets/js/lodash.debounce.min.js,sha256=GLzlQH04WDUNYN7i39ttHHejSdu-CpAvfWgDgKDn-OY,4448
|
19
20
|
nova/trame/view/theme/assets/js/lodash.throttle.min.js,sha256=9csqjX-M-LVGJnF3z4ha1R_36O5AfkFE8rPHkxmt3tE,4677
|
20
21
|
nova/trame/view/theme/assets/vuetify_config.json,sha256=JBWBfDvxBAWTw3zi0loorFvwwTksTUjQTWR4zYSQPSY,5095
|
21
|
-
nova/trame/view/theme/theme.py,sha256=
|
22
|
+
nova/trame/view/theme/theme.py,sha256=OFUtq1IWriFcDu-346J67ZrSES8IOI9PTY_4Vwg7bZQ,11820
|
22
23
|
nova/trame/view/utilities/local_storage.py,sha256=vD8f2VZIpxhIKjZwEaD7siiPCTZO4cw9AfhwdawwYLY,3218
|
23
|
-
nova/trame/view_model/remote_file_input.py,sha256=
|
24
|
-
nova_trame-0.
|
25
|
-
nova_trame-0.
|
26
|
-
nova_trame-0.
|
27
|
-
nova_trame-0.
|
28
|
-
nova_trame-0.
|
24
|
+
nova/trame/view_model/remote_file_input.py,sha256=ojEOJ8ZPkajpbAaZi9VLj7g-uBjhb8BMrTdMmwf_J6A,3367
|
25
|
+
nova_trame-0.18.0.dist-info/LICENSE,sha256=Iu5QiDbwNbREg75iYaxIJ_V-zppuv4QFuBhAW-qiAlM,1061
|
26
|
+
nova_trame-0.18.0.dist-info/METADATA,sha256=KaHiL5ELy26kn_xo1myVKyBLURbNBfUH4Pjwpn8YAUo,1446
|
27
|
+
nova_trame-0.18.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
28
|
+
nova_trame-0.18.0.dist-info/entry_points.txt,sha256=J2AmeSwiTYZ4ZqHHp9HO6v4MaYQTTBPbNh6WtoqOT58,42
|
29
|
+
nova_trame-0.18.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|