source: trunk/GSASIIIO.py @ 2041

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

enter space group (R -3 c) for Alumina image calibrant - gives better set of lines
some revision to SS derivatives
add ImageJ format (both big & little endian) to tiff importer

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