source: trunk/GSASIIIO.py @ 2001

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

add initial Image imports code

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