source: trunk/GSASIIIO.py @ 1217

Last change on this file since 1217 was 1217, checked in by vondreele, 8 years ago

add reading of CheMin? tif & png files
continue work on small angle stuff

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