source: trunk/GSASIIIO.py @ 1925

Last change on this file since 1925 was 1925, checked in by vondreele, 6 years ago

create AddHatomDialog? & work on OnHydAtomAdd?
allow reading of .cor images made at APS 1ID

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