source: trunk/GSASIIIO.py @ 1834

Last change on this file since 1834 was 1834, checked in by vondreele, 7 years ago

redefine azimuthal angle in Sample Parms as the center of the azimuthal bin; the azimuthat angle in Instrument parms is defined as the mean angle for polarization (only 0-90 & slightly different)
some cosmetic changes to GUI displays
fix imports/G2pwd_fxye.py to handle multi time-maps found in HIPPO data & the slightly screwy one in HIPD data

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