source: trunk/GSASIIIO.py @ 1572

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

complete SS indexing, apply hklm extinction rules
cleanup indexing, cell refine, load cell, make new phase, calibration, etc.

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