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.
@@ -1,4 +1,5 @@
1
+ from .file_upload import FileUpload
1
2
  from .input_field import InputField
2
3
  from .remote_file_input import RemoteFileInput
3
4
 
4
- __all__ = ["InputField", "RemoteFileInput"]
5
+ __all__ = ["FileUpload", "InputField", "RemoteFileInput"]
@@ -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.vm.on_update_bind.connect(
188
- client.JSEval(exec=f"{model_name} = $event; flushState('{model_name.split('.')[0].split('[')[0]}');").exec
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()
@@ -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-1-1 overflow-hidden pt-0 pb-0", fluid=True):
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.17.3
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=fopr6mVqcpDcVYK9ue7SLUHyswgvRPcFESTq86mu1R8,128
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=k2yrwkell_g0sGnWR9XLL1LxkmFLr8-AGhduo8E-N4A,8669
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=yBaHOvugzxh9vsBOGlOpp2IVMuLj922P6MSXoSz7tk0,11820
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=WHWCQkZBGeKLe1aTPbtVNI8tn-PDt64mi1-561uuBpQ,3320
24
- nova_trame-0.17.3.dist-info/LICENSE,sha256=Iu5QiDbwNbREg75iYaxIJ_V-zppuv4QFuBhAW-qiAlM,1061
25
- nova_trame-0.17.3.dist-info/METADATA,sha256=PcFti25euMmdCYTN4r2q1kdxUiXDbC7DLlOiu1SapHY,1345
26
- nova_trame-0.17.3.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
27
- nova_trame-0.17.3.dist-info/entry_points.txt,sha256=J2AmeSwiTYZ4ZqHHp9HO6v4MaYQTTBPbNh6WtoqOT58,42
28
- nova_trame-0.17.3.dist-info/RECORD,,
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,,