ipyvasp 0.9.2__py2.py3-none-any.whl → 0.9.4__py2.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.
- ipyvasp/__init__.py +3 -3
- ipyvasp/_lattice.py +33 -18
- ipyvasp/_version.py +1 -1
- ipyvasp/bsdos.py +1 -7
- ipyvasp/core/plot_toolkit.py +1 -1
- ipyvasp/lattice.py +2 -2
- ipyvasp/utils.py +0 -32
- ipyvasp/widgets.py +248 -288
- {ipyvasp-0.9.2.dist-info → ipyvasp-0.9.4.dist-info}/METADATA +1 -1
- ipyvasp-0.9.4.dist-info/RECORD +25 -0
- ipyvasp-0.9.2.dist-info/RECORD +0 -25
- {ipyvasp-0.9.2.dist-info → ipyvasp-0.9.4.dist-info}/LICENSE +0 -0
- {ipyvasp-0.9.2.dist-info → ipyvasp-0.9.4.dist-info}/WHEEL +0 -0
- {ipyvasp-0.9.2.dist-info → ipyvasp-0.9.4.dist-info}/entry_points.txt +0 -0
- {ipyvasp-0.9.2.dist-info → ipyvasp-0.9.4.dist-info}/top_level.txt +0 -0
ipyvasp/__init__.py
CHANGED
|
@@ -6,6 +6,8 @@ widgets for interactive visualization and bulk analysis.
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
__all__ = [ # For documentation purpose
|
|
9
|
+
"Files",
|
|
10
|
+
"OUTCAR",
|
|
9
11
|
"get_axes",
|
|
10
12
|
"plt2text",
|
|
11
13
|
"plt2html",
|
|
@@ -14,11 +16,9 @@ __all__ = [ # For documentation purpose
|
|
|
14
16
|
"image2plt",
|
|
15
17
|
"iplot2plt",
|
|
16
18
|
"webshow",
|
|
17
|
-
"list_files",
|
|
18
19
|
"load_results",
|
|
19
20
|
"parse_text",
|
|
20
21
|
"summarize",
|
|
21
|
-
"OUTCAR",
|
|
22
22
|
]
|
|
23
23
|
|
|
24
24
|
from ._version import __version__
|
|
@@ -30,7 +30,7 @@ from .bsdos import *
|
|
|
30
30
|
from .potential import *
|
|
31
31
|
from .evals_dataframe import *
|
|
32
32
|
from .utils import *
|
|
33
|
-
from .widgets import BandsWidget, KpathWidget,
|
|
33
|
+
from .widgets import Files, BandsWidget, KpathWidget, summarize, load_results
|
|
34
34
|
from .core import plot_toolkit, spatial_toolkit
|
|
35
35
|
from .core.spatial_toolkit import to_basis, to_R3, get_TM, get_bz, rotation
|
|
36
36
|
from .core.plot_toolkit import (
|
ipyvasp/_lattice.py
CHANGED
|
@@ -11,7 +11,8 @@ from scipy.spatial import ConvexHull, KDTree
|
|
|
11
11
|
import plotly.graph_objects as go
|
|
12
12
|
|
|
13
13
|
import matplotlib.pyplot as plt # For viewpoint
|
|
14
|
-
from matplotlib.collections import LineCollection
|
|
14
|
+
from matplotlib.collections import LineCollection, PatchCollection
|
|
15
|
+
from matplotlib.patches import Rectangle
|
|
15
16
|
from mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3DCollection
|
|
16
17
|
import matplotlib.colors as mplc
|
|
17
18
|
|
|
@@ -171,17 +172,25 @@ def atoms_color():
|
|
|
171
172
|
)
|
|
172
173
|
|
|
173
174
|
|
|
174
|
-
def periodic_table():
|
|
175
|
-
"Display colorerd elements in periodic table."
|
|
175
|
+
def periodic_table(selection=None):
|
|
176
|
+
"Display colorerd elements in periodic table. Use a list of atoms to only color a selection."
|
|
176
177
|
_copy_names = np.array(
|
|
177
178
|
[f"$^{{{str(i+1)}}}${k}" for i, k in enumerate(_atom_colors.keys())]
|
|
178
179
|
)
|
|
179
|
-
|
|
180
|
+
blank = []
|
|
181
|
+
if isinstance(selection,(list, tuple, str)):
|
|
182
|
+
if isinstance(selection, str):
|
|
183
|
+
selection = selection.split()
|
|
184
|
+
blank = [key for key in _atom_colors if not (key in selection)]
|
|
185
|
+
|
|
186
|
+
_copy_array = np.array([[1,1,1,0] if key in blank else [*value,1] for key, value in _atom_colors.items()])
|
|
180
187
|
|
|
181
|
-
array = np.ones((180, 3))
|
|
182
188
|
names = ["" for i in range(180)] # keep as list before modification
|
|
189
|
+
fc = np.ones((180, 4))
|
|
190
|
+
ec = np.zeros((180,3)) + (0.4 if blank else 0.9 )
|
|
191
|
+
offsets = np.array([[(i,j) for i in range(18)] for j in range(10)]).reshape((-1,2)) - 0.5
|
|
183
192
|
|
|
184
|
-
inds = [
|
|
193
|
+
inds = np.array([
|
|
185
194
|
(0, 0),
|
|
186
195
|
(17, 1),
|
|
187
196
|
(18, 2),
|
|
@@ -196,23 +205,29 @@ def periodic_table():
|
|
|
196
205
|
*[(111 + i, 103 + i) for i in range(15)],
|
|
197
206
|
*[(147 + i, 57 + i) for i in range(14)],
|
|
198
207
|
*[(165 + i, 89 + i) for i in range(14)],
|
|
199
|
-
]
|
|
208
|
+
], dtype=int)
|
|
200
209
|
|
|
201
210
|
for i, j in inds:
|
|
202
|
-
|
|
211
|
+
fc[i,:] = _copy_array[j]
|
|
203
212
|
names[i] = _copy_names[j]
|
|
204
213
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
214
|
+
fidx = [i for i, _ in inds] # only plot at elements posistions,otherwise they overlap
|
|
215
|
+
offsets = offsets[fidx]
|
|
216
|
+
fc, ec = fc[fidx], ec[fidx]
|
|
217
|
+
names = np.array(names)[fidx]
|
|
218
|
+
|
|
219
|
+
# We are adding patches, because imshow does not properly appear in PDF of latex
|
|
220
|
+
ax = ptk.get_axes((7, 3.9),left=0.01,right=0.99,top=0.99,bottom=0.01)
|
|
221
|
+
patches = np.array([Rectangle(offset,0.9 if i in [92,110] else 1,1) for i, offset in zip(fidx,offsets)])
|
|
222
|
+
pc = PatchCollection(patches, facecolors=fc, edgecolors=ec,linewidths=(0.7,))
|
|
223
|
+
ax.add_collection(pc)
|
|
224
|
+
|
|
225
|
+
for (x,y), text, c in zip(offsets + 0.5, names, fc):
|
|
226
|
+
c = "k" if np.linalg.norm(c[:3]) > 1 else "w"
|
|
227
|
+
plt.text(x,y, text, color=c, ha="center", va="center")
|
|
209
228
|
|
|
210
|
-
for i in range(18):
|
|
211
|
-
for j in range(10):
|
|
212
|
-
c = "k" if np.linalg.norm(array[j, i]) > 1 else "w"
|
|
213
|
-
plt.text(i, j, names[j, i], color=c, ha="center", va="center")
|
|
214
229
|
ax.set_axis_off()
|
|
215
|
-
|
|
230
|
+
ax.set(xlim=[-0.6,17.6],ylim=[9.6,-0.6]) # to show borders correctly
|
|
216
231
|
return ax
|
|
217
232
|
|
|
218
233
|
|
|
@@ -299,7 +314,7 @@ def export_poscar(path=None, content=None):
|
|
|
299
314
|
SYSTEM = header[0].strip()
|
|
300
315
|
comment = header[1].strip() if len(header) > 1 else "Exported by Pivopty"
|
|
301
316
|
|
|
302
|
-
scale = float(file_lines[1].strip())
|
|
317
|
+
scale = float(file_lines[1].strip().split()[0]) # some people add comments here too
|
|
303
318
|
if scale < 0: # If that is for volume
|
|
304
319
|
scale = 1
|
|
305
320
|
|
ipyvasp/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.9.
|
|
1
|
+
__version__ = "0.9.4"
|
ipyvasp/bsdos.py
CHANGED
|
@@ -578,13 +578,7 @@ class Bands(_BandsDosBase):
|
|
|
578
578
|
|
|
579
579
|
def view_bands(self, height="450px"):
|
|
580
580
|
"Initialize and return `ipyvasp.widgets.BandsWidget` to view bandstructure interactively."
|
|
581
|
-
|
|
582
|
-
return wdg.BandsWidget(
|
|
583
|
-
use_vaspout=use_vaspout,
|
|
584
|
-
path=str(self.source.path.parent),
|
|
585
|
-
glob=self.source.path.name,
|
|
586
|
-
height=height,
|
|
587
|
-
)
|
|
581
|
+
return wdg.BandsWidget([self.source.path,],height=height)
|
|
588
582
|
|
|
589
583
|
|
|
590
584
|
_multiply_doc = """multiply : float
|
ipyvasp/core/plot_toolkit.py
CHANGED
|
@@ -1019,7 +1019,7 @@ def iplot2widget(fig, fig_widget=None, template=None):
|
|
|
1019
1019
|
elif not isinstance(fig_widget, go.FigureWidget):
|
|
1020
1020
|
raise ValueError("fig_widget must be FigureWidget")
|
|
1021
1021
|
else:
|
|
1022
|
-
scene = fig_widget.layout.scene # keep scene from widget
|
|
1022
|
+
scene = fig_widget.layout.scene if fig_widget.data else fig.layout.scene# keep scene from widget, but if looks a new fig, keep from previous
|
|
1023
1023
|
|
|
1024
1024
|
fig_widget.data = [] # Clear previous data
|
|
1025
1025
|
if template is not None:
|
ipyvasp/lattice.py
CHANGED
|
@@ -432,11 +432,11 @@ class POSCAR:
|
|
|
432
432
|
def view_weas(self, **kwargs):
|
|
433
433
|
return weas_viewer(self, **kwargs)
|
|
434
434
|
|
|
435
|
-
def view_kpath(self):
|
|
435
|
+
def view_kpath(self, height='400px'):
|
|
436
436
|
"Initialize a KpathWidget instance to view kpath for current POSCAR, and you can select others too."
|
|
437
437
|
from .widgets import KpathWidget
|
|
438
438
|
|
|
439
|
-
return KpathWidget(
|
|
439
|
+
return KpathWidget([self.path,],height=height)
|
|
440
440
|
|
|
441
441
|
@_sub_doc(plat.iplot_lattice)
|
|
442
442
|
@_sig_kwargs(plat.iplot_lattice, ("poscar_data",))
|
ipyvasp/utils.py
CHANGED
|
@@ -3,7 +3,6 @@ __all__ = [
|
|
|
3
3
|
"set_dir",
|
|
4
4
|
"interpolate_data",
|
|
5
5
|
"rolling_mean",
|
|
6
|
-
"list_files",
|
|
7
6
|
"color",
|
|
8
7
|
"transform_color",
|
|
9
8
|
"create_colormap",
|
|
@@ -257,37 +256,6 @@ def rolling_mean(
|
|
|
257
256
|
|
|
258
257
|
return mean_all
|
|
259
258
|
|
|
260
|
-
|
|
261
|
-
def list_files(path=".", glob="*", exclude=None, files_only=False, dirs_only=False):
|
|
262
|
-
"""
|
|
263
|
-
Returns a tuple of files in a directory recursively based on glob pattern.
|
|
264
|
-
|
|
265
|
-
Parameters
|
|
266
|
-
----------
|
|
267
|
-
path : str, current directory by default
|
|
268
|
-
glob : str, glob pattern, '*' by default
|
|
269
|
-
exclude : str, regular expression pattern to exclude files
|
|
270
|
-
files_only : bool, if True, returns only files
|
|
271
|
-
dirs_only : bool, if True, returns only directories
|
|
272
|
-
|
|
273
|
-
Returns
|
|
274
|
-
-------
|
|
275
|
-
tuple of pathlib.Path objects
|
|
276
|
-
"""
|
|
277
|
-
if files_only and dirs_only:
|
|
278
|
-
raise ValueError("files_only and dirs_only cannot be both True")
|
|
279
|
-
|
|
280
|
-
path = Path(path)
|
|
281
|
-
files = [p for p in path.glob(glob)]
|
|
282
|
-
if exclude:
|
|
283
|
-
files = [p for p in files if not re.search(exclude, str(p))]
|
|
284
|
-
if files_only:
|
|
285
|
-
files = [p for p in files if p.is_file()]
|
|
286
|
-
if dirs_only:
|
|
287
|
-
files = [p for p in files if p.is_dir()]
|
|
288
|
-
return tuple(sorted(files)) # sorting is important here
|
|
289
|
-
|
|
290
|
-
|
|
291
259
|
@contextmanager
|
|
292
260
|
def prevent_overwrite(path) -> Path:
|
|
293
261
|
"""Contextmanager to prevents overwiting as file by adding numbers in given path.
|
ipyvasp/widgets.py
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
__all__ = [
|
|
2
2
|
"load_results",
|
|
3
3
|
"summarize",
|
|
4
|
-
"
|
|
4
|
+
"Files",
|
|
5
5
|
"PropsPicker",
|
|
6
6
|
"BandsWidget",
|
|
7
7
|
"KpathWidget",
|
|
8
8
|
]
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
import inspect
|
|
11
|
+
import inspect, re
|
|
12
12
|
from time import time
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from collections.abc import Iterable
|
|
@@ -57,8 +57,8 @@ def summarize(files, func, **kwargs):
|
|
|
57
57
|
if not callable(func):
|
|
58
58
|
raise TypeError("Argument `func` must be a function.")
|
|
59
59
|
|
|
60
|
-
if not isinstance(files, Iterable):
|
|
61
|
-
raise TypeError("Argument `files` must be an iterable of PathLike objects
|
|
60
|
+
if not isinstance(files, Iterable): # Files is instance of Iterable due to __iter__ method
|
|
61
|
+
raise TypeError("Argument `files` must be an iterable of PathLike objects")
|
|
62
62
|
|
|
63
63
|
if not isinstance(files, dict):
|
|
64
64
|
files = {str(path): path for path in files} # make a dictionary of paths
|
|
@@ -78,15 +78,12 @@ def summarize(files, func, **kwargs):
|
|
|
78
78
|
{**output, "FILE": name}
|
|
79
79
|
) # add the file name to the output at the end
|
|
80
80
|
|
|
81
|
-
unique_keys =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
for key in [key for out in outputs for key in out.keys()]:
|
|
85
|
-
if key not in unique_keys:
|
|
86
|
-
unique_keys.append(key)
|
|
81
|
+
unique_keys = {} # handle missing keys with types
|
|
82
|
+
for key,value in [item for out in outputs for item in out.items()]:
|
|
83
|
+
unique_keys[key] = '' if isinstance(value, str) else None
|
|
87
84
|
|
|
88
85
|
return pd.DataFrame(
|
|
89
|
-
{
|
|
86
|
+
{key: [out.get(key, ph) for out in outputs] for key,ph in unique_keys.items()}
|
|
90
87
|
)
|
|
91
88
|
|
|
92
89
|
|
|
@@ -102,257 +99,215 @@ def fix_signature(cls):
|
|
|
102
99
|
cls.__signature__ = inspect.signature(cls.__init__)
|
|
103
100
|
return cls
|
|
104
101
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
"""A widget for selecting files from a directory and its subdirectories.
|
|
102
|
+
class Files:
|
|
103
|
+
"""Creates a Batch of files in a directory recursively based on glob pattern or given list of files.
|
|
104
|
+
This is a boilerplate abstraction to do analysis in multiple calculations simultaneously.
|
|
109
105
|
|
|
110
106
|
Parameters
|
|
111
107
|
----------
|
|
112
|
-
|
|
113
|
-
glob : str,
|
|
114
|
-
exclude : str,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
To add extra controls and widgets, use `.interactive`/`.interact` methods instead.
|
|
118
|
-
|
|
119
|
-
Returns
|
|
120
|
-
-------
|
|
121
|
-
A FilesWidget object where you can filter files by typing in the text box, and select files from the dropdown.
|
|
122
|
-
"""
|
|
123
|
-
|
|
124
|
-
def __init__(
|
|
125
|
-
self,
|
|
126
|
-
path: str = ".",
|
|
127
|
-
glob: str = "*",
|
|
128
|
-
exclude: str = None,
|
|
129
|
-
on_file_changed=None,
|
|
130
|
-
) -> None:
|
|
131
|
-
for prop in (path, glob, exclude):
|
|
132
|
-
if prop and not isinstance(prop, str):
|
|
133
|
-
raise ValueError(f"Expected string, got {type(prop)}")
|
|
134
|
-
|
|
135
|
-
super().__init__(_dom_classes=["FilesWidget"]) # This makes it truely a widget
|
|
136
|
-
self._files = [] # Selections stored as Path objects
|
|
137
|
-
self._widgets = {
|
|
138
|
-
"input": Text(
|
|
139
|
-
value=path,
|
|
140
|
-
description="Path:",
|
|
141
|
-
tooltip="The path to the directory to search.",
|
|
142
|
-
),
|
|
143
|
-
"glob": Text(
|
|
144
|
-
value=glob,
|
|
145
|
-
description="Glob:",
|
|
146
|
-
tooltip="The glob pattern to match files against. See https://docs.python.org/3/library/glob.html",
|
|
147
|
-
),
|
|
148
|
-
"exclude": Text(
|
|
149
|
-
value=exclude or "",
|
|
150
|
-
description="Exclude:",
|
|
151
|
-
tooltip="A regex pattern to exclude files from the selection.",
|
|
152
|
-
),
|
|
153
|
-
"lock": Checkbox(
|
|
154
|
-
value=False,
|
|
155
|
-
description="Lock selection",
|
|
156
|
-
tooltip="Lock the current selection and prevent changes.",
|
|
157
|
-
),
|
|
158
|
-
"files": Dropdown(
|
|
159
|
-
options=[], description="File:", tooltip="Select a file from the list."
|
|
160
|
-
),
|
|
161
|
-
}
|
|
162
|
-
self.children = [
|
|
163
|
-
self._widgets["input"],
|
|
164
|
-
self._widgets["glob"],
|
|
165
|
-
self._widgets["exclude"],
|
|
166
|
-
]
|
|
167
|
-
self._widgets["lock"].observe(self._lock_selection, names=["value"])
|
|
168
|
-
|
|
169
|
-
for key, value in self._widgets.items():
|
|
170
|
-
if key not in ["files", "lock"]:
|
|
171
|
-
value.on_submit(self._process)
|
|
172
|
-
|
|
173
|
-
if on_file_changed:
|
|
174
|
-
if not callable(on_file_changed):
|
|
175
|
-
raise TypeError(
|
|
176
|
-
"Argument `on_file_changed` must be a function that takes path as an argument."
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
out = ipw.Output()
|
|
180
|
-
self.layout.max_height = "90vh" # Only if output is present
|
|
181
|
-
self._widgets["output"] = out
|
|
182
|
-
|
|
183
|
-
@out.capture(clear_output=True, wait=True)
|
|
184
|
-
def on_change(change):
|
|
185
|
-
on_file_changed(self.selected)
|
|
108
|
+
path_or_files : str, current directory by default or list of files or an instance of Files.
|
|
109
|
+
glob : str, glob pattern, '*' by default. Not used if files supplied above.
|
|
110
|
+
exclude : str, regular expression pattern to exclude files.
|
|
111
|
+
files_only : bool, if True, returns only files.
|
|
112
|
+
dirs_only : bool, if True, returns only directories.
|
|
186
113
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if
|
|
199
|
-
|
|
200
|
-
|
|
114
|
+
Use methods on return such as `summarize`, `with_name`, `filtered`, `interact` and others.
|
|
115
|
+
"""
|
|
116
|
+
def __init__(self, path_or_files = '.', glob = '*', exclude = None,files_only = False, dirs_only=False):
|
|
117
|
+
if isinstance(path_or_files, Files):
|
|
118
|
+
self._files = path_or_files._files
|
|
119
|
+
return # Do nothing
|
|
120
|
+
|
|
121
|
+
if files_only and dirs_only:
|
|
122
|
+
raise ValueError("files_only and dirs_only cannot be both True")
|
|
123
|
+
|
|
124
|
+
files = []
|
|
125
|
+
if isinstance(path_or_files,(str, Path)):
|
|
126
|
+
path = Path(path_or_files)
|
|
127
|
+
files = [p for p in path.glob(glob)]
|
|
201
128
|
else:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
self.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def
|
|
240
|
-
"
|
|
241
|
-
|
|
242
|
-
|
|
129
|
+
others = []
|
|
130
|
+
for item in path_or_files:
|
|
131
|
+
if isinstance(item, str):
|
|
132
|
+
item = Path(item)
|
|
133
|
+
elif not isinstance(item, Path):
|
|
134
|
+
raise TypeError(f"Expected str or Path in sequence, got {type(item)}")
|
|
135
|
+
|
|
136
|
+
if item.exists():
|
|
137
|
+
files.append(item)
|
|
138
|
+
else:
|
|
139
|
+
others.append(str(item))
|
|
140
|
+
|
|
141
|
+
if others:
|
|
142
|
+
print(f"Skipping paths that do not exist: {list(set(others))}")
|
|
143
|
+
|
|
144
|
+
if exclude:
|
|
145
|
+
files = [p for p in files if not re.search(exclude, str(p))]
|
|
146
|
+
if files_only:
|
|
147
|
+
files = [p for p in files if p.is_file()]
|
|
148
|
+
if dirs_only:
|
|
149
|
+
files = [p for p in files if p.is_dir()]
|
|
150
|
+
|
|
151
|
+
self._files = tuple(sorted(files))
|
|
152
|
+
|
|
153
|
+
def __repr__(self):
|
|
154
|
+
if not self: return "Files()"
|
|
155
|
+
return "Files(\n" + ',\n'.join(f' {f!r}' for f in self._files) + "\n)"
|
|
156
|
+
|
|
157
|
+
def __getitem__(self, index): return self._files[index]
|
|
158
|
+
def __iter__(self): return self._files.__iter__()
|
|
159
|
+
def __len__(self): return len(self._files)
|
|
160
|
+
def __bool__(self): return bool(self._files)
|
|
161
|
+
|
|
162
|
+
def map(self,func):
|
|
163
|
+
"Map files to a function!"
|
|
164
|
+
return map(func, self._files)
|
|
165
|
+
|
|
166
|
+
def with_name(self, name):
|
|
167
|
+
"Change name of all files. Only keeps existing files."
|
|
168
|
+
return self.__class__([f.with_name(name) for f in self._files])
|
|
169
|
+
|
|
170
|
+
def filtered(self, include=None, exclude=None, files_only = False, dirs_only=False):
|
|
171
|
+
"Filter all files. Only keeps existing file."
|
|
172
|
+
files = [p for p in self._files if re.search(include, str(p))] if include else self._files
|
|
173
|
+
return self.__class__(files, exclude=exclude,dirs_only=dirs_only,files_only=files_only)
|
|
243
174
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
self
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
175
|
+
def summarize(self, func, **kwargs):
|
|
176
|
+
"Apply a func(apth) -> dict and create a dataframe."
|
|
177
|
+
return summarize(self._files,func, **kwargs)
|
|
178
|
+
|
|
179
|
+
def load_results(self):
|
|
180
|
+
"Load result.json files from these paths into a dataframe."
|
|
181
|
+
return load_results(self._files)
|
|
182
|
+
|
|
183
|
+
def input_info(self, *tags):
|
|
184
|
+
"Grab input information into a dataframe from POSCAR and INCAR. Provide INCAR tags (case-insinsitive) to select only few of them."
|
|
185
|
+
from .lattice import POSCAR
|
|
186
|
+
|
|
187
|
+
def info(path, tags):
|
|
188
|
+
p = POSCAR(path).data
|
|
189
|
+
lines = [[v.strip() for v in line.split('=')]
|
|
190
|
+
for line in path.with_name('INCAR').read_text().splitlines()
|
|
191
|
+
if '=' in line]
|
|
192
|
+
if tags:
|
|
193
|
+
tags = [t.upper() for t in tags] # can send lowercase tag
|
|
194
|
+
lines = [(k,v) for k,v in lines if k in tags]
|
|
195
|
+
d = {k:v for k,v in lines if not k.startswith('#')}
|
|
196
|
+
d.update({k:len(v) for k,v in p.types.items()})
|
|
197
|
+
d.update(zip('abcvαβγ', [*p.norms,p.volume,*p.angles]))
|
|
198
|
+
return d
|
|
199
|
+
|
|
200
|
+
return self.with_name('POSCAR').summarize(info, tags=tags)
|
|
201
|
+
|
|
202
|
+
def update(self, path_or_files, glob = '*',**kwargs):
|
|
203
|
+
"Update files inplace with similar parameters as initialization. Useful for widgets such as BandsWidget to preserve their state while files swapping."
|
|
204
|
+
self._files = self.__class__(path_or_files, glob = glob, **kwargs)
|
|
205
|
+
if (dd := getattr(self, '_dd', None)): # update dropdown
|
|
206
|
+
old = dd.value
|
|
207
|
+
dd.options = self._files
|
|
208
|
+
if old in dd.options:
|
|
209
|
+
dd.value = old
|
|
210
|
+
|
|
211
|
+
def interactive(self, func, *args,
|
|
212
|
+
free_widgets=None,
|
|
254
213
|
options={"manual": False},
|
|
255
214
|
height="400px",
|
|
256
|
-
|
|
257
|
-
|
|
215
|
+
panel_size = 25,
|
|
216
|
+
**kwargs):
|
|
258
217
|
"""
|
|
259
|
-
Interact with
|
|
218
|
+
Interact with `func(path, *args, <free_widgets,optional>, **kwargs)` that takes selected Path as first argument. Returns a widget that saves attributes of the function call such as .f, .args, .kwargs.
|
|
219
|
+
`args` are widgets to be modified by func, such as plotly's FigureWidget.
|
|
220
|
+
Note that `free_widgets` can also be passed to function but does not run function when changed.
|
|
221
|
+
|
|
260
222
|
See docs of self.interact for more details on the parameters. kwargs are passed to ipywidgets.interactive to create controls.
|
|
261
223
|
|
|
224
|
+
>>> import plotly.graph_objects as go
|
|
225
|
+
>>> import ipyvasp as ipv
|
|
226
|
+
>>> fs = ipv.Files('.','**/POSCAR')
|
|
227
|
+
>>> def plot(path, fig, bl,plot_cell,eqv_sites):
|
|
228
|
+
>>> ipv.iplot2widget(ipv.POSCAR(path).iplot_lattice(
|
|
229
|
+
>>> bond_length=bl,plot_cell=plot_cell,eqv_sites=eqv_sites
|
|
230
|
+
>>> ),fig_widget=fig) # it keeps updating same view
|
|
231
|
+
>>> out = fs.interactive(plot, go.FigureWidget(),bl=(0,6,2),plot_cell=True, eqv_sites=True)
|
|
262
232
|
|
|
263
|
-
>>> fw = FilesWidget()
|
|
264
|
-
>>> out = fw.interactive(lambda path: print(path.read_text())) # prints contents of selected file on output widget
|
|
265
233
|
>>> out.f # function
|
|
266
234
|
>>> out.args # arguments
|
|
267
235
|
>>> out.kwargs # keyword arguments
|
|
268
236
|
>>> out.result # result of function call which is same as out.f(*out.args, **out.kwargs)
|
|
269
|
-
>>> out.files_widget # reference to FilesWidget created, not the same as fw because it is a new instance
|
|
270
|
-
|
|
271
237
|
|
|
272
238
|
.. note::
|
|
273
239
|
If you don't need to interpret the result of the function call, you can use the @self.interact decorator instead.
|
|
274
|
-
|
|
275
|
-
.. note::
|
|
276
|
-
Each time an underlying new FilesWidget instance is created which picks input from previous one but stays separate. You can access it with `.files_widget` attribute of interactive.
|
|
277
240
|
"""
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
path=self._widgets["input"].value,
|
|
281
|
-
glob=self._widgets["glob"].value,
|
|
282
|
-
exclude=self._widgets["exclude"].value,
|
|
283
|
-
)
|
|
284
|
-
info = ipw.HTML().add_class("FW-Progess")
|
|
241
|
+
info = ipw.HTML().add_class("fprogess")
|
|
242
|
+
dd = Dropdown(description='File', options=self._files)
|
|
285
243
|
|
|
286
|
-
def interact_func(fname, **
|
|
244
|
+
def interact_func(fname, **kws):
|
|
287
245
|
if fname: # This would be None if no file is selected
|
|
288
246
|
info.value = _progress_svg
|
|
289
247
|
try:
|
|
290
248
|
start = time()
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
func(
|
|
295
|
-
|
|
296
|
-
) # Have Path object absolue if user changes directory
|
|
249
|
+
if 'free_widgets' in func.__code__.co_varnames:
|
|
250
|
+
kws['free_widgets'] = free_widgets # user allowed to pass this
|
|
251
|
+
names = ', '.join(func.__code__.co_varnames)
|
|
252
|
+
print(f"Running {func.__name__}({names})")
|
|
253
|
+
func(Path(fname).absolute(), *args, **kws) # Have Path object absolue if user changes directory
|
|
297
254
|
print(f"Finished in {time() - start:.3f} seconds.")
|
|
298
255
|
finally:
|
|
299
256
|
info.value = ""
|
|
300
257
|
|
|
301
|
-
out = ipw.interactive(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
out.files_widget = new_fw # save reference to FilesWidget
|
|
306
|
-
out.output_widget = out.children[
|
|
307
|
-
-1
|
|
308
|
-
] # save reference to output widget for other widgets to use
|
|
258
|
+
out = ipw.interactive(interact_func, options, fname=dd, **kwargs)
|
|
259
|
+
out._dd = dd # save reference to dropdown
|
|
260
|
+
out.output_widget = out.children[-1] # save reference to output widget for other widgets to use
|
|
309
261
|
|
|
310
262
|
if options.get("manual", False):
|
|
311
|
-
out.interact_button = out.children[
|
|
312
|
-
-2
|
|
313
|
-
] # save reference to interact button for other widgets to use
|
|
263
|
+
out.interact_button = out.children[-2] # save reference to interact button for other widgets to use
|
|
314
264
|
|
|
315
265
|
output = out.children[-1] # get output widget
|
|
316
|
-
output.clear_output(
|
|
317
|
-
wait=True
|
|
318
|
-
) # clear output by waiting to avoid flickering, this is important
|
|
266
|
+
output.clear_output(wait=True) # clear output by waiting to avoid flickering, this is important
|
|
319
267
|
output.layout = Layout(
|
|
320
268
|
overflow="auto", max_height="100%", width="100%"
|
|
321
269
|
) # make output scrollable and avoid overflow
|
|
322
270
|
|
|
323
271
|
others = out.children[1:-1] # exclude files_dd and Output widget
|
|
324
|
-
|
|
325
|
-
|
|
272
|
+
if not isinstance(panel_size,int):
|
|
273
|
+
raise TypeError('panel_size should be integer in units of em')
|
|
274
|
+
|
|
275
|
+
_style = f"""<style>
|
|
276
|
+
.files-interact {{
|
|
326
277
|
--jp-widgets-inline-label-width: 4em;
|
|
327
|
-
--jp-widgets-inline-width:
|
|
278
|
+
--jp-widgets-inline-width: {panel_size-2}em;
|
|
328
279
|
--jp-widgets-inline-width-short: 9em;
|
|
329
|
-
}
|
|
330
|
-
.
|
|
331
|
-
.
|
|
332
|
-
.
|
|
333
|
-
.
|
|
334
|
-
.
|
|
280
|
+
}}
|
|
281
|
+
.files-interact {{max-height:{height};width:100%;}}
|
|
282
|
+
.files-interact > div {{overflow:auto;max-height:100%;padding:8px;}}
|
|
283
|
+
.files-interact > div:first-child {{width:{panel_size}em}}
|
|
284
|
+
.files-interact > div:last-child {{width:calc(100% - {panel_size}em)}}
|
|
285
|
+
.files-interact .fprogess {{position:absolute !important; left:50%; top:50%; transform:translate(-50%,-50%); z-index:1}}
|
|
335
286
|
</style>"""
|
|
336
287
|
if others:
|
|
337
288
|
others = [ipw.HTML(f"<hr/>{_style}"), *others]
|
|
338
289
|
else:
|
|
339
290
|
others = [ipw.HTML(_style)]
|
|
340
291
|
|
|
341
|
-
if
|
|
342
|
-
raise TypeError("
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
292
|
+
if free_widgets and not isinstance(free_widgets, (list, tuple)):
|
|
293
|
+
raise TypeError("free_widgets must be a list or tuple of widgets.")
|
|
294
|
+
|
|
295
|
+
for w in args:
|
|
296
|
+
if not isinstance(w,ipw.DOMWidget):
|
|
297
|
+
raise TypeError(f'args can only contain a DOMWidget instance, got {type(w)}')
|
|
298
|
+
|
|
299
|
+
if args:
|
|
348
300
|
output.layout.max_height = "200px"
|
|
301
|
+
output.layout.min_height = "8em" # fisrt fix
|
|
349
302
|
out_collapser = Checkbox(description="Hide output widget", value=False)
|
|
350
303
|
|
|
351
304
|
def toggle_output(change):
|
|
352
305
|
if out_collapser.value:
|
|
353
306
|
output.layout.height = "0px" # dont use display = 'none' as it will clear widgets and wont show again
|
|
307
|
+
output.layout.min_height = "0px"
|
|
354
308
|
else:
|
|
355
309
|
output.layout.height = "auto"
|
|
310
|
+
output.layout.min_height = "8em"
|
|
356
311
|
|
|
357
312
|
out_collapser.observe(toggle_output, "value")
|
|
358
313
|
others.append(out_collapser)
|
|
@@ -361,94 +316,92 @@ class FilesWidget(VBox):
|
|
|
361
316
|
others = [
|
|
362
317
|
*others,
|
|
363
318
|
ipw.HTML(f"<hr/>"),
|
|
364
|
-
*(
|
|
319
|
+
*(free_widgets or []),
|
|
365
320
|
] # add hr to separate other controls
|
|
366
321
|
|
|
367
322
|
out.children = [
|
|
368
323
|
HBox(
|
|
369
324
|
[ # reset children to include new widgets
|
|
370
325
|
VBox(
|
|
371
|
-
children=[
|
|
326
|
+
children=[dd, VBox(others)]
|
|
372
327
|
), # other widgets in box to make scrollable independent file selection
|
|
373
328
|
VBox(
|
|
374
|
-
children=[
|
|
329
|
+
children=[output, *args, info]
|
|
375
330
|
), # output in box to make scrollable,
|
|
376
331
|
],
|
|
377
332
|
layout=Layout(height=height, max_height=height),
|
|
378
|
-
).add_class("
|
|
333
|
+
).add_class("files-interact")
|
|
379
334
|
] # important for every widget separately
|
|
380
335
|
return out
|
|
381
|
-
|
|
382
|
-
def
|
|
383
|
-
self
|
|
384
|
-
|
|
385
|
-
|
|
336
|
+
|
|
337
|
+
def _attributed_interactive(self, box, func, *args, **kwargs):
|
|
338
|
+
box._files = self
|
|
339
|
+
box._interact = self.interactive(func, *args, **kwargs)
|
|
340
|
+
box.children = box._interact.children
|
|
341
|
+
box._files._dd = box._interact._dd
|
|
342
|
+
|
|
343
|
+
def interact(self, *args,
|
|
344
|
+
free_widgets=None,
|
|
386
345
|
options={"manual": False},
|
|
387
346
|
height="400px",
|
|
347
|
+
panel_size=25,
|
|
388
348
|
**kwargs,
|
|
389
349
|
):
|
|
390
|
-
"""Interact with a
|
|
391
|
-
A CSS class '
|
|
350
|
+
"""Interact with a `func(path, *args, <free_widgets,optional>, **kwargs)`. `path` is passed from selected File.
|
|
351
|
+
A CSS class 'files-interact' is added to the final widget to let you style it.
|
|
392
352
|
|
|
393
353
|
Parameters
|
|
394
354
|
----------
|
|
395
|
-
|
|
355
|
+
args :
|
|
396
356
|
Any displayable widget can be passed. These are placed below the output widget of interact.
|
|
397
|
-
For example you can add plotly's FigureWidget that updates based on the selection,
|
|
398
|
-
|
|
399
|
-
Default is None. If not None, these are assumed to be ipywidgets and are placed below the widgets created by kwargs.
|
|
357
|
+
For example you can add plotly's FigureWidget that updates based on the selection, these are passed to function after path.
|
|
358
|
+
free_widgets : list/tuple
|
|
359
|
+
Default is None. If not None, these are assumed to be ipywidgets and are placed below the widgets created by kwargs.
|
|
360
|
+
These can be passed to the decorated function if added as arguemnt there like `func(..., free_widgets)`, but don't trigger execution.
|
|
400
361
|
options : dict
|
|
401
|
-
Default is {'
|
|
362
|
+
Default is {'manual':False}. If True, the decorated function is not called automatically, and you have to call it manually on button press. You can pass button name as 'manual_name' in options.
|
|
402
363
|
height : str
|
|
403
364
|
Default is '90vh'. height of the final widget. This is important to avoid very long widgets.
|
|
404
|
-
|
|
365
|
+
panel_size: int
|
|
366
|
+
Side panel size in units of em.
|
|
405
367
|
|
|
406
368
|
kwargs are passed to ipywidgets.interactive and decorated function. Resulting widgets are placed below the file selection widget.
|
|
407
|
-
|
|
408
|
-
`other_widgets` can be controlled by `other_controls` externally. For example, you can add a button to update a plotly's FigureWidget.
|
|
369
|
+
Widgets in `args` can be controlled by `free_widgets` externally if defined gloablly or inside function if you pass `free_widgets` as argument like `func(..., free_widgets)`.
|
|
409
370
|
|
|
410
371
|
The decorated function can be called later separately as well, and has .args and .kwargs attributes to access the latest arguments
|
|
411
372
|
and .result method to access latest. For a function `f`, `f.result` is same as `f(*f.args, **f.kwargs)`.
|
|
412
373
|
|
|
413
|
-
|
|
414
|
-
>>>
|
|
415
|
-
>>>
|
|
416
|
-
>>>
|
|
417
|
-
>>>
|
|
418
|
-
>>>
|
|
419
|
-
>>>
|
|
420
|
-
|
|
374
|
+
>>> import plotly.graph_objects as go
|
|
375
|
+
>>> import ipyvasp as ipv
|
|
376
|
+
>>> fs = ipv.Files('.','**/POSCAR')
|
|
377
|
+
>>> @fs.interact(go.FigureWidget(),bl=(0,6,2),plot_cell=True, eqv_sites=True)
|
|
378
|
+
>>> def plot(path, fig, bl,plot_cell,eqv_sites):
|
|
379
|
+
>>> ipv.iplot2widget(ipv.POSCAR(path).iplot_lattice(
|
|
380
|
+
>>> bond_length=bl,plot_cell=plot_cell,eqv_sites=eqv_sites
|
|
381
|
+
>>> ),fig_widget=fig) # it keeps updating same view
|
|
421
382
|
|
|
422
383
|
.. note::
|
|
423
384
|
Use self.interactive to get a widget that stores the argements and can be called later in a notebook cell.
|
|
424
385
|
"""
|
|
425
386
|
|
|
426
387
|
def inner(func):
|
|
427
|
-
display(
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
height=height,
|
|
434
|
-
**kwargs,
|
|
435
|
-
)
|
|
388
|
+
display(self.interactive(func, *args,
|
|
389
|
+
free_widgets=free_widgets,
|
|
390
|
+
options=options,
|
|
391
|
+
height=height,
|
|
392
|
+
panel_size=panel_size,
|
|
393
|
+
**kwargs)
|
|
436
394
|
)
|
|
437
395
|
return func
|
|
438
|
-
|
|
439
396
|
return inner
|
|
397
|
+
|
|
398
|
+
def kpath_widget(self, height='400px'):
|
|
399
|
+
"Get KpathWidget instance with these files."
|
|
400
|
+
return KpathWidget(files = self.with_name('POSCAR'), height = height)
|
|
440
401
|
|
|
441
|
-
def
|
|
442
|
-
"
|
|
443
|
-
|
|
444
|
-
return summarize(
|
|
445
|
-
{
|
|
446
|
-
key: value
|
|
447
|
-
for key, value in zip(self._widgets["files"].options, self._files)
|
|
448
|
-
},
|
|
449
|
-
func,
|
|
450
|
-
**kwargs,
|
|
451
|
-
)
|
|
402
|
+
def bands_widget(self, height='450px'):
|
|
403
|
+
"Get BandsWidget instance with these files."
|
|
404
|
+
return BandsWidget(files=self._files, height=height)
|
|
452
405
|
|
|
453
406
|
|
|
454
407
|
@fix_signature
|
|
@@ -647,12 +600,12 @@ class BandsWidget(VBox):
|
|
|
647
600
|
Two attributes are important:
|
|
648
601
|
self.clicked_data returns the last clicked point, that can also be stored as VBM, CBM etc, using Click dropdown.
|
|
649
602
|
self.selected_data returns the last selection of points within a box or lasso. You can plot that output separately as plt.plot(data.xs, data.ys) after a selection.
|
|
603
|
+
You can use `self.files.update` method to change source files without effecting state of widget.
|
|
650
604
|
"""
|
|
651
605
|
|
|
652
|
-
def __init__(self,
|
|
606
|
+
def __init__(self, files, height="450px"):
|
|
653
607
|
super().__init__(_dom_classes=["BandsWidget"])
|
|
654
608
|
self._bands = None
|
|
655
|
-
self._use_vaspout = use_vaspout
|
|
656
609
|
self._fig = go.FigureWidget()
|
|
657
610
|
self._tsd = Dropdown(
|
|
658
611
|
description="Style", options=["plotly_white", "plotly_dark"]
|
|
@@ -668,14 +621,9 @@ class BandsWidget(VBox):
|
|
|
668
621
|
self._click_dict = {} # store clicked data
|
|
669
622
|
self._select_dict = {} # store selection data
|
|
670
623
|
self._kwargs = {}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
}
|
|
675
|
-
self._interact = FilesWidget(**file_widget_kwargs).interactive(
|
|
676
|
-
self._load_data,
|
|
677
|
-
other_widgets=[self._fig],
|
|
678
|
-
other_controls=[
|
|
624
|
+
|
|
625
|
+
Files(files)._attributed_interactive(self, self._load_data, self._fig,
|
|
626
|
+
free_widgets=[
|
|
679
627
|
self._tsd,
|
|
680
628
|
self._brange,
|
|
681
629
|
self._ktcicks,
|
|
@@ -686,8 +634,7 @@ class BandsWidget(VBox):
|
|
|
686
634
|
],
|
|
687
635
|
height=height,
|
|
688
636
|
)
|
|
689
|
-
|
|
690
|
-
self.children = self._interact.children
|
|
637
|
+
|
|
691
638
|
self._tsd.observe(self._change_theme, "value")
|
|
692
639
|
self._click.observe(self._click_save_data, "value")
|
|
693
640
|
self._ktcicks.observe(self._warn_update, "value")
|
|
@@ -696,13 +643,19 @@ class BandsWidget(VBox):
|
|
|
696
643
|
@property
|
|
697
644
|
def path(self):
|
|
698
645
|
"Returns currently selected path."
|
|
699
|
-
return self.
|
|
646
|
+
return self._interact._dd.value
|
|
647
|
+
|
|
648
|
+
@property
|
|
649
|
+
def files(self):
|
|
650
|
+
"Use slef.files.update(...) to keep state of widget preserved."
|
|
651
|
+
return self._files
|
|
700
652
|
|
|
701
|
-
def _load_data(self, path): # Automatically redirectes to output widget
|
|
653
|
+
def _load_data(self, path, fig): # Automatically redirectes to output widget
|
|
654
|
+
if not hasattr(self, '_interact'): return # First time not availablebu
|
|
702
655
|
self._interact.output_widget.clear_output(wait=True) # Why need again?
|
|
703
656
|
with self._interact.output_widget:
|
|
704
657
|
self._bands = (
|
|
705
|
-
vp.
|
|
658
|
+
vp.Vasprun(path) if path.parts[-1].endswith('xml') else vp.Vaspout(path)
|
|
706
659
|
).bands
|
|
707
660
|
self._ppicks.update(self.bands.source.summary)
|
|
708
661
|
self._ktcicks.value = ", ".join(
|
|
@@ -714,7 +667,7 @@ class BandsWidget(VBox):
|
|
|
714
667
|
else:
|
|
715
668
|
self._click.options = ["None", "VBM", "CBM"]
|
|
716
669
|
|
|
717
|
-
if (file :=
|
|
670
|
+
if (file := path.parent / "result.json").is_file():
|
|
718
671
|
self._result = serializer.load(str(file.absolute())) # Old data loaded
|
|
719
672
|
|
|
720
673
|
pdata = self.bands.source.poscar.data
|
|
@@ -756,6 +709,7 @@ class BandsWidget(VBox):
|
|
|
756
709
|
return self._select_dict.get("data", None)
|
|
757
710
|
|
|
758
711
|
def _update_graph(self, btn):
|
|
712
|
+
if not hasattr(self, '_interact'): return # First time not available
|
|
759
713
|
self._interact.output_widget.clear_output(wait=True) # Why need again?
|
|
760
714
|
with self._interact.output_widget:
|
|
761
715
|
hsk = [
|
|
@@ -811,7 +765,7 @@ class BandsWidget(VBox):
|
|
|
811
765
|
serializer.dump(
|
|
812
766
|
data_dict,
|
|
813
767
|
format="json",
|
|
814
|
-
outfile=self.
|
|
768
|
+
outfile=self.path.parent / "result.json",
|
|
815
769
|
)
|
|
816
770
|
|
|
817
771
|
if (
|
|
@@ -854,8 +808,8 @@ class BandsWidget(VBox):
|
|
|
854
808
|
|
|
855
809
|
@property
|
|
856
810
|
def results(self):
|
|
857
|
-
"Generate a
|
|
858
|
-
return load_results(self.
|
|
811
|
+
"Generate a dataframe form result.json file in each folder."
|
|
812
|
+
return load_results(self._interact._dd.options)
|
|
859
813
|
|
|
860
814
|
|
|
861
815
|
@fix_signature
|
|
@@ -870,9 +824,11 @@ class KpathWidget(VBox):
|
|
|
870
824
|
- To update point(s), select point(s) from the select box and click on a scatter point in figure or use KPOINT input to update it manually, e.g. if a point is not available on plot.
|
|
871
825
|
- Add labels to the points by typing in the "Labels" box such as "Γ,X" or "Γ 5,X" that will add 5 points in interval.
|
|
872
826
|
- To break the path between two points "Γ" and "X" type "Γ 0,X" in the "Labels" box, zero means no points in interval.
|
|
827
|
+
|
|
828
|
+
You can use `self.files.update` method to change source files without effecting state of widget.
|
|
873
829
|
"""
|
|
874
830
|
|
|
875
|
-
def __init__(self, height="400px"
|
|
831
|
+
def __init__(self, files, height="400px"):
|
|
876
832
|
super().__init__(_dom_classes=["KpathWidget"])
|
|
877
833
|
self._fig = go.FigureWidget()
|
|
878
834
|
self._sm = SelectMultiple(options=[], layout=Layout(width="auto"))
|
|
@@ -885,7 +841,7 @@ class KpathWidget(VBox):
|
|
|
885
841
|
self._clicktime = None
|
|
886
842
|
self._kpoints = {}
|
|
887
843
|
|
|
888
|
-
|
|
844
|
+
free_widgets = [
|
|
889
845
|
HBox([self._add, self._del, self._tsb], layout=Layout(min_height="24px")),
|
|
890
846
|
ipw.HTML(
|
|
891
847
|
"<style>.KpathWidget .widget-select-multiple { min-height: 180px; }\n .widget-select-multiple > select {height: 100%;}</style>"
|
|
@@ -894,13 +850,11 @@ class KpathWidget(VBox):
|
|
|
894
850
|
self._lab,
|
|
895
851
|
self._kpt,
|
|
896
852
|
]
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
self._update_fig,
|
|
853
|
+
|
|
854
|
+
Files(files)._attributed_interactive(self,
|
|
855
|
+
self._update_fig, self._fig, free_widgets=free_widgets, height=height
|
|
900
856
|
)
|
|
901
|
-
|
|
902
|
-
self.children = self._interact.children
|
|
903
|
-
|
|
857
|
+
|
|
904
858
|
self._tsb.on_click(self._update_theme)
|
|
905
859
|
self._add.on_click(self._toggle_lock)
|
|
906
860
|
self._del.on_click(self._del_point)
|
|
@@ -910,14 +864,20 @@ class KpathWidget(VBox):
|
|
|
910
864
|
@property
|
|
911
865
|
def path(self):
|
|
912
866
|
"Returns currently selected path."
|
|
913
|
-
return self._interact.
|
|
867
|
+
return self._interact._dd.value # itself a Path object
|
|
868
|
+
|
|
869
|
+
@property
|
|
870
|
+
def files(self):
|
|
871
|
+
"Use slef.files.update(...) to keep state of widget preserved."
|
|
872
|
+
return self._files
|
|
914
873
|
|
|
915
874
|
@property
|
|
916
875
|
def poscar(self):
|
|
917
876
|
"POSCAR class associated to current selection."
|
|
918
877
|
return self._poscar
|
|
919
878
|
|
|
920
|
-
def _update_fig(self, path):
|
|
879
|
+
def _update_fig(self, path, fig):
|
|
880
|
+
if not hasattr(self, '_interact'): return # First time not available
|
|
921
881
|
from .lattice import POSCAR # to avoid circular import
|
|
922
882
|
|
|
923
883
|
with self._interact.output_widget:
|
|
@@ -926,10 +886,10 @@ class KpathWidget(VBox):
|
|
|
926
886
|
)
|
|
927
887
|
self._poscar = POSCAR(path)
|
|
928
888
|
ptk.iplot2widget(
|
|
929
|
-
self._poscar.iplot_bz(fill=False, color="red"),
|
|
889
|
+
self._poscar.iplot_bz(fill=False, color="red"), fig, template
|
|
930
890
|
)
|
|
931
|
-
with
|
|
932
|
-
|
|
891
|
+
with fig.batch_animate():
|
|
892
|
+
fig.add_trace(
|
|
933
893
|
go.Scatter3d(
|
|
934
894
|
x=[],
|
|
935
895
|
y=[],
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
ipyvasp/__init__.py,sha256=rlorju9arMtHw1QRYPljday-PyZWJdSCxg4lw3g6t0Q,1409
|
|
2
|
+
ipyvasp/__main__.py,sha256=eJV1TZSiT8mC_VqAeksNnBI2I8mKMiPkEIlwikbtOjI,216
|
|
3
|
+
ipyvasp/_enplots.py,sha256=D38paN8zqZgluNAwmCwcocd7-_h_T0HTGolI1eBkDes,37484
|
|
4
|
+
ipyvasp/_lattice.py,sha256=GxG0C4lwVGvBYIy3jwR1kahWR7L6kJlqjIiQGgESjcM,104135
|
|
5
|
+
ipyvasp/_version.py,sha256=iPcoATf7BiWjSu-KocRdM5zFTR4wx4ktCHlGGpvdc1M,23
|
|
6
|
+
ipyvasp/bsdos.py,sha256=1rG68S-dLEYveIWGK7r8CRa7Qqlqno0l1ncfo2ocihk,30424
|
|
7
|
+
ipyvasp/cli.py,sha256=aWFEVhNmnW8eSOp5uh95JaDwLQ9K9nlCQcbnOSuhWgw,6844
|
|
8
|
+
ipyvasp/evals_dataframe.py,sha256=-sqxK7LPV6sYDO_XXmZ80FznOaXTkVdbqJKKvTUtMak,20637
|
|
9
|
+
ipyvasp/lattice.py,sha256=t4s1qh6IJsoeXcCa9M9IhjAQp2s78lqiGhOfEkCW19s,30638
|
|
10
|
+
ipyvasp/misc.py,sha256=SZJ_ePUR2-HEKYTEpDHVRVE7zpIQVTCjiuw0BCC9UTU,2349
|
|
11
|
+
ipyvasp/potential.py,sha256=tzA73c5lkp6ahLSJchMrU043-QWaOV0nIOUA7VMmfKQ,11408
|
|
12
|
+
ipyvasp/surface.py,sha256=MjE5oB0wW6Pca_C-xu8rN6OMH7lUEeNPNyM7Kz_Im-8,23766
|
|
13
|
+
ipyvasp/utils.py,sha256=rVyD5SkO_Y7ok5W-fJeQys9X8pLLbDK7VOgbAbcE4WU,14227
|
|
14
|
+
ipyvasp/widgets.py,sha256=iWkH7PbzZFPUZ2LrSRLafTPxXfiRfNFG5Ddaq3Wd_BU,45655
|
|
15
|
+
ipyvasp/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
ipyvasp/core/parser.py,sha256=C3CaZsJbPME_ttYlYy4DXeOdL7dnkXs-cHRwFZL6bio,38058
|
|
17
|
+
ipyvasp/core/plot_toolkit.py,sha256=3RoPsND5gPssBSfS5H4TjoZ2Qz7B97vpxeKadc2cRRs,36046
|
|
18
|
+
ipyvasp/core/serializer.py,sha256=XpqnfVGsUXiN2CuVRPyqsSVouxRBO-UH6AnsHnPYvZY,36729
|
|
19
|
+
ipyvasp/core/spatial_toolkit.py,sha256=8DBYTiBFWJ7OBKuvOPw7UoEVCyNjJhSW0OcudjYZvAw,14748
|
|
20
|
+
ipyvasp-0.9.4.dist-info/LICENSE,sha256=F3SO5RiAZOMfmMGf1KOuk2g_c4ObvuBJhd9iBLDgXoQ,1263
|
|
21
|
+
ipyvasp-0.9.4.dist-info/METADATA,sha256=PvaEna3-zdOpEQhg5_iD9ECRt7N4nqI5aPluv6-VJ9M,2420
|
|
22
|
+
ipyvasp-0.9.4.dist-info/WHEEL,sha256=iYlv5fX357PQyRT2o6tw1bN-YcKFFHKqB_LwHO5wP-g,110
|
|
23
|
+
ipyvasp-0.9.4.dist-info/entry_points.txt,sha256=C7m0Sjmr14wFjflCkWXLzr5N6-cQj8uJC9n82mUtzt8,44
|
|
24
|
+
ipyvasp-0.9.4.dist-info/top_level.txt,sha256=ftziWlMWu_1VpDP1sRTFrkfBnWxAi393HYDVu4wRhUk,8
|
|
25
|
+
ipyvasp-0.9.4.dist-info/RECORD,,
|
ipyvasp-0.9.2.dist-info/RECORD
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
ipyvasp/__init__.py,sha256=7o41i5eYlNKg1Hsv0DLNFZ81GilxB02IXAJN-QiJQi0,1420
|
|
2
|
-
ipyvasp/__main__.py,sha256=eJV1TZSiT8mC_VqAeksNnBI2I8mKMiPkEIlwikbtOjI,216
|
|
3
|
-
ipyvasp/_enplots.py,sha256=D38paN8zqZgluNAwmCwcocd7-_h_T0HTGolI1eBkDes,37484
|
|
4
|
-
ipyvasp/_lattice.py,sha256=DN3VZVjQ5fbzUrbpft75ieaP5ddyr6mIuZRHOwrFrK8,103052
|
|
5
|
-
ipyvasp/_version.py,sha256=I3ASgj0LYAmGGQhqxkAYEEiiLOpQ1UAZGOd0bJAXSRw,23
|
|
6
|
-
ipyvasp/bsdos.py,sha256=ZtQji-W11UdFFicAoWZjlqVhI5tqYu_jpKyPPWKkeeo,30634
|
|
7
|
-
ipyvasp/cli.py,sha256=aWFEVhNmnW8eSOp5uh95JaDwLQ9K9nlCQcbnOSuhWgw,6844
|
|
8
|
-
ipyvasp/evals_dataframe.py,sha256=-sqxK7LPV6sYDO_XXmZ80FznOaXTkVdbqJKKvTUtMak,20637
|
|
9
|
-
ipyvasp/lattice.py,sha256=wz5CKv5YJbELvG2lbrYnXoT7u9R70Jkr_i3olE4Hx4I,30643
|
|
10
|
-
ipyvasp/misc.py,sha256=SZJ_ePUR2-HEKYTEpDHVRVE7zpIQVTCjiuw0BCC9UTU,2349
|
|
11
|
-
ipyvasp/potential.py,sha256=tzA73c5lkp6ahLSJchMrU043-QWaOV0nIOUA7VMmfKQ,11408
|
|
12
|
-
ipyvasp/surface.py,sha256=MjE5oB0wW6Pca_C-xu8rN6OMH7lUEeNPNyM7Kz_Im-8,23766
|
|
13
|
-
ipyvasp/utils.py,sha256=hiAV76jEMuDt1Wp22imrnsSgetjxmUKlskRA7CcK6BU,15261
|
|
14
|
-
ipyvasp/widgets.py,sha256=fZ2b7EYYxuaAVfklnLa0VJ00U9Uyd7SqXrzt0hbLOvI,45400
|
|
15
|
-
ipyvasp/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
ipyvasp/core/parser.py,sha256=C3CaZsJbPME_ttYlYy4DXeOdL7dnkXs-cHRwFZL6bio,38058
|
|
17
|
-
ipyvasp/core/plot_toolkit.py,sha256=8t5svyWbOm-PS7ZvIptnK6F46kp6uwoGNdohPv5dQKA,35962
|
|
18
|
-
ipyvasp/core/serializer.py,sha256=XpqnfVGsUXiN2CuVRPyqsSVouxRBO-UH6AnsHnPYvZY,36729
|
|
19
|
-
ipyvasp/core/spatial_toolkit.py,sha256=8DBYTiBFWJ7OBKuvOPw7UoEVCyNjJhSW0OcudjYZvAw,14748
|
|
20
|
-
ipyvasp-0.9.2.dist-info/LICENSE,sha256=F3SO5RiAZOMfmMGf1KOuk2g_c4ObvuBJhd9iBLDgXoQ,1263
|
|
21
|
-
ipyvasp-0.9.2.dist-info/METADATA,sha256=rbDpOJNtuinZ9AJi0pBcBTAYHLuoC_IbBvQagjXSHG4,2420
|
|
22
|
-
ipyvasp-0.9.2.dist-info/WHEEL,sha256=iYlv5fX357PQyRT2o6tw1bN-YcKFFHKqB_LwHO5wP-g,110
|
|
23
|
-
ipyvasp-0.9.2.dist-info/entry_points.txt,sha256=C7m0Sjmr14wFjflCkWXLzr5N6-cQj8uJC9n82mUtzt8,44
|
|
24
|
-
ipyvasp-0.9.2.dist-info/top_level.txt,sha256=ftziWlMWu_1VpDP1sRTFrkfBnWxAi393HYDVu4wRhUk,8
|
|
25
|
-
ipyvasp-0.9.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|