source: trunk/GSASIIIO.py @ 1261

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

reorg exports to implement directory selection

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