source: trunk/GSASIIIO.py @ 1997

Last change on this file since 1997 was 1997, checked in by toby, 6 years ago

Add API for direct image read (G2IO.ExportPowderList?) and powder exports w/o GUI (G2IO.ExportPowderList?); redo export to add new method (Writer)

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