source: trunk/GSASIIIO.py @ 1331

Last change on this file since 1331 was 1331, checked in by vondreele, 9 years ago

fix cake integration problem - in G2IO return in wrong indent
fix seq refinement display problem in G2obj self.eObj.assgnVars[v] returned a list not a name

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