source: trunk/GSASIIIO.py @ 1619

Last change on this file since 1619 was 1619, checked in by toby, 9 years ago

add columnsorter for hetrogeneous seq ref; reorg to start moving widgets out of g2grid

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 100.7 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2014-12-27 03:58:22 +0000 (Sat, 27 Dec 2014) $
4# $Author: toby $
5# $Revision: 1619 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 1619 2014-12-27 03:58:22Z toby $
8########### SVN repository information ###################
9'''
10*GSASIIIO: Misc I/O routines*
11=============================
12
13Module with miscellaneous routines for input and output. Many
14are GUI routines to interact with user.
15
16Includes support for image reading.
17
18Also includes base classes for data import routines.
19
20'''
21"""GSASIIIO: functions for IO of data
22   Copyright: 2008, Robert B. Von Dreele (Argonne National Laboratory)
23"""
24import wx
25import math
26import numpy as np
27import cPickle
28import sys
29import re
30import random as ran
31import GSASIIpath
32GSASIIpath.SetVersionNumber("$Revision: 1619 $")
33import GSASIIgrid as G2gd
34import GSASIIspc as G2spc
35import GSASIIlattice as G2lat
36import GSASIIpwdGUI as G2pdG
37import GSASIIElem as G2el
38import GSASIIstrIO as G2stIO
39import GSASIImapvars as G2mv
40import GSASIIctrls as G2G
41import os
42import os.path as ospath
43
44DEBUG = False       #=True for various prints
45TRANSP = False      #=true to transpose images for testing
46npsind = lambda x: np.sin(x*np.pi/180.)
47
48def sfloat(S):
49    'Convert a string to float. An empty field is treated as zero'
50    if S.strip():
51        return float(S)
52    else:
53        return 0.0
54
55def sint(S):
56    'Convert a string to int. An empty field is treated as zero'
57    if S.strip():
58        return int(S)
59    else:
60        return 0
61
62def trim(val):
63    '''Simplify a string containing leading and trailing spaces
64    as well as newlines, tabs, repeated spaces etc. into a shorter and
65    more simple string, by replacing all ranges of whitespace
66    characters with a single space.
67
68    :param str val: the string to be simplified
69
70    :returns: the (usually) shortened version of the string
71    '''
72    return re.sub('\s+', ' ', val).strip()
73
74def makeInstDict(names,data,codes):
75    inst = dict(zip(names,zip(data,data,codes)))
76    for item in inst:
77        inst[item] = list(inst[item])
78    return inst
79
80def FileDlgFixExt(dlg,file):
81    'this is needed to fix a problem in linux wx.FileDialog'
82    ext = dlg.GetWildcard().split('|')[2*dlg.GetFilterIndex()+1].strip('*')
83    if ext not in file:
84        file += ext
85    return file
86       
87def GetPowderPeaks(fileName):
88    'Read powder peaks from a file'
89    sind = lambda x: math.sin(x*math.pi/180.)
90    asind = lambda x: 180.*math.asin(x)/math.pi
91    Cuka = 1.54052
92    File = open(fileName,'Ur')
93    Comments = []
94    peaks = []
95    S = File.readline()
96    while S:
97        if S[:1] == '#':
98            Comments.append(S[:-1])
99        else:
100            item = S.split()
101            if len(item) == 1:
102                peaks.append([float(item[0]),1.0])
103            elif len(item) > 1:
104                peaks.append([float(item[0]),float(item[0])])
105        S = File.readline()
106    File.close()
107    if Comments:
108       print 'Comments on file:'
109       for Comment in Comments: print Comment
110    Peaks = []
111    if peaks[0][0] > peaks[-1][0]:          # d-spacings - assume CuKa
112        for peak in peaks:
113            dsp = peak[0]
114            sth = Cuka/(2.0*dsp)
115            if sth < 1.0:
116                tth = 2.0*asind(sth)
117            else:
118                break
119            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
120    else:                                   #2-thetas - assume Cuka (for now)
121        for peak in peaks:
122            tth = peak[0]
123            dsp = Cuka/(2.0*sind(tth/2.0))
124            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
125    return Comments,Peaks
126
127def CheckImageFile(G2frame,imagefile):
128    '''Get an new image file name if the specified one does not
129    exist
130
131    :param wx.Frame G2frame: main GSAS-II Frame and data object
132
133    :param str imagefile: name of image file
134
135    :returns: imagefile, if it exists, or the name of a file
136      that does exist or False if the user presses Cancel
137
138    '''
139    if not os.path.exists(imagefile):
140        print 'Image file '+imagefile+' not found'
141        fil = imagefile.replace('\\','/') # windows?!
142        # see if we can find a file with same name or in a similarly named sub-dir
143        pth,fil = os.path.split(fil)
144        prevpth = None
145        while pth and pth != prevpth:
146            prevpth = pth
147            if os.path.exists(os.path.join(G2frame.dirname,fil)):
148                print 'found image file '+os.path.join(G2frame.dirname,fil)
149                return os.path.join(G2frame.dirname,fil)
150            pth,enddir = os.path.split(pth)
151            fil = os.path.join(enddir,fil)
152        # not found as a subdirectory, drop common parts of path for last saved & image file names
153        #    if image was .../A/B/C/imgs/ima.ge
154        #      & GPX was  .../A/B/C/refs/fil.gpx but is now .../NEW/TEST/TEST1
155        #    will look for .../NEW/TEST/TEST1/imgs/ima.ge, .../NEW/TEST/imgs/ima.ge, .../NEW/imgs/ima.ge and so on
156        Controls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
157        gpxPath = Controls.get('LastSavedAs','').replace('\\','/').split('/') # blank in older .GPX files
158        imgPath = imagefile.replace('\\','/').split('/')
159        for p1,p2 in zip(gpxPath,imgPath):
160            if p1 == p2:
161                gpxPath.pop(0),imgPath.pop(0)
162            else:
163                break
164        fil = os.path.join(*imgPath) # file with non-common prefix elements
165        prevpth = None
166        pth = os.path.abspath(G2frame.dirname)
167        while pth and pth != prevpth:
168            prevpth = pth
169            if os.path.exists(os.path.join(pth,fil)):
170                print 'found image file '+os.path.join(pth,fil)
171                return os.path.join(pth,fil)
172            pth,enddir = os.path.split(pth)
173        GSASIIpath.IPyBreak()
174
175    if not os.path.exists(imagefile):
176        dlg = wx.FileDialog(G2frame, 'Previous image file not found; open here', '.', '',\
177        'Any image file (*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img)\
178        |*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img|\
179        European detector file (*.edf)|*.edf|\
180        Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|\
181        MAR file (*.mar*)|*.mar*|\
182        GE Image (*.ge*;*.avg;*.sum)|*.ge*;*.avg;*.sum|\
183        ADSC Image (*.img)|*.img|\
184        All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
185        try:
186            dlg.SetFilename(''+ospath.split(imagefile)[1])
187            if dlg.ShowModal() == wx.ID_OK:
188                imagefile = dlg.GetPath()
189            else:
190                imagefile = False
191        finally:
192            dlg.Destroy()
193    return imagefile
194
195def EditImageParms(parent,Data,Comments,Image,filename):
196    dlg = wx.Dialog(parent, wx.ID_ANY, 'Edit image parameters',
197                    style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
198    def onClose(event):
199        dlg.EndModal(wx.ID_OK)
200    mainsizer = wx.BoxSizer(wx.VERTICAL)
201    h,w = Image.shape[:2]
202    mainsizer.Add(wx.StaticText(dlg,wx.ID_ANY,
203                                'File '+str(filename)+'\nImage size: '+str(h)+' x '+str(w)),
204                  0,wx.ALIGN_LEFT|wx.ALL, 2)
205   
206    vsizer = wx.BoxSizer(wx.HORIZONTAL)
207    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Wavelength (\xC5) '),
208               0,wx.ALIGN_LEFT|wx.ALL, 2)
209    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'wavelength')
210    vsizer.Add(wdgt)
211    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
212
213    vsizer = wx.BoxSizer(wx.HORIZONTAL)
214    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Pixel size (\xb5m). Width '),
215               0,wx.ALIGN_LEFT|wx.ALL, 2)
216    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],0,
217                                 size=(50,-1))
218    vsizer.Add(wdgt)
219    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Height '),
220               wx.ALIGN_LEFT|wx.ALL, 2)
221    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],1,
222                                 size=(50,-1))
223    vsizer.Add(wdgt)
224    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
225
226    vsizer = wx.BoxSizer(wx.HORIZONTAL)
227    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Sample to detector (mm) '),
228               0,wx.ALIGN_LEFT|wx.ALL, 2)
229    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'distance')
230    vsizer.Add(wdgt)
231    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
232
233    vsizer = wx.BoxSizer(wx.HORIZONTAL)
234    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Beam center (pixels). X = '),
235               0,wx.ALIGN_LEFT|wx.ALL, 2)
236    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],0,
237                                 size=(75,-1))
238    vsizer.Add(wdgt)
239    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Y = '),
240               wx.ALIGN_LEFT|wx.ALL, 2)
241    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],1,
242                                 size=(75,-1))
243    vsizer.Add(wdgt)
244    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
245
246    vsizer = wx.BoxSizer(wx.HORIZONTAL)
247    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Comments '),
248               0,wx.ALIGN_LEFT|wx.ALL, 2)
249    wdgt = G2G.ValidatedTxtCtrl(dlg,Comments,0,size=(250,-1))
250    vsizer.Add(wdgt)
251    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
252
253    btnsizer = wx.StdDialogButtonSizer()
254    OKbtn = wx.Button(dlg, wx.ID_OK, 'Continue')
255    OKbtn.SetDefault()
256    OKbtn.Bind(wx.EVT_BUTTON,onClose)
257    btnsizer.AddButton(OKbtn) # not sure why this is needed
258    btnsizer.Realize()
259    mainsizer.Add(btnsizer, 1, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 5)
260    dlg.SetSizer(mainsizer)
261    dlg.CenterOnParent()
262    dlg.ShowModal()
263
264def GetImageData(G2frame,imagefile,imageOnly=False):
265    '''Read an image with the file reader keyed by the
266    file extension
267
268    :param wx.Frame G2frame: main GSAS-II Frame and data object.
269
270    :param str imagefile: name of image file
271
272    :param bool imageOnly: If True return only the image,
273      otherwise  (default) return more (see below)
274
275    :returns: an image as a numpy array or a list of four items:
276      Comments, Data, Npix and the Image, as selected by imageOnly
277
278    '''
279    ext = ospath.splitext(imagefile)[1]
280    Comments = []
281    Image = None
282    if ext == '.tif' or ext == '.tiff':
283        Comments,Data,Npix,Image = GetTifData(imagefile)
284        if Npix == 0:
285            print("GetTifData failed to read "+str(filename)+" Trying PIL")
286            import scipy.misc
287            Image = scipy.misc.imread(imagefile,flatten=True)
288            Npix = Image.size
289            Comments = ['no metadata']
290            Data = {'wavelength': 0.1, 'pixelSize': [200, 200], 'distance': 100.0}
291            Data['size'] = list(Image.shape)
292            Data['center'] = [int(i/2) for i in Image.shape]
293            if not imageOnly:
294                EditImageParms(G2frame,Data,Comments,Image,imagefile)
295    elif ext == '.edf':
296        Comments,Data,Npix,Image = GetEdfData(imagefile)
297    elif ext == '.img':
298        Comments,Data,Npix,Image = GetImgData(imagefile)
299        Image[0][0] = 0
300    elif ext == '.mar3450' or ext == '.mar2300':
301        Comments,Data,Npix,Image = GetMAR345Data(imagefile)
302    elif ext in ['.sum','.avg'] or 'ge' in ext:
303        Comments,Data,Npix,Image = GetGEsumData(imagefile)
304    elif ext == '.G2img':
305        Comments,Data,Npix,Image = GetG2Image(imagefile)
306    elif ext == '.png':
307        Comments,Data,Npix,Image = GetPNGData(imagefile)
308        if not imageOnly:
309            EditImageParms(G2frame,Data,Comments,Image,imagefile)
310    else:
311        print 'Extension for file '+imagefile+' not recognized'
312    if Image is None:
313        raise Exception('No image read')
314    if imageOnly:
315        if TRANSP:
316            return Image.T
317        else:
318            return Image
319    else:
320        if TRANSP:
321            return Comments,Data,Npix,Image.T
322        else:
323            return Comments,Data,Npix,Image
324       
325def PutG2Image(filename,Comments,Data,Npix,image):
326    'Write an image as a python pickle - might be better as an .edf file?'
327    File = open(filename,'wb')
328    cPickle.dump([Comments,Data,Npix,image],File,1)
329    File.close()
330    return
331   
332def GetG2Image(filename):
333    'Read an image as a python pickle'
334    File = open(filename,'rb')
335    Comments,Data,Npix,image = cPickle.load(File)
336    File.close()
337    return Comments,Data,Npix,image
338   
339def GetEdfData(filename,imageOnly=False):   
340    'Read European detector data edf file'
341    import struct as st
342    import array as ar
343    if not imageOnly:
344        print 'Read European detector data edf file: ',filename
345    File = open(filename,'rb')
346    fileSize = os.stat(filename).st_size
347    head = File.read(3072)
348    lines = head.split('\n')
349    sizexy = [0,0]
350    pixSize = [154,154]     #Pixium4700?
351    cent = [0,0]
352    wave = 1.54187  #default <CuKa>
353    dist = 1000.
354    head = ['European detector data',]
355    for line in lines:
356        line = line.replace(';',' ').strip()
357        fields = line.split()
358        if 'Dim_1' in line:
359            sizexy[0] = int(fields[2])
360        elif 'Dim_2' in line:
361            sizexy[1] = int(fields[2])
362        elif 'DataType' in line:
363            dType = fields[2]
364        elif 'wavelength' in line:
365            wave = float(fields[2])
366        elif 'Size' in line:
367            imSize = int(fields[2])
368        elif 'DataType' in lines:
369            dType = fields[2]
370        elif 'pixel_size_x' in line:
371            pixSize[0] = float(fields[2])
372        elif 'pixel_size_y' in line:
373            pixSize[1] = float(fields[2])
374        elif 'beam_center_x' in line:
375            cent[0] = float(fields[2])
376        elif 'beam_center_y' in line:
377            cent[1] = float(fields[2])
378        elif 'refined_distance' in line:
379            dist = float(fields[2])
380        if line:
381            head.append(line)
382        else:   #blank line at end of header
383            break 
384    File.seek(fileSize-imSize)
385    if dType == 'UnsignedShort':       
386        image = np.array(ar.array('H',File.read(imSize)),dtype=np.int32)
387    elif dType == 'UnsignedInt':
388        image = np.array(ar.array('L',File.read(imSize)),dtype=np.int32)
389    elif dType == 'UnsignedLong':
390        image = np.array(ar.array('L',File.read(imSize)),dtype=np.int32)       
391    image = np.reshape(image,(sizexy[1],sizexy[0]))
392    data = {'pixelSize':pixSize,'wavelength':wave,'distance':dist,'center':cent,'size':sizexy}
393    Npix = sizexy[0]*sizexy[1]
394    File.close()   
395    if imageOnly:
396        return image
397    else:
398        return head,data,Npix,image
399       
400def GetGEsumData(filename,imageOnly=False):
401    'Read SUM file as produced at 1-ID from G.E. images'
402    import struct as st
403    import array as ar
404    if not imageOnly:
405        print 'Read GE sum file: ',filename   
406    File = open(filename,'rb')
407    if '.sum' in filename:
408        head = ['GE detector sum data from APS 1-ID',]
409        sizexy = [2048,2048]
410    elif '.avg' in filename or '.ge' in filename:
411        head = ['GE detector avg or ge* data from APS 1-ID',]
412        sizexy = [2048,2048]
413    else:
414        head = ['GE detector raw data from APS 1-ID',]
415        File.seek(18)
416        size,nframes = st.unpack('<ih',File.read(6))
417        sizexy = [2048,2048]
418        pos = 8192
419        File.seek(pos)
420    Npix = sizexy[0]*sizexy[1]
421    if '.sum' in filename:
422        image = np.array(ar.array('f',File.read(4*Npix)),dtype=np.int32)
423    elif '.avg' in filename or '.ge' in filename:
424        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
425    else:
426        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
427        while nframes > 1:
428            image += np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
429            nframes -= 1
430    image = np.reshape(image,(sizexy[1],sizexy[0]))
431    data = {'pixelSize':[200,200],'wavelength':0.15,'distance':250.0,'center':[204.8,204.8],'size':sizexy} 
432    File.close()   
433    if imageOnly:
434        return image
435    else:
436        return head,data,Npix,image
437       
438def GetImgData(filename,imageOnly=False):
439    'Read an ADSC image file'
440    import struct as st
441    import array as ar
442    if not imageOnly:
443        print 'Read ADSC img file: ',filename
444    File = open(filename,'rb')
445    head = File.read(511)
446    lines = head.split('\n')
447    head = []
448    center = [0,0]
449    for line in lines[1:-2]:
450        line = line.strip()[:-1]
451        if line:
452            if 'SIZE1' in line:
453                size = int(line.split('=')[1])
454                Npix = size*size
455            elif 'WAVELENGTH' in line:
456                wave = float(line.split('=')[1])
457            elif 'BIN' in line:
458                if line.split('=')[1] == '2x2':
459                    pixel=(102,102)
460                else:
461                    pixel = (51,51)
462            elif 'DISTANCE' in line:
463                distance = float(line.split('=')[1])
464            elif 'CENTER_X' in line:
465                center[0] = float(line.split('=')[1])
466            elif 'CENTER_Y' in line:
467                center[1] = float(line.split('=')[1])
468            head.append(line)
469    data = {'pixelSize':pixel,'wavelength':wave,'distance':distance,'center':center,'size':[size,size]}
470    image = []
471    row = 0
472    pos = 512
473    File.seek(pos)
474    image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
475    image = np.reshape(image,(sizexy[1],sizexy[0]))
476#    image = np.zeros(shape=(size,size),dtype=np.int32)   
477#    while row < size:
478#        File.seek(pos)
479#        line = ar.array('H',File.read(2*size))
480#        image[row] = np.asarray(line)
481#        row += 1
482#        pos += 2*size
483    File.close()
484    if imageOnly:
485        return image
486    else:
487        return lines[1:-2],data,Npix,image
488       
489def GetMAR345Data(filename,imageOnly=False):
490    'Read a MAR-345 image plate image'
491    import array as ar
492    import struct as st
493    try:
494        import pack_f as pf
495    except:
496        msg = wx.MessageDialog(None, message="Unable to load the GSAS MAR image decompression, pack_f",
497                               caption="Import Error",
498                               style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
499        msg.ShowModal()
500        return None,None,None,None
501
502    if not imageOnly:
503        print 'Read Mar345 file: ',filename
504    File = open(filename,'rb')
505    head = File.read(4095)
506    numbers = st.unpack('<iiiiiiiiii',head[:40])
507    lines = head[128:].split('\n')
508    head = []
509    for line in lines:
510        line = line.strip()
511        if 'PIXEL' in line:
512            values = line.split()
513            pixel = (int(values[2]),int(values[4]))     #in microns
514        elif 'WAVELENGTH' in line:
515            wave = float(line.split()[1])
516        elif 'DISTANCE' in line:
517            distance = float(line.split()[1])           #in mm
518        elif 'CENTER' in line:
519            values = line.split()
520            center = [float(values[2])/10.,float(values[4])/10.]    #make in mm from pixels
521        if line: 
522            head.append(line)
523    data = {'pixelSize':pixel,'wavelength':wave,'distance':distance,'center':center}
524    for line in head:
525        if 'FORMAT' in line[0:6]:
526            items = line.split()
527            size = int(items[1])
528            Npix = size*size
529    pos = 4096
530    data['size'] = [size,size]
531    File.seek(pos)
532    line = File.read(8)
533    while 'CCP4' not in line:       #get past overflow list for now
534        line = File.read(8)
535        pos += 8
536    pos += 37
537    File.seek(pos)
538    raw = File.read()
539    File.close()
540    image = np.zeros(shape=(size,size),dtype=np.int32)
541    image = np.flipud(pf.pack_f(len(raw),raw,size,image).T)  #transpose to get it right way around & flip
542    if imageOnly:
543        return image
544    else:
545        return head,data,Npix,image
546       
547def GetPNGData(filename,imageOnly=False):
548    '''Read an image in a png format, assumes image is converted from CheMin tif file
549    so default parameters are that machine.
550    '''
551    import scipy.misc
552    Image = scipy.misc.imread(filename,flatten=True)
553    Npix = Image.size
554    Comments = ['no metadata']
555    pixy = list(Image.shape)
556    sizexy = [40,40]
557    Data = {'wavelength': 1.78892, 'pixelSize': sizexy, 'distance': 18.0,'size':pixy}
558    Data['center'] = [pixy[0]*sizexy[0]/1000,pixy[1]*sizexy[1]/2000]
559    if imageOnly:
560        return Image.T
561    else:
562        return Comments,Data,Npix,Image.T
563
564def GetTifData(filename,imageOnly=False):
565    '''Read an image in a pseudo-tif format,
566    as produced by a wide variety of software, almost always
567    incorrectly in some way.
568    '''
569    import struct as st
570    try:
571        import Image as Im
572    except ImportError:
573        try:
574            from PIL import Image as Im
575        except ImportError:
576            print "PIL/pillow Image module not present. TIFs cannot be read without this"
577            raise Exception("PIL/pillow Image module not found")
578    import array as ar
579    import ReadMarCCDFrame as rmf
580    File = open(filename,'rb')
581    dataType = 5
582    center = [None,None]
583    wavelength = None
584    distance = None
585    try:
586        Meta = open(filename+'.metadata','Ur')
587        head = Meta.readlines()
588        for line in head:
589            line = line.strip()
590            if 'dataType=' in line:
591                dataType = int(line.split('=')[1])
592        Meta.close()
593    except IOError:
594        print 'no metadata file found - will try to read file anyway'
595        head = ['no metadata file found',]
596       
597    tag = File.read(2)
598    byteOrd = '<'
599    if tag == 'II' and int(st.unpack('<h',File.read(2))[0]) == 42:     #little endian
600        IFD = int(st.unpack(byteOrd+'i',File.read(4))[0])
601    elif tag == 'MM' and int(st.unpack('>h',File.read(2))[0]) == 42:   #big endian
602        byteOrd = '>'
603        IFD = int(st.unpack(byteOrd+'i',File.read(4))[0])       
604    else:
605        lines = ['not a detector tiff file',]
606        return lines,0,0,0
607    File.seek(IFD)                                                  #get number of directory entries
608    NED = int(st.unpack(byteOrd+'h',File.read(2))[0])
609    IFD = {}
610    nSlice = 1
611    for ied in range(NED):
612        Tag,Type = st.unpack(byteOrd+'Hh',File.read(4))
613        nVal = st.unpack(byteOrd+'i',File.read(4))[0]
614        if DEBUG: print 'Try:',Tag,Type,nVal
615        if Type == 1:
616            Value = st.unpack(byteOrd+nVal*'b',File.read(nVal))
617        elif Type == 2:
618            Value = st.unpack(byteOrd+'i',File.read(4))
619        elif Type == 3:
620            Value = st.unpack(byteOrd+nVal*'h',File.read(nVal*2))
621            x = st.unpack(byteOrd+nVal*'h',File.read(nVal*2))
622        elif Type == 4:
623            if Tag in [273,279]:
624                nSlice = nVal
625                nVal = 1
626            Value = st.unpack(byteOrd+nVal*'i',File.read(nVal*4))
627        elif Type == 5:
628            Value = st.unpack(byteOrd+nVal*'i',File.read(nVal*4))
629        elif Type == 11:
630            Value = st.unpack(byteOrd+nVal*'f',File.read(nVal*4))
631        IFD[Tag] = [Type,nVal,Value]
632        if DEBUG: print Tag,IFD[Tag]
633    sizexy = [IFD[256][2][0],IFD[257][2][0]]
634    [nx,ny] = sizexy
635    Npix = nx*ny
636    if 34710 in IFD:
637        if not imageOnly:
638            print 'Read MAR CCD tiff file: ',filename
639        marFrame = rmf.marFrame(File,byteOrd,IFD)
640        image = np.flipud(np.array(np.asarray(marFrame.image),dtype=np.int32))
641        tifType = marFrame.filetitle
642        pixy = [marFrame.pixelsizeX/1000.0,marFrame.pixelsizeY/1000.0]
643        head = marFrame.outputHead()
644# extract resonable wavelength from header
645        wavelength = marFrame.sourceWavelength*1e-5
646        wavelength = (marFrame.opticsWavelength > 0) and marFrame.opticsWavelength*1e-5 or wavelength
647        wavelength = (wavelength <= 0) and None or wavelength
648# extract resonable distance from header
649        distance = (marFrame.startXtalToDetector+marFrame.endXtalToDetector)*5e-4
650        distance = (distance <= 0) and marFrame.xtalToDetector*1e-3 or distance
651        distance = (distance <= 0) and None or distance
652# extract resonable center from header
653        center = [marFrame.beamX*marFrame.pixelsizeX*1e-9,marFrame.beamY*marFrame.pixelsizeY*1e-9]
654        center = (center[0] != 0 and center[1] != 0) and center or [None,None]
655#print head,tifType,pixy
656    elif nSlice > 1:    #CheMin multislice tif file!
657        tifType = 'CheMin'
658        pixy = [40,40]
659        image = np.flipud(np.array(Im.open(filename)))*10.
660        distance = 18.0
661        center = [pixy[0]*sizexy[0]/2000,0]     #the CheMin beam stop is here
662        wavelength = 1.78892
663    elif 272 in IFD:
664        ifd = IFD[272]
665        File.seek(ifd[2][0])
666        S = File.read(ifd[1])
667        if 'PILATUS' in S:
668            tifType = 'Pilatus'
669            dataType = 0
670            pixy = [172,172]
671            File.seek(4096)
672            if not imageOnly:
673                print 'Read Pilatus tiff file: ',filename
674            image = ar.array('L',File.read(4*Npix))
675            image = np.array(np.asarray(image),dtype=np.int32)
676        else:
677            if IFD[258][2][0] == 16:
678                tifType = 'GE'
679                pixy = [200,200]
680                File.seek(8)
681                if not imageOnly:
682                    print 'Read GE-detector tiff file: ',filename
683                image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
684            elif IFD[258][2][0] == 32:
685                tifType = 'CHESS'
686                pixy = [200,200]
687                File.seek(8)
688                if not imageOnly:
689                    print 'Read CHESS-detector tiff file: ',filename
690                image = np.array(ar.array('L',File.read(4*Npix)),dtype=np.int32)
691           
692    elif 262 in IFD and IFD[262][2][0] > 4:
693        tifType = 'DND'
694        pixy = [158,158]
695        File.seek(512)
696        if not imageOnly:
697            print 'Read DND SAX/WAX-detector tiff file: ',filename
698        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
699    elif sizexy == [1536,1536]:
700        tifType = 'APS Gold'
701        pixy = [150,150]
702        File.seek(64)
703        if not imageOnly:
704            print 'Read Gold tiff file:',filename
705        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
706    elif sizexy == [2048,2048] or sizexy == [1024,1024] or sizexy == [3072,3072]:
707        if IFD[273][2][0] == 8:
708            if IFD[258][2][0] == 32:
709                tifType = 'PE'
710                pixy = [200,200]
711                File.seek(8)
712                if not imageOnly:
713                    print 'Read APS PE-detector tiff file: ',filename
714                if dataType == 5:
715                    image = np.array(ar.array('f',File.read(4*Npix)),dtype=np.float32)
716                else:
717                    image = np.array(ar.array('I',File.read(4*Npix)),dtype=np.int32)
718               
719        elif IFD[273][2][0] == 4096:
720            if sizexy[0] == 3072:
721                pixy =  [73,73]
722                tifType = 'MAR225'           
723            else:
724                pixy = [158,158]
725                tifType = 'MAR325'           
726            File.seek(4096)
727            if not imageOnly:
728                print 'Read MAR CCD tiff file: ',filename
729            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
730        elif IFD[273][2][0] == 512:
731            tiftype = '11-ID-C'
732            pixy = [200,200]
733            File.seek(512)
734            if not imageOnly:
735                print 'Read 11-ID-C tiff file: ',filename
736            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)           
737    elif sizexy == [4096,4096]:
738        if IFD[273][2][0] == 8:
739            if IFD[258][2][0] == 16:
740                tifType = 'scanCCD'
741                pixy = [9,9]
742                File.seek(8)
743                if not imageOnly:
744                    print 'Read APS scanCCD tiff file: ',filename
745                image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
746        elif IFD[273][2][0] == 4096:
747            tifType = 'Rayonix'
748            pixy = [73.242,73.242]
749            File.seek(4096)
750            if not imageOnly:
751                print 'Read Rayonix MX300HE tiff file: ',filename
752            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
753#    elif sizexy == [960,960]:
754#        tiftype = 'PE-BE'
755#        pixy = (200,200)
756#        File.seek(8)
757#        if not imageOnly:
758#            print 'Read Gold tiff file:',filename
759#        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
760           
761    else:
762        lines = ['not a known detector tiff file',]
763        return lines,0,0,0
764       
765    image = np.reshape(image,(sizexy[1],sizexy[0]))
766    center = (not center[0]) and [pixy[0]*sizexy[0]/2000,pixy[1]*sizexy[1]/2000] or center
767    wavelength = (not wavelength) and 0.10 or wavelength
768    distance = (not distance) and 100.0 or distance
769    data = {'pixelSize':pixy,'wavelength':wavelength,'distance':distance,'center':center,'size':sizexy}
770    File.close()   
771    if imageOnly:
772        return image
773    else:
774        return head,data,Npix,image
775   
776#def GetTifData(filename,imageOnly=False):
777#    import struct as st
778#    import array as ar
779#    File = open(filename,'rb')
780#    dataType = 5
781#    try:
782#        Meta = open(filename+'.metadata','Ur')
783#        head = Meta.readlines()
784#        for line in head:
785#            line = line.strip()
786#            if 'dataType=' in line:
787#                dataType = int(line.split('=')[1])
788#        Meta.close()
789#    except IOError:
790#        print 'no metadata file found - will try to read file anyway'
791#        head = ['no metadata file found',]
792#       
793#    tag = File.read(2)
794#    byteOrd = '<'
795#    if tag == 'II' and int(st.unpack('<h',File.read(2))[0]) == 42:     #little endian
796#        IFD = int(st.unpack(byteOrd+'i',File.read(4))[0])
797#    elif tag == 'MM' and int(st.unpack('>h',File.read(2))[0]) == 42:   #big endian
798#        byteOrd = '>'
799#        IFD = int(st.unpack(byteOrd+'i',File.read(4))[0])       
800#    else:
801#        lines = ['not a detector tiff file',]
802#        return lines,0,0,0
803#    File.seek(IFD)                                                  #get number of directory entries
804#    NED = int(st.unpack(byteOrd+'h',File.read(2))[0])
805#    IFD = {}
806#    for ied in range(NED):
807#        Tag,Type = st.unpack(byteOrd+'Hh',File.read(4))
808#        nVal = st.unpack(byteOrd+'i',File.read(4))[0]
809#        if Type == 1:
810#            Value = st.unpack(byteOrd+nVal*'b',File.read(nVal))
811#        elif Type == 2:
812#            Value = st.unpack(byteOrd+'i',File.read(4))
813#        elif Type == 3:
814#            Value = st.unpack(byteOrd+nVal*'h',File.read(nVal*2))
815#            x = st.unpack(byteOrd+nVal*'h',File.read(nVal*2))
816#        elif Type == 4:
817#            Value = st.unpack(byteOrd+nVal*'i',File.read(nVal*4))
818#        elif Type == 5:
819#            Value = st.unpack(byteOrd+nVal*'i',File.read(nVal*4))
820#        elif Type == 11:
821#            Value = st.unpack(byteOrd+nVal*'f',File.read(nVal*4))
822#        IFD[Tag] = [Type,nVal,Value]
823##        print Tag,IFD[Tag]
824#    sizexy = [IFD[256][2][0],IFD[257][2][0]]
825#    [nx,ny] = sizexy
826#    Npix = nx*ny
827#    if 272 in IFD:
828#        ifd = IFD[272]
829#        File.seek(ifd[2][0])
830#        S = File.read(ifd[1])
831#        if 'PILATUS' in S:
832#            tifType = 'Pilatus'
833#            dataType = 0
834#            pixy = (172,172)
835#            File.seek(4096)
836#            if not imageOnly:
837#                print 'Read Pilatus tiff file: ',filename
838#            image = ar.array('L',File.read(4*Npix))
839#            image = np.array(np.asarray(image),dtype=np.int32)
840#    elif 262 in IFD and IFD[262][2][0] > 4:
841#        tifType = 'DND'
842#        pixy = (158,158)
843#        File.seek(512)
844#        if not imageOnly:
845#            print 'Read DND SAX/WAX-detector tiff file: ',filename
846#        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
847#    elif sizexy == [1536,1536]:
848#        tifType = 'APS Gold'
849#        pixy = (150,150)
850#        File.seek(64)
851#        if not imageOnly:
852#            print 'Read Gold tiff file:',filename
853#        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
854#    elif sizexy == [2048,2048] or sizexy == [1024,1024] or sizexy == [3072,3072]:
855#        if IFD[273][2][0] == 8:
856#            if IFD[258][2][0] == 32:
857#                tifType = 'PE'
858#                pixy = (200,200)
859#                File.seek(8)
860#                if not imageOnly:
861#                    print 'Read APS PE-detector tiff file: ',filename
862#                if dataType == 5:
863#                    image = np.array(ar.array('f',File.read(4*Npix)),dtype=np.float32)
864#                else:
865#                    image = np.array(ar.array('I',File.read(4*Npix)),dtype=np.int32)
866#        elif IFD[273][2][0] == 4096:
867#            if sizexy[0] == 3072:
868#                pixy =  (73,73)
869#                tifType = 'MAR225'           
870#            else:
871#                pixy = (158,158)
872#                tifType = 'MAR325'           
873#            File.seek(4096)
874#            if not imageOnly:
875#                print 'Read MAR CCD tiff file: ',filename
876#            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
877#        elif IFD[273][2][0] == 512:
878#            tiftype = '11-ID-C'
879#            pixy = [200,200]
880#            File.seek(512)
881#            if not imageOnly:
882#                print 'Read 11-ID-C tiff file: ',filename
883#            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)           
884#    elif sizexy == [4096,4096]:
885#        if IFD[273][2][0] == 8:
886#            if IFD[258][2][0] == 16:
887#                tifType = 'scanCCD'
888#                pixy = (9,9)
889#                File.seek(8)
890#                if not imageOnly:
891#                    print 'Read APS scanCCD tiff file: ',filename
892#                image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
893#        elif IFD[273][2][0] == 4096:
894#            tifType = 'Rayonix'
895#            pixy = (73.242,73.242)
896#            File.seek(4096)
897#            if not imageOnly:
898#                print 'Read Rayonix MX300HE tiff file: ',filename
899#            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
900##    elif sizexy == [960,960]:
901##        tiftype = 'PE-BE'
902##        pixy = (200,200)
903##        File.seek(8)
904##        if not imageOnly:
905##            print 'Read Gold tiff file:',filename
906##        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
907#           
908#    else:
909#        lines = ['not a known detector tiff file',]
910#        return lines,0,0,0
911#       
912#    image = np.reshape(image,(sizexy[1],sizexy[0]))
913#    center = [pixy[0]*sizexy[0]/2000,pixy[1]*sizexy[1]/2000]
914#    data = {'pixelSize':pixy,'wavelength':0.10,'distance':100.0,'center':center,'size':sizexy}
915#    File.close()   
916#    if imageOnly:
917#        return image
918#    else:
919#        return head,data,Npix,image
920#   
921def ProjFileOpen(G2frame):
922    'Read a GSAS-II project file and load into the G2 data tree'
923    if not os.path.exists(G2frame.GSASprojectfile):
924        print ('\n*** Error attempt to open project file that does not exist:\n   '+
925               str(G2frame.GSASprojectfile))
926        return
927    file = open(G2frame.GSASprojectfile,'rb')
928    print 'load from file: ',G2frame.GSASprojectfile
929    G2frame.SetTitle("GSAS-II data tree: "+
930                     os.path.split(G2frame.GSASprojectfile)[1])
931    wx.BeginBusyCursor()
932    try:
933        while True:
934            try:
935                data = cPickle.load(file)
936            except EOFError:
937                break
938            datum = data[0]
939           
940            Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=datum[0])
941            if 'PWDR' in datum[0]:               
942                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
943                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
944                G2frame.PatternTree.SetItemPyData(Id,datum[1][:3])  #temp. trim off junk (patch?)
945            elif datum[0].startswith('HKLF'): 
946                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
947                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
948                G2frame.PatternTree.SetItemPyData(Id,datum[1])
949            else:
950                G2frame.PatternTree.SetItemPyData(Id,datum[1])
951            for datus in data[1:]:
952                sub = G2frame.PatternTree.AppendItem(Id,datus[0])
953#patch
954                if datus[0] == 'Instrument Parameters' and len(datus[1]) == 1:
955                    if 'PWDR' in datum[0]:
956                        datus[1] = [dict(zip(datus[1][3],zip(datus[1][0],datus[1][1],datus[1][2]))),{}]
957                    else:
958                        datus[1] = [dict(zip(datus[1][2],zip(datus[1][0],datus[1][1]))),{}]
959                    for item in datus[1][0]:               #zip makes tuples - now make lists!
960                        datus[1][0][item] = list(datus[1][0][item])
961#end patch
962                G2frame.PatternTree.SetItemPyData(sub,datus[1])
963            if 'IMG' in datum[0]:                   #retrieve image default flag & data if set
964                Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
965                if Data['setDefault']:
966                    G2frame.imageDefault = Data               
967        file.close()
968        print('project load successful')
969        G2frame.NewPlot = True
970    except:
971        msg = wx.MessageDialog(G2frame,message="Error reading file "+
972            str(G2frame.GSASprojectfile)+". This is not a GSAS-II .gpx file",
973            caption="Load Error",style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
974        msg.ShowModal()
975    finally:
976        wx.EndBusyCursor()
977        G2frame.Status.SetStatusText('To reorder tree items, use mouse RB to drag/drop them')
978   
979def ProjFileSave(G2frame):
980    'Save a GSAS-II project file'
981    if not G2frame.PatternTree.IsEmpty():
982        file = open(G2frame.GSASprojectfile,'wb')
983        print 'save to file: ',G2frame.GSASprojectfile
984        # stick the file name into the tree, if possible
985        try:
986            Controls = G2frame.PatternTree.GetItemPyData(
987                G2gd.GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
988            Controls['LastSavedAs'] = os.path.abspath(G2frame.GSASprojectfile)
989        except:
990            pass
991        wx.BeginBusyCursor()
992        try:
993            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
994            while item:
995                data = []
996                name = G2frame.PatternTree.GetItemText(item)
997                data.append([name,G2frame.PatternTree.GetItemPyData(item)])
998                item2, cookie2 = G2frame.PatternTree.GetFirstChild(item)
999                while item2:
1000                    name = G2frame.PatternTree.GetItemText(item2)
1001                    data.append([name,G2frame.PatternTree.GetItemPyData(item2)])
1002                    item2, cookie2 = G2frame.PatternTree.GetNextChild(item, cookie2)                           
1003                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)                           
1004                cPickle.dump(data,file,1)
1005            file.close()
1006        finally:
1007            wx.EndBusyCursor()
1008        print('project save successful')
1009
1010def SaveIntegration(G2frame,PickId,data):
1011    'Save image integration results as powder pattern(s)'
1012    azms = G2frame.Integrate[1]
1013    X = G2frame.Integrate[2][:-1]
1014    N = len(X)
1015    Id = G2frame.PatternTree.GetItemParent(PickId)
1016    name = G2frame.PatternTree.GetItemText(Id)
1017    name = name.replace('IMG ',data['type']+' ')
1018    Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Comments'))
1019    if 'PWDR' in name:
1020        names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
1021        codes = [0 for i in range(11)]
1022    elif 'SASD' in name:
1023        names = ['Type','Lam','Zero','Azimuth'] 
1024        codes = [0 for i in range(4)]
1025        X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
1026    Xminmax = [X[0],X[-1]]
1027    LRazm = data['LRazimuth']
1028    Azms = []
1029    if data['fullIntegrate'] and data['outAzimuths'] == 1:
1030        Azms = [45.0,]                              #a poor man's average?
1031    else:
1032        for i,azm in enumerate(azms[:-1]):
1033            Azms.append((azms[i+1]+azm)/2.)
1034    for i,azm in enumerate(azms[:-1]):
1035        Aname = name+" Azm= %.2f"%(Azms[i])
1036        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1037        nOcc = 0
1038        while item:
1039            Name = G2frame.PatternTree.GetItemText(item)
1040            if Aname in Name:
1041                nOcc += 1
1042            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1043        if nOcc:
1044            Aname += '(%d)'%(nOcc)
1045        Sample = G2pdG.SetDefaultSample()
1046        Sample['Gonio. radius'] = data['distance']
1047        Sample['Omega'] = data['GonioAngles'][0]
1048        Sample['Chi'] = data['GonioAngles'][1]
1049        Sample['Phi'] = data['GonioAngles'][2]
1050        if 'PWDR' in Aname:
1051            parms = ['PXC',data['wavelength'],0.0,0.99,1.0,-0.10,0.4,0.30,1.0,0.0001,Azms[i]]    #set polarization for synchrotron radiation!
1052        elif 'SASD' in Aname:
1053            Sample['Trans'] = data['SampleAbs'][0]
1054            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
1055        Y = G2frame.Integrate[0][i]
1056        W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
1057        Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=Aname)
1058        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
1059        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
1060        if 'PWDR' in Aname:
1061            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
1062                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1063        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
1064        for item in inst[0]:
1065            inst[0][item] = list(inst[0][item])
1066        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
1067        if 'PWDR' in Aname:
1068            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1069            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Peak List'),{'sigDict':{},'peaks':[]})
1070            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
1071            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Unit Cells List'),[])
1072            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Reflection Lists'),{})
1073        elif 'SASD' in Aname:             
1074            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1075            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1076            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1077        valuesdict = {
1078            'wtFactor':1.0,
1079            'Dummy':False,
1080            'ranId':ran.randint(0,sys.maxint),
1081            }
1082        G2frame.PatternTree.SetItemPyData(
1083            Id,[valuesdict,
1084                [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
1085    return Id       #last powder pattern generated
1086           
1087# def powderFxyeSave(G2frame,exports,powderfile):
1088#     'Save a powder histogram as a GSAS FXYE file'
1089#     head,tail = ospath.split(powderfile)
1090#     name,ext = tail.split('.')
1091#     for i,export in enumerate(exports):
1092#         filename = ospath.join(head,name+'-%03d.'%(i)+ext)
1093#         prmname = filename.strip(ext)+'prm'
1094#         prm = open(prmname,'w')      #old style GSAS parm file
1095#         PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1096#         Inst = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame, \
1097#             PickId, 'Instrument Parameters'))[0]
1098#         prm.write( '            123456789012345678901234567890123456789012345678901234567890        '+'\n')
1099#         prm.write( 'INS   BANK      1                                                               '+'\n')
1100#         prm.write(('INS   HTYPE   %sR                                                              '+'\n')%(Inst['Type'][0]))
1101#         if 'Lam1' in Inst:              #Ka1 & Ka2
1102#             prm.write(('INS  1 ICONS%10.7f%10.7f    0.0000               0.990    0     0.500   '+'\n')%(Inst['Lam1'][0],Inst['Lam2'][0]))
1103#         elif 'Lam' in Inst:             #single wavelength
1104#             prm.write(('INS  1 ICONS%10.7f%10.7f    0.0000               0.990    0     0.500   '+'\n')%(Inst['Lam'][1],0.0))
1105#         prm.write( 'INS  1 IRAD     0                                                               '+'\n')
1106#         prm.write( 'INS  1I HEAD                                                                    '+'\n')
1107#         prm.write( 'INS  1I ITYP    0    0.0000  180.0000         1                                 '+'\n')
1108#         prm.write(('INS  1DETAZM%10.3f                                                          '+'\n')%(Inst['Azimuth'][0]))
1109#         prm.write( 'INS  1PRCF1     3    8   0.00100                                                '+'\n')
1110#         prm.write(('INS  1PRCF11     %15.6g%15.6g%15.6g%15.6g   '+'\n')%(Inst['U'][1],Inst['V'][1],Inst['W'][1],0.0))
1111#         prm.write(('INS  1PRCF12     %15.6g%15.6g%15.6g%15.6g   '+'\n')%(Inst['X'][1],Inst['Y'][1],Inst['SH/L'][1]/2.,Inst['SH/L'][1]/2.))
1112#         prm.close()
1113#         file = open(filename,'w')
1114#         print 'save powder pattern to file: ',filename
1115#         x,y,w,yc,yb,yd = G2frame.PatternTree.GetItemPyData(PickId)[1]
1116#         file.write(powderfile+'\n')
1117#         file.write('Instrument parameter file:'+ospath.split(prmname)[1]+'\n')
1118#         file.write('BANK 1 %d %d CONS %.2f %.2f 0 0 FXYE\n'%(len(x),len(x),\
1119#             100.*x[0],100.*(x[1]-x[0])))
1120#         s = list(np.sqrt(1./np.array(w)))       
1121#         XYW = zip(x,y,s)
1122#         for X,Y,S in XYW:
1123#             file.write("%15.6g %15.6g %15.6g\n" % (100.*X,Y,max(S,1.0)))
1124#         file.close()
1125#         print 'powder pattern file '+filename+' written'
1126       
1127# def powderXyeSave(G2frame,exports,powderfile):
1128#     'Save a powder histogram as a Topas XYE file'
1129#     head,tail = ospath.split(powderfile)
1130#     name,ext = tail.split('.')
1131#     for i,export in enumerate(exports):
1132#         filename = ospath.join(head,name+'-%03d.'%(i)+ext)
1133#         PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1134#         file = open(filename,'w')
1135#         file.write('#%s\n'%(export))
1136#         print 'save powder pattern to file: ',filename
1137#         x,y,w,yc,yb,yd = G2frame.PatternTree.GetItemPyData(PickId)[1]
1138#         s = list(np.sqrt(1./np.array(w)))       
1139#         XYW = zip(x,y,s)
1140#         for X,Y,W in XYW:
1141#             file.write("%15.6g %15.6g %15.6g\n" % (X,Y,W))
1142#         file.close()
1143#         print 'powder pattern file '+filename+' written'
1144       
1145def PDFSave(G2frame,exports):
1146    'Save a PDF G(r) and S(Q) in column formats'
1147    for export in exports:
1148        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1149        SQname = 'S(Q)'+export[4:]
1150        GRname = 'G(R)'+export[4:]
1151        sqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.sq')
1152        grfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.gr')
1153        sqId = G2gd.GetPatternTreeItemId(G2frame, PickId, SQname)
1154        grId = G2gd.GetPatternTreeItemId(G2frame, PickId, GRname)
1155        sqdata = np.array(G2frame.PatternTree.GetItemPyData(sqId)[1][:2]).T
1156        grdata = np.array(G2frame.PatternTree.GetItemPyData(grId)[1][:2]).T
1157        sqfile = open(sqfilename,'w')
1158        grfile = open(grfilename,'w')
1159        sqfile.write('#T S(Q) %s\n'%(export))
1160        grfile.write('#T G(R) %s\n'%(export))
1161        sqfile.write('#L Q     S(Q)\n')
1162        grfile.write('#L R     G(R)\n')
1163        for q,sq in sqdata:
1164            sqfile.write("%15.6g %15.6g\n" % (q,sq))
1165        sqfile.close()
1166        for r,gr in grdata:
1167            grfile.write("%15.6g %15.6g\n" % (r,gr))
1168        grfile.close()
1169   
1170def PeakListSave(G2frame,file,peaks):
1171    'Save powder peaks to a data file'
1172    print 'save peak list to file: ',G2frame.peaklistfile
1173    if not peaks:
1174        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1175        try:
1176            result = dlg.ShowModal()
1177        finally:
1178            dlg.Destroy()
1179        return
1180    for peak in peaks:
1181        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
1182            (peak[0],peak[2],peak[4],peak[6]))
1183    print 'peak list saved'
1184             
1185def IndexPeakListSave(G2frame,peaks):
1186    'Save powder peaks from the indexing list'
1187    file = open(G2frame.peaklistfile,'wa')
1188    print 'save index peak list to file: ',G2frame.peaklistfile
1189    wx.BeginBusyCursor()
1190    try:
1191        if not peaks:
1192            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1193            try:
1194                result = dlg.ShowModal()
1195            finally:
1196                dlg.Destroy()
1197            return
1198        for peak in peaks:
1199            file.write("%12.6f\n" % (peak[7]))
1200        file.close()
1201    finally:
1202        wx.EndBusyCursor()
1203    print 'index peak list saved'
1204   
1205def SetNewPhase(Name='New Phase',SGData=None,cell=None,Super=None):
1206    '''Create a new phase dict with default values for various parameters
1207
1208    :param str Name: Name for new Phase
1209
1210    :param dict SGData: space group data from :func:`GSASIIspc:SpcGroup`;
1211      defaults to data for P 1
1212
1213    :param list cell: unit cell parameter list; defaults to
1214      [1.0,1.0,1.0,90.,90,90.,1.]
1215
1216    '''
1217    if SGData is None: SGData = G2spc.SpcGroup('P 1')[1]
1218    if cell is None: cell=[1.0,1.0,1.0,90.,90,90.,1.]
1219    phaseData = {
1220        'ranId':ran.randint(0,sys.maxint),
1221        'General':{
1222            'Name':Name,
1223            'Type':'nuclear',
1224            'AtomPtrs':[3,1,7,9],
1225            'SGData':SGData,
1226            'Cell':[False,]+cell,
1227            'Pawley dmin':1.0,
1228            'Data plot type':'None',
1229            'SH Texture':{
1230                'Order':0,
1231                'Model':'cylindrical',
1232                'Sample omega':[False,0.0],
1233                'Sample chi':[False,0.0],
1234                'Sample phi':[False,0.0],
1235                'SH Coeff':[False,{}],
1236                'SHShow':False,
1237                'PFhkl':[0,0,1],
1238                'PFxyz':[0,0,1],
1239                'PlotType':'Pole figure'}},
1240        'Atoms':[],
1241        'Drawing':{},
1242        'Histograms':{},
1243        'Pawley ref':[],
1244        'RBModels':{},
1245        }
1246    if Super and Super.get('Use',False):
1247        phaseData['General'].update({'Type':'modulated','Super':True,'SuperSg':Super['ssSymb']})
1248        phaseData['General']['SSGData'] = G2spc.SSpcGroup(SGData,Super['ssSymb'])
1249        phaseData['General']['SuperVec'] = [Super['ModVec'],False,Super['maxH']]
1250
1251    return phaseData
1252       
1253class MultipleChoicesDialog(wx.Dialog):
1254    '''A dialog that offers a series of choices, each with a
1255    title and a wx.Choice widget. Intended to be used Modally.
1256    typical input:
1257
1258        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1259        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1260       
1261    selections are placed in self.chosen when OK is pressed
1262    '''
1263    def __init__(self,choicelist,headinglist,
1264                 head='Select options',
1265                 title='Please select from options below',
1266                 parent=None):
1267        self.chosen = []
1268        wx.Dialog.__init__(
1269            self,parent,wx.ID_ANY,head, 
1270            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1271        panel = wx.Panel(self)
1272        mainSizer = wx.BoxSizer(wx.VERTICAL)
1273        mainSizer.Add((10,10),1)
1274        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1275        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1276        self.ChItems = []
1277        for choice,lbl in zip(choicelist,headinglist):
1278            mainSizer.Add((10,10),1)
1279            self.chosen.append(0)
1280            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1281            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1282            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1283            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1284
1285        OkBtn = wx.Button(panel,-1,"Ok")
1286        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1287        cancelBtn = wx.Button(panel,-1,"Cancel")
1288        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1289        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1290        btnSizer.Add((20,20),1)
1291        btnSizer.Add(OkBtn)
1292        btnSizer.Add((20,20),1)
1293        btnSizer.Add(cancelBtn)
1294        btnSizer.Add((20,20),1)
1295        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1296        panel.SetSizer(mainSizer)
1297        panel.Fit()
1298        self.Fit()
1299       
1300    def OnOk(self,event):
1301        parent = self.GetParent()
1302        if parent is not None: parent.Raise()
1303        # save the results from the choice widgets
1304        self.chosen = []
1305        for w in self.ChItems:
1306            self.chosen.append(w.GetSelection())
1307        self.EndModal(wx.ID_OK)             
1308           
1309    def OnCancel(self,event):
1310        parent = self.GetParent()
1311        if parent is not None: parent.Raise()
1312        self.chosen = []
1313        self.EndModal(wx.ID_CANCEL)             
1314           
1315def ExtractFileFromZip(filename, selection=None, confirmread=True,
1316                       confirmoverwrite=True, parent=None,
1317                       multipleselect=False):
1318    '''If the filename is a zip file, extract a file from that
1319    archive.
1320
1321    :param list Selection: used to predefine the name of the file
1322      to be extracted. Filename case and zip directory name are
1323      ignored in selection; the first matching file is used.
1324
1325    :param bool confirmread: if True asks the user to confirm before expanding
1326      the only file in a zip
1327
1328    :param bool confirmoverwrite: if True asks the user to confirm
1329      before overwriting if the extracted file already exists
1330
1331    :param bool multipleselect: if True allows more than one zip
1332      file to be extracted, a list of file(s) is returned.
1333      If only one file is present, do not ask which one, otherwise
1334      offer a list of choices (unless selection is used).
1335   
1336    :returns: the name of the file that has been created or a
1337      list of files (see multipleselect)
1338
1339    If the file is not a zipfile, return the name of the input file.
1340    If the zipfile is empty or no file has been selected, return None
1341    '''
1342    import zipfile # do this now, since we can save startup time by doing this only on need
1343    import shutil
1344    zloc = os.path.split(filename)[0]
1345    if not zipfile.is_zipfile(filename):
1346        #print("not zip")
1347        return filename
1348
1349    z = zipfile.ZipFile(filename,'r')
1350    zinfo = z.infolist()
1351
1352    if len(zinfo) == 0:
1353        #print('Zip has no files!')
1354        zlist = [-1]
1355    if selection:
1356        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1357        if selection.lower() in choices:
1358            zlist = [choices.index(selection.lower())]
1359        else:
1360            print('debug: file '+str(selection)+' was not found in '+str(filename))
1361            zlist = [-1]
1362    elif len(zinfo) == 1 and confirmread:
1363        result = wx.ID_NO
1364        dlg = wx.MessageDialog(
1365            parent,
1366            'Is file '+str(zinfo[0].filename)+
1367            ' what you want to extract from '+
1368            str(os.path.split(filename)[1])+'?',
1369            'Confirm file', 
1370            wx.YES_NO | wx.ICON_QUESTION)
1371        try:
1372            result = dlg.ShowModal()
1373        finally:
1374            dlg.Destroy()
1375        if result == wx.ID_NO:
1376            zlist = [-1]
1377        else:
1378            zlist = [0]
1379    elif len(zinfo) == 1:
1380        zlist = [0]
1381    elif multipleselect:
1382        # select one or more from a from list
1383        choices = [i.filename for i in zinfo]
1384        dlg = G2gd.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1385            'Choose file(s)',choices)
1386        if dlg.ShowModal() == wx.ID_OK:
1387            zlist = dlg.GetSelections()
1388        else:
1389            zlist = []
1390        dlg.Destroy()
1391    else:
1392        # select one from a from list
1393        choices = [i.filename for i in zinfo]
1394        dlg = wx.SingleChoiceDialog(parent,
1395            'Select file to extract from zip file'+str(filename),'Choose file',
1396            choices,)
1397        if dlg.ShowModal() == wx.ID_OK:
1398            zlist = [dlg.GetSelection()]
1399        else:
1400            zlist = [-1]
1401        dlg.Destroy()
1402       
1403    outlist = []
1404    for zindex in zlist:
1405        if zindex >= 0:
1406            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1407            if os.path.exists(efil) and confirmoverwrite:
1408                result = wx.ID_NO
1409                dlg = wx.MessageDialog(parent,
1410                    'File '+str(efil)+' already exists. OK to overwrite it?',
1411                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1412                try:
1413                    result = dlg.ShowModal()
1414                finally:
1415                    dlg.Destroy()
1416                if result == wx.ID_NO:
1417                    zindex = -1
1418        if zindex >= 0:
1419            # extract the file to the current directory, regardless of it's original path
1420            #z.extract(zinfo[zindex],zloc)
1421            eloc,efil = os.path.split(zinfo[zindex].filename)
1422            outfile = os.path.join(zloc, efil)
1423            fpin = z.open(zinfo[zindex])
1424            fpout = file(outfile, "wb")
1425            shutil.copyfileobj(fpin, fpout)
1426            fpin.close()
1427            fpout.close()
1428            outlist.append(outfile)
1429    z.close()
1430    if multipleselect and len(outlist) >= 1:
1431        return outlist
1432    elif len(outlist) == 1:
1433        return outlist[0]
1434    else:
1435        return None
1436
1437######################################################################
1438# base classes for reading various types of data files
1439#   not used directly, only by subclassing
1440######################################################################
1441E,SGData = G2spc.SpcGroup('P 1') # data structure for default space group
1442P1SGData = SGData
1443class ImportBaseclass(object):
1444    '''Defines a base class for the reading of input files (diffraction
1445    data, coordinates,...). See :ref:`Writing a Import Routine<Import_routines>`
1446    for an explanation on how to use a subclass of this class.
1447    '''
1448    class ImportException(Exception):
1449        '''Defines an Exception that is used when an import routine hits an expected error,
1450        usually in .Reader.
1451
1452        Good practice is that the Reader should define a value in self.errors that
1453        tells the user some information about what is wrong with their file.         
1454        '''
1455        pass
1456
1457    def __init__(self,
1458                 formatName,
1459                 longFormatName=None,
1460                 extensionlist=[],
1461                 strictExtension=False,
1462                 ):
1463        self.formatName = formatName # short string naming file type
1464        if longFormatName: # longer string naming file type
1465            self.longFormatName = longFormatName
1466        else:
1467            self.longFormatName = formatName
1468        # define extensions that are allowed for the file type
1469        # for windows, remove any extensions that are duplicate, as case is ignored
1470        if sys.platform == 'windows' and extensionlist:
1471            extensionlist = list(set([s.lower() for s in extensionlist]))
1472        self.extensionlist = extensionlist
1473        # If strictExtension is True, the file will not be read, unless
1474        # the extension matches one in the extensionlist
1475        self.strictExtension = strictExtension
1476        self.errors = ''
1477        self.warnings = ''
1478        # used for readers that will use multiple passes to read
1479        # more than one data block
1480        self.repeat = False
1481        self.selections = []
1482        self.repeatcount = 0
1483        self.readfilename = '?'
1484        #print 'created',self.__class__
1485
1486    def ReInitialize(self):
1487        'Reinitialize the Reader to initial settings'
1488        self.errors = ''
1489        self.warnings = ''
1490        self.repeat = False
1491        self.repeatcount = 0
1492        self.readfilename = '?'
1493
1494    def BlockSelector(self, ChoiceList, ParentFrame=None,
1495                      title='Select a block',
1496                      size=None, header='Block Selector',
1497                      useCancel=True):
1498        ''' Provide a wx dialog to select a block if the file contains more
1499        than one set of data and one must be selected
1500        '''
1501        if useCancel:
1502            dlg = wx.SingleChoiceDialog(
1503                ParentFrame,title, header,ChoiceList)
1504        else:
1505            dlg = wx.SingleChoiceDialog(
1506                ParentFrame,title, header,ChoiceList,
1507                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1508        if size: dlg.SetSize(size)
1509        dlg.CenterOnParent()
1510        if dlg.ShowModal() == wx.ID_OK:
1511            sel = dlg.GetSelection()
1512            return sel
1513        else:
1514            return None
1515        dlg.Destroy()
1516
1517    def MultipleBlockSelector(self, ChoiceList, ParentFrame=None,
1518        title='Select a block',size=None, header='Block Selector'):
1519        '''Provide a wx dialog to select a block of data if the
1520        file contains more than one set of data and one must be
1521        selected.
1522
1523        :returns: a list of the selected blocks
1524        '''
1525        dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1526            wx.CHOICEDLG_STYLE)
1527        dlg.CenterOnParent()
1528        if size: dlg.SetSize(size)
1529        if dlg.ShowModal() == wx.ID_OK:
1530            sel = dlg.GetSelections()
1531        else:
1532            return []
1533        dlg.Destroy()
1534        selected = []
1535        if len(ChoiceList) in sel:
1536            return range(len(ChoiceList))
1537        else:
1538            return sel
1539        return selected
1540
1541    def MultipleChoicesDialog(self, choicelist, headinglist, ParentFrame=None, **kwargs):
1542        '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1543        widget. Typical input:
1544       
1545           * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1546           
1547           * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1548           
1549        optional keyword parameters are: head (window title) and title
1550        returns a list of selected indicies for each choice (or None)
1551        '''
1552        result = None
1553        dlg = MultipleChoicesDialog(choicelist,headinglist,
1554            parent=ParentFrame, **kwargs)         
1555        dlg.CenterOnParent()
1556        if dlg.ShowModal() == wx.ID_OK:
1557            result = dlg.chosen
1558        dlg.Destroy()
1559        return result
1560
1561    def ShowBusy(self):
1562        wx.BeginBusyCursor()
1563        wx.Yield() # make it happen now!
1564
1565    def DoneBusy(self):
1566        wx.EndBusyCursor()
1567        wx.Yield() # make it happen now!
1568       
1569#    def Reader(self, filename, filepointer, ParentFrame=None, **unused):
1570#        '''This method must be supplied in the child class to read the file.
1571#        if the read fails either return False or raise an Exception
1572#        preferably of type ImportException.
1573#        '''
1574#        #start reading
1575#        raise ImportException("Error occurred while...")
1576#        self.errors += "Hint for user on why the error occur
1577#        return False # if an error occurs
1578#        return True # if read OK
1579
1580    def ExtensionValidator(self, filename):
1581        '''This methods checks if the file has the correct extension
1582        Return False if this filename will not be supported by this reader
1583        Return True if the extension matches the list supplied by the reader
1584        Return None if the reader allows un-registered extensions
1585        '''
1586        if filename:
1587            ext = os.path.splitext(filename)[1]
1588            if sys.platform == 'windows': ext = ext.lower()
1589            if ext in self.extensionlist: return True
1590            if self.strictExtension: return False
1591        return None
1592
1593    def ContentsValidator(self, filepointer):
1594        '''This routine will attempt to determine if the file can be read
1595        with the current format.
1596        This will typically be overridden with a method that
1597        takes a quick scan of [some of]
1598        the file contents to do a "sanity" check if the file
1599        appears to match the selected format.
1600        Expected to be called via self.Validator()
1601        '''
1602        #filepointer.seek(0) # rewind the file pointer
1603        return True
1604
1605    def CIFValidator(self, filepointer):
1606        '''A :meth:`ContentsValidator` for use to validate CIF files.
1607        '''
1608        for i,l in enumerate(filepointer):
1609            if i >= 1000: return True
1610            '''Encountered only blank lines or comments in first 1000
1611            lines. This is unlikely, but assume it is CIF anyway, since we are
1612            even less likely to find a file with nothing but hashes and
1613            blank lines'''
1614            line = l.strip()
1615            if len(line) == 0: # ignore blank lines
1616                continue 
1617            elif line.startswith('#'): # ignore comments
1618                continue 
1619            elif line.startswith('data_'): # on the right track, accept this file
1620                return True
1621            else: # found something invalid
1622                self.errors = 'line '+str(i+1)+' contains unexpected data:\n'
1623                self.errors += '  '+str(l)
1624                self.errors += '  Note: a CIF should only have blank lines or comments before'
1625                self.errors += '        a data_ statement begins a block.'
1626                return False 
1627
1628class ImportPhase(ImportBaseclass):
1629    '''Defines a base class for the reading of files with coordinates
1630
1631    Objects constructed that subclass this (in import/G2phase_*.py etc.) will be used
1632    in :meth:`GSASII.GSASII.OnImportPhase`.
1633    See :ref:`Writing a Import Routine<Import_Routines>`
1634    for an explanation on how to use this class.
1635
1636    '''
1637    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1638        strictExtension=False,):
1639        # call parent __init__
1640        ImportBaseclass.__init__(self,formatName,longFormatName,
1641            extensionlist,strictExtension)
1642        self.Phase = None # a phase must be created with G2IO.SetNewPhase in the Reader
1643        self.Constraints = None
1644
1645    def PhaseSelector(self, ChoiceList, ParentFrame=None,
1646        title='Select a phase', size=None,header='Phase Selector'):
1647        ''' Provide a wx dialog to select a phase if the file contains more
1648        than one phase
1649        '''
1650        return self.BlockSelector(ChoiceList,ParentFrame,title,
1651            size,header)
1652
1653class ImportStructFactor(ImportBaseclass):
1654    '''Defines a base class for the reading of files with tables
1655    of structure factors.
1656
1657    Structure factors are read with a call to :meth:`GSASII.GSASII.OnImportSfact`
1658    which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`, which calls
1659    methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1660    :meth:`Reader`.
1661
1662    See :ref:`Writing a Import Routine<Import_Routines>`
1663    for an explanation on how to use import classes in general. The specifics
1664    for reading a structure factor histogram require that
1665    the ``Reader()`` routine in the import
1666    class need to do only a few things: It
1667    should load :attr:`RefDict` item ``'RefList'`` with the reflection list,
1668    and set :attr:`Parameters` with the instrument parameters
1669    (initialized with :meth:`InitParameters` and set with :meth:`UpdateParameters`).
1670    '''
1671    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1672        strictExtension=False,):
1673        ImportBaseclass.__init__(self,formatName,longFormatName,
1674            extensionlist,strictExtension)
1675
1676        # define contents of Structure Factor entry
1677        self.Parameters = []
1678        'self.Parameters is a list with two dicts for data parameter settings'
1679        self.InitParameters()
1680        self.RefDict = {'RefList':[],'FF':[],'Super':0}
1681        self.Banks = []             #for multi bank data (usually TOF)
1682        '''self.RefDict is a dict containing the reflection information, as read from the file.
1683        Item 'RefList' contains the reflection information. See the
1684        :ref:`Single Crystal Reflection Data Structure<XtalRefl_table>`
1685        for the contents of each row. Dict element 'FF'
1686        contains the form factor values for each element type; if this entry
1687        is left as initialized (an empty list) it will be initialized as needed later.
1688        '''
1689    def ReInitialize(self):
1690        'Reinitialize the Reader to initial settings'
1691        ImportBaseclass.ReInitialize(self)
1692        self.InitParameters()
1693        self.Banks = []             #for multi bank data (usually TOF)
1694        self.RefDict = {'RefList':[],'FF':[],'Super':0}
1695       
1696    def InitParameters(self):
1697        'initialize the instrument parameters structure'
1698        Lambda = 0.70926
1699        HistType = 'SXC'
1700        self.Parameters = [{'Type':[HistType,HistType], # create the structure
1701                            'Lam':[Lambda,Lambda]
1702                            }, {}]
1703        'Parameters is a list with two dicts for data parameter settings'
1704
1705    def UpdateParameters(self,Type=None,Wave=None):
1706        'Revise the instrument parameters'
1707        if Type is not None:
1708            self.Parameters[0]['Type'] = [Type,Type]
1709        if Wave is not None:
1710            self.Parameters[0]['Lam'] = [Wave,Wave]           
1711                       
1712######################################################################
1713class ImportPowderData(ImportBaseclass):
1714    '''Defines a base class for the reading of files with powder data.
1715
1716    Objects constructed that subclass this (in import/G2pwd_*.py etc.) will be used
1717    in :meth:`GSASII.GSASII.OnImportPowder`.
1718    See :ref:`Writing a Import Routine<Import_Routines>`
1719    for an explanation on how to use this class.
1720    '''
1721    def __init__(self,
1722                 formatName,
1723                 longFormatName=None,
1724                 extensionlist=[],
1725                 strictExtension=False,
1726                 ):
1727        ImportBaseclass.__init__(self,formatName,
1728                                            longFormatName,
1729                                            extensionlist,
1730                                            strictExtension)
1731        self.clockWd = None  # used in TOF
1732        self.ReInitialize()
1733       
1734    def ReInitialize(self):
1735        'Reinitialize the Reader to initial settings'
1736        ImportBaseclass.ReInitialize(self)
1737        self.powderentry = ['',None,None] #  (filename,Pos,Bank)
1738        self.powderdata = [] # Powder dataset
1739        '''A powder data set is a list with items [x,y,w,yc,yb,yd]:
1740                np.array(x), # x-axis values
1741                np.array(y), # powder pattern intensities
1742                np.array(w), # 1/sig(intensity)^2 values (weights)
1743                np.array(yc), # calc. intensities (zero)
1744                np.array(yb), # calc. background (zero)
1745                np.array(yd), # obs-calc profiles
1746        '''                           
1747        self.comments = []
1748        self.idstring = ''
1749        self.Sample = G2pdG.SetDefaultSample()
1750        self.GSAS = None     # used in TOF
1751        self.repeat_instparm = True # Should a parm file be
1752        #                             used for multiple histograms?
1753        self.instparm = None # name hint from file of instparm to use
1754        self.instfile = '' # full path name to instrument parameter file
1755        self.instbank = '' # inst parm bank number
1756        self.instmsg = ''  # a label that gets printed to show
1757                           # where instrument parameters are from
1758        self.numbanks = 1
1759        self.instdict = {} # place items here that will be transferred to the instrument parameters
1760        self.pwdparms = {} # place parameters that are transferred directly to the tree
1761                           # here (typically from an existing GPX file)
1762######################################################################
1763class ImportSmallAngleData(ImportBaseclass):
1764    '''Defines a base class for the reading of files with small angle data.
1765    See :ref:`Writing a Import Routine<Import_Routines>`
1766    for an explanation on how to use this class.
1767    '''
1768    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1769        strictExtension=False,):
1770           
1771        ImportBaseclass.__init__(self,formatName,longFormatName,extensionlist,
1772            strictExtension)
1773        self.ReInitialize()
1774       
1775    def ReInitialize(self):
1776        'Reinitialize the Reader to initial settings'
1777        ImportBaseclass.ReInitialize(self)
1778        self.smallangleentry = ['',None,None] #  (filename,Pos,Bank)
1779        self.smallangledata = [] # SASD dataset
1780        '''A small angle data set is a list with items [x,y,w,yc,yd]:
1781                np.array(x), # x-axis values
1782                np.array(y), # powder pattern intensities
1783                np.array(w), # 1/sig(intensity)^2 values (weights)
1784                np.array(yc), # calc. intensities (zero)
1785                np.array(yd), # obs-calc profiles
1786                np.array(yb), # preset bkg
1787        '''                           
1788        self.comments = []
1789        self.idstring = ''
1790        self.Sample = G2pdG.SetDefaultSample()
1791        self.GSAS = None     # used in TOF
1792        self.clockWd = None  # used in TOF
1793        self.numbanks = 1
1794        self.instdict = {} # place items here that will be transferred to the instrument parameters
1795######################################################################
1796class ExportBaseclass(object):
1797    '''Defines a base class for the exporting of GSAS-II results.
1798
1799    This class is subclassed in the various exports/G2export_*.py files. Those files
1800    are imported in :meth:`GSASII.GSASII._init_Exports` which defines the
1801    appropriate menu items for each one and the .Exporter method is called
1802    directly from the menu item.
1803   
1804    '''
1805    def __init__(self,
1806                 G2frame,
1807                 formatName,
1808                 extension,
1809                 longFormatName=None,
1810                 ):
1811        self.G2frame = G2frame
1812        self.formatName = formatName # short string naming file type
1813        self.extension = extension
1814        if longFormatName: # longer string naming file type
1815            self.longFormatName = longFormatName
1816        else:
1817            self.longFormatName = formatName
1818        self.OverallParms = {}
1819        self.Phases = {}
1820        self.Histograms = {}
1821        self.powderDict = {}
1822        self.xtalDict = {}
1823        self.parmDict = {}
1824        self.sigDict = {}
1825        # updated in InitExport:
1826        self.currentExportType = None # type of export that has been requested
1827        # updated in ExportSelect (when used):
1828        self.phasenam = None # a list of selected phases
1829        self.histnam = None # a list of selected histograms
1830        self.filename = None # name of file to be written (single export) or template (multiple files)
1831        self.dirname = '' # name of directory where file(s) will be written
1832        self.fullpath = '' # name of file being written -- full path
1833       
1834        # items that should be defined in a subclass of this class
1835        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1836        # The following types are defined: 'project', "phase", "powder", "single"
1837        self.multiple = False # set as True if the class can export multiple phases or histograms
1838        # self.multiple is ignored for "project" exports
1839
1840    def InitExport(self,event):
1841        '''Determines the type of menu that called the Exporter and
1842        misc initialization.
1843        '''
1844        self.filename = None # name of file to be written (single export)
1845        self.dirname = '' # name of file to be written (multiple export)
1846        if event:
1847            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1848
1849    def MakePWDRfilename(self,hist):
1850        '''Make a filename root (no extension) from a PWDR histogram name
1851
1852        :param str hist: the histogram name in data tree (starts with "PWDR ")
1853        '''
1854        file0 = ''
1855        file1 = hist[5:]
1856        # replace repeated blanks
1857        while file1 != file0:
1858            file0 = file1
1859            file1 = file0.replace('  ',' ').strip()
1860        file0 = file1.replace('Azm= ','A')
1861        # if angle has unneeded decimal places on aziumuth, remove them
1862        if file0[-3:] == '.00': file0 = file0[:-3]
1863        file0 = file0.replace('.','_')
1864        file0 = file0.replace(' ','_')
1865        return file0
1866
1867    def ExportSelect(self,AskFile='ask'):
1868        '''Selects histograms or phases when needed. Sets a default file name when
1869        requested in self.filename; always sets a default directory in self.dirname.
1870
1871        :param bool AskFile: Determines how this routine processes getting a
1872          location to store the current export(s).
1873         
1874          * if AskFile is 'ask' (default option), get the name of the file to be written;
1875            self.filename and self.dirname are always set. In the case where
1876            multiple files must be generated, the export routine should do this
1877            based on self.filename as a template.
1878          * if AskFile is 'dir', get the name of the directory to be used;
1879            self.filename is not used, but self.dirname is always set. The export routine
1880            will always generate the file name.
1881          * if AskFile is 'single', get only the name of the directory to be used when
1882            multiple items will be written (as multiple files) are used
1883            *or* a complete file name is requested when a single file
1884            name is selected. self.dirname is always set and self.filename used
1885            only when a single file is selected.
1886          * if AskFile is 'default', creates a name of the file to be used from
1887            the name of the project (.gpx) file. If the project has not been saved,
1888            then the name of file is requested.
1889            self.filename and self.dirname are always set. In the case where
1890            multiple file names must be generated, the export routine should do this
1891            based on self.filename.
1892          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1893            file. If the project has not been saved, then a directory is requested.
1894            self.filename is not used.
1895
1896        :returns: True in case of an error
1897        '''
1898       
1899        numselected = 1
1900        if self.currentExportType == 'phase':
1901            if len(self.Phases) == 0:
1902                self.G2frame.ErrorDialog(
1903                    'Empty project',
1904                    'Project does not contain any phases.')
1905                return True
1906            elif len(self.Phases) == 1:
1907                self.phasenam = self.Phases.keys()
1908            elif self.multiple: 
1909                choices = sorted(self.Phases.keys())
1910                phasenum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1911                if phasenum is None: return True
1912                self.phasenam = [choices[i] for i in phasenum]
1913                if not self.phasenam: return True
1914                numselected = len(self.phasenam)
1915            else:
1916                choices = sorted(self.Phases.keys())
1917                phasenum = G2gd.ItemSelector(choices,self.G2frame)
1918                if phasenum is None: return True
1919                self.phasenam = [choices[phasenum]]
1920                numselected = len(self.phasenam)
1921        elif self.currentExportType == 'single':
1922            if len(self.xtalDict) == 0:
1923                self.G2frame.ErrorDialog(
1924                    'Empty project',
1925                    'Project does not contain any single crystal data.')
1926                return True
1927            elif len(self.xtalDict) == 1:
1928                self.histnam = self.xtalDict.values()
1929            elif self.multiple:
1930                choices = sorted(self.xtalDict.values())
1931                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1932                if not hnum: return True
1933                self.histnam = [choices[i] for i in hnum]
1934                numselected = len(self.histnam)
1935            else:
1936                choices = sorted(self.xtalDict.values())
1937                hnum = G2gd.ItemSelector(choices,self.G2frame)
1938                if hnum is None: return True
1939                self.histnam = [choices[hnum]]
1940                numselected = len(self.histnam)
1941        elif self.currentExportType == 'powder':
1942            if len(self.powderDict) == 0:
1943                self.G2frame.ErrorDialog(
1944                    'Empty project',
1945                    'Project does not contain any powder data.')
1946                return True
1947            elif len(self.powderDict) == 1:
1948                self.histnam = self.powderDict.values()
1949            elif self.multiple:
1950                choices = sorted(self.powderDict.values())
1951                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1952                if not hnum: return True
1953                self.histnam = [choices[i] for i in hnum]
1954                numselected = len(self.histnam)
1955            else:
1956                choices = sorted(self.powderDict.values())
1957                hnum = G2gd.ItemSelector(choices,self.G2frame)
1958                if hnum is None: return True
1959                self.histnam = [choices[hnum]]
1960                numselected = len(self.histnam)
1961        elif self.currentExportType == 'image':
1962            if len(self.Histograms) == 0:
1963                self.G2frame.ErrorDialog(
1964                    'Empty project',
1965                    'Project does not contain any images.')
1966                return True
1967            elif len(self.Histograms) == 1:
1968                self.histnam = self.Histograms.keys()
1969            else:
1970                choices = sorted(self.Histograms.keys())
1971                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1972                if self.multiple:
1973                    if not hnum: return True
1974                    self.histnam = [choices[i] for i in hnum]
1975                else:
1976                    if hnum is None: return True
1977                    self.histnam = [choices[hnum]]
1978                numselected = len(self.histnam)
1979        if self.currentExportType == 'map':
1980            # search for phases with maps
1981            mapPhases = []
1982            choices = []
1983            for phasenam in sorted(self.Phases):
1984                phasedict = self.Phases[phasenam] # pointer to current phase info           
1985                if len(phasedict['General']['Map'].get('rho',[])):
1986                    mapPhases.append(phasenam)
1987                    if phasedict['General']['Map'].get('Flip'):
1988                        choices.append('Charge flip map: '+str(phasenam))
1989                    elif phasedict['General']['Map'].get('MapType'):
1990                        choices.append(
1991                            str(phasedict['General']['Map'].get('MapType'))
1992                            + ' map: ' + str(phasenam))
1993                    else:
1994                        choices.append('unknown map: '+str(phasenam))
1995            # select a map if needed
1996            if len(mapPhases) == 0:
1997                self.G2frame.ErrorDialog(
1998                    'Empty project',
1999                    'Project does not contain any maps.')
2000                return True
2001            elif len(mapPhases) == 1:
2002                self.phasenam = mapPhases
2003            else: 
2004                phasenum = G2gd.ItemSelector(choices,self.G2frame,multiple=self.multiple)
2005                if self.multiple:
2006                    if not phasenum: return True
2007                    self.phasenam = [mapPhases[i] for i in phasenum]
2008                else:
2009                    if phasenum is None: return True
2010                    self.phasenam = [mapPhases[phasenum]]
2011            numselected = len(self.phasenam)
2012
2013        # items selected, now set self.dirname and usually self.filename
2014        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
2015            AskFile == 'default' and not self.G2frame.GSASprojectfile
2016            ):
2017            filename = self.askSaveFile()
2018            if not filename: return True
2019            self.dirname,self.filename = os.path.split(filename)
2020        elif AskFile == 'dir' or AskFile == 'single' or (
2021            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
2022            ):
2023            self.dirname = self.askSaveDirectory()
2024            if not self.dirname: return True
2025        elif AskFile == 'default-dir' or AskFile == 'default':
2026            self.dirname,self.filename = os.path.split(
2027                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
2028                )
2029        else:
2030            raise Exception('This should not happen!')
2031       
2032    def loadParmDict(self):
2033        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
2034        refined values to those from the last cycle and set the uncertainties for the
2035        refined parameters in another dict (self.sigDict).
2036
2037        Expands the parm & sig dicts to include values derived from constraints.
2038        '''
2039        self.parmDict = {}
2040        self.sigDict = {}
2041        rigidbodyDict = {}
2042        covDict = {}
2043        consDict = {}
2044        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2045        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2046        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2047        while item:
2048            name = self.G2frame.PatternTree.GetItemText(item)
2049            if name == 'Rigid bodies':
2050                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
2051            elif name == 'Covariance':
2052                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
2053            elif name == 'Constraints':
2054                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
2055            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2056        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2057        self.parmDict.update(rbDict)
2058        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2059        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables =  G2stIO.GetPhaseData(
2060            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
2061        self.parmDict.update(phaseDict)
2062        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
2063            Phases,Histograms,Print=False,resetRefList=False)
2064        self.parmDict.update(hapDict)
2065        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
2066        self.parmDict.update(histDict)
2067        self.parmDict.update(zip(
2068            covDict.get('varyList',[]),
2069            covDict.get('variables',[])))
2070        self.sigDict = dict(zip(
2071            covDict.get('varyList',[]),
2072            covDict.get('sig',[])))
2073        # expand to include constraints: first compile a list of constraints
2074        constList = []
2075        for item in consDict:
2076            if item.startswith('_'): continue
2077            constList += consDict[item]
2078        # now process the constraints
2079        G2mv.InitVars()
2080        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
2081        varyList = covDict.get('varyListStart')
2082        if varyList is None and len(constDict) == 0:
2083            # no constraints can use varyList
2084            varyList = covDict.get('varyList')
2085        elif varyList is None:
2086            # old GPX file from before pre-constraint varyList is saved
2087            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
2088            raise Exception(' *** Export aborted ***')
2089        else:
2090            varyList = list(varyList)
2091        try:
2092            groups,parmlist = G2mv.GroupConstraints(constDict)
2093            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,self.parmDict)
2094        except:
2095            # this really should not happen
2096            print ' *** ERROR - constraints are internally inconsistent ***'
2097            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
2098            print 'Errors',errmsg
2099            if warnmsg: print 'Warnings',warnmsg
2100            raise Exception(' *** CIF creation aborted ***')
2101        # add the constrained values to the parameter dictionary
2102        G2mv.Dict2Map(self.parmDict,varyList)
2103        # and add their uncertainties into the esd dictionary (sigDict)
2104        if covDict.get('covMatrix') is not None:
2105            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
2106
2107    def loadTree(self):
2108        '''Load the contents of the data tree into a set of dicts
2109        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
2110        & self.xtalDict)
2111       
2112        * The childrenless data tree items are overall parameters/controls for the
2113          entire project and are placed in self.OverallParms
2114        * Phase items are placed in self.Phases
2115        * Data items are placed in self.Histogram. The key for these data items
2116          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
2117        '''
2118        self.OverallParms = {}
2119        self.powderDict = {}
2120        self.xtalDict = {}
2121        self.Phases = {}
2122        self.Histograms = {}
2123        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2124        histType = None       
2125        if self.currentExportType == 'phase':
2126            # if exporting phases load them here
2127            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
2128            if not sub:
2129                print 'no phases found'
2130                return True
2131            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
2132            while item:
2133                phaseName = self.G2frame.PatternTree.GetItemText(item)
2134                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
2135                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
2136            return
2137        elif self.currentExportType == 'single':
2138            histType = 'HKLF'
2139        elif self.currentExportType == 'powder':
2140            histType = 'PWDR'
2141        elif self.currentExportType == 'image':
2142            histType = 'IMG'
2143
2144        if histType: # Loading just one kind of tree entry
2145            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2146            while item:
2147                name = self.G2frame.PatternTree.GetItemText(item)
2148                if name.startswith(histType):
2149                    if self.Histograms.get(name): # there is already an item with this name
2150                        print('Histogram name '+str(name)+' is repeated. Renaming')
2151                        if name[-1] == '9':
2152                            name = name[:-1] + '10'
2153                        elif name[-1] in '012345678':
2154                            name = name[:-1] + str(int(name[-1])+1)
2155                        else:                           
2156                            name += '-1'
2157                    self.Histograms[name] = {}
2158                    # the main info goes into Data, but the 0th
2159                    # element contains refinement results, carry
2160                    # that over too now.
2161                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
2162                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
2163                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2164                    while item2: 
2165                        child = self.G2frame.PatternTree.GetItemText(item2)
2166                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
2167                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
2168                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2169            # index powder and single crystal histograms by number
2170            for hist in self.Histograms:
2171                if hist.startswith("PWDR"): 
2172                    d = self.powderDict
2173                elif hist.startswith("HKLF"): 
2174                    d = self.xtalDict
2175                else:
2176                    return                   
2177                i = self.Histograms[hist].get('hId')
2178                if i is None and not d.keys():
2179                    i = 0
2180                elif i is None or i in d.keys():
2181                    i = max(d.keys())+1
2182                d[i] = hist
2183            return
2184        # else standard load: using all interlinked phases and histograms
2185        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2186        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2187        while item:
2188            name = self.G2frame.PatternTree.GetItemText(item)
2189            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2190            if not item2: 
2191                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
2192            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2193        # index powder and single crystal histograms
2194        for hist in self.Histograms:
2195            i = self.Histograms[hist]['hId']
2196            if hist.startswith("PWDR"): 
2197                self.powderDict[i] = hist
2198            elif hist.startswith("HKLF"): 
2199                self.xtalDict[i] = hist
2200
2201    def dumpTree(self,mode='type'):
2202        '''Print out information on the data tree dicts loaded in loadTree
2203        '''
2204        print '\nOverall'
2205        if mode == 'type':
2206            def Show(arg): return type(arg)
2207        else:
2208            def Show(arg): return arg
2209        for key in self.OverallParms:
2210            print '  ',key,Show(self.OverallParms[key])
2211        print 'Phases'
2212        for key1 in self.Phases:
2213            print '    ',key1,Show(self.Phases[key1])
2214        print 'Histogram'
2215        for key1 in self.Histograms:
2216            print '    ',key1,Show(self.Histograms[key1])
2217            for key2 in self.Histograms[key1]:
2218                print '      ',key2,Show(self.Histograms[key1][key2])
2219
2220    def defaultSaveFile(self):
2221        return os.path.abspath(
2222            os.path.splitext(self.G2frame.GSASprojectfile
2223                             )[0]+self.extension)
2224       
2225    def askSaveFile(self):
2226        '''Ask the user to supply a file name
2227
2228        :returns: a file name (str) or None if Cancel is pressed
2229        '''
2230        defnam = os.path.splitext(
2231            os.path.split(self.G2frame.GSASprojectfile)[1]
2232            )[0]+self.extension
2233        dlg = wx.FileDialog(
2234            self.G2frame, 'Input name for file to write', '.', defnam,
2235            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2236            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2237        dlg.CenterOnParent()
2238        try:
2239            if dlg.ShowModal() == wx.ID_OK:
2240                filename = dlg.GetPath()
2241                # make sure extension is correct
2242                filename = os.path.splitext(filename)[0]+self.extension
2243            else:
2244                filename = None
2245        finally:
2246            dlg.Destroy()
2247        return filename
2248
2249    def askSaveDirectory(self):
2250        '''Ask the user to supply a directory name. Path name is used as the
2251        starting point for the next export path search.
2252
2253        :returns: a directory name (str) or None if Cancel is pressed
2254        '''
2255        if self.G2frame.exportDir:
2256            startdir = self.G2frame.exportDir
2257        elif self.G2frame.GSASprojectfile:
2258            startdir = os.path.split(self.G2frame.GSASprojectfile)[0]
2259        elif self.G2frame.dirname:
2260            startdir = self.G2frame.dirname
2261        else:
2262            startdir = ''
2263        dlg = wx.DirDialog(
2264            self.G2frame, 'Input directory where file(s) will be written', startdir,
2265            wx.DD_DEFAULT_STYLE)
2266        dlg.CenterOnParent()
2267        try:
2268            if dlg.ShowModal() == wx.ID_OK:
2269                filename = dlg.GetPath()
2270                self.G2frame.exportDir = filename
2271            else:
2272                filename = None
2273        finally:
2274            dlg.Destroy()
2275        return filename
2276
2277    # Tools for file writing.
2278    def OpenFile(self,fil=None,mode='w'):
2279        '''Open the output file
2280
2281        :param str fil: The name of the file to open. If None (default)
2282          the name defaults to self.dirname + self.filename.
2283          If an extension is supplied, it is not overridded,
2284          but if not, the default extension is used.
2285        :returns: the file object opened by the routine which is also
2286          saved as self.fp
2287        '''
2288        if not fil:
2289            if not os.path.splitext(self.filename)[1]:
2290                self.filename += self.extension
2291            fil = os.path.join(self.dirname,self.filename)
2292        self.fullpath = fil
2293        self.fp = open(fil,mode)
2294        return self.fp
2295
2296    def Write(self,line):
2297        '''write a line of output, attaching a line-end character
2298
2299        :param str line: the text to be written.
2300        '''
2301        self.fp.write(line+'\n')
2302    def CloseFile(self,fp=None):
2303        '''Close a file opened in OpenFile
2304
2305        :param file fp: the file object to be closed. If None (default)
2306          file object self.fp is closed.
2307        '''
2308        if fp is None:
2309            fp = self.fp
2310            self.fp = None
2311        fp.close()
2312    # Tools to pull information out of the data arrays
2313    def GetCell(self,phasenam):
2314        """Gets the unit cell parameters and their s.u.'s for a selected phase
2315
2316        :param str phasenam: the name for the selected phase
2317        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2318          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2319          cell values and `cellSig` has their uncertainties.
2320        """
2321        phasedict = self.Phases[phasenam] # pointer to current phase info
2322        try:
2323            pfx = str(phasedict['pId'])+'::'
2324            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2325            cellSig = G2stIO.getCellEsd(pfx,
2326                                        phasedict['General']['SGData'],A,
2327                                        self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2328            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2329            return cellList,cellSig
2330        except KeyError:
2331            cell = phasedict['General']['Cell'][1:]
2332            return cell,7*[0]
2333   
2334    def GetAtoms(self,phasenam):
2335        """Gets the atoms associated with a phase. Can be used with standard
2336        or macromolecular phases
2337
2338        :param str phasenam: the name for the selected phase
2339        :returns: a list of items for eac atom where each item is a list containing:
2340          label, typ, mult, xyz, and td, where
2341
2342          * label and typ are the atom label and the scattering factor type (str)
2343          * mult is the site multiplicity (int)
2344          * xyz is contains a list with four pairs of numbers:
2345            x, y, z and fractional occupancy and
2346            their standard uncertainty (or a negative value)
2347          * td is contains a list with either one or six pairs of numbers:
2348            if one number it is U\ :sub:`iso` and with six numbers it is
2349            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2350            paired with their standard uncertainty (or a negative value)
2351        """
2352        phasedict = self.Phases[phasenam] # pointer to current phase info           
2353        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2354        cfrac = cx+3
2355        fpfx = str(phasedict['pId'])+'::Afrac:'       
2356        atomslist = []
2357        for i,at in enumerate(phasedict['Atoms']):
2358            if phasedict['General']['Type'] == 'macromolecular':
2359                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2360            else:
2361                label = at[ct-1]
2362            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2363            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2364            mult = at[cs+1]
2365            typ = at[ct]
2366            xyz = []
2367            for j,v in enumerate(('x','y','z')):
2368                val = at[cx+j]
2369                pfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2370                sig = self.sigDict.get(pfx,-0.000009)
2371                xyz.append((val,sig))
2372            xyz.append((fval,fsig))
2373            td = []
2374            if at[cia] == 'I':
2375                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2376                val = self.parmDict.get(pfx,at[cia+1])
2377                sig = self.sigDict.get(pfx,-0.0009)
2378                td.append((val,sig))
2379            else:
2380                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2381                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2382                    val = self.parmDict.get(pfx,at[cia+2+i])
2383                    sig = self.sigDict.get(pfx,-0.0009)
2384                    td.append((val,sig))
2385            atomslist.append((label,typ,mult,xyz,td))
2386        return atomslist
2387######################################################################
2388
2389def ReadCIF(URLorFile):
2390    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2391    (from James Hester).
2392    The open routine gets confused with DOS names that begin with a letter and colon
2393    "C:\dir\" so this routine will try to open the passed name as a file and if that
2394    fails, try it as a URL
2395
2396    :param str URLorFile: string containing a URL or a file name. Code will try first
2397      to open it as a file and then as a URL.
2398
2399    :returns: a PyCifRW CIF object.
2400    '''
2401    import CifFile as cif # PyCifRW from James Hester
2402
2403    # alternate approach:
2404    #import urllib
2405    #ciffile = 'file:'+urllib.pathname2url(filename)
2406   
2407    try:
2408        fp = open(URLorFile,'r')
2409        cf = cif.ReadCif(fp)
2410        fp.close()
2411        return cf
2412    except IOError:
2413        return cif.ReadCif(URLorFile)
2414
2415if __name__ == '__main__':
2416    app = wx.PySimpleApp()
2417    frm = wx.Frame(None) # create a frame
2418    frm.Show(True)
2419    filename = '/tmp/notzip.zip'
2420    filename = '/tmp/all.zip'
2421    #filename = '/tmp/11bmb_7652.zip'
2422   
2423    #selection=None, confirmoverwrite=True, parent=None
2424    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2425    print ExtractFileFromZip(filename,multipleselect=True)
2426                             #confirmread=False, confirmoverwrite=False)
2427
2428    # choicelist=[ ('a','b','c'),
2429    #              ('test1','test2'),('no choice',)]
2430    # titles = [ 'a, b or c', 'tests', 'No option here']
2431    # dlg = MultipleChoicesDialog(
2432    #     choicelist,titles,
2433    #     parent=frm)
2434    # if dlg.ShowModal() == wx.ID_OK:
2435    #     print 'Got OK'
Note: See TracBrowser for help on using the repository browser.