source: trunk/GSASIIIO.py @ 1276

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

seq. ref. bugs: wrong file name on msg; fix wildcarded constraints.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 101.5 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2014-04-11 19:31:29 +0000 (Fri, 11 Apr 2014) $
4# $Author: toby $
5# $Revision: 1276 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 1276 2014-04-11 19:31:29Z 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: 1276 $")
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.flipud(np.array(Im.open(filename)))*10.
604        distance = 18.0
605        center = [pixy[0]*sizexy[0]/2000,0]     #the CheMin beam stop is here
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                tifType = 'GE'
623                pixy = (200,200)
624                File.seek(8)
625                if not imageOnly:
626                    print 'Read GE-detector tiff file: ',filename
627                image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
628            elif IFD[258][2][0] == 32:
629                tifType = 'CHESS'
630                pixy = (200,200)
631                File.seek(8)
632                if not imageOnly:
633                    print 'Read CHESS-detector tiff file: ',filename
634                image = np.array(ar.array('L',File.read(4*Npix)),dtype=np.int32)
635           
636    elif 262 in IFD and IFD[262][2][0] > 4:
637        tifType = 'DND'
638        pixy = (158,158)
639        File.seek(512)
640        if not imageOnly:
641            print 'Read DND SAX/WAX-detector tiff file: ',filename
642        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
643    elif sizexy == [1536,1536]:
644        tifType = 'APS Gold'
645        pixy = (150,150)
646        File.seek(64)
647        if not imageOnly:
648            print 'Read Gold tiff file:',filename
649        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
650    elif sizexy == [2048,2048] or sizexy == [1024,1024] or sizexy == [3072,3072]:
651        if IFD[273][2][0] == 8:
652            if IFD[258][2][0] == 32:
653                tifType = 'PE'
654                pixy = (200,200)
655                File.seek(8)
656                if not imageOnly:
657                    print 'Read APS PE-detector tiff file: ',filename
658                if dataType == 5:
659                    image = np.array(ar.array('f',File.read(4*Npix)),dtype=np.float32)
660                else:
661                    image = np.array(ar.array('I',File.read(4*Npix)),dtype=np.int32)
662               
663        elif IFD[273][2][0] == 4096:
664            if sizexy[0] == 3072:
665                pixy =  (73,73)
666                tifType = 'MAR225'           
667            else:
668                pixy = (158,158)
669                tifType = 'MAR325'           
670            File.seek(4096)
671            if not imageOnly:
672                print 'Read MAR CCD tiff file: ',filename
673            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
674        elif IFD[273][2][0] == 512:
675            tiftype = '11-ID-C'
676            pixy = [200,200]
677            File.seek(512)
678            if not imageOnly:
679                print 'Read 11-ID-C tiff file: ',filename
680            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)           
681    elif sizexy == [4096,4096]:
682        if IFD[273][2][0] == 8:
683            if IFD[258][2][0] == 16:
684                tifType = 'scanCCD'
685                pixy = (9,9)
686                File.seek(8)
687                if not imageOnly:
688                    print 'Read APS scanCCD tiff file: ',filename
689                image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
690        elif IFD[273][2][0] == 4096:
691            tifType = 'Rayonix'
692            pixy = (73.242,73.242)
693            File.seek(4096)
694            if not imageOnly:
695                print 'Read Rayonix MX300HE tiff file: ',filename
696            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
697#    elif sizexy == [960,960]:
698#        tiftype = 'PE-BE'
699#        pixy = (200,200)
700#        File.seek(8)
701#        if not imageOnly:
702#            print 'Read Gold tiff file:',filename
703#        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
704           
705    else:
706        lines = ['not a known detector tiff file',]
707        return lines,0,0,0
708       
709    image = np.reshape(image,(sizexy[1],sizexy[0]))
710    center = (not center[0]) and [pixy[0]*sizexy[0]/2000,pixy[1]*sizexy[1]/2000] or center
711    wavelength = (not wavelength) and 0.10 or wavelength
712    distance = (not distance) and 100.0 or distance
713    data = {'pixelSize':pixy,'wavelength':wavelength,'distance':distance,'center':center,'size':sizexy}
714    File.close()   
715    if imageOnly:
716        return image
717    else:
718        return head,data,Npix,image
719   
720#def GetTifData(filename,imageOnly=False):
721#    import struct as st
722#    import array as ar
723#    File = open(filename,'rb')
724#    dataType = 5
725#    try:
726#        Meta = open(filename+'.metadata','Ur')
727#        head = Meta.readlines()
728#        for line in head:
729#            line = line.strip()
730#            if 'dataType=' in line:
731#                dataType = int(line.split('=')[1])
732#        Meta.close()
733#    except IOError:
734#        print 'no metadata file found - will try to read file anyway'
735#        head = ['no metadata file found',]
736#       
737#    tag = File.read(2)
738#    byteOrd = '<'
739#    if tag == 'II' and int(st.unpack('<h',File.read(2))[0]) == 42:     #little endian
740#        IFD = int(st.unpack(byteOrd+'i',File.read(4))[0])
741#    elif tag == 'MM' and int(st.unpack('>h',File.read(2))[0]) == 42:   #big endian
742#        byteOrd = '>'
743#        IFD = int(st.unpack(byteOrd+'i',File.read(4))[0])       
744#    else:
745#        lines = ['not a detector tiff file',]
746#        return lines,0,0,0
747#    File.seek(IFD)                                                  #get number of directory entries
748#    NED = int(st.unpack(byteOrd+'h',File.read(2))[0])
749#    IFD = {}
750#    for ied in range(NED):
751#        Tag,Type = st.unpack(byteOrd+'Hh',File.read(4))
752#        nVal = st.unpack(byteOrd+'i',File.read(4))[0]
753#        if Type == 1:
754#            Value = st.unpack(byteOrd+nVal*'b',File.read(nVal))
755#        elif Type == 2:
756#            Value = st.unpack(byteOrd+'i',File.read(4))
757#        elif Type == 3:
758#            Value = st.unpack(byteOrd+nVal*'h',File.read(nVal*2))
759#            x = st.unpack(byteOrd+nVal*'h',File.read(nVal*2))
760#        elif Type == 4:
761#            Value = st.unpack(byteOrd+nVal*'i',File.read(nVal*4))
762#        elif Type == 5:
763#            Value = st.unpack(byteOrd+nVal*'i',File.read(nVal*4))
764#        elif Type == 11:
765#            Value = st.unpack(byteOrd+nVal*'f',File.read(nVal*4))
766#        IFD[Tag] = [Type,nVal,Value]
767##        print Tag,IFD[Tag]
768#    sizexy = [IFD[256][2][0],IFD[257][2][0]]
769#    [nx,ny] = sizexy
770#    Npix = nx*ny
771#    if 272 in IFD:
772#        ifd = IFD[272]
773#        File.seek(ifd[2][0])
774#        S = File.read(ifd[1])
775#        if 'PILATUS' in S:
776#            tifType = 'Pilatus'
777#            dataType = 0
778#            pixy = (172,172)
779#            File.seek(4096)
780#            if not imageOnly:
781#                print 'Read Pilatus tiff file: ',filename
782#            image = ar.array('L',File.read(4*Npix))
783#            image = np.array(np.asarray(image),dtype=np.int32)
784#    elif 262 in IFD and IFD[262][2][0] > 4:
785#        tifType = 'DND'
786#        pixy = (158,158)
787#        File.seek(512)
788#        if not imageOnly:
789#            print 'Read DND SAX/WAX-detector tiff file: ',filename
790#        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
791#    elif sizexy == [1536,1536]:
792#        tifType = 'APS Gold'
793#        pixy = (150,150)
794#        File.seek(64)
795#        if not imageOnly:
796#            print 'Read Gold tiff file:',filename
797#        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
798#    elif sizexy == [2048,2048] or sizexy == [1024,1024] or sizexy == [3072,3072]:
799#        if IFD[273][2][0] == 8:
800#            if IFD[258][2][0] == 32:
801#                tifType = 'PE'
802#                pixy = (200,200)
803#                File.seek(8)
804#                if not imageOnly:
805#                    print 'Read APS PE-detector tiff file: ',filename
806#                if dataType == 5:
807#                    image = np.array(ar.array('f',File.read(4*Npix)),dtype=np.float32)
808#                else:
809#                    image = np.array(ar.array('I',File.read(4*Npix)),dtype=np.int32)
810#        elif IFD[273][2][0] == 4096:
811#            if sizexy[0] == 3072:
812#                pixy =  (73,73)
813#                tifType = 'MAR225'           
814#            else:
815#                pixy = (158,158)
816#                tifType = 'MAR325'           
817#            File.seek(4096)
818#            if not imageOnly:
819#                print 'Read MAR CCD tiff file: ',filename
820#            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
821#        elif IFD[273][2][0] == 512:
822#            tiftype = '11-ID-C'
823#            pixy = [200,200]
824#            File.seek(512)
825#            if not imageOnly:
826#                print 'Read 11-ID-C tiff file: ',filename
827#            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)           
828#    elif sizexy == [4096,4096]:
829#        if IFD[273][2][0] == 8:
830#            if IFD[258][2][0] == 16:
831#                tifType = 'scanCCD'
832#                pixy = (9,9)
833#                File.seek(8)
834#                if not imageOnly:
835#                    print 'Read APS scanCCD tiff file: ',filename
836#                image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
837#        elif IFD[273][2][0] == 4096:
838#            tifType = 'Rayonix'
839#            pixy = (73.242,73.242)
840#            File.seek(4096)
841#            if not imageOnly:
842#                print 'Read Rayonix MX300HE tiff file: ',filename
843#            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
844##    elif sizexy == [960,960]:
845##        tiftype = 'PE-BE'
846##        pixy = (200,200)
847##        File.seek(8)
848##        if not imageOnly:
849##            print 'Read Gold tiff file:',filename
850##        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
851#           
852#    else:
853#        lines = ['not a known detector tiff file',]
854#        return lines,0,0,0
855#       
856#    image = np.reshape(image,(sizexy[1],sizexy[0]))
857#    center = [pixy[0]*sizexy[0]/2000,pixy[1]*sizexy[1]/2000]
858#    data = {'pixelSize':pixy,'wavelength':0.10,'distance':100.0,'center':center,'size':sizexy}
859#    File.close()   
860#    if imageOnly:
861#        return image
862#    else:
863#        return head,data,Npix,image
864#   
865def ProjFileOpen(G2frame):
866    'Read a GSAS-II project file and load into the G2 data tree'
867    file = open(G2frame.GSASprojectfile,'rb')
868    print 'load from file: ',G2frame.GSASprojectfile
869    G2frame.SetTitle("GSAS-II data tree: "+
870                     os.path.split(G2frame.GSASprojectfile)[1])
871    wx.BeginBusyCursor()
872    try:
873        while True:
874            try:
875                data = cPickle.load(file)
876            except EOFError:
877                break
878            datum = data[0]
879           
880            Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=datum[0])
881            if 'PWDR' in datum[0]:               
882                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
883                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
884                G2frame.PatternTree.SetItemPyData(Id,datum[1][:3])  #temp. trim off junk (patch?)
885            elif datum[0].startswith('HKLF'): 
886                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
887                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
888                G2frame.PatternTree.SetItemPyData(Id,datum[1])
889            else:
890                G2frame.PatternTree.SetItemPyData(Id,datum[1])
891            for datus in data[1:]:
892                sub = G2frame.PatternTree.AppendItem(Id,datus[0])
893#patch
894                if datus[0] == 'Instrument Parameters' and len(datus[1]) == 1:
895                    if 'PWDR' in datum[0]:
896                        datus[1] = [dict(zip(datus[1][3],zip(datus[1][0],datus[1][1],datus[1][2]))),{}]
897                    else:
898                        datus[1] = [dict(zip(datus[1][2],zip(datus[1][0],datus[1][1]))),{}]
899                    for item in datus[1][0]:               #zip makes tuples - now make lists!
900                        datus[1][0][item] = list(datus[1][0][item])
901#end patch
902                G2frame.PatternTree.SetItemPyData(sub,datus[1])
903            if 'IMG' in datum[0]:                   #retrieve image default flag & data if set
904                Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
905                if Data['setDefault']:
906                    G2frame.imageDefault = Data               
907        file.close()
908        print('project load successful')
909        G2frame.NewPlot = True
910    except:
911        msg = wx.MessageDialog(G2frame,message="Error reading file "+
912            str(G2frame.GSASprojectfile)+". This is not a GSAS-II .gpx file",
913            caption="Load Error",style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
914        msg.ShowModal()
915    finally:
916        wx.EndBusyCursor()
917   
918def ProjFileSave(G2frame):
919    'Save a GSAS-II project file'
920    if not G2frame.PatternTree.IsEmpty():
921        file = open(G2frame.GSASprojectfile,'wb')
922        print 'save to file: ',G2frame.GSASprojectfile
923        wx.BeginBusyCursor()
924        try:
925            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
926            while item:
927                data = []
928                name = G2frame.PatternTree.GetItemText(item)
929                data.append([name,G2frame.PatternTree.GetItemPyData(item)])
930                item2, cookie2 = G2frame.PatternTree.GetFirstChild(item)
931                while item2:
932                    name = G2frame.PatternTree.GetItemText(item2)
933                    data.append([name,G2frame.PatternTree.GetItemPyData(item2)])
934                    item2, cookie2 = G2frame.PatternTree.GetNextChild(item, cookie2)                           
935                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)                           
936                cPickle.dump(data,file,1)
937            file.close()
938        finally:
939            wx.EndBusyCursor()
940        print('project save successful')
941
942def SaveIntegration(G2frame,PickId,data):
943    'Save image integration results as powder pattern(s)'
944    azms = G2frame.Integrate[1]
945    X = G2frame.Integrate[2][:-1]
946    N = len(X)
947    Id = G2frame.PatternTree.GetItemParent(PickId)
948    name = G2frame.PatternTree.GetItemText(Id)
949    name = name.replace('IMG ',data['type']+' ')
950    Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Comments'))
951    if 'PWDR' in name:
952        names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
953        codes = [0 for i in range(11)]
954    elif 'SASD' in name:
955        names = ['Type','Lam','Zero','Azimuth'] 
956        codes = [0 for i in range(4)]
957        X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
958    Xminmax = [X[0],X[-1]]
959    LRazm = data['LRazimuth']
960    Azms = []
961    if data['fullIntegrate'] and data['outAzimuths'] == 1:
962        Azms = [45.0,]                              #a poor man's average?
963    else:
964        for i,azm in enumerate(azms[:-1]):
965            Azms.append((azms[i+1]+azm)/2.)
966    for i,azm in enumerate(azms[:-1]):
967        name += " Azm= %.2f"%(Azms[i])
968        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
969        nOcc = 0
970        while item:
971            Name = G2frame.PatternTree.GetItemText(item)
972            if name in Name:
973                nOcc += 1
974            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
975        if nOcc:
976            name += '(%d)'%(nOcc)
977        Sample = G2pdG.SetDefaultSample()
978        Sample['Gonio. radius'] = data['distance']
979        Sample['Omega'] = data['GonioAngles'][0]
980        Sample['Chi'] = data['GonioAngles'][1]
981        Sample['Phi'] = data['GonioAngles'][2]
982        if 'PWDR' in name:
983            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!
984        elif 'SASD' in name:
985            Sample['Trans'] = data['SampleAbs'][0]
986            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
987        Y = G2frame.Integrate[0][i]
988        W = 1./Y                    #probably not true
989        Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=name)
990        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
991        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
992        if 'PWDR' in name:
993            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
994                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
995        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
996        for item in inst[0]:
997            inst[0][item] = list(inst[0][item])
998        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
999        if 'PWDR' in name:
1000            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1001            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Peak List'),[])
1002            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Index Peak List'),[])
1003            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Unit Cells List'),[])
1004            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Reflection Lists'),{})
1005        elif 'SASD' in name:             
1006            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1007            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1008            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1009        valuesdict = {
1010            'wtFactor':1.0,
1011            'Dummy':False,
1012            'ranId':ran.randint(0,sys.maxint),
1013            }
1014        G2frame.PatternTree.SetItemPyData(
1015            Id,[valuesdict,
1016                [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
1017    G2frame.PatternTree.SelectItem(Id)
1018    G2frame.PatternTree.Expand(Id)
1019    G2frame.PatternId = Id
1020           
1021# def powderFxyeSave(G2frame,exports,powderfile):
1022#     'Save a powder histogram as a GSAS FXYE file'
1023#     head,tail = ospath.split(powderfile)
1024#     name,ext = tail.split('.')
1025#     for i,export in enumerate(exports):
1026#         filename = ospath.join(head,name+'-%03d.'%(i)+ext)
1027#         prmname = filename.strip(ext)+'prm'
1028#         prm = open(prmname,'w')      #old style GSAS parm file
1029#         PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1030#         Inst = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame, \
1031#             PickId, 'Instrument Parameters'))[0]
1032#         prm.write( '            123456789012345678901234567890123456789012345678901234567890        '+'\n')
1033#         prm.write( 'INS   BANK      1                                                               '+'\n')
1034#         prm.write(('INS   HTYPE   %sR                                                              '+'\n')%(Inst['Type'][0]))
1035#         if 'Lam1' in Inst:              #Ka1 & Ka2
1036#             prm.write(('INS  1 ICONS%10.7f%10.7f    0.0000               0.990    0     0.500   '+'\n')%(Inst['Lam1'][0],Inst['Lam2'][0]))
1037#         elif 'Lam' in Inst:             #single wavelength
1038#             prm.write(('INS  1 ICONS%10.7f%10.7f    0.0000               0.990    0     0.500   '+'\n')%(Inst['Lam'][1],0.0))
1039#         prm.write( 'INS  1 IRAD     0                                                               '+'\n')
1040#         prm.write( 'INS  1I HEAD                                                                    '+'\n')
1041#         prm.write( 'INS  1I ITYP    0    0.0000  180.0000         1                                 '+'\n')
1042#         prm.write(('INS  1DETAZM%10.3f                                                          '+'\n')%(Inst['Azimuth'][0]))
1043#         prm.write( 'INS  1PRCF1     3    8   0.00100                                                '+'\n')
1044#         prm.write(('INS  1PRCF11     %15.6g%15.6g%15.6g%15.6g   '+'\n')%(Inst['U'][1],Inst['V'][1],Inst['W'][1],0.0))
1045#         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.))
1046#         prm.close()
1047#         file = open(filename,'w')
1048#         print 'save powder pattern to file: ',filename
1049#         x,y,w,yc,yb,yd = G2frame.PatternTree.GetItemPyData(PickId)[1]
1050#         file.write(powderfile+'\n')
1051#         file.write('Instrument parameter file:'+ospath.split(prmname)[1]+'\n')
1052#         file.write('BANK 1 %d %d CONS %.2f %.2f 0 0 FXYE\n'%(len(x),len(x),\
1053#             100.*x[0],100.*(x[1]-x[0])))
1054#         s = list(np.sqrt(1./np.array(w)))       
1055#         XYW = zip(x,y,s)
1056#         for X,Y,S in XYW:
1057#             file.write("%15.6g %15.6g %15.6g\n" % (100.*X,Y,max(S,1.0)))
1058#         file.close()
1059#         print 'powder pattern file '+filename+' written'
1060       
1061# def powderXyeSave(G2frame,exports,powderfile):
1062#     'Save a powder histogram as a Topas XYE file'
1063#     head,tail = ospath.split(powderfile)
1064#     name,ext = tail.split('.')
1065#     for i,export in enumerate(exports):
1066#         filename = ospath.join(head,name+'-%03d.'%(i)+ext)
1067#         PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1068#         file = open(filename,'w')
1069#         file.write('#%s\n'%(export))
1070#         print 'save powder pattern to file: ',filename
1071#         x,y,w,yc,yb,yd = G2frame.PatternTree.GetItemPyData(PickId)[1]
1072#         s = list(np.sqrt(1./np.array(w)))       
1073#         XYW = zip(x,y,s)
1074#         for X,Y,W in XYW:
1075#             file.write("%15.6g %15.6g %15.6g\n" % (X,Y,W))
1076#         file.close()
1077#         print 'powder pattern file '+filename+' written'
1078       
1079def PDFSave(G2frame,exports):
1080    'Save a PDF G(r) and S(Q) in column formats'
1081    for export in exports:
1082        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1083        SQname = 'S(Q)'+export[4:]
1084        GRname = 'G(R)'+export[4:]
1085        sqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.sq')
1086        grfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.gr')
1087        sqId = G2gd.GetPatternTreeItemId(G2frame, PickId, SQname)
1088        grId = G2gd.GetPatternTreeItemId(G2frame, PickId, GRname)
1089        sqdata = np.array(G2frame.PatternTree.GetItemPyData(sqId)[1][:2]).T
1090        grdata = np.array(G2frame.PatternTree.GetItemPyData(grId)[1][:2]).T
1091        sqfile = open(sqfilename,'w')
1092        grfile = open(grfilename,'w')
1093        sqfile.write('#T S(Q) %s\n'%(export))
1094        grfile.write('#T G(R) %s\n'%(export))
1095        sqfile.write('#L Q     S(Q)\n')
1096        grfile.write('#L R     G(R)\n')
1097        for q,sq in sqdata:
1098            sqfile.write("%15.6g %15.6g\n" % (q,sq))
1099        sqfile.close()
1100        for r,gr in grdata:
1101            grfile.write("%15.6g %15.6g\n" % (r,gr))
1102        grfile.close()
1103   
1104def PeakListSave(G2frame,file,peaks):
1105    'Save powder peaks to a data file'
1106    print 'save peak list to file: ',G2frame.peaklistfile
1107    if not peaks:
1108        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1109        try:
1110            result = dlg.ShowModal()
1111        finally:
1112            dlg.Destroy()
1113        return
1114    for peak in peaks:
1115        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
1116            (peak[0],peak[2],peak[4],peak[6]))
1117    print 'peak list saved'
1118             
1119def IndexPeakListSave(G2frame,peaks):
1120    'Save powder peaks from the indexing list'
1121    file = open(G2frame.peaklistfile,'wa')
1122    print 'save index peak list to file: ',G2frame.peaklistfile
1123    wx.BeginBusyCursor()
1124    try:
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("%12.6f\n" % (peak[7]))
1134        file.close()
1135    finally:
1136        wx.EndBusyCursor()
1137    print 'index peak list saved'
1138   
1139def SetNewPhase(Name='New Phase',SGData=None,cell=None):
1140    '''Create a new phase dict with default values for various parameters
1141
1142    :param str Name: Name for new Phase
1143
1144    :param dict SGData: space group data from :func:`GSASIIspc:SpcGroup`;
1145      defaults to data for P 1
1146
1147    :param list cell: unit cell parameter list; defaults to
1148      [1.0,1.0,1.0,90.,90,90.,1.]
1149
1150    '''
1151    if SGData is None: SGData = G2spc.SpcGroup('P 1')[1]
1152    if cell is None: cell=[1.0,1.0,1.0,90.,90,90.,1.]
1153    phaseData = {
1154        'ranId':ran.randint(0,sys.maxint),
1155        'General':{
1156            'Name':Name,
1157            'Type':'nuclear',
1158            'AtomPtrs':[3,1,7,9],
1159            'SGData':SGData,
1160            'Cell':[False,]+cell,
1161            'Pawley dmin':1.0,
1162            'Data plot type':'None',
1163            'SH Texture':{
1164                'Order':0,
1165                'Model':'cylindrical',
1166                'Sample omega':[False,0.0],
1167                'Sample chi':[False,0.0],
1168                'Sample phi':[False,0.0],
1169                'SH Coeff':[False,{}],
1170                'SHShow':False,
1171                'PFhkl':[0,0,1],
1172                'PFxyz':[0,0,1],
1173                'PlotType':'Pole figure'}},
1174        'Atoms':[],
1175        'Drawing':{},
1176        'Histograms':{},
1177        'Pawley ref':[],
1178        'RBModels':{},
1179        }
1180    return phaseData
1181       
1182class MultipleChoicesDialog(wx.Dialog):
1183    '''A dialog that offers a series of choices, each with a
1184    title and a wx.Choice widget. Intended to be used Modally.
1185    typical input:
1186
1187        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1188        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1189       
1190    selections are placed in self.chosen when OK is pressed
1191    '''
1192    def __init__(self,choicelist,headinglist,
1193                 head='Select options',
1194                 title='Please select from options below',
1195                 parent=None):
1196        self.chosen = []
1197        wx.Dialog.__init__(
1198            self,parent,wx.ID_ANY,head, 
1199            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1200        panel = wx.Panel(self)
1201        mainSizer = wx.BoxSizer(wx.VERTICAL)
1202        mainSizer.Add((10,10),1)
1203        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1204        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1205        self.ChItems = []
1206        for choice,lbl in zip(choicelist,headinglist):
1207            mainSizer.Add((10,10),1)
1208            self.chosen.append(0)
1209            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1210            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1211            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1212            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1213
1214        OkBtn = wx.Button(panel,-1,"Ok")
1215        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1216        cancelBtn = wx.Button(panel,-1,"Cancel")
1217        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1218        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1219        btnSizer.Add((20,20),1)
1220        btnSizer.Add(OkBtn)
1221        btnSizer.Add((20,20),1)
1222        btnSizer.Add(cancelBtn)
1223        btnSizer.Add((20,20),1)
1224        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1225        panel.SetSizer(mainSizer)
1226        panel.Fit()
1227        self.Fit()
1228       
1229    def OnOk(self,event):
1230        parent = self.GetParent()
1231        if parent is not None: parent.Raise()
1232        # save the results from the choice widgets
1233        self.chosen = []
1234        for w in self.ChItems:
1235            self.chosen.append(w.GetSelection())
1236        self.EndModal(wx.ID_OK)             
1237           
1238    def OnCancel(self,event):
1239        parent = self.GetParent()
1240        if parent is not None: parent.Raise()
1241        self.chosen = []
1242        self.EndModal(wx.ID_CANCEL)             
1243           
1244def ExtractFileFromZip(filename, selection=None, confirmread=True,
1245                       confirmoverwrite=True, parent=None,
1246                       multipleselect=False):
1247    '''If the filename is a zip file, extract a file from that
1248    archive.
1249
1250    :param list Selection: used to predefine the name of the file
1251      to be extracted. Filename case and zip directory name are
1252      ignored in selection; the first matching file is used.
1253
1254    :param bool confirmread: if True asks the user to confirm before expanding
1255      the only file in a zip
1256
1257    :param bool confirmoverwrite: if True asks the user to confirm
1258      before overwriting if the extracted file already exists
1259
1260    :param bool multipleselect: if True allows more than one zip
1261      file to be extracted, a list of file(s) is returned.
1262      If only one file is present, do not ask which one, otherwise
1263      offer a list of choices (unless selection is used).
1264   
1265    :returns: the name of the file that has been created or a
1266      list of files (see multipleselect)
1267
1268    If the file is not a zipfile, return the name of the input file.
1269    If the zipfile is empty or no file has been selected, return None
1270    '''
1271    import zipfile # do this now, since we can save startup time by doing this only on need
1272    import shutil
1273    zloc = os.path.split(filename)[0]
1274    if not zipfile.is_zipfile(filename):
1275        #print("not zip")
1276        return filename
1277
1278    z = zipfile.ZipFile(filename,'r')
1279    zinfo = z.infolist()
1280
1281    if len(zinfo) == 0:
1282        #print('Zip has no files!')
1283        zlist = [-1]
1284    if selection:
1285        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1286        if selection.lower() in choices:
1287            zlist = [choices.index(selection.lower())]
1288        else:
1289            print('debug: file '+str(selection)+' was not found in '+str(filename))
1290            zlist = [-1]
1291    elif len(zinfo) == 1 and confirmread:
1292        result = wx.ID_NO
1293        dlg = wx.MessageDialog(
1294            parent,
1295            'Is file '+str(zinfo[0].filename)+
1296            ' what you want to extract from '+
1297            str(os.path.split(filename)[1])+'?',
1298            'Confirm file', 
1299            wx.YES_NO | wx.ICON_QUESTION)
1300        try:
1301            result = dlg.ShowModal()
1302        finally:
1303            dlg.Destroy()
1304        if result == wx.ID_NO:
1305            zlist = [-1]
1306        else:
1307            zlist = [0]
1308    elif len(zinfo) == 1:
1309        zlist = [0]
1310    elif multipleselect:
1311        # select one or more from a from list
1312        choices = [i.filename for i in zinfo]
1313        dlg = G2gd.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1314            'Choose file(s)',choices)
1315        if dlg.ShowModal() == wx.ID_OK:
1316            zlist = dlg.GetSelections()
1317        else:
1318            zlist = []
1319        dlg.Destroy()
1320    else:
1321        # select one from a from list
1322        choices = [i.filename for i in zinfo]
1323        dlg = wx.SingleChoiceDialog(parent,
1324            'Select file to extract from zip file'+str(filename),'Choose file',
1325            choices,)
1326        if dlg.ShowModal() == wx.ID_OK:
1327            zlist = [dlg.GetSelection()]
1328        else:
1329            zlist = [-1]
1330        dlg.Destroy()
1331       
1332    outlist = []
1333    for zindex in zlist:
1334        if zindex >= 0:
1335            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1336            if os.path.exists(efil) and confirmoverwrite:
1337                result = wx.ID_NO
1338                dlg = wx.MessageDialog(parent,
1339                    'File '+str(efil)+' already exists. OK to overwrite it?',
1340                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1341                try:
1342                    result = dlg.ShowModal()
1343                finally:
1344                    dlg.Destroy()
1345                if result == wx.ID_NO:
1346                    zindex = -1
1347        if zindex >= 0:
1348            # extract the file to the current directory, regardless of it's original path
1349            #z.extract(zinfo[zindex],zloc)
1350            eloc,efil = os.path.split(zinfo[zindex].filename)
1351            outfile = os.path.join(zloc, efil)
1352            fpin = z.open(zinfo[zindex])
1353            fpout = file(outfile, "wb")
1354            shutil.copyfileobj(fpin, fpout)
1355            fpin.close()
1356            fpout.close()
1357            outlist.append(outfile)
1358    z.close()
1359    if multipleselect and len(outlist) >= 1:
1360        return outlist
1361    elif len(outlist) == 1:
1362        return outlist[0]
1363    else:
1364        return None
1365
1366######################################################################
1367# base classes for reading various types of data files
1368#   not used directly, only by subclassing
1369######################################################################
1370E,SGData = G2spc.SpcGroup('P 1') # data structure for default space group
1371P1SGData = SGData
1372class ImportBaseclass(object):
1373    '''Defines a base class for the reading of input files (diffraction
1374    data, coordinates,...). See :ref:`Writing a Import Routine<Import_routines>`
1375    for an explanation on how to use a subclass of this class.
1376    '''
1377    class ImportException(Exception):
1378        '''Defines an Exception that is used when an import routine hits an expected error,
1379        usually in .Reader.
1380
1381        Good practice is that the Reader should define a value in self.errors that
1382        tells the user some information about what is wrong with their file.         
1383        '''
1384        pass
1385
1386    def __init__(self,
1387                 formatName,
1388                 longFormatName=None,
1389                 extensionlist=[],
1390                 strictExtension=False,
1391                 ):
1392        self.formatName = formatName # short string naming file type
1393        if longFormatName: # longer string naming file type
1394            self.longFormatName = longFormatName
1395        else:
1396            self.longFormatName = formatName
1397        # define extensions that are allowed for the file type
1398        # for windows, remove any extensions that are duplicate, as case is ignored
1399        if sys.platform == 'windows' and extensionlist:
1400            extensionlist = list(set([s.lower() for s in extensionlist]))
1401        self.extensionlist = extensionlist
1402        # If strictExtension is True, the file will not be read, unless
1403        # the extension matches one in the extensionlist
1404        self.strictExtension = strictExtension
1405        self.errors = ''
1406        self.warnings = ''
1407        # used for readers that will use multiple passes to read
1408        # more than one data block
1409        self.repeat = False
1410        self.repeatcount = 0
1411        self.readfilename = '?'
1412        #print 'created',self.__class__
1413
1414    def ReInitialize(self):
1415        'Reinitialize the Reader to initial settings'
1416        self.errors = ''
1417        self.warnings = ''
1418        self.repeat = False
1419        self.repeatcount = 0
1420        self.readfilename = '?'
1421
1422    def BlockSelector(self, ChoiceList, ParentFrame=None,
1423                      title='Select a block',
1424                      size=None, header='Block Selector',
1425                      useCancel=True):
1426        ''' Provide a wx dialog to select a block if the file contains more
1427        than one set of data and one must be selected
1428        '''
1429        if useCancel:
1430            dlg = wx.SingleChoiceDialog(
1431                ParentFrame,title, header,ChoiceList)
1432        else:
1433            dlg = wx.SingleChoiceDialog(
1434                ParentFrame,title, header,ChoiceList,
1435                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1436        if size: dlg.SetSize(size)
1437        dlg.CenterOnParent()
1438        if dlg.ShowModal() == wx.ID_OK:
1439            sel = dlg.GetSelection()
1440            return sel
1441        else:
1442            return None
1443        dlg.Destroy()
1444
1445    def MultipleBlockSelector(self, ChoiceList, ParentFrame=None,
1446        title='Select a block',size=None, header='Block Selector'):
1447        '''Provide a wx dialog to select a block of data if the
1448        file contains more than one set of data and one must be
1449        selected.
1450
1451        :returns: a list of the selected blocks
1452        '''
1453        dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1454            wx.CHOICEDLG_STYLE)
1455        dlg.CenterOnParent()
1456        if size: dlg.SetSize(size)
1457        if dlg.ShowModal() == wx.ID_OK:
1458            sel = dlg.GetSelections()
1459        else:
1460            return []
1461        dlg.Destroy()
1462        selected = []
1463        if len(ChoiceList) in sel:
1464            return range(len(ChoiceList))
1465        else:
1466            return sel
1467        return selected
1468
1469    def MultipleChoicesDialog(self, choicelist, headinglist, ParentFrame=None, **kwargs):
1470        '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1471        widget. Typical input:
1472       
1473           * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1474           
1475           * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1476           
1477        optional keyword parameters are: head (window title) and title
1478        returns a list of selected indicies for each choice (or None)
1479        '''
1480        result = None
1481        dlg = MultipleChoicesDialog(choicelist,headinglist,
1482            parent=ParentFrame, **kwargs)         
1483        dlg.CenterOnParent()
1484        if dlg.ShowModal() == wx.ID_OK:
1485            result = dlg.chosen
1486        dlg.Destroy()
1487        return result
1488
1489    def ShowBusy(self):
1490        wx.BeginBusyCursor()
1491        wx.Yield() # make it happen now!
1492
1493    def DoneBusy(self):
1494        wx.EndBusyCursor()
1495        wx.Yield() # make it happen now!
1496       
1497#    def Reader(self, filename, filepointer, ParentFrame=None, **unused):
1498#        '''This method must be supplied in the child class to read the file.
1499#        if the read fails either return False or raise an Exception
1500#        preferably of type ImportException.
1501#        '''
1502#        #start reading
1503#        raise ImportException("Error occurred while...")
1504#        self.errors += "Hint for user on why the error occur
1505#        return False # if an error occurs
1506#        return True # if read OK
1507
1508    def ExtensionValidator(self, filename):
1509        '''This methods checks if the file has the correct extension
1510        Return False if this filename will not be supported by this reader
1511        Return True if the extension matches the list supplied by the reader
1512        Return None if the reader allows un-registered extensions
1513        '''
1514        if filename:
1515            ext = os.path.splitext(filename)[1]
1516            if sys.platform == 'windows': ext = ext.lower()
1517            if ext in self.extensionlist: return True
1518            if self.strictExtension: return False
1519        return None
1520
1521    def ContentsValidator(self, filepointer):
1522        '''This routine will attempt to determine if the file can be read
1523        with the current format.
1524        This will typically be overridden with a method that
1525        takes a quick scan of [some of]
1526        the file contents to do a "sanity" check if the file
1527        appears to match the selected format.
1528        Expected to be called via self.Validator()
1529        '''
1530        #filepointer.seek(0) # rewind the file pointer
1531        return True
1532
1533    def CIFValidator(self, filepointer):
1534        '''A :meth:`ContentsValidator` for use to validate CIF files.
1535        '''
1536        for i,l in enumerate(filepointer):
1537            if i >= 1000: return True
1538            '''Encountered only blank lines or comments in first 1000
1539            lines. This is unlikely, but assume it is CIF anyway, since we are
1540            even less likely to find a file with nothing but hashes and
1541            blank lines'''
1542            line = l.strip()
1543            if len(line) == 0: # ignore blank lines
1544                continue 
1545            elif line.startswith('#'): # ignore comments
1546                continue 
1547            elif line.startswith('data_'): # on the right track, accept this file
1548                return True
1549            else: # found something invalid
1550                self.errors = 'line '+str(i+1)+' contains unexpected data:\n'
1551                self.errors += '  '+str(l)
1552                self.errors += '  Note: a CIF should only have blank lines or comments before'
1553                self.errors += '        a data_ statement begins a block.'
1554                return False 
1555
1556class ImportPhase(ImportBaseclass):
1557    '''Defines a base class for the reading of files with coordinates
1558
1559    Objects constructed that subclass this (in import/G2phase_*.py etc.) will be used
1560    in :meth:`GSASII.GSASII.OnImportPhase`.
1561    See :ref:`Writing a Import Routine<Import_Routines>`
1562    for an explanation on how to use this class.
1563
1564    '''
1565    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1566        strictExtension=False,):
1567        # call parent __init__
1568        ImportBaseclass.__init__(self,formatName,longFormatName,
1569            extensionlist,strictExtension)
1570        self.Phase = None # a phase must be created with G2IO.SetNewPhase in the Reader
1571        self.Constraints = None
1572
1573    def PhaseSelector(self, ChoiceList, ParentFrame=None,
1574        title='Select a phase', size=None,header='Phase Selector'):
1575        ''' Provide a wx dialog to select a phase if the file contains more
1576        than one phase
1577        '''
1578        return self.BlockSelector(ChoiceList,ParentFrame,title,
1579            size,header)
1580
1581class ImportStructFactor(ImportBaseclass):
1582    '''Defines a base class for the reading of files with tables
1583    of structure factors.
1584
1585    Structure factors are read with a call to :meth:`GSASII.GSASII.OnImportSfact`
1586    which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`, which calls
1587    methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1588    :meth:`Reader`.
1589
1590    See :ref:`Writing a Import Routine<Import_Routines>`
1591    for an explanation on how to use import classes in general. The specifics
1592    for reading a structure factor histogram require that
1593    the ``Reader()`` routine in the import
1594    class need to do only a few things: It
1595    should load :attr:`RefDict` item ``'RefList'`` with the reflection list,
1596    and set :attr:`Parameters` with the instrument parameters
1597    (initialized with :meth:`InitParameters` and set with :meth:`UpdateParameters`).
1598    Also, set :attr:`Controls`,
1599    which specifies how the histogram is plotted
1600    (initialized with :meth:`InitControls` and set with :meth:`UpdateControls`).
1601    '''
1602    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1603        strictExtension=False,):
1604        ImportBaseclass.__init__(self,formatName,longFormatName,
1605            extensionlist,strictExtension)
1606
1607        # define contents of Structure Factor entry
1608        self.Parameters = []
1609        'self.Parameters is a list with two dicts for data parameter settings'
1610        self.InitParameters()
1611        self.Controls = {}
1612        'self.Controls is a dict with plotting controls'
1613        self.InitControls() # initialize the above
1614        self.RefDict = {'RefList':[],'FF':[]}
1615        '''self.RefDict is a dict containing the reflection information, as read from the file.
1616        Item 'RefList' contains the reflection information. See the
1617        :ref:`Single Crystal Reflection Data Structure<XtalRefl_table>`
1618        for the contents of each row. Dict element 'FF'
1619        contains the form factor values for each element type; if this entry
1620        is left as initialized (an empty list) it will be initialized as needed later.
1621        '''
1622    def ReInitialize(self):
1623        'Reinitialize the Reader to initial settings'
1624        ImportBaseclass.ReInitialize(self)
1625        self.InitParameters()
1626        self.InitControls()
1627        self.RefDict = {'RefList':[],'FF':[]}
1628
1629       
1630    def InitControls(self):
1631        'initialize the controls structure'
1632        self.Controls = { # dictionary with plotting controls
1633            'Type' : 'Fosq',
1634            'ifFc' : False,    #
1635            'HKLmax' : [None,None,None],
1636            'HKLmin' : [None,None,None],
1637            'FoMax' : None,   # maximum observed structure factor as Fo
1638            'Zone' : '001',
1639            'Layer' : 0,
1640            'Scale' : 1.0,
1641            'log-lin' : 'lin',
1642            }
1643
1644    def InitParameters(self):
1645        'initialize the instrument parameters structure'
1646        Lambda = 0.70926
1647        HistType = 'SXC'
1648        self.Parameters = [{'Type':[HistType,HistType], # create the structure
1649                            'Lam':[Lambda,Lambda]
1650                            }, {}]
1651        'Parameters is a list with two dicts for data parameter settings'
1652
1653    def UpdateParameters(self,Type=None,Wave=None):
1654        'Revise the instrument parameters'
1655        if Type is not None:
1656            self.Parameters[0]['Type'] = [Type,Type]
1657        if Wave is not None:
1658            self.Parameters[0]['Lam'] = [Wave,Wave]
1659           
1660    def UpdateControls(self,Type='Fosq',FcalcPresent=False):
1661        '''Scan through the reflections to update the Controls dictionary
1662        '''
1663        self.Controls['Type'] = Type
1664        self.Controls['ifFc'] = FcalcPresent
1665        HKLmax = [None,None,None]
1666        HKLmin = [None,None,None]
1667        Fo2max = None
1668        for refl in self.RefDict['RefList']:
1669            HKL = refl[:3]
1670            if Fo2max is None:
1671                Fo2max = refl[8]
1672            else:
1673                Fo2max = max(Fo2max,refl[8])
1674            for i,hkl in enumerate(HKL):
1675                if HKLmax[i] is None:
1676                    HKLmax[i] = hkl
1677                    HKLmin[i] = hkl
1678                else:
1679                    HKLmax[i] = max(HKLmax[i],hkl)
1680                    HKLmin[i] = min(HKLmin[i],hkl)
1681        self.Controls['HKLmax'] = HKLmax
1682        self.Controls['HKLmin'] = HKLmin
1683        if Type ==  'Fosq':
1684            self.Controls['FoMax'] = np.sqrt(Fo2max)
1685        elif Type ==  'Fo':
1686            self.Controls['FoMax'] = Fo2max
1687        else:
1688            print "Unsupported Struct Fact type in ImportStructFactor.UpdateControls"
1689            raise Exception,"Unsupported Struct Fact type in ImportStructFactor.UpdateControls"
1690
1691######################################################################
1692class ImportPowderData(ImportBaseclass):
1693    '''Defines a base class for the reading of files with powder data.
1694
1695    Objects constructed that subclass this (in import/G2pwd_*.py etc.) will be used
1696    in :meth:`GSASII.GSASII.OnImportPowder`.
1697    See :ref:`Writing a Import Routine<Import_Routines>`
1698    for an explanation on how to use this class.
1699    '''
1700    # define some default instrument parameter files
1701    # just like GSAS, sigh
1702    defaultIparm_lbl = []
1703    defaultIparms = []
1704    defaultIparm_lbl.append('CuKa lab data')
1705    defaultIparms.append({
1706        'INS   HTYPE ':'PXC ',
1707        'INS  1 ICONS':'  1.540500  1.544300       0.0         0       0.7    0       0.5   ',
1708        'INS  1PRCF1 ':'    3    8      0.01                                                ',
1709        'INS  1PRCF11':'   2.000000E+00  -2.000000E+00   5.000000E+00   0.000000E+00        ',
1710        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.150000E-01   0.150000E-01        ',
1711        })
1712    defaultIparm_lbl.append('0.6A synch')
1713    defaultIparms.append({
1714        'INS   HTYPE ':'PXC ',
1715        'INS  1 ICONS':'  0.600000  0.000000       0.0         0      0.99    0       0.5   ',
1716        'INS  1PRCF1 ':'    3    8      0.01                                                ',
1717        'INS  1PRCF11':'   1.000000E+00  -1.000000E+00   0.300000E+00   0.000000E+00        ',
1718        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.100000E-01   0.100000E-01        ',
1719        })
1720    defaultIparm_lbl.append('1.5A CW neutron data')
1721    defaultIparms.append({
1722        'INS   HTYPE ':'PNC',
1723        'INS  1 ICONS':'   1.54020   0.00000   0.04000         0',
1724        'INS  1PRCF1 ':'    3    8     0.005',
1725        'INS  1PRCF11':'   0.239700E+03  -0.298200E+03   0.180800E+03   0.000000E+00',
1726        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.400000E-01   0.300000E-01',
1727        })
1728    defaultIparm_lbl.append('10m TOF backscattering bank')
1729    defaultIparms.append({
1730        'INS   HTYPE ':'PNT',
1731        'INS  1 ICONS':'   5000.00      0.00      0.00',
1732        'INS  1BNKPAR':'    1.0000   150.000',       
1733        'INS  1PRCF1 ':'    1    8   0.01000',
1734        'INS  1PRCF11':'   0.000000E+00   5.000000E+00   3.000000E-02   1.000000E-03',
1735        'INS  1PRCF12':'   0.000000E+00   4.000000E+01   0.000000E+00   0.000000E+00',       
1736        })
1737    defaultIparm_lbl.append('10m TOF 90deg bank')
1738    defaultIparms.append({
1739        'INS   HTYPE ':'PNT',
1740        'INS  1 ICONS':'   3500.00      0.00      0.00',
1741        'INS  1BNKPAR':'    1.0000    90.000',       
1742        'INS  1PRCF1 ':'    1    8   0.01000',
1743        'INS  1PRCF11':'   0.000000E+00   5.000000E+00   3.000000E-02   4.000000E-03',
1744        'INS  1PRCF12':'   0.000000E+00   8.000000E+01   0.000000E+00   0.000000E+00',       
1745        })
1746    defaultIparm_lbl.append('63m POWGEN 90deg bank')
1747    defaultIparms.append({
1748        'INS   HTYPE ':'PNT',
1749        'INS  1 ICONS':'  22585.80      0.00      0.00',
1750        'INS  1BNKPAR':'    1.0000    90.000',       
1751        'INS  1PRCF1 ':'    1    8   0.01000',
1752        'INS  1PRCF11':'   0.000000E+00   1.000000E+00   3.000000E-02   4.000000E-03',
1753        'INS  1PRCF12':'   0.000000E+00   8.000000E+01   0.000000E+00   0.000000E+00',       
1754        })
1755    def __init__(self,
1756                 formatName,
1757                 longFormatName=None,
1758                 extensionlist=[],
1759                 strictExtension=False,
1760                 ):
1761        ImportBaseclass.__init__(self,formatName,
1762                                            longFormatName,
1763                                            extensionlist,
1764                                            strictExtension)
1765        self.clockWd = None  # used in TOF
1766        self.ReInitialize()
1767       
1768    def ReInitialize(self):
1769        'Reinitialize the Reader to initial settings'
1770        ImportBaseclass.ReInitialize(self)
1771        self.powderentry = ['',None,None] #  (filename,Pos,Bank)
1772        self.powderdata = [] # Powder dataset
1773        '''A powder data set is a list with items [x,y,w,yc,yb,yd]:
1774                np.array(x), # x-axis values
1775                np.array(y), # powder pattern intensities
1776                np.array(w), # 1/sig(intensity)^2 values (weights)
1777                np.array(yc), # calc. intensities (zero)
1778                np.array(yb), # calc. background (zero)
1779                np.array(yd), # obs-calc profiles
1780        '''                           
1781        self.comments = []
1782        self.idstring = ''
1783        self.Sample = G2pdG.SetDefaultSample()
1784        self.GSAS = None     # used in TOF
1785        self.repeat_instparm = True # Should a parm file be
1786        #                             used for multiple histograms?
1787        self.instparm = None # name hint from file of instparm to use
1788        self.instfile = '' # full path name to instrument parameter file
1789        self.instbank = '' # inst parm bank number
1790        self.instmsg = ''  # a label that gets printed to show
1791                           # where instrument parameters are from
1792        self.numbanks = 1
1793        self.instdict = {} # place items here that will be transferred to the instrument parameters
1794######################################################################
1795class ImportSmallAngleData(ImportBaseclass):
1796    '''Defines a base class for the reading of files with small angle data.
1797    See :ref:`Writing a Import Routine<Import_Routines>`
1798    for an explanation on how to use this class.
1799    '''
1800    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1801        strictExtension=False,):
1802           
1803        ImportBaseclass.__init__(self,formatName,longFormatName,extensionlist,
1804            strictExtension)
1805        self.ReInitialize()
1806       
1807    def ReInitialize(self):
1808        'Reinitialize the Reader to initial settings'
1809        ImportBaseclass.ReInitialize(self)
1810        self.smallangleentry = ['',None,None] #  (filename,Pos,Bank)
1811        self.smallangledata = [] # SASD dataset
1812        '''A small angle data set is a list with items [x,y,w,yc,yd]:
1813                np.array(x), # x-axis values
1814                np.array(y), # powder pattern intensities
1815                np.array(w), # 1/sig(intensity)^2 values (weights)
1816                np.array(yc), # calc. intensities (zero)
1817                np.array(yd), # obs-calc profiles
1818        '''                           
1819        self.comments = []
1820        self.idstring = ''
1821        self.Sample = G2pdG.SetDefaultSample()
1822        self.GSAS = None     # used in TOF
1823        self.clockWd = None  # used in TOF
1824        self.numbanks = 1
1825        self.instdict = {} # place items here that will be transferred to the instrument parameters
1826######################################################################
1827class ExportBaseclass(object):
1828    '''Defines a base class for the exporting of GSAS-II results.
1829
1830    This class is subclassed in the various exports/G2export_*.py files. Those files
1831    are imported in :meth:`GSASII.GSASII._init_Exports` which defines the
1832    appropriate menu items for each one and the .Exporter method is called
1833    directly from the menu item.
1834   
1835    '''
1836    def __init__(self,
1837                 G2frame,
1838                 formatName,
1839                 extension,
1840                 longFormatName=None,
1841                 ):
1842        self.G2frame = G2frame
1843        self.formatName = formatName # short string naming file type
1844        self.extension = extension
1845        if longFormatName: # longer string naming file type
1846            self.longFormatName = longFormatName
1847        else:
1848            self.longFormatName = formatName
1849        self.OverallParms = {}
1850        self.Phases = {}
1851        self.Histograms = {}
1852        self.powderDict = {}
1853        self.xtalDict = {}
1854        self.parmDict = {}
1855        self.sigDict = {}
1856        # updated in InitExport:
1857        self.currentExportType = None # type of export that has been requested
1858        # updated in ExportSelect (when used):
1859        self.phasenam = None # a list of selected phases
1860        self.histnam = None # a list of selected histograms
1861        self.filename = None # name of file to be written (single export) or template (multiple files)
1862        self.dirname = '' # name of directory where file(s) will be written
1863        self.fullpath = '' # name of file being written -- full path
1864       
1865        # items that should be defined in a subclass of this class
1866        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1867        # The following types are defined: 'project', "phase", "powder", "single"
1868        self.multiple = False # set as True if the class can export multiple phases or histograms
1869        # self.multiple is ignored for "project" exports
1870
1871    def InitExport(self,event):
1872        '''Determines the type of menu that called the Exporter and
1873        misc initialization.
1874        '''
1875        self.filename = None # name of file to be written (single export)
1876        self.dirname = '' # name of file to be written (multiple export)
1877        if event:
1878            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1879
1880    def MakePWDRfilename(self,hist):
1881        '''Make a filename root (no extension) from a PWDR histogram name
1882
1883        :param str hist: the histogram name in data tree (starts with "PWDR ")
1884        '''
1885        file0 = ''
1886        file1 = hist[5:]
1887        # replace repeated blanks
1888        while file1 != file0:
1889            file0 = file1
1890            file1 = file0.replace('  ',' ').strip()
1891        file0 = file1.replace('Azm= ','A')
1892        # if angle has unneeded decimal places on aziumuth, remove them
1893        if file0[-3:] == '.00': file0 = file0[:-3]
1894        file0 = file0.replace('.','_')
1895        file0 = file0.replace(' ','_')
1896        return file0
1897
1898    def ExportSelect(self,AskFile='ask'):
1899        '''Selects histograms or phases when needed. Sets a default file name when
1900        requested in self.filename; always sets a default directory in self.dirname.
1901
1902        :param bool AskFile: Determines how this routine processes getting a
1903          location to store the current export(s).
1904          * if AskFile is 'ask' (default option), get the name of the file to be written;
1905            self.filename and self.dirname are always set. In the case where
1906            multiple files must be generated, the export routine should do this
1907            based on self.filename as a template.
1908          * if AskFile is 'dir', get the name of the directory to be used;
1909            self.filename is not used, but self.dirname is always set. The export routine
1910            will always generate the file name.
1911          * if AskFile is 'single', get only the name of the directory to be used when
1912            multiple items will be written (as multiple files) are used
1913            *or* a complete file name is requested when a single file
1914            name is selected. self.dirname is always set and self.filename used
1915            only when a single file is selected.
1916          * if AskFile is 'default', creates a name of the file to be used from
1917            the name of the project (.gpx) file. If the project has not been saved,
1918            then the name of file is requested.
1919            self.filename and self.dirname are always set. In the case where
1920            multiple file names must be generated, the export routine should do this
1921            based on self.filename.
1922          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1923            file. If the project has not been saved, then a directory is requested.
1924            self.filename is not used.
1925
1926        :returns: True in case of an error
1927        '''
1928       
1929        numselected = 1
1930        if self.currentExportType == 'phase':
1931            if len(self.Phases) == 0:
1932                self.G2frame.ErrorDialog(
1933                    'Empty project',
1934                    'Project does not contain any phases.')
1935                return True
1936            elif len(self.Phases) == 1:
1937                self.phasenam = self.Phases.keys()
1938            elif self.multiple: 
1939                choices = sorted(self.Phases.keys())
1940                phasenum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1941                if phasenum is None: return True
1942                self.phasenam = [choices[i] for i in phasenum]
1943                if not self.phasenam: return True
1944                numselected = len(self.phasenam)
1945            else:
1946                choices = sorted(self.Phases.keys())
1947                phasenum = G2gd.ItemSelector(choices,self.G2frame)
1948                if phasenum is None: return True
1949                self.phasenam = [choices[phasenum]]
1950                numselected = len(self.phasenam)
1951        elif self.currentExportType == 'single':
1952            if len(self.xtalDict) == 0:
1953                self.G2frame.ErrorDialog(
1954                    'Empty project',
1955                    'Project does not contain any single crystal data.')
1956                return True
1957            elif len(self.xtalDict) == 1:
1958                self.histnam = self.xtalDict.values()
1959            elif self.multiple:
1960                choices = sorted(self.xtalDict.values())
1961                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1962                if not hnum: return True
1963                self.histnam = [choices[i] for i in hnum]
1964                numselected = len(self.histnam)
1965            else:
1966                choices = sorted(self.xtalDict.values())
1967                hnum = G2gd.ItemSelector(choices,self.G2frame)
1968                if hnum is None: return True
1969                self.histnam = [choices[hnum]]
1970                numselected = len(self.histnam)
1971        elif self.currentExportType == 'powder':
1972            if len(self.powderDict) == 0:
1973                self.G2frame.ErrorDialog(
1974                    'Empty project',
1975                    'Project does not contain any powder data.')
1976                return True
1977            elif len(self.powderDict) == 1:
1978                self.histnam = self.powderDict.values()
1979            elif self.multiple:
1980                choices = sorted(self.powderDict.values())
1981                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1982                if not hnum: return True
1983                self.histnam = [choices[i] for i in hnum]
1984                numselected = len(self.histnam)
1985            else:
1986                choices = sorted(self.powderDict.values())
1987                hnum = G2gd.ItemSelector(choices,self.G2frame)
1988                if hnum is None: return True
1989                self.histnam = [choices[hnum]]
1990                numselected = len(self.histnam)
1991        elif self.currentExportType == 'image':
1992            if len(self.Histograms) == 0:
1993                self.G2frame.ErrorDialog(
1994                    'Empty project',
1995                    'Project does not contain any images.')
1996                return True
1997            elif len(self.Histograms) == 1:
1998                self.histnam = self.Histograms.keys()
1999            else:
2000                choices = sorted(self.Histograms.keys())
2001                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=self.multiple)
2002                if self.multiple:
2003                    if not hnum: return True
2004                    self.histnam = [choices[i] for i in hnum]
2005                else:
2006                    if hnum is None: return True
2007                    self.histnam = [choices[hnum]]
2008                numselected = len(self.histnam)
2009        if self.currentExportType == 'map':
2010            # search for phases with maps
2011            mapPhases = []
2012            choices = []
2013            for phasenam in sorted(self.Phases):
2014                phasedict = self.Phases[phasenam] # pointer to current phase info           
2015                if len(phasedict['General']['Map'].get('rho',[])):
2016                    mapPhases.append(phasenam)
2017                    if phasedict['General']['Map'].get('Flip'):
2018                        choices.append('Charge flip map: '+str(phasenam))
2019                    elif phasedict['General']['Map'].get('MapType'):
2020                        choices.append(
2021                            str(phasedict['General']['Map'].get('MapType'))
2022                            + ' map: ' + str(phasenam))
2023                    else:
2024                        choices.append('unknown map: '+str(phasenam))
2025            # select a map if needed
2026            if len(mapPhases) == 0:
2027                self.G2frame.ErrorDialog(
2028                    'Empty project',
2029                    'Project does not contain any maps.')
2030                return True
2031            elif len(mapPhases) == 1:
2032                self.phasenam = mapPhases
2033            else: 
2034                phasenum = G2gd.ItemSelector(choices,self.G2frame,multiple=self.multiple)
2035                if self.multiple:
2036                    if not phasenum: return True
2037                    self.phasenam = [mapPhases[i] for i in phasenum]
2038                else:
2039                    if phasenum is None: return True
2040                    self.phasenam = [mapPhases[phasenum]]
2041            numselected = len(self.phasenam)
2042
2043        # items selected, now set self.dirname and usually self.filename
2044        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
2045            AskFile == 'default' and not self.G2frame.GSASprojectfile
2046            ):
2047            filename = self.askSaveFile()
2048            if not filename: return True
2049            self.dirname,self.filename = os.path.split(filename)
2050        elif AskFile == 'dir' or AskFile == 'single' or (
2051            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
2052            ):
2053            self.dirname = self.askSaveDirectory()
2054            if not self.dirname: return True
2055        elif AskFile == 'default-dir' or AskFile == 'default':
2056            self.dirname,self.filename = os.path.split(
2057                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
2058                )
2059        else:
2060            raise Exception('This should not happen!')
2061       
2062    def loadParmDict(self):
2063        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
2064        refined values to those from the last cycle and set the uncertainties for the
2065        refined parameters in another dict (self.sigDict).
2066
2067        Expands the parm & sig dicts to include values derived from constraints.
2068        '''
2069        self.parmDict = {}
2070        self.sigDict = {}
2071        rigidbodyDict = {}
2072        covDict = {}
2073        consDict = {}
2074        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2075        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2076        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2077        while item:
2078            name = self.G2frame.PatternTree.GetItemText(item)
2079            if name == 'Rigid bodies':
2080                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
2081            elif name == 'Covariance':
2082                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
2083            elif name == 'Constraints':
2084                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
2085            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2086        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2087        self.parmDict.update(rbDict)
2088        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2089        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables =  G2stIO.GetPhaseData(
2090            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
2091        self.parmDict.update(phaseDict)
2092        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
2093            Phases,Histograms,Print=False,resetRefList=False)
2094        self.parmDict.update(hapDict)
2095        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
2096        self.parmDict.update(histDict)
2097        self.parmDict.update(zip(
2098            covDict.get('varyList',[]),
2099            covDict.get('variables',[])))
2100        self.sigDict = dict(zip(
2101            covDict.get('varyList',[]),
2102            covDict.get('sig',[])))
2103        # expand to include constraints: first compile a list of constraints
2104        constList = []
2105        for item in consDict:
2106            if item.startswith('_'): continue
2107            constList += consDict[item]
2108        # now process the constraints
2109        G2mv.InitVars()
2110        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
2111        varyList = covDict.get('varyListStart')
2112        if varyList is None and len(constDict) == 0:
2113            # no constraints can use varyList
2114            varyList = covDict.get('varyList')
2115        elif varyList is None:
2116            # old GPX file from before pre-constraint varyList is saved
2117            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
2118            raise Exception(' *** Export aborted ***')
2119        else:
2120            varyList = list(varyList)
2121        try:
2122            groups,parmlist = G2mv.GroupConstraints(constDict)
2123            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,self.parmDict)
2124        except:
2125            # this really should not happen
2126            print ' *** ERROR - constraints are internally inconsistent ***'
2127            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
2128            print 'Errors',errmsg
2129            if warnmsg: print 'Warnings',warnmsg
2130            raise Exception(' *** CIF creation aborted ***')
2131        # add the constrained values to the parameter dictionary
2132        G2mv.Dict2Map(self.parmDict,varyList)
2133        # and add their uncertainties into the esd dictionary (sigDict)
2134        if covDict.get('covMatrix') is not None:
2135            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
2136
2137    def loadTree(self):
2138        '''Load the contents of the data tree into a set of dicts
2139        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
2140        & self.xtalDict)
2141       
2142        * The childrenless data tree items are overall parameters/controls for the
2143          entire project and are placed in self.OverallParms
2144        * Phase items are placed in self.Phases
2145        * Data items are placed in self.Histogram. The key for these data items
2146          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
2147        '''
2148        self.OverallParms = {}
2149        self.powderDict = {}
2150        self.xtalDict = {}
2151        self.Phases = {}
2152        self.Histograms = {}
2153        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2154        histType = None       
2155        if self.currentExportType == 'phase':
2156            # if exporting phases load them here
2157            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
2158            if not sub:
2159                print 'no phases found'
2160                return True
2161            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
2162            while item:
2163                phaseName = self.G2frame.PatternTree.GetItemText(item)
2164                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
2165                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
2166            return
2167        elif self.currentExportType == 'single':
2168            histType = 'HKLF'
2169        elif self.currentExportType == 'powder':
2170            histType = 'PWDR'
2171        elif self.currentExportType == 'image':
2172            histType = 'IMG'
2173
2174        if histType: # Loading just one kind of tree entry
2175            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2176            while item:
2177                name = self.G2frame.PatternTree.GetItemText(item)
2178                if name.startswith(histType):
2179                    if self.Histograms.get(name): # there is already an item with this name
2180                        print('Histogram name '+str(name)+' is repeated. Renaming')
2181                        if name[-1] == '9':
2182                            name = name[:-1] + '10'
2183                        elif name[-1] in '012345678':
2184                            name = name[:-1] + str(int(name[-1])+1)
2185                        else:                           
2186                            name += '-1'
2187                    self.Histograms[name] = {}
2188                    # the main info goes into Data, but the 0th
2189                    # element contains refinement results, carry
2190                    # that over too now.
2191                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
2192                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
2193                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2194                    while item2: 
2195                        child = self.G2frame.PatternTree.GetItemText(item2)
2196                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
2197                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
2198                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2199            # index powder and single crystal histograms by number
2200            for hist in self.Histograms:
2201                if hist.startswith("PWDR"): 
2202                    d = self.powderDict
2203                elif hist.startswith("HKLF"): 
2204                    d = self.xtalDict
2205                else:
2206                    return                   
2207                i = self.Histograms[hist].get('hId')
2208                if i is None and not d.keys():
2209                    i = 0
2210                elif i is None or i in d.keys():
2211                    i = max(d.keys())+1
2212                d[i] = hist
2213            return
2214        # else standard load: using all interlinked phases and histograms
2215        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2216        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2217        while item:
2218            name = self.G2frame.PatternTree.GetItemText(item)
2219            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2220            if not item2: 
2221                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
2222            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2223        # index powder and single crystal histograms
2224        for hist in self.Histograms:
2225            i = self.Histograms[hist]['hId']
2226            if hist.startswith("PWDR"): 
2227                self.powderDict[i] = hist
2228            elif hist.startswith("HKLF"): 
2229                self.xtalDict[i] = hist
2230
2231    def dumpTree(self,mode='type'):
2232        '''Print out information on the data tree dicts loaded in loadTree
2233        '''
2234        print '\nOverall'
2235        if mode == 'type':
2236            def Show(arg): return type(arg)
2237        else:
2238            def Show(arg): return arg
2239        for key in self.OverallParms:
2240            print '  ',key,Show(self.OverallParms[key])
2241        print 'Phases'
2242        for key1 in self.Phases:
2243            print '    ',key1,Show(self.Phases[key1])
2244        print 'Histogram'
2245        for key1 in self.Histograms:
2246            print '    ',key1,Show(self.Histograms[key1])
2247            for key2 in self.Histograms[key1]:
2248                print '      ',key2,Show(self.Histograms[key1][key2])
2249
2250    def defaultSaveFile(self):
2251        return os.path.abspath(
2252            os.path.splitext(self.G2frame.GSASprojectfile
2253                             )[0]+self.extension)
2254       
2255    def askSaveFile(self):
2256        '''Ask the user to supply a file name
2257
2258        :returns: a file name (str) or None if Cancel is pressed
2259        '''
2260        defnam = os.path.splitext(
2261            os.path.split(self.G2frame.GSASprojectfile)[1]
2262            )[0]+self.extension
2263        dlg = wx.FileDialog(
2264            self.G2frame, 'Input name for file to write', '.', defnam,
2265            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2266            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2267        dlg.CenterOnParent()
2268        try:
2269            if dlg.ShowModal() == wx.ID_OK:
2270                filename = dlg.GetPath()
2271                # make sure extension is correct
2272                filename = os.path.splitext(filename)[0]+self.extension
2273            else:
2274                filename = None
2275        finally:
2276            dlg.Destroy()
2277        return filename
2278
2279    def askSaveDirectory(self):
2280        '''Ask the user to supply a directory name. Path name is used as the
2281        starting point for the next export path search.
2282
2283        :returns: a directory name (str) or None if Cancel is pressed
2284        '''
2285        if self.G2frame.exportDir:
2286            startdir = self.G2frame.exportDir
2287        elif self.G2frame.GSASprojectfile:
2288            startdir = os.path.split(self.G2frame.GSASprojectfile)[0]
2289        elif self.G2frame.dirname:
2290            startdir = self.G2frame.dirname
2291        else:
2292            startdir = ''
2293        dlg = wx.DirDialog(
2294            self.G2frame, 'Input directory where file(s) will be written', startdir,
2295            wx.DD_DEFAULT_STYLE)
2296        dlg.CenterOnParent()
2297        try:
2298            if dlg.ShowModal() == wx.ID_OK:
2299                filename = dlg.GetPath()
2300                self.G2frame.exportDir = filename
2301            else:
2302                filename = None
2303        finally:
2304            dlg.Destroy()
2305        return filename
2306
2307    # Tools for file writing.
2308    def OpenFile(self,fil=None,mode='w'):
2309        '''Open the output file
2310
2311        :param str fil: The name of the file to open. If None (default)
2312          the name defaults to self.dirname + self.filename.
2313          If an extension is supplied, it is not overridded,
2314          but if not, the default extension is used.
2315        :returns: the file object opened by the routine which is also
2316          saved as self.fp
2317        '''
2318        if not fil:
2319            if not os.path.splitext(self.filename)[1]:
2320                self.filename += self.extension
2321            fil = os.path.join(self.dirname,self.filename)
2322        self.fullpath = fil
2323        self.fp = open(fil,mode)
2324        return self.fp
2325
2326    def Write(self,line):
2327        '''write a line of output, attaching a line-end character
2328
2329        :param str line: the text to be written.
2330        '''
2331        self.fp.write(line+'\n')
2332    def CloseFile(self,fp=None):
2333        '''Close a file opened in OpenFile
2334
2335        :param file fp: the file object to be closed. If None (default)
2336          file object self.fp is closed.
2337        '''
2338        if fp is None:
2339            fp = self.fp
2340            self.fp = None
2341        fp.close()
2342    # Tools to pull information out of the data arrays
2343    def GetCell(self,phasenam):
2344        """Gets the unit cell parameters and their s.u.'s for a selected phase
2345
2346        :param str phasenam: the name for the selected phase
2347        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2348          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2349          cell values and `cellSig` has their uncertainties.
2350        """
2351        phasedict = self.Phases[phasenam] # pointer to current phase info
2352        try:
2353            pfx = str(phasedict['pId'])+'::'
2354            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2355            cellSig = G2stIO.getCellEsd(pfx,
2356                                        phasedict['General']['SGData'],A,
2357                                        self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2358            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2359            return cellList,cellSig
2360        except KeyError:
2361            cell = phasedict['General']['Cell'][1:]
2362            return cell,7*[0]
2363   
2364    def GetAtoms(self,phasenam):
2365        """Gets the atoms associated with a phase. Can be used with standard
2366        or macromolecular phases
2367
2368        :param str phasenam: the name for the selected phase
2369        :returns: a list of items for eac atom where each item is a list containing:
2370          label, typ, mult, xyz, and td, where
2371
2372          * label and typ are the atom label and the scattering factor type (str)
2373          * mult is the site multiplicity (int)
2374          * xyz is contains a list with four pairs of numbers:
2375            x, y, z and fractional occupancy and
2376            their standard uncertainty (or a negative value)
2377          * td is contains a list with either one or six pairs of numbers:
2378            if one number it is U\ :sub:`iso` and with six numbers it is
2379            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2380            paired with their standard uncertainty (or a negative value)
2381        """
2382        phasedict = self.Phases[phasenam] # pointer to current phase info           
2383        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2384        cfrac = cx+3
2385        fpfx = str(phasedict['pId'])+'::Afrac:'       
2386        atomslist = []
2387        for i,at in enumerate(phasedict['Atoms']):
2388            if phasedict['General']['Type'] == 'macromolecular':
2389                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2390            else:
2391                label = at[ct-1]
2392            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2393            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2394            mult = at[cs+1]
2395            typ = at[ct]
2396            xyz = []
2397            for j,v in enumerate(('x','y','z')):
2398                val = at[cx+j]
2399                pfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2400                sig = self.sigDict.get(pfx,-0.000009)
2401                xyz.append((val,sig))
2402            xyz.append((fval,fsig))
2403            td = []
2404            if at[cia] == 'I':
2405                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2406                val = self.parmDict.get(pfx,at[cia+1])
2407                sig = self.sigDict.get(pfx,-0.0009)
2408                td.append((val,sig))
2409            else:
2410                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2411                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2412                    val = self.parmDict.get(pfx,at[cia+2+i])
2413                    sig = self.sigDict.get(pfx,-0.0009)
2414                    td.append((val,sig))
2415            atomslist.append((label,typ,mult,xyz,td))
2416        return atomslist
2417######################################################################
2418
2419def ReadCIF(URLorFile):
2420    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2421    (from James Hester).
2422    The open routine gets confused with DOS names that begin with a letter and colon
2423    "C:\dir\" so this routine will try to open the passed name as a file and if that
2424    fails, try it as a URL
2425
2426    :param str URLorFile: string containing a URL or a file name. Code will try first
2427      to open it as a file and then as a URL.
2428
2429    :returns: a PyCifRW CIF object.
2430    '''
2431    import CifFile as cif # PyCifRW from James Hester
2432
2433    # alternate approach:
2434    #import urllib
2435    #ciffile = 'file:'+urllib.pathname2url(filename)
2436   
2437    try:
2438        fp = open(URLorFile,'r')
2439        cf = cif.ReadCif(fp)
2440        fp.close()
2441        return cf
2442    except IOError:
2443        return cif.ReadCif(URLorFile)
2444
2445if __name__ == '__main__':
2446    app = wx.PySimpleApp()
2447    frm = wx.Frame(None) # create a frame
2448    frm.Show(True)
2449    filename = '/tmp/notzip.zip'
2450    filename = '/tmp/all.zip'
2451    #filename = '/tmp/11bmb_7652.zip'
2452   
2453    #selection=None, confirmoverwrite=True, parent=None
2454    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2455    print ExtractFileFromZip(filename,multipleselect=True)
2456                             #confirmread=False, confirmoverwrite=False)
2457
2458    # choicelist=[ ('a','b','c'),
2459    #              ('test1','test2'),('no choice',)]
2460    # titles = [ 'a, b or c', 'tests', 'No option here']
2461    # dlg = MultipleChoicesDialog(
2462    #     choicelist,titles,
2463    #     parent=frm)
2464    # if dlg.ShowModal() == wx.ID_OK:
2465    #     print 'Got OK'
Note: See TracBrowser for help on using the repository browser.