source: trunk/GSASIIIO.py @ 1603

Last change on this file since 1603 was 1603, checked in by vondreele, 9 years ago

fix typo

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