source: trunk/GSASIIIO.py @ 1223

Last change on this file since 1223 was 1223, checked in by toby, 8 years ago

move small angle readers; improve reinitialization

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 98.1 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2014-02-19 03:48:38 +0000 (Wed, 19 Feb 2014) $
4# $Author: toby $
5# $Revision: 1223 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 1223 2014-02-19 03:48:38Z toby $
8########### SVN repository information ###################
9'''
10*GSASIIIO: Misc I/O routines*
11=============================
12
13Module with miscellaneous routines for input and output. Many
14are GUI routines to interact with user.
15
16Includes support for image reading.
17
18Also includes base classes for data import routines.
19
20'''
21"""GSASIIIO: functions for IO of data
22   Copyright: 2008, Robert B. Von Dreele (Argonne National Laboratory)
23"""
24import wx
25import math
26import numpy as np
27import cPickle
28import sys
29import re
30import random as ran
31import GSASIIpath
32GSASIIpath.SetVersionNumber("$Revision: 1223 $")
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            Sample['Trans'] = data['SampleAbs'][0]
985            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
986        Y = G2frame.Integrate[0][i]
987        W = 1./Y                    #probably not true
988        if Id:
989            G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Comments'),Comments)                   
990            G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Limits'),[tuple(Xminmax),Xminmax])
991            if 'PWDR' in name:
992                G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Background'),[['chebyschev',1,3,1.0,0.0,0.0],
993                    {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
994            inst = [dict(zip(names,zip(parms,parms,codes))),{}]
995            for item in inst[0]:
996                inst[0][item] = list(inst[0][item])
997            G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Instrument Parameters'),inst)
998            if 'PWDR' in name:
999                G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Peak List'),[])
1000                G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Index Peak List'),[])
1001                G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Unit Cells List'),[])             
1002                G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Reflection Lists'),{})
1003            elif 'SASD' in name:             
1004                G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Substances'),G2pdG.SetDefaultSubstances())
1005                G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Models'),G2pdG.SetDefaultSASDModel())
1006        else:
1007            Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=name+" Azm= %.2f"%(Azms[i]))
1008            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
1009            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
1010            if 'PWDR' in name:
1011                G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
1012                    {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1013            inst = [dict(zip(names,zip(parms,parms,codes))),{}]
1014            for item in inst[0]:
1015                inst[0][item] = list(inst[0][item])
1016            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
1017            if 'PWDR' in name:
1018                G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1019                G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Peak List'),[])
1020                G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Index Peak List'),[])
1021                G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Unit Cells List'),[])
1022                G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Reflection Lists'),{})
1023            elif 'SASD' in name:             
1024                G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1025                G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1026                G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1027            valuesdict = {
1028                'wtFactor':1.0,
1029                'Dummy':False,
1030                'ranId':ran.randint(0,sys.maxint),
1031                }
1032        G2frame.PatternTree.SetItemPyData(
1033            Id,[valuesdict,
1034                [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
1035    G2frame.PatternTree.SelectItem(Id)
1036    G2frame.PatternTree.Expand(Id)
1037    G2frame.PatternId = Id
1038           
1039def powderFxyeSave(G2frame,exports,powderfile):
1040    'Save a powder histogram as a GSAS FXYE file'
1041    head,tail = ospath.split(powderfile)
1042    name,ext = tail.split('.')
1043    for i,export in enumerate(exports):
1044        filename = ospath.join(head,name+'-%03d.'%(i)+ext)
1045        prmname = filename.strip(ext)+'prm'
1046        prm = open(prmname,'w')      #old style GSAS parm file
1047        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1048        Inst = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame, \
1049            PickId, 'Instrument Parameters'))[0]
1050        prm.write( '            123456789012345678901234567890123456789012345678901234567890        '+'\n')
1051        prm.write( 'INS   BANK      1                                                               '+'\n')
1052        prm.write(('INS   HTYPE   %sR                                                              '+'\n')%(Inst['Type'][0]))
1053        if 'Lam1' in Inst:              #Ka1 & Ka2
1054            prm.write(('INS  1 ICONS%10.7f%10.7f    0.0000               0.990    0     0.500   '+'\n')%(Inst['Lam1'][0],Inst['Lam2'][0]))
1055        elif 'Lam' in Inst:             #single wavelength
1056            prm.write(('INS  1 ICONS%10.7f%10.7f    0.0000               0.990    0     0.500   '+'\n')%(Inst['Lam'][1],0.0))
1057        prm.write( 'INS  1 IRAD     0                                                               '+'\n')
1058        prm.write( 'INS  1I HEAD                                                                    '+'\n')
1059        prm.write( 'INS  1I ITYP    0    0.0000  180.0000         1                                 '+'\n')
1060        prm.write(('INS  1DETAZM%10.3f                                                          '+'\n')%(Inst['Azimuth'][0]))
1061        prm.write( 'INS  1PRCF1     3    8   0.00100                                                '+'\n')
1062        prm.write(('INS  1PRCF11     %15.6g%15.6g%15.6g%15.6g   '+'\n')%(Inst['U'][1],Inst['V'][1],Inst['W'][1],0.0))
1063        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.))
1064        prm.close()
1065        file = open(filename,'w')
1066        print 'save powder pattern to file: ',filename
1067        x,y,w,yc,yb,yd = G2frame.PatternTree.GetItemPyData(PickId)[1]
1068        file.write(powderfile+'\n')
1069        file.write('Instrument parameter file:'+ospath.split(prmname)[1]+'\n')
1070        file.write('BANK 1 %d %d CONS %.2f %.2f 0 0 FXYE\n'%(len(x),len(x),\
1071            100.*x[0],100.*(x[1]-x[0])))
1072        s = list(np.sqrt(1./np.array(w)))       
1073        XYW = zip(x,y,s)
1074        for X,Y,S in XYW:
1075            file.write("%15.6g %15.6g %15.6g\n" % (100.*X,Y,max(S,1.0)))
1076        file.close()
1077        print 'powder pattern file '+filename+' written'
1078       
1079def powderXyeSave(G2frame,exports,powderfile):
1080    'Save a powder histogram as a Topas XYE file'
1081    head,tail = ospath.split(powderfile)
1082    name,ext = tail.split('.')
1083    for i,export in enumerate(exports):
1084        filename = ospath.join(head,name+'-%03d.'%(i)+ext)
1085        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1086        file = open(filename,'w')
1087        file.write('#%s\n'%(export))
1088        print 'save powder pattern to file: ',filename
1089        x,y,w,yc,yb,yd = G2frame.PatternTree.GetItemPyData(PickId)[1]
1090        s = list(np.sqrt(1./np.array(w)))       
1091        XYW = zip(x,y,s)
1092        for X,Y,W in XYW:
1093            file.write("%15.6g %15.6g %15.6g\n" % (X,Y,W))
1094        file.close()
1095        print 'powder pattern file '+filename+' written'
1096       
1097def PDFSave(G2frame,exports):
1098    'Save a PDF G(r) and S(Q) in column formats'
1099    for export in exports:
1100        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1101        SQname = 'S(Q)'+export[4:]
1102        GRname = 'G(R)'+export[4:]
1103        sqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.sq')
1104        grfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.gr')
1105        sqId = G2gd.GetPatternTreeItemId(G2frame, PickId, SQname)
1106        grId = G2gd.GetPatternTreeItemId(G2frame, PickId, GRname)
1107        sqdata = np.array(G2frame.PatternTree.GetItemPyData(sqId)[1][:2]).T
1108        grdata = np.array(G2frame.PatternTree.GetItemPyData(grId)[1][:2]).T
1109        sqfile = open(sqfilename,'w')
1110        grfile = open(grfilename,'w')
1111        sqfile.write('#T S(Q) %s\n'%(export))
1112        grfile.write('#T G(R) %s\n'%(export))
1113        sqfile.write('#L Q     S(Q)\n')
1114        grfile.write('#L R     G(R)\n')
1115        for q,sq in sqdata:
1116            sqfile.write("%15.6g %15.6g\n" % (q,sq))
1117        sqfile.close()
1118        for r,gr in grdata:
1119            grfile.write("%15.6g %15.6g\n" % (r,gr))
1120        grfile.close()
1121   
1122def PeakListSave(G2frame,file,peaks):
1123    'Save powder peaks to a data file'
1124    print 'save peak list to file: ',G2frame.peaklistfile
1125    if not peaks:
1126        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1127        try:
1128            result = dlg.ShowModal()
1129        finally:
1130            dlg.Destroy()
1131        return
1132    for peak in peaks:
1133        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
1134            (peak[0],peak[2],peak[4],peak[6]))
1135    print 'peak list saved'
1136             
1137def IndexPeakListSave(G2frame,peaks):
1138    'Save powder peaks from the indexing list'
1139    file = open(G2frame.peaklistfile,'wa')
1140    print 'save index peak list to file: ',G2frame.peaklistfile
1141    wx.BeginBusyCursor()
1142    try:
1143        if not peaks:
1144            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1145            try:
1146                result = dlg.ShowModal()
1147            finally:
1148                dlg.Destroy()
1149            return
1150        for peak in peaks:
1151            file.write("%12.6f\n" % (peak[7]))
1152        file.close()
1153    finally:
1154        wx.EndBusyCursor()
1155    print 'index peak list saved'
1156   
1157def SetNewPhase(Name='New Phase',SGData=None,cell=None):
1158    '''Create a new phase dict with default values for various parameters
1159
1160    :param str Name: Name for new Phase
1161
1162    :param dict SGData: space group data from :func:`GSASIIspc:SpcGroup`;
1163      defaults to data for P 1
1164
1165    :param list cell: unit cell parameter list; defaults to
1166      [1.0,1.0,1.0,90.,90,90.,1.]
1167
1168    '''
1169    if SGData is None: SGData = G2spc.SpcGroup('P 1')[1]
1170    if cell is None: cell=[1.0,1.0,1.0,90.,90,90.,1.]
1171    phaseData = {
1172        'ranId':ran.randint(0,sys.maxint),
1173        'General':{
1174            'Name':Name,
1175            'Type':'nuclear',
1176            'AtomPtrs':[3,1,7,9],
1177            'SGData':SGData,
1178            'Cell':[False,]+cell,
1179            'Pawley dmin':1.0,
1180            'Data plot type':'None',
1181            'SH Texture':{
1182                'Order':0,
1183                'Model':'cylindrical',
1184                'Sample omega':[False,0.0],
1185                'Sample chi':[False,0.0],
1186                'Sample phi':[False,0.0],
1187                'SH Coeff':[False,{}],
1188                'SHShow':False,
1189                'PFhkl':[0,0,1],
1190                'PFxyz':[0,0,1],
1191                'PlotType':'Pole figure'}},
1192        'Atoms':[],
1193        'Drawing':{},
1194        'Histograms':{},
1195        'Pawley ref':[],
1196        'RBModels':{},
1197        }
1198    return phaseData
1199       
1200class MultipleChoicesDialog(wx.Dialog):
1201    '''A dialog that offers a series of choices, each with a
1202    title and a wx.Choice widget. Intended to be used Modally.
1203    typical input:
1204
1205        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1206        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1207       
1208    selections are placed in self.chosen when OK is pressed
1209    '''
1210    def __init__(self,choicelist,headinglist,
1211                 head='Select options',
1212                 title='Please select from options below',
1213                 parent=None):
1214        self.chosen = []
1215        wx.Dialog.__init__(
1216            self,parent,wx.ID_ANY,head, 
1217            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1218        panel = wx.Panel(self)
1219        mainSizer = wx.BoxSizer(wx.VERTICAL)
1220        mainSizer.Add((10,10),1)
1221        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1222        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1223        self.ChItems = []
1224        for choice,lbl in zip(choicelist,headinglist):
1225            mainSizer.Add((10,10),1)
1226            self.chosen.append(0)
1227            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1228            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1229            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1230            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1231
1232        OkBtn = wx.Button(panel,-1,"Ok")
1233        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1234        cancelBtn = wx.Button(panel,-1,"Cancel")
1235        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1236        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1237        btnSizer.Add((20,20),1)
1238        btnSizer.Add(OkBtn)
1239        btnSizer.Add((20,20),1)
1240        btnSizer.Add(cancelBtn)
1241        btnSizer.Add((20,20),1)
1242        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1243        panel.SetSizer(mainSizer)
1244        panel.Fit()
1245        self.Fit()
1246       
1247    def OnOk(self,event):
1248        parent = self.GetParent()
1249        if parent is not None: parent.Raise()
1250        # save the results from the choice widgets
1251        self.chosen = []
1252        for w in self.ChItems:
1253            self.chosen.append(w.GetSelection())
1254        self.EndModal(wx.ID_OK)             
1255           
1256    def OnCancel(self,event):
1257        parent = self.GetParent()
1258        if parent is not None: parent.Raise()
1259        self.chosen = []
1260        self.EndModal(wx.ID_CANCEL)             
1261           
1262def ExtractFileFromZip(filename, selection=None, confirmread=True,
1263                       confirmoverwrite=True, parent=None,
1264                       multipleselect=False):
1265    '''If the filename is a zip file, extract a file from that
1266    archive.
1267
1268    :param list Selection: used to predefine the name of the file
1269      to be extracted. Filename case and zip directory name are
1270      ignored in selection; the first matching file is used.
1271
1272    :param bool confirmread: if True asks the user to confirm before expanding
1273      the only file in a zip
1274
1275    :param bool confirmoverwrite: if True asks the user to confirm
1276      before overwriting if the extracted file already exists
1277
1278    :param bool multipleselect: if True allows more than one zip
1279      file to be extracted, a list of file(s) is returned.
1280      If only one file is present, do not ask which one, otherwise
1281      offer a list of choices (unless selection is used).
1282   
1283    :returns: the name of the file that has been created or a
1284      list of files (see multipleselect)
1285
1286    If the file is not a zipfile, return the name of the input file.
1287    If the zipfile is empty or no file has been selected, return None
1288    '''
1289    import zipfile # do this now, since we can save startup time by doing this only on need
1290    import shutil
1291    zloc = os.path.split(filename)[0]
1292    if not zipfile.is_zipfile(filename):
1293        #print("not zip")
1294        return filename
1295
1296    z = zipfile.ZipFile(filename,'r')
1297    zinfo = z.infolist()
1298
1299    if len(zinfo) == 0:
1300        #print('Zip has no files!')
1301        zlist = [-1]
1302    if selection:
1303        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1304        if selection.lower() in choices:
1305            zlist = [choices.index(selection.lower())]
1306        else:
1307            print('debug: file '+str(selection)+' was not found in '+str(filename))
1308            zlist = [-1]
1309    elif len(zinfo) == 1 and confirmread:
1310        result = wx.ID_NO
1311        dlg = wx.MessageDialog(
1312            parent,
1313            'Is file '+str(zinfo[0].filename)+
1314            ' what you want to extract from '+
1315            str(os.path.split(filename)[1])+'?',
1316            'Confirm file', 
1317            wx.YES_NO | wx.ICON_QUESTION)
1318        try:
1319            result = dlg.ShowModal()
1320        finally:
1321            dlg.Destroy()
1322        if result == wx.ID_NO:
1323            zlist = [-1]
1324        else:
1325            zlist = [0]
1326    elif len(zinfo) == 1:
1327        zlist = [0]
1328    elif multipleselect:
1329        # select one or more from a from list
1330        choices = [i.filename for i in zinfo]
1331        dlg = wx.MultiChoiceDialog(parent,'Select file(s) to extract from zip file'+str(filename),
1332            'Choose file(s)',choices,wx.CHOICEDLG_STYLE,)
1333        if dlg.ShowModal() == wx.ID_OK:
1334            zlist = dlg.GetSelections()
1335        else:
1336            zlist = []
1337        dlg.Destroy()
1338    else:
1339        # select one from a from list
1340        choices = [i.filename for i in zinfo]
1341        dlg = wx.SingleChoiceDialog(parent,
1342            'Select file to extract from zip file'+str(filename),'Choose file',
1343            choices,)
1344        if dlg.ShowModal() == wx.ID_OK:
1345            zlist = [dlg.GetSelection()]
1346        else:
1347            zlist = [-1]
1348        dlg.Destroy()
1349       
1350    outlist = []
1351    for zindex in zlist:
1352        if zindex >= 0:
1353            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1354            if os.path.exists(efil) and confirmoverwrite:
1355                result = wx.ID_NO
1356                dlg = wx.MessageDialog(parent,
1357                    'File '+str(efil)+' already exists. OK to overwrite it?',
1358                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1359                try:
1360                    result = dlg.ShowModal()
1361                finally:
1362                    dlg.Destroy()
1363                if result == wx.ID_NO:
1364                    zindex = -1
1365        if zindex >= 0:
1366            # extract the file to the current directory, regardless of it's original path
1367            #z.extract(zinfo[zindex],zloc)
1368            eloc,efil = os.path.split(zinfo[zindex].filename)
1369            outfile = os.path.join(zloc, efil)
1370            fpin = z.open(zinfo[zindex])
1371            fpout = file(outfile, "wb")
1372            shutil.copyfileobj(fpin, fpout)
1373            fpin.close()
1374            fpout.close()
1375            outlist.append(outfile)
1376    z.close()
1377    if multipleselect and len(outlist) >= 1:
1378        return outlist
1379    elif len(outlist) == 1:
1380        return outlist[0]
1381    else:
1382        return None
1383
1384######################################################################
1385# base classes for reading various types of data files
1386#   not used directly, only by subclassing
1387######################################################################
1388E,SGData = G2spc.SpcGroup('P 1') # data structure for default space group
1389P1SGData = SGData
1390class ImportBaseclass(object):
1391    '''Defines a base class for the reading of input files (diffraction
1392    data, coordinates,...). See :ref:`Writing a Import Routine<Import_routines>`
1393    for an explanation on how to use a subclass of this class.
1394    '''
1395    class ImportException(Exception):
1396        '''Defines an Exception that is used when an import routine hits an expected error,
1397        usually in .Reader.
1398
1399        Good practice is that the Reader should define a value in self.errors that
1400        tells the user some information about what is wrong with their file.         
1401        '''
1402        pass
1403
1404    def __init__(self,
1405                 formatName,
1406                 longFormatName=None,
1407                 extensionlist=[],
1408                 strictExtension=False,
1409                 ):
1410        self.formatName = formatName # short string naming file type
1411        if longFormatName: # longer string naming file type
1412            self.longFormatName = longFormatName
1413        else:
1414            self.longFormatName = formatName
1415        # define extensions that are allowed for the file type
1416        # for windows, remove any extensions that are duplicate, as case is ignored
1417        if sys.platform == 'windows' and extensionlist:
1418            extensionlist = list(set([s.lower() for s in extensionlist]))
1419        self.extensionlist = extensionlist
1420        # If strictExtension is True, the file will not be read, unless
1421        # the extension matches one in the extensionlist
1422        self.strictExtension = strictExtension
1423        self.errors = ''
1424        self.warnings = ''
1425        # used for readers that will use multiple passes to read
1426        # more than one data block
1427        self.repeat = False
1428        self.repeatcount = 0
1429        #print 'created',self.__class__
1430
1431    def ReInitialize(self):
1432        'Reinitialize the Reader to initial settings'
1433        self.errors = ''
1434        self.warnings = ''
1435        self.repeat = False
1436        self.repeatcount = 0
1437
1438    def BlockSelector(self, ChoiceList, ParentFrame=None,
1439                      title='Select a block',
1440                      size=None, header='Block Selector',
1441                      useCancel=True):
1442        ''' Provide a wx dialog to select a block if the file contains more
1443        than one set of data and one must be selected
1444        '''
1445        if useCancel:
1446            dlg = wx.SingleChoiceDialog(
1447                ParentFrame,title, header,ChoiceList)
1448        else:
1449            dlg = wx.SingleChoiceDialog(
1450                ParentFrame,title, header,ChoiceList,
1451                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1452        if size: dlg.SetSize(size)
1453        dlg.CenterOnParent()
1454        if dlg.ShowModal() == wx.ID_OK:
1455            sel = dlg.GetSelection()
1456            return sel
1457        else:
1458            return None
1459        dlg.Destroy()
1460
1461    def MultipleBlockSelector(self, ChoiceList, ParentFrame=None,
1462        title='Select a block',size=None, header='Block Selector'):
1463        '''Provide a wx dialog to select a block of data if the
1464        file contains more than one set of data and one must be
1465        selected.
1466
1467        :returns: a list of the selected blocks
1468        '''
1469        dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1470            wx.CHOICEDLG_STYLE)
1471        dlg.CenterOnParent()
1472        if size: dlg.SetSize(size)
1473        if dlg.ShowModal() == wx.ID_OK:
1474            sel = dlg.GetSelections()
1475        else:
1476            return []
1477        dlg.Destroy()
1478        selected = []
1479        if len(ChoiceList) in sel:
1480            return range(len(ChoiceList))
1481        else:
1482            return sel
1483        return selected
1484
1485    def MultipleChoicesDialog(self, choicelist, headinglist, ParentFrame=None, **kwargs):
1486        '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1487        widget. Typical input:
1488       
1489           * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1490           
1491           * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1492           
1493        optional keyword parameters are: head (window title) and title
1494        returns a list of selected indicies for each choice (or None)
1495        '''
1496        result = None
1497        dlg = MultipleChoicesDialog(choicelist,headinglist,
1498            parent=ParentFrame, **kwargs)         
1499        dlg.CenterOnParent()
1500        if dlg.ShowModal() == wx.ID_OK:
1501            result = dlg.chosen
1502        dlg.Destroy()
1503        return result
1504
1505    def ShowBusy(self):
1506        wx.BeginBusyCursor()
1507        wx.Yield() # make it happen now!
1508
1509    def DoneBusy(self):
1510        wx.EndBusyCursor()
1511        wx.Yield() # make it happen now!
1512       
1513#    def Reader(self, filename, filepointer, ParentFrame=None, **unused):
1514#        '''This method must be supplied in the child class to read the file.
1515#        if the read fails either return False or raise an Exception
1516#        preferably of type ImportException.
1517#        '''
1518#        #start reading
1519#        raise ImportException("Error occurred while...")
1520#        self.errors += "Hint for user on why the error occur
1521#        return False # if an error occurs
1522#        return True # if read OK
1523
1524    def ExtensionValidator(self, filename):
1525        '''This methods checks if the file has the correct extension
1526        Return False if this filename will not be supported by this reader
1527        Return True if the extension matches the list supplied by the reader
1528        Return None if the reader allows un-registered extensions
1529        '''
1530        if filename:
1531            ext = os.path.splitext(filename)[1]
1532            if sys.platform == 'windows': ext = ext.lower()
1533            if ext in self.extensionlist: return True
1534            if self.strictExtension: return False
1535        return None
1536
1537    def ContentsValidator(self, filepointer):
1538        '''This routine will attempt to determine if the file can be read
1539        with the current format.
1540        This will typically be overridden with a method that
1541        takes a quick scan of [some of]
1542        the file contents to do a "sanity" check if the file
1543        appears to match the selected format.
1544        Expected to be called via self.Validator()
1545        '''
1546        #filepointer.seek(0) # rewind the file pointer
1547        return True
1548
1549    def CIFValidator(self, filepointer):
1550        '''A :meth:`ContentsValidator` for use to validate CIF files.
1551        '''
1552        for i,l in enumerate(filepointer):
1553            if i >= 1000: return True
1554            '''Encountered only blank lines or comments in first 1000
1555            lines. This is unlikely, but assume it is CIF anyway, since we are
1556            even less likely to find a file with nothing but hashes and
1557            blank lines'''
1558            line = l.strip()
1559            if len(line) == 0: # ignore blank lines
1560                continue 
1561            elif line.startswith('#'): # ignore comments
1562                continue 
1563            elif line.startswith('data_'): # on the right track, accept this file
1564                return True
1565            else: # found something invalid
1566                self.errors = 'line '+str(i+1)+' contains unexpected data:\n'
1567                self.errors += '  '+str(l)
1568                self.errors += '  Note: a CIF should only have blank lines or comments before'
1569                self.errors += '        a data_ statement begins a block.'
1570                return False 
1571
1572class ImportPhase(ImportBaseclass):
1573    '''Defines a base class for the reading of files with coordinates
1574
1575    Objects constructed that subclass this (in import/G2phase_*.py) will be used
1576    in :meth:`GSASII.GSASII.OnImportPhase`.
1577    See :ref:`Writing a Import Routine<Import_Routines>`
1578    for an explanation on how to use this class.
1579
1580    '''
1581    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1582        strictExtension=False,):
1583        # call parent __init__
1584        ImportBaseclass.__init__(self,formatName,longFormatName,
1585            extensionlist,strictExtension)
1586        self.Phase = None # a phase must be created with G2IO.SetNewPhase in the Reader
1587        self.Constraints = None
1588
1589    def PhaseSelector(self, ChoiceList, ParentFrame=None,
1590        title='Select a phase', size=None,header='Phase Selector'):
1591        ''' Provide a wx dialog to select a phase if the file contains more
1592        than one phase
1593        '''
1594        return self.BlockSelector(ChoiceList,ParentFrame,title,
1595            size,header)
1596
1597class ImportStructFactor(ImportBaseclass):
1598    '''Defines a base class for the reading of files with tables
1599    of structure factors.
1600
1601    Structure factors are read with a call to :meth:`GSASII.GSASII.OnImportSfact`
1602    which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`, which calls
1603    methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1604    :meth:`Reader`.
1605
1606    See :ref:`Writing a Import Routine<Import_Routines>`
1607    for an explanation on how to use import classes in general. The specifics
1608    for reading a structure factor histogram require that
1609    the ``Reader()`` routine in the import
1610    class need to do only a few things: It
1611    should load :attr:`RefDict` item ``'RefList'`` with the reflection list,
1612    and set :attr:`Parameters` with the instrument parameters
1613    (initialized with :meth:`InitParameters` and set with :meth:`UpdateParameters`).
1614    Also, set :attr:`Controls`,
1615    which specifies how the histogram is plotted
1616    (initialized with :meth:`InitControls` and set with :meth:`UpdateControls`).
1617    '''
1618    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1619        strictExtension=False,):
1620        ImportBaseclass.__init__(self,formatName,longFormatName,
1621            extensionlist,strictExtension)
1622
1623        # define contents of Structure Factor entry
1624        self.Parameters = []
1625        'self.Parameters is a list with two dicts for data parameter settings'
1626        self.InitParameters()
1627        self.Controls = {}
1628        'self.Controls is a dict with plotting controls'
1629        self.InitControls() # initialize the above
1630        self.RefDict = {'RefList':[],'FF':[]}
1631        '''self.RefDict is a dict containing the reflection information, as read from the file.
1632        Item 'RefList' contains the reflection information. See the
1633        :ref:`Single Crystal Reflection Data Structure<XtalRefl_table>`
1634        for the contents of each row. Dict element 'FF'
1635        contains the form factor values for each element type; if this entry
1636        is left as initialized (an empty list) it will be initialized as needed later.
1637        '''
1638    def ReInitialize(self):
1639        'Reinitialize the Reader to initial settings'
1640        ImportBaseclass.ReInitialize(self)
1641        self.InitParameters()
1642        self.InitControls()
1643        self.RefDict = {'RefList':[],'FF':[]}
1644
1645       
1646    def InitControls(self):
1647        'initialize the controls structure'
1648        self.Controls = { # dictionary with plotting controls
1649            'Type' : 'Fosq',
1650            'ifFc' : False,    #
1651            'HKLmax' : [None,None,None],
1652            'HKLmin' : [None,None,None],
1653            'FoMax' : None,   # maximum observed structure factor as Fo
1654            'Zone' : '001',
1655            'Layer' : 0,
1656            'Scale' : 1.0,
1657            'log-lin' : 'lin',
1658            }
1659
1660    def InitParameters(self):
1661        'initialize the instrument parameters structure'
1662        Lambda = 0.70926
1663        HistType = 'SXC'
1664        self.Parameters = [{'Type':[HistType,HistType], # create the structure
1665                            'Lam':[Lambda,Lambda]
1666                            }, {}]
1667        'Parameters is a list with two dicts for data parameter settings'
1668
1669    def UpdateParameters(self,Type=None,Wave=None):
1670        'Revise the instrument parameters'
1671        if Type is not None:
1672            self.Parameters[0]['Type'] = [Type,Type]
1673        if Wave is not None:
1674            self.Parameters[0]['Lam'] = [Wave,Wave]
1675           
1676    def UpdateControls(self,Type='Fosq',FcalcPresent=False):
1677        '''Scan through the reflections to update the Controls dictionary
1678        '''
1679        self.Controls['Type'] = Type
1680        self.Controls['ifFc'] = FcalcPresent
1681        HKLmax = [None,None,None]
1682        HKLmin = [None,None,None]
1683        Fo2max = None
1684        for refl in self.RefDict['RefList']:
1685            HKL = refl[:3]
1686            if Fo2max is None:
1687                Fo2max = refl[8]
1688            else:
1689                Fo2max = max(Fo2max,refl[8])
1690            for i,hkl in enumerate(HKL):
1691                if HKLmax[i] is None:
1692                    HKLmax[i] = hkl
1693                    HKLmin[i] = hkl
1694                else:
1695                    HKLmax[i] = max(HKLmax[i],hkl)
1696                    HKLmin[i] = min(HKLmin[i],hkl)
1697        self.Controls['HKLmax'] = HKLmax
1698        self.Controls['HKLmin'] = HKLmin
1699        if Type ==  'Fosq':
1700            self.Controls['FoMax'] = np.sqrt(Fo2max)
1701        elif Type ==  'Fo':
1702            self.Controls['FoMax'] = Fo2max
1703        else:
1704            print "Unsupported Struct Fact type in ImportStructFactor.UpdateControls"
1705            raise Exception,"Unsupported Struct Fact type in ImportStructFactor.UpdateControls"
1706
1707######################################################################
1708class ImportPowderData(ImportBaseclass):
1709    '''Defines a base class for the reading of files with powder data.
1710    See :ref:`Writing a Import Routine<Import_Routines>`
1711    for an explanation on how to use this class.
1712    '''
1713    # define some default instrument parameter files
1714    # just like GSAS, sigh
1715    defaultIparm_lbl = []
1716    defaultIparms = []
1717    defaultIparm_lbl.append('CuKa lab data')
1718    defaultIparms.append({
1719        'INS   HTYPE ':'PXC ',
1720        'INS  1 ICONS':'  1.540500  1.544300       0.0         0       0.7    0       0.5   ',
1721        'INS  1PRCF1 ':'    3    8      0.01                                                ',
1722        'INS  1PRCF11':'   2.000000E+00  -2.000000E+00   5.000000E+00   0.000000E+00        ',
1723        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.150000E-01   0.150000E-01        ',
1724        })
1725    defaultIparm_lbl.append('0.6A synch')
1726    defaultIparms.append({
1727        'INS   HTYPE ':'PXC ',
1728        'INS  1 ICONS':'  0.600000  0.000000       0.0         0      0.99    0       0.5   ',
1729        'INS  1PRCF1 ':'    3    8      0.01                                                ',
1730        'INS  1PRCF11':'   1.000000E+00  -1.000000E+00   0.300000E+00   0.000000E+00        ',
1731        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.100000E-01   0.100000E-01        ',
1732        })
1733    defaultIparm_lbl.append('1.5A CW neutron data')
1734    defaultIparms.append({
1735        'INS   HTYPE ':'PNC',
1736        'INS  1 ICONS':'   1.54020   0.00000   0.04000         0',
1737        'INS  1PRCF1 ':'    3    8     0.005',
1738        'INS  1PRCF11':'   0.239700E+03  -0.298200E+03   0.180800E+03   0.000000E+00',
1739        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.400000E-01   0.300000E-01',
1740        })
1741    defaultIparm_lbl.append('10m TOF backscattering bank')
1742    defaultIparms.append({
1743        'INS   HTYPE ':'PNT',
1744        'INS  1 ICONS':'   5000.00      0.00      0.00',
1745        'INS  1BNKPAR':'    1.0000   150.000',       
1746        'INS  1PRCF1 ':'    1    8   0.01000',
1747        'INS  1PRCF11':'   0.000000E+00   5.000000E+00   3.000000E-02   1.000000E-03',
1748        'INS  1PRCF12':'   0.000000E+00   4.000000E+01   0.000000E+00   0.000000E+00',       
1749        })
1750    defaultIparm_lbl.append('10m TOF 90deg bank')
1751    defaultIparms.append({
1752        'INS   HTYPE ':'PNT',
1753        'INS  1 ICONS':'   3500.00      0.00      0.00',
1754        'INS  1BNKPAR':'    1.0000    90.000',       
1755        'INS  1PRCF1 ':'    1    8   0.01000',
1756        'INS  1PRCF11':'   0.000000E+00   5.000000E+00   3.000000E-02   4.000000E-03',
1757        'INS  1PRCF12':'   0.000000E+00   8.000000E+01   0.000000E+00   0.000000E+00',       
1758        })
1759    defaultIparm_lbl.append('63m POWGEN 90deg bank')
1760    defaultIparms.append({
1761        'INS   HTYPE ':'PNT',
1762        'INS  1 ICONS':'  22585.80      0.00      0.00',
1763        'INS  1BNKPAR':'    1.0000    90.000',       
1764        'INS  1PRCF1 ':'    1    8   0.01000',
1765        'INS  1PRCF11':'   0.000000E+00   1.000000E+00   3.000000E-02   4.000000E-03',
1766        'INS  1PRCF12':'   0.000000E+00   8.000000E+01   0.000000E+00   0.000000E+00',       
1767        })
1768    def __init__(self,
1769                 formatName,
1770                 longFormatName=None,
1771                 extensionlist=[],
1772                 strictExtension=False,
1773                 ):
1774        ImportBaseclass.__init__(self,formatName,
1775                                            longFormatName,
1776                                            extensionlist,
1777                                            strictExtension)
1778        self.ReInitialize()
1779       
1780    def ReInitialize(self):
1781        'Reinitialize the Reader to initial settings'
1782        ImportBaseclass.ReInitialize(self)
1783        self.powderentry = ['',None,None] #  (filename,Pos,Bank)
1784        self.powderdata = [] # Powder dataset
1785        '''A powder data set is a list with items [x,y,w,yc,yb,yd]:
1786                np.array(x), # x-axis values
1787                np.array(y), # powder pattern intensities
1788                np.array(w), # 1/sig(intensity)^2 values (weights)
1789                np.array(yc), # calc. intensities (zero)
1790                np.array(yb), # calc. background (zero)
1791                np.array(yd), # obs-calc profiles
1792        '''                           
1793        self.comments = []
1794        self.idstring = ''
1795        self.Sample = G2pdG.SetDefaultSample()
1796        self.GSAS = None     # used in TOF
1797        self.clockWd = None  # used in TOF
1798        self.repeat_instparm = True # Should a parm file be
1799        #                             used for multiple histograms?
1800        self.instparm = None # name hint
1801        self.instfile = '' # full path name to instrument parameter file
1802        self.instbank = '' # inst parm bank number
1803        self.instmsg = ''  # a label that gets printed to show
1804                           # where instrument parameters are from
1805        self.numbanks = 1
1806        self.instdict = {} # place items here that will be transferred to the instrument parameters
1807######################################################################
1808class ImportSmallAngleData(ImportBaseclass):
1809    '''Defines a base class for the reading of files with small angle data.
1810    See :ref:`Writing a Import Routine<Import_Routines>`
1811    for an explanation on how to use this class.
1812    '''
1813    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1814        strictExtension=False,):
1815           
1816        ImportBaseclass.__init__(self,formatName,longFormatName,extensionlist,
1817            strictExtension)
1818        self.ReInitialize()
1819       
1820    def ReInitialize(self):
1821        'Reinitialize the Reader to initial settings'
1822        ImportBaseclass.ReInitialize(self)
1823        self.smallangleentry = ['',None,None] #  (filename,Pos,Bank)
1824        self.smallangledata = [] # SASD dataset
1825        '''A small angle data set is a list with items [x,y,w,yc,yd]:
1826                np.array(x), # x-axis values
1827                np.array(y), # powder pattern intensities
1828                np.array(w), # 1/sig(intensity)^2 values (weights)
1829                np.array(yc), # calc. intensities (zero)
1830                np.array(yd), # obs-calc profiles
1831        '''                           
1832        self.comments = []
1833        self.idstring = ''
1834        self.Sample = G2pdG.SetDefaultSample()
1835        self.GSAS = None     # used in TOF
1836        self.clockWd = None  # used in TOF
1837        self.numbanks = 1
1838        self.instdict = {} # place items here that will be transferred to the instrument parameters
1839######################################################################
1840class ExportBaseclass(object):
1841    '''Defines a base class for the exporting of GSAS-II results
1842    '''
1843    def __init__(self,
1844                 G2frame,
1845                 formatName,
1846                 extension,
1847                 longFormatName=None,
1848                 ):
1849        self.G2frame = G2frame
1850        self.formatName = formatName # short string naming file type
1851        self.extension = extension
1852        if longFormatName: # longer string naming file type
1853            self.longFormatName = longFormatName
1854        else:
1855            self.longFormatName = formatName
1856        self.OverallParms = {}
1857        self.Phases = {}
1858        self.Histograms = {}
1859        self.powderDict = {}
1860        self.xtalDict = {}
1861        self.parmDict = {}
1862        self.sigDict = {}
1863        # updated in InitExport:
1864        self.currentExportType = None # type of export that has been requested
1865        # updated in ExportSelect (when used):
1866        self.phasenam = None # a list of selected phases
1867        self.histnam = None # a list of selected histograms
1868        self.filename = None # name of file to be written
1869       
1870        # items that should be defined in a subclass of this class
1871        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1872        # The following types are defined: 'project', "phase", "powder", "single"
1873        self.multiple = False # set as True if the class can export multiple phases or histograms
1874        # self.multiple is ignored for "project" exports
1875
1876    def InitExport(self,event):
1877        '''Determines the type of menu that called the Exporter.
1878        '''
1879        if event:
1880            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1881
1882    def ExportSelect(self,AskFile=True):
1883        '''Selects histograms or phases when needed. Sets a default file name.
1884
1885        :param bool AskFile: if AskFile is True (default) get the name of the file
1886          in a dialog
1887        :returns: True in case of an error
1888        '''
1889       
1890        if self.currentExportType == 'phase':
1891            if len(self.Phases) == 0:
1892                self.G2frame.ErrorDialog(
1893                    'Empty project',
1894                    'Project does not contain any phases.')
1895                return True
1896            elif len(self.Phases) == 1:
1897                self.phasenam = self.Phases.keys()
1898            elif self.multiple: 
1899                choices = sorted(self.Phases.keys())
1900                phasenum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1901                if phasenum is None: return True
1902                self.phasenam = [choices[i] for i in phasenum]
1903                if not self.phasenam: return True
1904            else:
1905                choices = sorted(self.Phases.keys())
1906                phasenum = G2gd.ItemSelector(choices,self.G2frame)
1907                if phasenum is None: return True
1908                self.phasenam = [choices[phasenum]]
1909        elif self.currentExportType == 'single':
1910            if len(self.xtalDict) == 0:
1911                self.G2frame.ErrorDialog(
1912                    'Empty project',
1913                    'Project does not contain any single crystal data.')
1914                return True
1915            elif len(self.xtalDict) == 1:
1916                self.histnam = self.xtalDict.values()
1917            elif self.multiple:
1918                choices = sorted(self.xtalDict.values())
1919                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1920                if not hnum: return True
1921                self.histnam = [choices[i] for i in hnum]
1922            else:
1923                choices = sorted(self.xtalDict.values())
1924                hnum = G2gd.ItemSelector(choices,self.G2frame)
1925                if hnum is None: return True
1926                self.histnam = [choices[hnum]]
1927        elif self.currentExportType == 'powder':
1928            if len(self.powderDict) == 0:
1929                self.G2frame.ErrorDialog(
1930                    'Empty project',
1931                    'Project does not contain any powder data.')
1932                return True
1933            elif len(self.powderDict) == 1:
1934                self.histnam = self.powderDict.values()
1935            elif self.multiple:
1936                choices = sorted(self.powderDict.values())
1937                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1938                if not hnum: return True
1939                self.histnam = [choices[i] for i in hnum]
1940            else:
1941                choices = sorted(self.powderDict.values())
1942                hnum = G2gd.ItemSelector(choices,self.G2frame)
1943                if hnum is None: return True
1944                self.histnam = [choices[hnum]]
1945        elif self.currentExportType == 'image':
1946            if len(self.Histograms) == 0:
1947                self.G2frame.ErrorDialog(
1948                    'Empty project',
1949                    'Project does not contain any images.')
1950                return True
1951            elif len(self.Histograms) == 1:
1952                self.histnam = self.Histograms.keys()
1953            else:
1954                choices = sorted(self.Histograms.keys())
1955                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1956                if self.multiple:
1957                    if not hnum: return True
1958                    self.histnam = [choices[i] for i in hnum]
1959                else:
1960                    if hnum is None: return True
1961                    self.histnam = [choices[hnum]]
1962        if self.currentExportType == 'map':
1963            # search for phases with maps
1964            mapPhases = []
1965            choices = []
1966            for phasenam in sorted(self.Phases):
1967                phasedict = self.Phases[phasenam] # pointer to current phase info           
1968                if len(phasedict['General']['Map'].get('rho',[])):
1969                    mapPhases.append(phasenam)
1970                    if phasedict['General']['Map'].get('Flip'):
1971                        choices.append('Charge flip map: '+str(phasenam))
1972                    elif phasedict['General']['Map'].get('MapType'):
1973                        choices.append(
1974                            str(phasedict['General']['Map'].get('MapType'))
1975                            + ' map: ' + str(phasenam))
1976                    else:
1977                        choices.append('unknown map: '+str(phasenam))
1978            # select a map if needed
1979            if len(mapPhases) == 0:
1980                self.G2frame.ErrorDialog(
1981                    'Empty project',
1982                    'Project does not contain any maps.')
1983                return True
1984            elif len(mapPhases) == 1:
1985                self.phasenam = mapPhases
1986            else: 
1987                phasenum = G2gd.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1988                if self.multiple:
1989                    if not phasenum: return True
1990                    self.phasenam = [mapPhases[i] for i in phasenum]
1991                else:
1992                    if phasenum is None: return True
1993                    self.phasenam = [mapPhases[phasenum]]
1994
1995        if AskFile:
1996            self.filename = self.askSaveFile()
1997        else:
1998            self.filename = self.defaultSaveFile()
1999        if not self.filename: return True
2000       
2001    # def SetupExport(self,event,AskFile=True):
2002    #     '''Determines the type of menu that called the Exporter. Selects histograms
2003    #     or phases when needed. Better to replace with individual calls to
2004    #     self.InitExport() and self.ExportSelect() so that the former can be called prior
2005    #     to self.LoadTree()
2006
2007    #     :param bool AskFile: if AskFile is True (default) get the name of the file
2008    #       in a dialog
2009    #     :returns: True in case of an error
2010    #     '''
2011    #     self.ExportInit(event)
2012    #     return self.ExportSelect(AskFile)
2013                   
2014    def loadParmDict(self):
2015        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
2016        refined values to those from the last cycle and set the uncertainties for the
2017        refined parameters in another dict (self.sigDict).
2018
2019        Expands the parm & sig dicts to include values derived from constraints.
2020        '''
2021        self.parmDict = {}
2022        self.sigDict = {}
2023        rigidbodyDict = {}
2024        covDict = {}
2025        consDict = {}
2026        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2027        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2028        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2029        while item:
2030            name = self.G2frame.PatternTree.GetItemText(item)
2031            if name == 'Rigid bodies':
2032                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
2033            elif name == 'Covariance':
2034                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
2035            elif name == 'Constraints':
2036                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
2037            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2038        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2039        self.parmDict.update(rbDict)
2040        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2041        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables =  G2stIO.GetPhaseData(
2042            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
2043        self.parmDict.update(phaseDict)
2044        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
2045            Phases,Histograms,Print=False,resetRefList=False)
2046        self.parmDict.update(hapDict)
2047        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
2048        self.parmDict.update(histDict)
2049        self.parmDict.update(zip(
2050            covDict.get('varyList',[]),
2051            covDict.get('variables',[])))
2052        self.sigDict = dict(zip(
2053            covDict.get('varyList',[]),
2054            covDict.get('sig',[])))
2055        # expand to include constraints: first compile a list of constraints
2056        constList = []
2057        for item in consDict:
2058            if item.startswith('_'): continue
2059            constList += consDict[item]
2060        # now process the constraints
2061        G2mv.InitVars()
2062        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
2063        varyList = covDict.get('varyListStart')
2064        if varyList is None and len(constDict) == 0:
2065            # no constraints can use varyList
2066            varyList = covDict.get('varyList')
2067        elif varyList is None:
2068            # old GPX file from before pre-constraint varyList is saved
2069            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
2070            raise Exception(' *** Export aborted ***')
2071        else:
2072            varyList = list(varyList)
2073        try:
2074            groups,parmlist = G2mv.GroupConstraints(constDict)
2075            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList)
2076        except:
2077            # this really should not happen
2078            print ' *** ERROR - constraints are internally inconsistent ***'
2079            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
2080            print 'Errors',errmsg
2081            if warnmsg: print 'Warnings',warnmsg
2082            raise Exception(' *** CIF creation aborted ***')
2083        # add the constrained values to the parameter dictionary
2084        G2mv.Dict2Map(self.parmDict,varyList)
2085        # and add their uncertainties into the esd dictionary (sigDict)
2086        if covDict.get('covMatrix') is not None:
2087            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
2088
2089    def loadTree(self):
2090        '''Load the contents of the data tree into a set of dicts
2091        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
2092        & self.xtalDict)
2093       
2094        * The childrenless data tree items are overall parameters/controls for the
2095          entire project and are placed in self.OverallParms
2096        * Phase items are placed in self.Phases
2097        * Data items are placed in self.Histogram. The key for these data items
2098          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
2099        '''
2100        self.OverallParms = {}
2101        self.powderDict = {}
2102        self.xtalDict = {}
2103        self.Phases = {}
2104        self.Histograms = {}
2105        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2106        histType = None       
2107        if self.currentExportType == 'phase':
2108            # if exporting phases load them here
2109            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
2110            if not sub:
2111                print 'no phases found'
2112                return True
2113            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
2114            while item:
2115                phaseName = self.G2frame.PatternTree.GetItemText(item)
2116                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
2117                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
2118            return
2119        elif self.currentExportType == 'single':
2120            histType = 'HKLF'
2121        elif self.currentExportType == 'powder':
2122            histType = 'PWDR'
2123        elif self.currentExportType == 'image':
2124            histType = 'IMG'
2125
2126        if histType: # Loading just one kind of tree entry
2127            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2128            while item:
2129                name = self.G2frame.PatternTree.GetItemText(item)
2130                if name.startswith(histType):
2131                    if self.Histograms.get(name): # there is already an item with this name
2132                        print('Histogram name '+str(name)+' is repeated. Renaming')
2133                        if name[-1] == '9':
2134                            name = name[:-1] + '10'
2135                        elif name[-1] in '012345678':
2136                            name = name[:-1] + str(int(name[-1])+1)
2137                        else:                           
2138                            name += '-1'
2139                    self.Histograms[name] = {}
2140                    # the main info goes into Data, but the 0th
2141                    # element contains refinement results, carry
2142                    # that over too now.
2143                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
2144                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
2145                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2146                    while item2: 
2147                        child = self.G2frame.PatternTree.GetItemText(item2)
2148                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
2149                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
2150                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2151            # index powder and single crystal histograms by number
2152            for hist in self.Histograms:
2153                if hist.startswith("PWDR"): 
2154                    d = self.powderDict
2155                elif hist.startswith("HKLF"): 
2156                    d = self.xtalDict
2157                else:
2158                    return                   
2159                i = self.Histograms[hist].get('hId')
2160                if i is None and not d.keys():
2161                    i = 0
2162                elif i is None or i in d.keys():
2163                    i = max(d.keys())+1
2164                d[i] = hist
2165            return
2166        # else standard load: using all interlinked phases and histograms
2167        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2168        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2169        while item:
2170            name = self.G2frame.PatternTree.GetItemText(item)
2171            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2172            if not item2: 
2173                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
2174            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2175        # index powder and single crystal histograms
2176        for hist in self.Histograms:
2177            i = self.Histograms[hist]['hId']
2178            if hist.startswith("PWDR"): 
2179                self.powderDict[i] = hist
2180            elif hist.startswith("HKLF"): 
2181                self.xtalDict[i] = hist
2182
2183    def dumpTree(self,mode='type'):
2184        '''Print out information on the data tree dicts loaded in loadTree
2185        '''
2186        print '\nOverall'
2187        if mode == 'type':
2188            def Show(arg): return type(arg)
2189        else:
2190            def Show(arg): return arg
2191        for key in self.OverallParms:
2192            print '  ',key,Show(self.OverallParms[key])
2193        print 'Phases'
2194        for key1 in self.Phases:
2195            print '    ',key1,Show(self.Phases[key1])
2196        print 'Histogram'
2197        for key1 in self.Histograms:
2198            print '    ',key1,Show(self.Histograms[key1])
2199            for key2 in self.Histograms[key1]:
2200                print '      ',key2,Show(self.Histograms[key1][key2])
2201
2202    def defaultSaveFile(self):
2203        return os.path.abspath(
2204            os.path.splitext(self.G2frame.GSASprojectfile
2205                             )[0]+self.extension)
2206       
2207    def askSaveFile(self):
2208        '''Ask the user to supply a file name
2209
2210        :returns: a file name (str)
2211        '''
2212        defnam = os.path.splitext(
2213            os.path.split(self.G2frame.GSASprojectfile)[1]
2214            )[0]+self.extension
2215        dlg = wx.FileDialog(
2216            self.G2frame, 'Input name for file to write', '.', defnam,
2217            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2218            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2219        dlg.CenterOnParent()
2220        try:
2221            if dlg.ShowModal() == wx.ID_OK:
2222                filename = dlg.GetPath()
2223                # make sure extension is correct
2224                filename = os.path.splitext(filename)[0]+self.extension
2225            else:
2226                filename = None
2227        finally:
2228            dlg.Destroy()
2229        return filename
2230
2231    # Tools for file writing.
2232    def OpenFile(self,fil=None):
2233        '''Open the output file
2234
2235        :param str fil: The name of the file to open. If None (default)
2236          the name defaults to self.filename.
2237        :returns: the file object opened by the routine which is also
2238          saved as self.fp
2239        '''
2240        if not fil:
2241            fil = self.filename
2242        self.fp = open(fil,'w')
2243        return self.fp
2244    def Write(self,line):
2245        '''write a line of output, attaching a line-end character
2246
2247        :param str line: the text to be written.
2248        '''
2249        self.fp.write(line+'\n')
2250    def CloseFile(self,fp=None):
2251        '''Close a file opened in OpenFile
2252
2253        :param file fp: the file object to be closed. If None (default)
2254          file object self.fp is closed.
2255        '''
2256        if fp is None:
2257            fp = self.fp
2258            self.fp = None
2259        fp.close()
2260    # Tools to pull information out of the data arrays
2261    def GetCell(self,phasenam):
2262        """Gets the unit cell parameters and their s.u.'s for a selected phase
2263
2264        :param str phasenam: the name for the selected phase
2265        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2266          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2267          cell values and `cellSig` has their uncertainties.
2268        """
2269        phasedict = self.Phases[phasenam] # pointer to current phase info
2270        try:
2271            pfx = str(phasedict['pId'])+'::'
2272            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2273            cellSig = G2stIO.getCellEsd(pfx,
2274                                        phasedict['General']['SGData'],A,
2275                                        self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2276            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2277            return cellList,cellSig
2278        except KeyError:
2279            cell = phasedict['General']['Cell'][1:]
2280            return cell,7*[0]
2281   
2282    def GetAtoms(self,phasenam):
2283        """Gets the atoms associated with a phase. Can be used with standard
2284        or macromolecular phases
2285
2286        :param str phasenam: the name for the selected phase
2287        :returns: a list of items for eac atom where each item is a list containing:
2288          label, typ, mult, xyz, and td, where
2289
2290          * label and typ are the atom label and the scattering factor type (str)
2291          * mult is the site multiplicity (int)
2292          * xyz is contains a list with four pairs of numbers:
2293            x, y, z and fractional occupancy and
2294            their standard uncertainty (or a negative value)
2295          * td is contains a list with either one or six pairs of numbers:
2296            if one number it is U\ :sub:`iso` and with six numbers it is
2297            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2298            paired with their standard uncertainty (or a negative value)
2299        """
2300        phasedict = self.Phases[phasenam] # pointer to current phase info           
2301        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2302        cfrac = cx+3
2303        fpfx = str(phasedict['pId'])+'::Afrac:'       
2304        atomslist = []
2305        for i,at in enumerate(phasedict['Atoms']):
2306            if phasedict['General']['Type'] == 'macromolecular':
2307                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2308            else:
2309                label = at[ct-1]
2310            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2311            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2312            mult = at[cs+1]
2313            typ = at[ct]
2314            xyz = []
2315            for j,v in enumerate(('x','y','z')):
2316                val = at[cx+j]
2317                pfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2318                sig = self.sigDict.get(pfx,-0.000009)
2319                xyz.append((val,sig))
2320            xyz.append((fval,fsig))
2321            td = []
2322            if at[cia] == 'I':
2323                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2324                val = self.parmDict.get(pfx,at[cia+1])
2325                sig = self.sigDict.get(pfx,-0.0009)
2326                td.append((val,sig))
2327            else:
2328                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2329                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2330                    val = self.parmDict.get(pfx,at[cia+2+i])
2331                    sig = self.sigDict.get(pfx,-0.0009)
2332                    td.append((val,sig))
2333            atomslist.append((label,typ,mult,xyz,td))
2334        return atomslist
2335######################################################################
2336
2337def ReadCIF(URLorFile):
2338    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2339    (from James Hester).
2340    The open routine gets confused with DOS names that begin with a letter and colon
2341    "C:\dir\" so this routine will try to open the passed name as a file and if that
2342    fails, try it as a URL
2343
2344    :param str URLorFile: string containing a URL or a file name. Code will try first
2345      to open it as a file and then as a URL.
2346
2347    :returns: a PyCifRW CIF object.
2348    '''
2349    import CifFile as cif # PyCifRW from James Hester
2350
2351    # alternate approach:
2352    #import urllib
2353    #ciffile = 'file:'+urllib.pathname2url(filename)
2354   
2355    try:
2356        fp = open(URLorFile,'r')
2357        cf = cif.ReadCif(fp)
2358        fp.close()
2359        return cf
2360    except IOError:
2361        return cif.ReadCif(URLorFile)
2362
2363if __name__ == '__main__':
2364    app = wx.PySimpleApp()
2365    frm = wx.Frame(None) # create a frame
2366    frm.Show(True)
2367    filename = '/tmp/notzip.zip'
2368    filename = '/tmp/all.zip'
2369    #filename = '/tmp/11bmb_7652.zip'
2370   
2371    #selection=None, confirmoverwrite=True, parent=None
2372    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2373    print ExtractFileFromZip(filename,multipleselect=True)
2374                             #confirmread=False, confirmoverwrite=False)
2375
2376    # choicelist=[ ('a','b','c'),
2377    #              ('test1','test2'),('no choice',)]
2378    # titles = [ 'a, b or c', 'tests', 'No option here']
2379    # dlg = MultipleChoicesDialog(
2380    #     choicelist,titles,
2381    #     parent=frm)
2382    # if dlg.ShowModal() == wx.ID_OK:
2383    #     print 'Got OK'
Note: See TracBrowser for help on using the repository browser.