1
2
3
4 """
5 High level interface to NeXus files.
6
7 Unlike the nxs routines which implement the NeXus API directly, the
8 nexus routines preload the entire file structure into memory and use
9 a natural syntax for navigating the data hierarchy. Large datasets
10 are not read until they are needed, and may be read or written one
11 slab at a time.
12
13 There are a number of functions which operate on files::
14
15 * tree = read(file) loads a structure from a file
16 * write(file, tree) saves a structure to a file
17 * dir(file) display the contents of a file
18
19 The tree returned from read() has an entry for each group, field and
20 attribute. You can traverse the hierarchy using the names of the
21 groups. For example, tree.Histogram1.instrument.detector.distance
22 is a field containing the distance to each pixel in the detector.
23 NeXus attribute names are tagged with a leading 'A', so for example,
24 tree.Histogram1.instrument.detector.distance.Aunits contains the
25 units attribute for the detector distances.
26
27 Properties of the nodes in the tree are referenced by nx attributes.
28 Depending on the node type, different nx attributes may be available.
29
30 Nodes (class NXnode) have attributes shared by both groups and fields::
31 * nxname node name
32 * nxclass node class for groups, 'SDS' for fields
33 * nxgroup group containing the entry, or None for the root
34 * nxattrs dictionary of NeXus attributes for the node
35
36 Groups (class NXgroup) have attributes for accessing children::
37 * nxentries dictionary of entries within the group
38 * nxcomponent('nxclass') return group entries of a particular class
39 * nxdir() print the list of entries in the group
40 * nxtree() print the list of entries and subentries in the group
41 * nxplot() plot signal and axes for the group, if available
42
43 Fields (class SDS) have attributes for accessing data:
44 * nxdims dimensions of data in the field
45 * nxtype data type
46 * nxdata data in the field
47 * nxdata_as('units') data returned in particular units
48 * nxslab slab context for the field
49
50 Linked fields (class NXlink) have attributes for accessing the link::
51 * nxlink reference to the linked field
52
53 Unknown fields (class Unknown) are groups with a name that doesn't
54 start with 'NX'. These groups are not read or written.
55
56 NeXus attributes (class NXattr) have a type and a value only::
57 * nxtype attribute type
58 * nxdata attribute data
59
60 Data can be stored in the NeXus file in a variety of units, depending
61 on which facility is storing the file. This makes life difficult
62 for reduction and analysis programs which must know the units they
63 are working with. Our solution to this problem is to allow you to
64 retrieve data from the file in particular units. For example, if
65 detector distance is stored in the file using millimeters you can
66 retrieve them in meters using::
67 entry.instrument.detector.distance.nxdata_as('m')
68 See help for nxsunit for more details on the unit formats supported.
69
70 The slab interface to field data works by opening the file handle
71 and keeping it open as long as the slab interface is needed. This
72 is done in python 2.5 using the with statement. Once the context
73 is entered, get() and put() methods on the node allow you to read
74 and write data a slab at a time. For example::
75
76 # Read a Ni x Nj x Nk array one vector at a time
77 with node.nxslab:
78 size = [1,1,node.nxdims[2]]
79 for i in range(node.nxdims[0]):
80 for j in range(node.nxdims[1]):
81 value = node.nxslab.get([i,j,0],size) # Read counts
82
83 The equivalent can be done in Python 2.4 and lower using the context
84 functions __enter__ and __exit__::
85
86 node.nxslab.__enter__()
87 ... do the slab functions ...
88 node.nxslab.__exit__()
89
90 You can traverse the tree by component class instead of component name.
91 Since there may be multiple components of the same class in one group
92 you will need to specify which one to use. For example,
93 tree.NXentry[0].NXinstrument[0].NXdetector[0].distance references the
94 first detector of the first instrument of the first entry. Unfortunately,
95 there is no guarantee regarding the order of the entries, and it may vary
96 from call to call, so the above is of limited utility.
97
98 The nxplot() method for groups uses matplotlib to plot the data. You
99 can replace this with your own plotter by setting nexus.NXgroup._plotter
100 to your own plotter class. The plotter class has one method::
101
102 plot(signal, axes, entry, title)
103
104 where signal is the field containing the data, axes are the fields
105 listing the signal sample points, entry is file/path within the file
106 to the data group and title is the title of the NXentry, if available.
107
108 The read() and write() functions are implemented within a specialized
109 NeXus class which allows all the usual API functions. You can subclass
110 this with your own definitions for NXgroup(), NXattr(), SDS() and NXlink()
111 if you want to change the nature of the tree. The properties of these
112 classes are closely coupled to the behaviour of read/write so refer to
113 the source if you need to do this.
114 """
115 __all__ = ['read', 'write', 'dir']
116
117 from copy import copy, deepcopy
118 import numpy
119 import nxs
120 import nxsunit
121
122
123 -class NeXus(nxs.NeXus):
124 """
125 Structure-based interface to the NeXus file API.
126
127 Usage:
128
129 file = NeXus(filename, ['r','rw','w'])
130 root = file.read()
131 - read the structure of the NeXus file. This returns a NeXus tree.
132 file.write(root)
133 - write a NeXus tree to the file.
134 data = file.readpath(path)
135 - read data from a particular path
136
137
138 Example::
139
140 nx = NeXus('REF_L_1346.nxs','r')
141 tree = nx.read()
142 for entry in tree.NXentry:
143 process(entry)
144 copy = NeXus('modified.nxs','w')
145 copy.write(tree)
146
147 Note that the large datasets are not loaded immediately. Instead, the
148 when the data set is requested, the file is reopened, the data read, and
149 the file closed again. open/close are available for when we want to
150 read/write slabs without the overhead of moving the file cursor each time.
151 The NXdata nodes in the returned tree hold the node values.
152
153 """
155 """
156 Read the nexus file structure from the file. Reading of large datasets
157 will be postponed. Returns a tree of NXgroup, NXattr, SDS and
158 NXlink nodes.
159
160 Subclasses can provide methods for individual NeXus classes such
161 as NXbeam or NXdata. Brave users can also specialize NXgroup,
162 NXattr, SDS and NXlink methods.
163 """
164 self.open()
165 self.openpath("/")
166 root = self._readgroup()
167 self.close()
168 root.nxgroup = None
169
170
171 self._readlinks(root)
172 return root
173
175 """
176 Write the nexus file structure to the file. The file is assumed to
177 start empty.
178
179 Updating individual nodes can be done using the napi interface, with
180 nx.handle as the nexus file handle.
181 """
182 self.open()
183 links = []
184
185
186 for entry in tree.nxentries.values():
187 links += self._writegroup(entry, path="")
188 self._writelinks(links)
189 self.close()
190
192 """
193 Read the data on a particular file path.
194
195 Returns a numpy array containing the data, a python scalar, or a
196 string depending on the shape and storage class.
197 """
198 self.open()
199 self.openpath(path)
200 return self.getdata()
201
203 """
204 Return the attributes for the currently open group/data or for
205 the file if no group or data object is open.
206 """
207 attrs = dict()
208 for i in range(self.getattrinfo()):
209 name,length,nxtype = self.getnextattr()
210 value = self.getattr(name, length, nxtype)
211 pair = self.NXattr(value,nxtype)
212 attrs[name] = pair
213
214 return attrs
215
217 """
218 Read a data node, returning SDS or NXlink depending on the
219 nature of the node.
220 """
221
222
223 path = path+"/"+name
224 self.opendata(name)
225 attrs = self._readattrs()
226 if 'target' in attrs and attrs['target'].nxdata != path:
227
228
229 data = NXlink(name,nxclass='SDS',attrs=attrs)
230 else:
231 dims,type = self.getinfo()
232 if numpy.prod(dims) < 1000:
233 value = self.getdata()
234 else:
235 value = None
236 data = self.SDS(name,type,dims,attrs=attrs,value=value,
237 file=self,path=path)
238 self.closedata()
239 return data
240
242 children = {}
243 for i in range(n):
244 name,nxclass = self.getnextentry()
245
246 if nxclass == 'SDS':
247 children[name] = self._readdata(name,path)
248 elif nxclass.startswith('NX'):
249 self.opengroup(name,nxclass)
250 children[name] = self._readgroup()
251 self.closegroup()
252 else:
253 children[name] = self.Unknown(name,nxclass=nxclass)
254 return children
255
257 """
258 Read the currently open group and all subgroups.
259 """
260
261
262
263 n,_,nxclass = self.getgroupinfo()
264
265 path = '/' + '/'.join(self.path)
266 name = path.split('/')[-1]
267 attrs = self._readattrs()
268 if 'target' in attrs and attrs['target'].nxdata != path:
269
270
271 group = self.NXlink(name,nxclass=nxclass,attrs=attrs)
272 else:
273
274 children = self._readchildren(n,path)
275
276
277
278 if hasattr(self,nxclass):
279 factory = getattr(self,nxclass)
280 else:
281 factory = self.NXgroup
282 group = factory(name,nxclass,attrs=attrs,entries=children)
283
284 for node in children.values():
285 node.nxgroup = group
286 return group
287
289 """
290 Convert linked nodes into direct references.
291 """
292 for entry in root.nxentries.values():
293 if isinstance(entry, NXlink):
294 link = root
295 for level in self._link_target.split('.'):
296 link = getattr(link,level)
297 entry.nxlink = link
298
299
300 - def NXattr(self, *args, **kw): return NXattr(*args, **kw)
301 - def SDS(self, *args, **kw): return SDS(*args, **kw)
302 - def NXgroup(self,*args,**kw): return NXgroup(*args, **kw)
303 - def NXlink(self, *args, **kw): return NXlink(*args, **kw)
304 - def Unknown(self, *args, **kw): return Unknown(*args, **kw)
305
307 """
308 Return the attributes for the currently open group/data or for
309 the file if no group or data object is open.
310 """
311 for name,pair in attrs.iteritems():
312
313 self.putattr(name,pair.nxdata,pair.nxtype)
314
316 """
317 Write the given data node.
318
319 NXlinks cannot be written until the linked group is created, so
320 this routine returns the set of links that need to be written.
321 Call writelinks on the list.
322 """
323
324 path = path + "/" + data.nxname
325
326
327
328 if hasattr(data,'_link_target'):
329 return [(path, data._link_target)]
330
331
332
333
334 if numpy.prod(data.nxdims) > 10000:
335
336 slab_dims = numpy.ones(len(data.nxdims),'i')
337 slab_dims[-1] = data.nxdims[-1]
338 self.compmakedata(data.nxname, data.nxtype, data.nxdims,
339 'lzw', slab_dims)
340 else:
341
342 self.makedata(data.nxname, data.nxtype, data.nxdims)
343 self.opendata(data.nxname)
344 self._writeattrs(data.nxattrs)
345 value = data.nxdata
346 if value is not None: self.putdata(value)
347 self.closedata()
348 return []
349
351 """
352 Write the given group structure, including the data.
353
354 NXlinks cannot be written until the linked group is created, so
355 this routine returns the set of links that need to be written.
356 Call writelinks on the list.
357 """
358 path = path + "/" + group.nxname
359
360
361 links = []
362 self.makegroup(group.nxname, group.nxclass)
363 self.opengroup(group.nxname, group.nxclass)
364 self._writeattrs(group.nxattrs)
365 if hasattr(group, '_link_target'):
366 links += [(path, group._link_target)]
367 for child in group.nxentries.values():
368 if child.nxclass == 'SDS':
369 links += self._writedata(child,path)
370 elif hasattr(child,'_link_target'):
371 links += [(path+"/"+child.nxname,child._link_target)]
372 else:
373 links += self._writegroup(child,path)
374 self.closegroup()
375 return links
376
378 """
379 Create links within the NeXus file as indicated by the set of pairs
380 returned by writegroup.
381 """
382 gid = {}
383
384
385 for path,target in links:
386 gid[target] = None
387
388
389 for target in gid.iterkeys():
390
391 self.openpath(target)
392
393
394
395 try:
396 gid[target] = self.getdataID()
397 except RuntimeError:
398 gid[target] = self.getgroupID()
399
400
401 for path,target in links:
402 if path != target:
403
404 parent = "/".join(path.split("/")[:-1])
405
406 self.openpath(parent)
407 self.makelink(gid[target])
408
410 """
411 Attributes need to keep track of nxtype as well as attribute value.
412 """
413 - def __init__(self,value=None,nxtype='char'):
414 self.nxdata,self.nxtype = value,nxtype
415
417 return str(self.nxdata)
418
420 if self.nxtype == 'char':
421 return "NXattr('%s','%s')"%(self.nxdata,self.nxtype)
422 else:
423 return "NXattr(%s,'%s')"%(self.nxdata,self.nxtype)
424
426 """
427 Abstract base class for elements in NeXus files.
428 The individual objects may be either SDS data objects or groups.
429
430 Children should be accessible directly as object attributes.
431 NeXus attributes accessible using .Aname rather than .name.
432 """
433 nxclass = "unknown"
434 nxname = "unknown"
435 nxgroup = None
436
438 return "%s:%s"%(self.nxclass,self.nxname)
439
441 return "NXnode('%s','%s')"%(self.nxclass,self.nxname)
442
444 for k,v in attrs.items():
445 setattr(self, 'A'+k, v)
447 return dict([(k[1:],v)
448 for k,v in self.__dict__.items()
449 if isinstance(v,NXattr)])
451 return dict([(k,v)
452 for k,v in self.__dict__.items()
453 if isinstance(v,NXnode) and not k.startswith('nx')])
454 nxattrs = property(_attrs,doc="NeXus attributes for node")
455 nxentries = property(_entries,doc="NeXus nodes within group")
456
458 if self.nxclass == 'SDS':
459 return " "*indent+self.nxname+'\n'
460 else:
461 return " "*indent+self.nxname+':'+self.nxclass+'\n'
462
465
467 result = ""
468 attrs = self.nxattrs
469 names = attrs.keys()
470 names.sort()
471 for k in names:
472 result += " "*indent+"@%s = %s\n"%(k,attrs[k].nxdata)
473 return result
474
475 - def _str_tree(self,indent=0,attrs=False,recursive=False):
476 """
477 Print current node and possibly subnodes.
478 """
479
480 result = self._str_name(indent=indent)
481 if attrs: result += self._str_attrs(indent=indent+2)
482 result += self._str_value(indent=indent+2)
483
484 entries = self.nxentries
485 names = entries.keys()
486 names.sort()
487 if recursive:
488 for k in names:
489 result += entries[k]._str_tree(indent=indent+2,
490 attrs=attrs, recursive=True)
491 else:
492 for k in names:
493 result += entries[k]._str_name(indent=indent+2)
494 return result
495
496 - def nxdir(self,attrs=False,recursive=False):
497 """Print directory"""
498 print self._str_tree(attrs=attrs,recursive=recursive)
500 """Print directory tree"""
501 print self._str_tree(attrs=attrs,recursive=True)
502
503 -class NXslab_context(object):
504 """
505 Context manager for NeXus fields.
506
507 The context manager opens the file and positions the cursor to the
508 correct field. Data can then be read or written using the get() and
509 put() methods. See SDS for details on how to use this class.
510 """
511
512 - def __init__(self, file, path, unit_converter):
513 self._file = file
514 self._path = path
515 self._converter = unit_converter
516
517 - def __enter__(self):
518 """
519 Open the datapath for reading slab by slab.
520 """
521 self._close_on_exit = not self._file.isopen
522 self._file.open()
523 self._file.openpath(self._path)
524 return self
525
526 - def __exit__(self, *args):
527 """
528 Close the file associated the data after reading.
529 """
530 if self._close_on_exit:
531 self._file.close()
532
533 - def get(self, offset, size, units=""):
534 """
535 Get a slab from the data array.
536
537 Offsets are 0-origin. Shape can be inferred from the data.
538 Offset and shape must each have one entry per dimension.
539
540 If units are specified, convert the values to the given units
541 before returning them.
542
543 Raises ValueError if this fails.
544
545 Corresponds to NXgetslab(handle,data,offset,shape)
546 """
547 value = self._file.getslab(offset,size)
548 return self._converter(value,units)
549
550 - def put(self, data, offset):
551 """
552 Put a slab into the data array.
553
554 Offsets are 0-origin. Shape can be inferred from the data.
555 Offset and shape must each have one entry per dimension.
556
557 Raises ValueError if this fails.
558
559 Corresponds to NXputslab(handle,data,offset,shape)
560 """
561 self._file.putslab(data, offset, data.shape)
562
564 """
565 NeXus data node SDS.
566
567 Operations are querying type and dimensions, reading the entire
568 data block or reading a single slab.
569
570 Attributes:
571
572 nxname - dataset name
573 nxclass - SDS (Scientific Data Set)
574 nxattrs - attributes for the data
575 nxgroup - group containing the data
576 nxdims - array dimensions (numpy shape)
577 nxtype - data type (numpy dtype)
578 nxdata - data values
579 nxdata_as('units') - data values in particular units
580 nxslab - context manager for reading/writing slabs
581
582 Example:
583
584 # Read the entire array
585 node.nxdata # The dataset in stored units
586 node.nxdata_as(units="mm") # read the entire dataset as millimeters
587
588 # Read a Ni x Nj x Nk array one vector at a time
589 with node.nxslab:
590 size = [1,1,node.nxdims[2]]
591 for i in range(node.nxdims[0]):
592 for j in range(node.nxdims[1]):
593 value = node.nxslab.get([i,j,0],size) # Read counts
594
595
596 """
597 - def __init__(self,name,dtype='',shape=[],file=None,path=None,
598 attrs=None,value=None,group=None):
599 self._file = file
600 self._path = path
601 self._value = value
602 self.nxclass = "SDS"
603 self.nxname = name
604 self.nxtype = dtype
605 self.nxdims = shape
606 self.nxgroup = group
607
608 self._setattrs(attrs)
609 if 'units' in attrs:
610 units = attrs['units'].nxdata
611 else:
612 units = None
613 self._converter = nxsunit.Converter(units)
614 self.nxslab = NXslab_context(file, path, self._converter)
615
617 """
618 If value is loaded, return the value as a string. If value is
619 not loaded, return the empty string. Only the first view values
620 for large arrays will be printed.
621 """
622 if self._value is not None:
623 return str(self._value)
624 return ""
625
627 v = str(self)
628 if '\n' in v:
629 v = '\n'.join([(" "*indent)+s for s in v.split('\n')])
630 return v+'\n'
631
632 - def _str_tree(self,indent=0,attrs=False,recursive=False):
633 dims = 'x'.join([str(n) for n in self.nxdims])
634
635 v = str(self)
636 if '\n' in v or v == "":
637 v = "%s(%s)"%(self.nxtype, dims)
638 v = " "*indent + "%s = %s"%(self.nxname, v)+'\n'
639 if attrs: v += self._str_attrs(indent=indent+2)
640 return v
641
643 """
644 Read the data in particular units.
645 """
646 if self._value is None:
647 self._value = self._file.readpath(self._path)
648
649 return self._converter(self._value,units)
650
651 nxdata = property(nxdata_as,doc="The data in default units; see also read() and slab()")
652
654 """
655 NeXus linked node.
656
657 The real node will be accessible by following the nxlink attribute.
658 """
659 - def __init__(self,name,nxclass="",attrs={},group=None):
660 self.nxclass = nxclass
661 self.nxname = name
662 self.nxlink = None
663 self.nxgroup = None
664
665 self._setattrs(attrs)
666 self._link_target = attrs['target'].nxdata
668 return "NXlink(%s)"%(self._link_target)
669 - def _str_tree(self,indent=0,attrs=False,recursive=False):
670 return " "*indent+self.nxname+' -> '+self._link_target+'\n'
671
673 """
674 Return the centers of the axes regardless if the axes contain
675 bin boundaries or centers.
676 """
677 def findc(axis, dimlen):
678 if len(axis.nxdata) == dimlen+1:
679 return (axis.nxdata[:-1] + axis.nxdata[-1:])/2
680 else:
681 assert len(axis.nxdata) == dimlen
682 return axis.nxdata
683 return [findc(a,signal.nxdims[i]) for i,a in enumerate(axes)]
684
686 """
687 Construct a label for a data field suitable for use on a graph axis.
688 """
689 if hasattr(field,'Along_name'):
690 return field.Along_name.nxdata
691 else:
692 return "%s (%s)"%(field.nxname,field.Aunits.nxdata)
693
695 import pylab
696 ax = pylab.gca()
697 im = pylab.mpl.image.NonUniformImage(ax, extent=(x[0],x[-1],y[0],y[-1]))
698 im.set_data(x,y,z)
699 ax.images.append(im)
700 ax.set_xlim(x[0],x[-1])
701 ax.set_ylim(y[0],y[-1])
702 pylab.gcf().canvas.draw_idle()
703
705 """
706 Matplotlib plotter object for NeXus data nodes.
707 """
708 - def plot(self, signal, axes, entry, title, **opts):
709 import pylab
710 pylab.cla()
711
712
713 axis_data = centers(signal, axes)
714 title = entry + '\n' + title
715
716
717 if len(signal.nxdims) == 1:
718 if signal.Aunits.nxdata == 'counts':
719 myopts=copy(opts)
720 myopts.setdefault('fmt','o')
721 myopts.setdefault('linestyle','None')
722 pylab.errorbar(axis_data[0], signal.nxdata,
723 numpy.sqrt(signal.nxdata), **myopts)
724 else:
725 pylab.scatter(axis_data[0], signal.nxdata, **opts)
726 pylab.xlabel(label(axes[0]))
727 pylab.ylabel(label(signal))
728 pylab.title(title)
729
730
731 elif len(signal.nxdims) == 2:
732
733 gridplot = imshow_irregular
734 if signal.Aunits.nxdata == 'counts':
735 gridplot(axis_data[1], axis_data[0],
736 numpy.log10(signal.nxdata+1), **opts)
737 else:
738 gridplot(axis_data[1], axis_data[0],
739 signal.nxdata, **opts)
740 pylab.xlabel(label(axes[1]))
741 pylab.ylabel(label(axes[0]))
742 pylab.title(title)
743
744
745 else:
746 raise RuntimeError, "Cannot plot a dataset of rank 3 or greater."
747 @staticmethod
749 import pylab
750 pylab.show()
751
753 """
754 NeXus group node. NXgroup nodes have no data associated with them,
755 but they do have attributes and children.
756 """
757
758 _plotter = PylabPlotter()
759
760 - def __init__(self,name, nxclass=NXnode.nxclass,
761 attrs={},entries={}, group=None):
762 self.nxclass = nxclass
763 self.nxname = name
764 self.nxgroup = group
765
766 self._setattrs(attrs)
767 for k,v in entries.items():
768 setattr(self, k, v)
771
773 """
774 Provide direct access to nodes via nxclass name.
775 """
776 if key.startswith('NX'):
777 return self.nxcomponent(key)
778 raise KeyError(key+" not in "+self.nxclass+":"+self.nxname)
779
781 """
782 Find all child nodes that have a particular class.
783 """
784 return [E for name,E in self.nxentries.items() if E.nxclass==nxclass]
785
787 """
788 Plot data contained within the group.
789 """
790
791
792 for node in self.nxentries.values():
793 if 'signal' in node.nxattrs:
794 signal = node
795 break
796 else:
797 raise RuntimeError('No plottable signal')
798
799
800 if hasattr(signal,'Aaxes'):
801 axes = [getattr(self,a) for a in signal.Aaxes.nxdata.split(':')]
802 else:
803 raise RuntimeError('Axes attribute missing from signal')
804
805
806 path = []
807 node = self
808 subtitle = ''
809 while node.nxgroup is not None:
810 if node.nxclass == 'NXentry':
811 subtitle = node.title.nxdata
812 path = [node.nxname] + path
813 node = node.nxgroup
814 path = [node.Afile_name.nxdata] + path
815 entry = "/".join(path)
816
817
818 self._plotter.plot(signal, axes, entry, subtitle)
819
821 """
822 Unknown group type; class does not start with NX or SDS.
823 """
824 - def __init__(self, name, nxclass=NXnode.nxclass):
825 self.nxname = name
826 self.nxclass = nxclass
828 return "Unknown('%s','%s')"%(self.nxname,self.nxclass)
829
830
831 -def read(filename, mode='r'):
832 """
833 Read a NeXus file, returning a tree of nodes
834 """
835 file = NeXus(filename,mode)
836 tree = file.read()
837 file.close()
838 return tree
839
840 -def write(filename, tree):
841 """
842 Write a NeXus file from a tree of nodes
843 """
844 file = NeXus(filename,'w5')
845 file.write(tree)
846
848 """
849 Read and summarize the named nexus file.
850 """
851 tree = read(file)
852 tree.nxtree()
853
855 """
856 Process command line commands in argv. argv should contain
857 program name, command, arguments, where command is one of
858 the following:
859 copy fromfile.nxs tofile.nxs
860 ls f1.nxs f2.nxs ...
861 """
862 if len(argv) > 1:
863 op = argv[1]
864 else:
865 op = 'help'
866 if op == 'ls':
867 for f in argv[2:]: dir(f)
868 elif op == 'copy' and len(argv)==4:
869 tree = read(argv[2])
870 write(argv[3], tree)
871 elif op == 'plot' and len(argv)==4:
872 tree = read(argv[2])
873 for entry in argv[3].split('.'):
874 tree = getattr(tree,entry)
875 tree.nxplot()
876 tree._plotter.show()
877
878 else:
879 usage = """
880 usage: %s cmd [args]
881 copy fromfile.nxs tofile.nxs
882 ls *.nxs
883 plot file.nxs entry.data
884 """%(argv[0],)
885 print usage
886
887 if __name__ == "__main__":
888 import sys
889 demo(sys.argv)
890