source: trunk/GSASIIIO.py @ 1228

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

remove diagnostic prints in image calibration stuff
some fine tuning of the image calibration
make CheMin? images "right" way around as requested by Dave Bish
Apply James Hester's fix to Pawley update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 98.0 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2014-02-24 21:43:15 +0000 (Mon, 24 Feb 2014) $
4# $Author: vondreele $
5# $Revision: 1228 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 1228 2014-02-24 21:43:15Z vondreele $
8########### SVN repository information ###################
9'''
10*GSASIIIO: Misc I/O routines*
11=============================
12
13Module with miscellaneous routines for input and output. Many
14are GUI routines to interact with user.
15
16Includes support for image reading.
17
18Also includes base classes for data import routines.
19
20'''
21"""GSASIIIO: functions for IO of data
22   Copyright: 2008, Robert B. Von Dreele (Argonne National Laboratory)
23"""
24import wx
25import math
26import numpy as np
27import cPickle
28import sys
29import re
30import random as ran
31import GSASIIpath
32GSASIIpath.SetVersionNumber("$Revision: 1228 $")
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           
1037def powderFxyeSave(G2frame,exports,powderfile):
1038    'Save a powder histogram as a GSAS FXYE file'
1039    head,tail = ospath.split(powderfile)
1040    name,ext = tail.split('.')
1041    for i,export in enumerate(exports):
1042        filename = ospath.join(head,name+'-%03d.'%(i)+ext)
1043        prmname = filename.strip(ext)+'prm'
1044        prm = open(prmname,'w')      #old style GSAS parm file
1045        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1046        Inst = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame, \
1047            PickId, 'Instrument Parameters'))[0]
1048        prm.write( '            123456789012345678901234567890123456789012345678901234567890        '+'\n')
1049        prm.write( 'INS   BANK      1                                                               '+'\n')
1050        prm.write(('INS   HTYPE   %sR                                                              '+'\n')%(Inst['Type'][0]))
1051        if 'Lam1' in Inst:              #Ka1 & Ka2
1052            prm.write(('INS  1 ICONS%10.7f%10.7f    0.0000               0.990    0     0.500   '+'\n')%(Inst['Lam1'][0],Inst['Lam2'][0]))
1053        elif 'Lam' in Inst:             #single wavelength
1054            prm.write(('INS  1 ICONS%10.7f%10.7f    0.0000               0.990    0     0.500   '+'\n')%(Inst['Lam'][1],0.0))
1055        prm.write( 'INS  1 IRAD     0                                                               '+'\n')
1056        prm.write( 'INS  1I HEAD                                                                    '+'\n')
1057        prm.write( 'INS  1I ITYP    0    0.0000  180.0000         1                                 '+'\n')
1058        prm.write(('INS  1DETAZM%10.3f                                                          '+'\n')%(Inst['Azimuth'][0]))
1059        prm.write( 'INS  1PRCF1     3    8   0.00100                                                '+'\n')
1060        prm.write(('INS  1PRCF11     %15.6g%15.6g%15.6g%15.6g   '+'\n')%(Inst['U'][1],Inst['V'][1],Inst['W'][1],0.0))
1061        prm.write(('INS  1PRCF12     %15.6g%15.6g%15.6g%15.6g   '+'\n')%(Inst['X'][1],Inst['Y'][1],Inst['SH/L'][1]/2.,Inst['SH/L'][1]/2.))
1062        prm.close()
1063        file = open(filename,'w')
1064        print 'save powder pattern to file: ',filename
1065        x,y,w,yc,yb,yd = G2frame.PatternTree.GetItemPyData(PickId)[1]
1066        file.write(powderfile+'\n')
1067        file.write('Instrument parameter file:'+ospath.split(prmname)[1]+'\n')
1068        file.write('BANK 1 %d %d CONS %.2f %.2f 0 0 FXYE\n'%(len(x),len(x),\
1069            100.*x[0],100.*(x[1]-x[0])))
1070        s = list(np.sqrt(1./np.array(w)))       
1071        XYW = zip(x,y,s)
1072        for X,Y,S in XYW:
1073            file.write("%15.6g %15.6g %15.6g\n" % (100.*X,Y,max(S,1.0)))
1074        file.close()
1075        print 'powder pattern file '+filename+' written'
1076       
1077def powderXyeSave(G2frame,exports,powderfile):
1078    'Save a powder histogram as a Topas XYE file'
1079    head,tail = ospath.split(powderfile)
1080    name,ext = tail.split('.')
1081    for i,export in enumerate(exports):
1082        filename = ospath.join(head,name+'-%03d.'%(i)+ext)
1083        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1084        file = open(filename,'w')
1085        file.write('#%s\n'%(export))
1086        print 'save powder pattern to file: ',filename
1087        x,y,w,yc,yb,yd = G2frame.PatternTree.GetItemPyData(PickId)[1]
1088        s = list(np.sqrt(1./np.array(w)))       
1089        XYW = zip(x,y,s)
1090        for X,Y,W in XYW:
1091            file.write("%15.6g %15.6g %15.6g\n" % (X,Y,W))
1092        file.close()
1093        print 'powder pattern file '+filename+' written'
1094       
1095def PDFSave(G2frame,exports):
1096    'Save a PDF G(r) and S(Q) in column formats'
1097    for export in exports:
1098        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1099        SQname = 'S(Q)'+export[4:]
1100        GRname = 'G(R)'+export[4:]
1101        sqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.sq')
1102        grfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.gr')
1103        sqId = G2gd.GetPatternTreeItemId(G2frame, PickId, SQname)
1104        grId = G2gd.GetPatternTreeItemId(G2frame, PickId, GRname)
1105        sqdata = np.array(G2frame.PatternTree.GetItemPyData(sqId)[1][:2]).T
1106        grdata = np.array(G2frame.PatternTree.GetItemPyData(grId)[1][:2]).T
1107        sqfile = open(sqfilename,'w')
1108        grfile = open(grfilename,'w')
1109        sqfile.write('#T S(Q) %s\n'%(export))
1110        grfile.write('#T G(R) %s\n'%(export))
1111        sqfile.write('#L Q     S(Q)\n')
1112        grfile.write('#L R     G(R)\n')
1113        for q,sq in sqdata:
1114            sqfile.write("%15.6g %15.6g\n" % (q,sq))
1115        sqfile.close()
1116        for r,gr in grdata:
1117            grfile.write("%15.6g %15.6g\n" % (r,gr))
1118        grfile.close()
1119   
1120def PeakListSave(G2frame,file,peaks):
1121    'Save powder peaks to a data file'
1122    print 'save peak list to file: ',G2frame.peaklistfile
1123    if not peaks:
1124        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1125        try:
1126            result = dlg.ShowModal()
1127        finally:
1128            dlg.Destroy()
1129        return
1130    for peak in peaks:
1131        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
1132            (peak[0],peak[2],peak[4],peak[6]))
1133    print 'peak list saved'
1134             
1135def IndexPeakListSave(G2frame,peaks):
1136    'Save powder peaks from the indexing list'
1137    file = open(G2frame.peaklistfile,'wa')
1138    print 'save index peak list to file: ',G2frame.peaklistfile
1139    wx.BeginBusyCursor()
1140    try:
1141        if not peaks:
1142            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1143            try:
1144                result = dlg.ShowModal()
1145            finally:
1146                dlg.Destroy()
1147            return
1148        for peak in peaks:
1149            file.write("%12.6f\n" % (peak[7]))
1150        file.close()
1151    finally:
1152        wx.EndBusyCursor()
1153    print 'index peak list saved'
1154   
1155def SetNewPhase(Name='New Phase',SGData=None,cell=None):
1156    '''Create a new phase dict with default values for various parameters
1157
1158    :param str Name: Name for new Phase
1159
1160    :param dict SGData: space group data from :func:`GSASIIspc:SpcGroup`;
1161      defaults to data for P 1
1162
1163    :param list cell: unit cell parameter list; defaults to
1164      [1.0,1.0,1.0,90.,90,90.,1.]
1165
1166    '''
1167    if SGData is None: SGData = G2spc.SpcGroup('P 1')[1]
1168    if cell is None: cell=[1.0,1.0,1.0,90.,90,90.,1.]
1169    phaseData = {
1170        'ranId':ran.randint(0,sys.maxint),
1171        'General':{
1172            'Name':Name,
1173            'Type':'nuclear',
1174            'AtomPtrs':[3,1,7,9],
1175            'SGData':SGData,
1176            'Cell':[False,]+cell,
1177            'Pawley dmin':1.0,
1178            'Data plot type':'None',
1179            'SH Texture':{
1180                'Order':0,
1181                'Model':'cylindrical',
1182                'Sample omega':[False,0.0],
1183                'Sample chi':[False,0.0],
1184                'Sample phi':[False,0.0],
1185                'SH Coeff':[False,{}],
1186                'SHShow':False,
1187                'PFhkl':[0,0,1],
1188                'PFxyz':[0,0,1],
1189                'PlotType':'Pole figure'}},
1190        'Atoms':[],
1191        'Drawing':{},
1192        'Histograms':{},
1193        'Pawley ref':[],
1194        'RBModels':{},
1195        }
1196    return phaseData
1197       
1198class MultipleChoicesDialog(wx.Dialog):
1199    '''A dialog that offers a series of choices, each with a
1200    title and a wx.Choice widget. Intended to be used Modally.
1201    typical input:
1202
1203        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1204        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1205       
1206    selections are placed in self.chosen when OK is pressed
1207    '''
1208    def __init__(self,choicelist,headinglist,
1209                 head='Select options',
1210                 title='Please select from options below',
1211                 parent=None):
1212        self.chosen = []
1213        wx.Dialog.__init__(
1214            self,parent,wx.ID_ANY,head, 
1215            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1216        panel = wx.Panel(self)
1217        mainSizer = wx.BoxSizer(wx.VERTICAL)
1218        mainSizer.Add((10,10),1)
1219        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1220        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1221        self.ChItems = []
1222        for choice,lbl in zip(choicelist,headinglist):
1223            mainSizer.Add((10,10),1)
1224            self.chosen.append(0)
1225            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1226            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1227            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1228            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1229
1230        OkBtn = wx.Button(panel,-1,"Ok")
1231        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1232        cancelBtn = wx.Button(panel,-1,"Cancel")
1233        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1234        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1235        btnSizer.Add((20,20),1)
1236        btnSizer.Add(OkBtn)
1237        btnSizer.Add((20,20),1)
1238        btnSizer.Add(cancelBtn)
1239        btnSizer.Add((20,20),1)
1240        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1241        panel.SetSizer(mainSizer)
1242        panel.Fit()
1243        self.Fit()
1244       
1245    def OnOk(self,event):
1246        parent = self.GetParent()
1247        if parent is not None: parent.Raise()
1248        # save the results from the choice widgets
1249        self.chosen = []
1250        for w in self.ChItems:
1251            self.chosen.append(w.GetSelection())
1252        self.EndModal(wx.ID_OK)             
1253           
1254    def OnCancel(self,event):
1255        parent = self.GetParent()
1256        if parent is not None: parent.Raise()
1257        self.chosen = []
1258        self.EndModal(wx.ID_CANCEL)             
1259           
1260def ExtractFileFromZip(filename, selection=None, confirmread=True,
1261                       confirmoverwrite=True, parent=None,
1262                       multipleselect=False):
1263    '''If the filename is a zip file, extract a file from that
1264    archive.
1265
1266    :param list Selection: used to predefine the name of the file
1267      to be extracted. Filename case and zip directory name are
1268      ignored in selection; the first matching file is used.
1269
1270    :param bool confirmread: if True asks the user to confirm before expanding
1271      the only file in a zip
1272
1273    :param bool confirmoverwrite: if True asks the user to confirm
1274      before overwriting if the extracted file already exists
1275
1276    :param bool multipleselect: if True allows more than one zip
1277      file to be extracted, a list of file(s) is returned.
1278      If only one file is present, do not ask which one, otherwise
1279      offer a list of choices (unless selection is used).
1280   
1281    :returns: the name of the file that has been created or a
1282      list of files (see multipleselect)
1283
1284    If the file is not a zipfile, return the name of the input file.
1285    If the zipfile is empty or no file has been selected, return None
1286    '''
1287    import zipfile # do this now, since we can save startup time by doing this only on need
1288    import shutil
1289    zloc = os.path.split(filename)[0]
1290    if not zipfile.is_zipfile(filename):
1291        #print("not zip")
1292        return filename
1293
1294    z = zipfile.ZipFile(filename,'r')
1295    zinfo = z.infolist()
1296
1297    if len(zinfo) == 0:
1298        #print('Zip has no files!')
1299        zlist = [-1]
1300    if selection:
1301        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1302        if selection.lower() in choices:
1303            zlist = [choices.index(selection.lower())]
1304        else:
1305            print('debug: file '+str(selection)+' was not found in '+str(filename))
1306            zlist = [-1]
1307    elif len(zinfo) == 1 and confirmread:
1308        result = wx.ID_NO
1309        dlg = wx.MessageDialog(
1310            parent,
1311            'Is file '+str(zinfo[0].filename)+
1312            ' what you want to extract from '+
1313            str(os.path.split(filename)[1])+'?',
1314            'Confirm file', 
1315            wx.YES_NO | wx.ICON_QUESTION)
1316        try:
1317            result = dlg.ShowModal()
1318        finally:
1319            dlg.Destroy()
1320        if result == wx.ID_NO:
1321            zlist = [-1]
1322        else:
1323            zlist = [0]
1324    elif len(zinfo) == 1:
1325        zlist = [0]
1326    elif multipleselect:
1327        # select one or more from a from list
1328        choices = [i.filename for i in zinfo]
1329        dlg = wx.MultiChoiceDialog(parent,'Select file(s) to extract from zip file'+str(filename),
1330            'Choose file(s)',choices,wx.CHOICEDLG_STYLE,)
1331        if dlg.ShowModal() == wx.ID_OK:
1332            zlist = dlg.GetSelections()
1333        else:
1334            zlist = []
1335        dlg.Destroy()
1336    else:
1337        # select one from a from list
1338        choices = [i.filename for i in zinfo]
1339        dlg = wx.SingleChoiceDialog(parent,
1340            'Select file to extract from zip file'+str(filename),'Choose file',
1341            choices,)
1342        if dlg.ShowModal() == wx.ID_OK:
1343            zlist = [dlg.GetSelection()]
1344        else:
1345            zlist = [-1]
1346        dlg.Destroy()
1347       
1348    outlist = []
1349    for zindex in zlist:
1350        if zindex >= 0:
1351            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1352            if os.path.exists(efil) and confirmoverwrite:
1353                result = wx.ID_NO
1354                dlg = wx.MessageDialog(parent,
1355                    'File '+str(efil)+' already exists. OK to overwrite it?',
1356                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1357                try:
1358                    result = dlg.ShowModal()
1359                finally:
1360                    dlg.Destroy()
1361                if result == wx.ID_NO:
1362                    zindex = -1
1363        if zindex >= 0:
1364            # extract the file to the current directory, regardless of it's original path
1365            #z.extract(zinfo[zindex],zloc)
1366            eloc,efil = os.path.split(zinfo[zindex].filename)
1367            outfile = os.path.join(zloc, efil)
1368            fpin = z.open(zinfo[zindex])
1369            fpout = file(outfile, "wb")
1370            shutil.copyfileobj(fpin, fpout)
1371            fpin.close()
1372            fpout.close()
1373            outlist.append(outfile)
1374    z.close()
1375    if multipleselect and len(outlist) >= 1:
1376        return outlist
1377    elif len(outlist) == 1:
1378        return outlist[0]
1379    else:
1380        return None
1381
1382######################################################################
1383# base classes for reading various types of data files
1384#   not used directly, only by subclassing
1385######################################################################
1386E,SGData = G2spc.SpcGroup('P 1') # data structure for default space group
1387P1SGData = SGData
1388class ImportBaseclass(object):
1389    '''Defines a base class for the reading of input files (diffraction
1390    data, coordinates,...). See :ref:`Writing a Import Routine<Import_routines>`
1391    for an explanation on how to use a subclass of this class.
1392    '''
1393    class ImportException(Exception):
1394        '''Defines an Exception that is used when an import routine hits an expected error,
1395        usually in .Reader.
1396
1397        Good practice is that the Reader should define a value in self.errors that
1398        tells the user some information about what is wrong with their file.         
1399        '''
1400        pass
1401
1402    def __init__(self,
1403                 formatName,
1404                 longFormatName=None,
1405                 extensionlist=[],
1406                 strictExtension=False,
1407                 ):
1408        self.formatName = formatName # short string naming file type
1409        if longFormatName: # longer string naming file type
1410            self.longFormatName = longFormatName
1411        else:
1412            self.longFormatName = formatName
1413        # define extensions that are allowed for the file type
1414        # for windows, remove any extensions that are duplicate, as case is ignored
1415        if sys.platform == 'windows' and extensionlist:
1416            extensionlist = list(set([s.lower() for s in extensionlist]))
1417        self.extensionlist = extensionlist
1418        # If strictExtension is True, the file will not be read, unless
1419        # the extension matches one in the extensionlist
1420        self.strictExtension = strictExtension
1421        self.errors = ''
1422        self.warnings = ''
1423        # used for readers that will use multiple passes to read
1424        # more than one data block
1425        self.repeat = False
1426        self.repeatcount = 0
1427        #print 'created',self.__class__
1428
1429    def ReInitialize(self):
1430        'Reinitialize the Reader to 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) 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    See :ref:`Writing a Import Routine<Import_Routines>`
1709    for an explanation on how to use this class.
1710    '''
1711    # define some default instrument parameter files
1712    # just like GSAS, sigh
1713    defaultIparm_lbl = []
1714    defaultIparms = []
1715    defaultIparm_lbl.append('CuKa lab data')
1716    defaultIparms.append({
1717        'INS   HTYPE ':'PXC ',
1718        'INS  1 ICONS':'  1.540500  1.544300       0.0         0       0.7    0       0.5   ',
1719        'INS  1PRCF1 ':'    3    8      0.01                                                ',
1720        'INS  1PRCF11':'   2.000000E+00  -2.000000E+00   5.000000E+00   0.000000E+00        ',
1721        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.150000E-01   0.150000E-01        ',
1722        })
1723    defaultIparm_lbl.append('0.6A synch')
1724    defaultIparms.append({
1725        'INS   HTYPE ':'PXC ',
1726        'INS  1 ICONS':'  0.600000  0.000000       0.0         0      0.99    0       0.5   ',
1727        'INS  1PRCF1 ':'    3    8      0.01                                                ',
1728        'INS  1PRCF11':'   1.000000E+00  -1.000000E+00   0.300000E+00   0.000000E+00        ',
1729        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.100000E-01   0.100000E-01        ',
1730        })
1731    defaultIparm_lbl.append('1.5A CW neutron data')
1732    defaultIparms.append({
1733        'INS   HTYPE ':'PNC',
1734        'INS  1 ICONS':'   1.54020   0.00000   0.04000         0',
1735        'INS  1PRCF1 ':'    3    8     0.005',
1736        'INS  1PRCF11':'   0.239700E+03  -0.298200E+03   0.180800E+03   0.000000E+00',
1737        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.400000E-01   0.300000E-01',
1738        })
1739    defaultIparm_lbl.append('10m TOF backscattering bank')
1740    defaultIparms.append({
1741        'INS   HTYPE ':'PNT',
1742        'INS  1 ICONS':'   5000.00      0.00      0.00',
1743        'INS  1BNKPAR':'    1.0000   150.000',       
1744        'INS  1PRCF1 ':'    1    8   0.01000',
1745        'INS  1PRCF11':'   0.000000E+00   5.000000E+00   3.000000E-02   1.000000E-03',
1746        'INS  1PRCF12':'   0.000000E+00   4.000000E+01   0.000000E+00   0.000000E+00',       
1747        })
1748    defaultIparm_lbl.append('10m TOF 90deg bank')
1749    defaultIparms.append({
1750        'INS   HTYPE ':'PNT',
1751        'INS  1 ICONS':'   3500.00      0.00      0.00',
1752        'INS  1BNKPAR':'    1.0000    90.000',       
1753        'INS  1PRCF1 ':'    1    8   0.01000',
1754        'INS  1PRCF11':'   0.000000E+00   5.000000E+00   3.000000E-02   4.000000E-03',
1755        'INS  1PRCF12':'   0.000000E+00   8.000000E+01   0.000000E+00   0.000000E+00',       
1756        })
1757    defaultIparm_lbl.append('63m POWGEN 90deg bank')
1758    defaultIparms.append({
1759        'INS   HTYPE ':'PNT',
1760        'INS  1 ICONS':'  22585.80      0.00      0.00',
1761        'INS  1BNKPAR':'    1.0000    90.000',       
1762        'INS  1PRCF1 ':'    1    8   0.01000',
1763        'INS  1PRCF11':'   0.000000E+00   1.000000E+00   3.000000E-02   4.000000E-03',
1764        'INS  1PRCF12':'   0.000000E+00   8.000000E+01   0.000000E+00   0.000000E+00',       
1765        })
1766    def __init__(self,
1767                 formatName,
1768                 longFormatName=None,
1769                 extensionlist=[],
1770                 strictExtension=False,
1771                 ):
1772        ImportBaseclass.__init__(self,formatName,
1773                                            longFormatName,
1774                                            extensionlist,
1775                                            strictExtension)
1776        self.ReInitialize()
1777       
1778    def ReInitialize(self):
1779        'Reinitialize the Reader to initial settings'
1780        ImportBaseclass.ReInitialize(self)
1781        self.powderentry = ['',None,None] #  (filename,Pos,Bank)
1782        self.powderdata = [] # Powder dataset
1783        '''A powder data set is a list with items [x,y,w,yc,yb,yd]:
1784                np.array(x), # x-axis values
1785                np.array(y), # powder pattern intensities
1786                np.array(w), # 1/sig(intensity)^2 values (weights)
1787                np.array(yc), # calc. intensities (zero)
1788                np.array(yb), # calc. background (zero)
1789                np.array(yd), # obs-calc profiles
1790        '''                           
1791        self.comments = []
1792        self.idstring = ''
1793        self.Sample = G2pdG.SetDefaultSample()
1794        self.GSAS = None     # used in TOF
1795        self.clockWd = None  # used in TOF
1796        self.repeat_instparm = True # Should a parm file be
1797        #                             used for multiple histograms?
1798        self.instparm = None # name hint
1799        self.instfile = '' # full path name to instrument parameter file
1800        self.instbank = '' # inst parm bank number
1801        self.instmsg = ''  # a label that gets printed to show
1802                           # where instrument parameters are from
1803        self.numbanks = 1
1804        self.instdict = {} # place items here that will be transferred to the instrument parameters
1805######################################################################
1806class ImportSmallAngleData(ImportBaseclass):
1807    '''Defines a base class for the reading of files with small angle data.
1808    See :ref:`Writing a Import Routine<Import_Routines>`
1809    for an explanation on how to use this class.
1810    '''
1811    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1812        strictExtension=False,):
1813           
1814        ImportBaseclass.__init__(self,formatName,longFormatName,extensionlist,
1815            strictExtension)
1816        self.ReInitialize()
1817       
1818    def ReInitialize(self):
1819        'Reinitialize the Reader to initial settings'
1820        ImportBaseclass.ReInitialize(self)
1821        self.smallangleentry = ['',None,None] #  (filename,Pos,Bank)
1822        self.smallangledata = [] # SASD dataset
1823        '''A small angle data set is a list with items [x,y,w,yc,yd]:
1824                np.array(x), # x-axis values
1825                np.array(y), # powder pattern intensities
1826                np.array(w), # 1/sig(intensity)^2 values (weights)
1827                np.array(yc), # calc. intensities (zero)
1828                np.array(yd), # obs-calc profiles
1829        '''                           
1830        self.comments = []
1831        self.idstring = ''
1832        self.Sample = G2pdG.SetDefaultSample()
1833        self.GSAS = None     # used in TOF
1834        self.clockWd = None  # used in TOF
1835        self.numbanks = 1
1836        self.instdict = {} # place items here that will be transferred to the instrument parameters
1837######################################################################
1838class ExportBaseclass(object):
1839    '''Defines a base class for the exporting of GSAS-II results
1840    '''
1841    def __init__(self,
1842                 G2frame,
1843                 formatName,
1844                 extension,
1845                 longFormatName=None,
1846                 ):
1847        self.G2frame = G2frame
1848        self.formatName = formatName # short string naming file type
1849        self.extension = extension
1850        if longFormatName: # longer string naming file type
1851            self.longFormatName = longFormatName
1852        else:
1853            self.longFormatName = formatName
1854        self.OverallParms = {}
1855        self.Phases = {}
1856        self.Histograms = {}
1857        self.powderDict = {}
1858        self.xtalDict = {}
1859        self.parmDict = {}
1860        self.sigDict = {}
1861        # updated in InitExport:
1862        self.currentExportType = None # type of export that has been requested
1863        # updated in ExportSelect (when used):
1864        self.phasenam = None # a list of selected phases
1865        self.histnam = None # a list of selected histograms
1866        self.filename = None # name of file to be written
1867       
1868        # items that should be defined in a subclass of this class
1869        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1870        # The following types are defined: 'project', "phase", "powder", "single"
1871        self.multiple = False # set as True if the class can export multiple phases or histograms
1872        # self.multiple is ignored for "project" exports
1873
1874    def InitExport(self,event):
1875        '''Determines the type of menu that called the Exporter.
1876        '''
1877        if event:
1878            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1879
1880    def ExportSelect(self,AskFile=True):
1881        '''Selects histograms or phases when needed. Sets a default file name.
1882
1883        :param bool AskFile: if AskFile is True (default) get the name of the file
1884          in a dialog
1885        :returns: True in case of an error
1886        '''
1887       
1888        if self.currentExportType == 'phase':
1889            if len(self.Phases) == 0:
1890                self.G2frame.ErrorDialog(
1891                    'Empty project',
1892                    'Project does not contain any phases.')
1893                return True
1894            elif len(self.Phases) == 1:
1895                self.phasenam = self.Phases.keys()
1896            elif self.multiple: 
1897                choices = sorted(self.Phases.keys())
1898                phasenum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1899                if phasenum is None: return True
1900                self.phasenam = [choices[i] for i in phasenum]
1901                if not self.phasenam: return True
1902            else:
1903                choices = sorted(self.Phases.keys())
1904                phasenum = G2gd.ItemSelector(choices,self.G2frame)
1905                if phasenum is None: return True
1906                self.phasenam = [choices[phasenum]]
1907        elif self.currentExportType == 'single':
1908            if len(self.xtalDict) == 0:
1909                self.G2frame.ErrorDialog(
1910                    'Empty project',
1911                    'Project does not contain any single crystal data.')
1912                return True
1913            elif len(self.xtalDict) == 1:
1914                self.histnam = self.xtalDict.values()
1915            elif self.multiple:
1916                choices = sorted(self.xtalDict.values())
1917                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1918                if not hnum: return True
1919                self.histnam = [choices[i] for i in hnum]
1920            else:
1921                choices = sorted(self.xtalDict.values())
1922                hnum = G2gd.ItemSelector(choices,self.G2frame)
1923                if hnum is None: return True
1924                self.histnam = [choices[hnum]]
1925        elif self.currentExportType == 'powder':
1926            if len(self.powderDict) == 0:
1927                self.G2frame.ErrorDialog(
1928                    'Empty project',
1929                    'Project does not contain any powder data.')
1930                return True
1931            elif len(self.powderDict) == 1:
1932                self.histnam = self.powderDict.values()
1933            elif self.multiple:
1934                choices = sorted(self.powderDict.values())
1935                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1936                if not hnum: return True
1937                self.histnam = [choices[i] for i in hnum]
1938            else:
1939                choices = sorted(self.powderDict.values())
1940                hnum = G2gd.ItemSelector(choices,self.G2frame)
1941                if hnum is None: return True
1942                self.histnam = [choices[hnum]]
1943        elif self.currentExportType == 'image':
1944            if len(self.Histograms) == 0:
1945                self.G2frame.ErrorDialog(
1946                    'Empty project',
1947                    'Project does not contain any images.')
1948                return True
1949            elif len(self.Histograms) == 1:
1950                self.histnam = self.Histograms.keys()
1951            else:
1952                choices = sorted(self.Histograms.keys())
1953                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1954                if self.multiple:
1955                    if not hnum: return True
1956                    self.histnam = [choices[i] for i in hnum]
1957                else:
1958                    if hnum is None: return True
1959                    self.histnam = [choices[hnum]]
1960        if self.currentExportType == 'map':
1961            # search for phases with maps
1962            mapPhases = []
1963            choices = []
1964            for phasenam in sorted(self.Phases):
1965                phasedict = self.Phases[phasenam] # pointer to current phase info           
1966                if len(phasedict['General']['Map'].get('rho',[])):
1967                    mapPhases.append(phasenam)
1968                    if phasedict['General']['Map'].get('Flip'):
1969                        choices.append('Charge flip map: '+str(phasenam))
1970                    elif phasedict['General']['Map'].get('MapType'):
1971                        choices.append(
1972                            str(phasedict['General']['Map'].get('MapType'))
1973                            + ' map: ' + str(phasenam))
1974                    else:
1975                        choices.append('unknown map: '+str(phasenam))
1976            # select a map if needed
1977            if len(mapPhases) == 0:
1978                self.G2frame.ErrorDialog(
1979                    'Empty project',
1980                    'Project does not contain any maps.')
1981                return True
1982            elif len(mapPhases) == 1:
1983                self.phasenam = mapPhases
1984            else: 
1985                phasenum = G2gd.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1986                if self.multiple:
1987                    if not phasenum: return True
1988                    self.phasenam = [mapPhases[i] for i in phasenum]
1989                else:
1990                    if phasenum is None: return True
1991                    self.phasenam = [mapPhases[phasenum]]
1992
1993        if AskFile:
1994            self.filename = self.askSaveFile()
1995        else:
1996            self.filename = self.defaultSaveFile()
1997        if not self.filename: return True
1998       
1999    # def SetupExport(self,event,AskFile=True):
2000    #     '''Determines the type of menu that called the Exporter. Selects histograms
2001    #     or phases when needed. Better to replace with individual calls to
2002    #     self.InitExport() and self.ExportSelect() so that the former can be called prior
2003    #     to self.LoadTree()
2004
2005    #     :param bool AskFile: if AskFile is True (default) get the name of the file
2006    #       in a dialog
2007    #     :returns: True in case of an error
2008    #     '''
2009    #     self.ExportInit(event)
2010    #     return self.ExportSelect(AskFile)
2011                   
2012    def loadParmDict(self):
2013        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
2014        refined values to those from the last cycle and set the uncertainties for the
2015        refined parameters in another dict (self.sigDict).
2016
2017        Expands the parm & sig dicts to include values derived from constraints.
2018        '''
2019        self.parmDict = {}
2020        self.sigDict = {}
2021        rigidbodyDict = {}
2022        covDict = {}
2023        consDict = {}
2024        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2025        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2026        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2027        while item:
2028            name = self.G2frame.PatternTree.GetItemText(item)
2029            if name == 'Rigid bodies':
2030                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
2031            elif name == 'Covariance':
2032                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
2033            elif name == 'Constraints':
2034                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
2035            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2036        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2037        self.parmDict.update(rbDict)
2038        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2039        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables =  G2stIO.GetPhaseData(
2040            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
2041        self.parmDict.update(phaseDict)
2042        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
2043            Phases,Histograms,Print=False,resetRefList=False)
2044        self.parmDict.update(hapDict)
2045        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
2046        self.parmDict.update(histDict)
2047        self.parmDict.update(zip(
2048            covDict.get('varyList',[]),
2049            covDict.get('variables',[])))
2050        self.sigDict = dict(zip(
2051            covDict.get('varyList',[]),
2052            covDict.get('sig',[])))
2053        # expand to include constraints: first compile a list of constraints
2054        constList = []
2055        for item in consDict:
2056            if item.startswith('_'): continue
2057            constList += consDict[item]
2058        # now process the constraints
2059        G2mv.InitVars()
2060        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
2061        varyList = covDict.get('varyListStart')
2062        if varyList is None and len(constDict) == 0:
2063            # no constraints can use varyList
2064            varyList = covDict.get('varyList')
2065        elif varyList is None:
2066            # old GPX file from before pre-constraint varyList is saved
2067            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
2068            raise Exception(' *** Export aborted ***')
2069        else:
2070            varyList = list(varyList)
2071        try:
2072            groups,parmlist = G2mv.GroupConstraints(constDict)
2073            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList)
2074        except:
2075            # this really should not happen
2076            print ' *** ERROR - constraints are internally inconsistent ***'
2077            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
2078            print 'Errors',errmsg
2079            if warnmsg: print 'Warnings',warnmsg
2080            raise Exception(' *** CIF creation aborted ***')
2081        # add the constrained values to the parameter dictionary
2082        G2mv.Dict2Map(self.parmDict,varyList)
2083        # and add their uncertainties into the esd dictionary (sigDict)
2084        if covDict.get('covMatrix') is not None:
2085            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
2086
2087    def loadTree(self):
2088        '''Load the contents of the data tree into a set of dicts
2089        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
2090        & self.xtalDict)
2091       
2092        * The childrenless data tree items are overall parameters/controls for the
2093          entire project and are placed in self.OverallParms
2094        * Phase items are placed in self.Phases
2095        * Data items are placed in self.Histogram. The key for these data items
2096          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
2097        '''
2098        self.OverallParms = {}
2099        self.powderDict = {}
2100        self.xtalDict = {}
2101        self.Phases = {}
2102        self.Histograms = {}
2103        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2104        histType = None       
2105        if self.currentExportType == 'phase':
2106            # if exporting phases load them here
2107            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
2108            if not sub:
2109                print 'no phases found'
2110                return True
2111            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
2112            while item:
2113                phaseName = self.G2frame.PatternTree.GetItemText(item)
2114                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
2115                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
2116            return
2117        elif self.currentExportType == 'single':
2118            histType = 'HKLF'
2119        elif self.currentExportType == 'powder':
2120            histType = 'PWDR'
2121        elif self.currentExportType == 'image':
2122            histType = 'IMG'
2123
2124        if histType: # Loading just one kind of tree entry
2125            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2126            while item:
2127                name = self.G2frame.PatternTree.GetItemText(item)
2128                if name.startswith(histType):
2129                    if self.Histograms.get(name): # there is already an item with this name
2130                        print('Histogram name '+str(name)+' is repeated. Renaming')
2131                        if name[-1] == '9':
2132                            name = name[:-1] + '10'
2133                        elif name[-1] in '012345678':
2134                            name = name[:-1] + str(int(name[-1])+1)
2135                        else:                           
2136                            name += '-1'
2137                    self.Histograms[name] = {}
2138                    # the main info goes into Data, but the 0th
2139                    # element contains refinement results, carry
2140                    # that over too now.
2141                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
2142                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
2143                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2144                    while item2: 
2145                        child = self.G2frame.PatternTree.GetItemText(item2)
2146                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
2147                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
2148                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2149            # index powder and single crystal histograms by number
2150            for hist in self.Histograms:
2151                if hist.startswith("PWDR"): 
2152                    d = self.powderDict
2153                elif hist.startswith("HKLF"): 
2154                    d = self.xtalDict
2155                else:
2156                    return                   
2157                i = self.Histograms[hist].get('hId')
2158                if i is None and not d.keys():
2159                    i = 0
2160                elif i is None or i in d.keys():
2161                    i = max(d.keys())+1
2162                d[i] = hist
2163            return
2164        # else standard load: using all interlinked phases and histograms
2165        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2166        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2167        while item:
2168            name = self.G2frame.PatternTree.GetItemText(item)
2169            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2170            if not item2: 
2171                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
2172            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2173        # index powder and single crystal histograms
2174        for hist in self.Histograms:
2175            i = self.Histograms[hist]['hId']
2176            if hist.startswith("PWDR"): 
2177                self.powderDict[i] = hist
2178            elif hist.startswith("HKLF"): 
2179                self.xtalDict[i] = hist
2180
2181    def dumpTree(self,mode='type'):
2182        '''Print out information on the data tree dicts loaded in loadTree
2183        '''
2184        print '\nOverall'
2185        if mode == 'type':
2186            def Show(arg): return type(arg)
2187        else:
2188            def Show(arg): return arg
2189        for key in self.OverallParms:
2190            print '  ',key,Show(self.OverallParms[key])
2191        print 'Phases'
2192        for key1 in self.Phases:
2193            print '    ',key1,Show(self.Phases[key1])
2194        print 'Histogram'
2195        for key1 in self.Histograms:
2196            print '    ',key1,Show(self.Histograms[key1])
2197            for key2 in self.Histograms[key1]:
2198                print '      ',key2,Show(self.Histograms[key1][key2])
2199
2200    def defaultSaveFile(self):
2201        return os.path.abspath(
2202            os.path.splitext(self.G2frame.GSASprojectfile
2203                             )[0]+self.extension)
2204       
2205    def askSaveFile(self):
2206        '''Ask the user to supply a file name
2207
2208        :returns: a file name (str)
2209        '''
2210        defnam = os.path.splitext(
2211            os.path.split(self.G2frame.GSASprojectfile)[1]
2212            )[0]+self.extension
2213        dlg = wx.FileDialog(
2214            self.G2frame, 'Input name for file to write', '.', defnam,
2215            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2216            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2217        dlg.CenterOnParent()
2218        try:
2219            if dlg.ShowModal() == wx.ID_OK:
2220                filename = dlg.GetPath()
2221                # make sure extension is correct
2222                filename = os.path.splitext(filename)[0]+self.extension
2223            else:
2224                filename = None
2225        finally:
2226            dlg.Destroy()
2227        return filename
2228
2229    # Tools for file writing.
2230    def OpenFile(self,fil=None):
2231        '''Open the output file
2232
2233        :param str fil: The name of the file to open. If None (default)
2234          the name defaults to self.filename.
2235        :returns: the file object opened by the routine which is also
2236          saved as self.fp
2237        '''
2238        if not fil:
2239            fil = self.filename
2240        self.fp = open(fil,'w')
2241        return self.fp
2242    def Write(self,line):
2243        '''write a line of output, attaching a line-end character
2244
2245        :param str line: the text to be written.
2246        '''
2247        self.fp.write(line+'\n')
2248    def CloseFile(self,fp=None):
2249        '''Close a file opened in OpenFile
2250
2251        :param file fp: the file object to be closed. If None (default)
2252          file object self.fp is closed.
2253        '''
2254        if fp is None:
2255            fp = self.fp
2256            self.fp = None
2257        fp.close()
2258    # Tools to pull information out of the data arrays
2259    def GetCell(self,phasenam):
2260        """Gets the unit cell parameters and their s.u.'s for a selected phase
2261
2262        :param str phasenam: the name for the selected phase
2263        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2264          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2265          cell values and `cellSig` has their uncertainties.
2266        """
2267        phasedict = self.Phases[phasenam] # pointer to current phase info
2268        try:
2269            pfx = str(phasedict['pId'])+'::'
2270            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2271            cellSig = G2stIO.getCellEsd(pfx,
2272                                        phasedict['General']['SGData'],A,
2273                                        self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2274            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2275            return cellList,cellSig
2276        except KeyError:
2277            cell = phasedict['General']['Cell'][1:]
2278            return cell,7*[0]
2279   
2280    def GetAtoms(self,phasenam):
2281        """Gets the atoms associated with a phase. Can be used with standard
2282        or macromolecular phases
2283
2284        :param str phasenam: the name for the selected phase
2285        :returns: a list of items for eac atom where each item is a list containing:
2286          label, typ, mult, xyz, and td, where
2287
2288          * label and typ are the atom label and the scattering factor type (str)
2289          * mult is the site multiplicity (int)
2290          * xyz is contains a list with four pairs of numbers:
2291            x, y, z and fractional occupancy and
2292            their standard uncertainty (or a negative value)
2293          * td is contains a list with either one or six pairs of numbers:
2294            if one number it is U\ :sub:`iso` and with six numbers it is
2295            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2296            paired with their standard uncertainty (or a negative value)
2297        """
2298        phasedict = self.Phases[phasenam] # pointer to current phase info           
2299        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2300        cfrac = cx+3
2301        fpfx = str(phasedict['pId'])+'::Afrac:'       
2302        atomslist = []
2303        for i,at in enumerate(phasedict['Atoms']):
2304            if phasedict['General']['Type'] == 'macromolecular':
2305                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2306            else:
2307                label = at[ct-1]
2308            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2309            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2310            mult = at[cs+1]
2311            typ = at[ct]
2312            xyz = []
2313            for j,v in enumerate(('x','y','z')):
2314                val = at[cx+j]
2315                pfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2316                sig = self.sigDict.get(pfx,-0.000009)
2317                xyz.append((val,sig))
2318            xyz.append((fval,fsig))
2319            td = []
2320            if at[cia] == 'I':
2321                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2322                val = self.parmDict.get(pfx,at[cia+1])
2323                sig = self.sigDict.get(pfx,-0.0009)
2324                td.append((val,sig))
2325            else:
2326                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2327                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2328                    val = self.parmDict.get(pfx,at[cia+2+i])
2329                    sig = self.sigDict.get(pfx,-0.0009)
2330                    td.append((val,sig))
2331            atomslist.append((label,typ,mult,xyz,td))
2332        return atomslist
2333######################################################################
2334
2335def ReadCIF(URLorFile):
2336    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2337    (from James Hester).
2338    The open routine gets confused with DOS names that begin with a letter and colon
2339    "C:\dir\" so this routine will try to open the passed name as a file and if that
2340    fails, try it as a URL
2341
2342    :param str URLorFile: string containing a URL or a file name. Code will try first
2343      to open it as a file and then as a URL.
2344
2345    :returns: a PyCifRW CIF object.
2346    '''
2347    import CifFile as cif # PyCifRW from James Hester
2348
2349    # alternate approach:
2350    #import urllib
2351    #ciffile = 'file:'+urllib.pathname2url(filename)
2352   
2353    try:
2354        fp = open(URLorFile,'r')
2355        cf = cif.ReadCif(fp)
2356        fp.close()
2357        return cf
2358    except IOError:
2359        return cif.ReadCif(URLorFile)
2360
2361if __name__ == '__main__':
2362    app = wx.PySimpleApp()
2363    frm = wx.Frame(None) # create a frame
2364    frm.Show(True)
2365    filename = '/tmp/notzip.zip'
2366    filename = '/tmp/all.zip'
2367    #filename = '/tmp/11bmb_7652.zip'
2368   
2369    #selection=None, confirmoverwrite=True, parent=None
2370    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2371    print ExtractFileFromZip(filename,multipleselect=True)
2372                             #confirmread=False, confirmoverwrite=False)
2373
2374    # choicelist=[ ('a','b','c'),
2375    #              ('test1','test2'),('no choice',)]
2376    # titles = [ 'a, b or c', 'tests', 'No option here']
2377    # dlg = MultipleChoicesDialog(
2378    #     choicelist,titles,
2379    #     parent=frm)
2380    # if dlg.ShowModal() == wx.ID_OK:
2381    #     print 'Got OK'
Note: See TracBrowser for help on using the repository browser.