source: trunk/GSASIIIO.py @ 2035

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

try RTFD build again with new approach

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 114.9 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2015-10-29 21:25:57 +0000 (Thu, 29 Oct 2015) $
4# $Author: toby $
5# $Revision: 2035 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 2035 2015-10-29 21:25:57Z toby $
8########### SVN repository information ###################
9'''
10*GSASIIIO: Misc I/O routines*
11=============================
12
13Module with miscellaneous routines for input and output. Many
14are GUI routines to interact with user.
15
16Includes support for image reading.
17
18Also includes base classes for data import routines.
19
20'''
21"""GSASIIIO: functions for IO of data
22   Copyright: 2008, Robert B. Von Dreele (Argonne National Laboratory)
23"""
24import wx
25import math
26import numpy as np
27import cPickle
28import sys
29import re
30import random as ran
31import GSASIIpath
32GSASIIpath.SetVersionNumber("$Revision: 2035 $")
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 sizexy == [4096,4096]:
949        if IFD[273][2][0] == 8:
950            if IFD[258][2][0] == 16:
951                tifType = 'scanCCD'
952                pixy = [9,9]
953                File.seek(8)
954                if not imageOnly:
955                    print 'Read APS scanCCD tiff file: ',filename
956                image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
957        elif IFD[273][2][0] == 4096:
958            tifType = 'Rayonix'
959            pixy = [73.242,73.242]
960            File.seek(4096)
961            if not imageOnly:
962                print 'Read Rayonix MX300HE tiff file: ',filename
963            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
964#    elif sizexy == [960,960]:
965#        tiftype = 'PE-BE'
966#        pixy = (200,200)
967#        File.seek(8)
968#        if not imageOnly:
969#            print 'Read Gold tiff file:',filename
970#        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
971           
972    else:
973        lines = ['not a known detector tiff file',]
974        return lines,0,0,0
975
976    if sizexy[1]*sizexy[0] != image.size: # test is resize is allowed
977        lines = ['not a known detector tiff file',]
978        return lines,0,0,0
979       
980    image = np.reshape(image,(sizexy[1],sizexy[0]))
981    center = (not center[0]) and [pixy[0]*sizexy[0]/2000,pixy[1]*sizexy[1]/2000] or center
982    wavelength = (not wavelength) and 0.10 or wavelength
983    distance = (not distance) and 100.0 or distance
984    data = {'pixelSize':pixy,'wavelength':wavelength,'distance':distance,'center':center,'size':sizexy}
985    File.close()   
986    if imageOnly:
987        return image
988    else:
989        return head,data,Npix,image
990   
991#def GetTifData(filename,imageOnly=False):
992#    import struct as st
993#    import array as ar
994#    File = open(filename,'rb')
995#    dataType = 5
996#    try:
997#        Meta = open(filename+'.metadata','Ur')
998#        head = Meta.readlines()
999#        for line in head:
1000#            line = line.strip()
1001#            if 'dataType=' in line:
1002#                dataType = int(line.split('=')[1])
1003#        Meta.close()
1004#    except IOError:
1005#        print 'no metadata file found - will try to read file anyway'
1006#        head = ['no metadata file found',]
1007#       
1008#    tag = File.read(2)
1009#    byteOrd = '<'
1010#    if tag == 'II' and int(st.unpack('<h',File.read(2))[0]) == 42:     #little endian
1011#        IFD = int(st.unpack(byteOrd+'i',File.read(4))[0])
1012#    elif tag == 'MM' and int(st.unpack('>h',File.read(2))[0]) == 42:   #big endian
1013#        byteOrd = '>'
1014#        IFD = int(st.unpack(byteOrd+'i',File.read(4))[0])       
1015#    else:
1016#        lines = ['not a detector tiff file',]
1017#        return lines,0,0,0
1018#    File.seek(IFD)                                                  #get number of directory entries
1019#    NED = int(st.unpack(byteOrd+'h',File.read(2))[0])
1020#    IFD = {}
1021#    for ied in range(NED):
1022#        Tag,Type = st.unpack(byteOrd+'Hh',File.read(4))
1023#        nVal = st.unpack(byteOrd+'i',File.read(4))[0]
1024#        if Type == 1:
1025#            Value = st.unpack(byteOrd+nVal*'b',File.read(nVal))
1026#        elif Type == 2:
1027#            Value = st.unpack(byteOrd+'i',File.read(4))
1028#        elif Type == 3:
1029#            Value = st.unpack(byteOrd+nVal*'h',File.read(nVal*2))
1030#            x = st.unpack(byteOrd+nVal*'h',File.read(nVal*2))
1031#        elif Type == 4:
1032#            Value = st.unpack(byteOrd+nVal*'i',File.read(nVal*4))
1033#        elif Type == 5:
1034#            Value = st.unpack(byteOrd+nVal*'i',File.read(nVal*4))
1035#        elif Type == 11:
1036#            Value = st.unpack(byteOrd+nVal*'f',File.read(nVal*4))
1037#        IFD[Tag] = [Type,nVal,Value]
1038##        print Tag,IFD[Tag]
1039#    sizexy = [IFD[256][2][0],IFD[257][2][0]]
1040#    [nx,ny] = sizexy
1041#    Npix = nx*ny
1042#    if 272 in IFD:
1043#        ifd = IFD[272]
1044#        File.seek(ifd[2][0])
1045#        S = File.read(ifd[1])
1046#        if 'PILATUS' in S:
1047#            tifType = 'Pilatus'
1048#            dataType = 0
1049#            pixy = (172,172)
1050#            File.seek(4096)
1051#            if not imageOnly:
1052#                print 'Read Pilatus tiff file: ',filename
1053#            image = ar.array('L',File.read(4*Npix))
1054#            image = np.array(np.asarray(image),dtype=np.int32)
1055#    elif 262 in IFD and IFD[262][2][0] > 4:
1056#        tifType = 'DND'
1057#        pixy = (158,158)
1058#        File.seek(512)
1059#        if not imageOnly:
1060#            print 'Read DND SAX/WAX-detector tiff file: ',filename
1061#        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
1062#    elif sizexy == [1536,1536]:
1063#        tifType = 'APS Gold'
1064#        pixy = (150,150)
1065#        File.seek(64)
1066#        if not imageOnly:
1067#            print 'Read Gold tiff file:',filename
1068#        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
1069#    elif sizexy == [2048,2048] or sizexy == [1024,1024] or sizexy == [3072,3072]:
1070#        if IFD[273][2][0] == 8:
1071#            if IFD[258][2][0] == 32:
1072#                tifType = 'PE'
1073#                pixy = (200,200)
1074#                File.seek(8)
1075#                if not imageOnly:
1076#                    print 'Read APS PE-detector tiff file: ',filename
1077#                if dataType == 5:
1078#                    image = np.array(ar.array('f',File.read(4*Npix)),dtype=np.float32)
1079#                else:
1080#                    image = np.array(ar.array('I',File.read(4*Npix)),dtype=np.int32)
1081#        elif IFD[273][2][0] == 4096:
1082#            if sizexy[0] == 3072:
1083#                pixy =  (73,73)
1084#                tifType = 'MAR225'           
1085#            else:
1086#                pixy = (158,158)
1087#                tifType = 'MAR325'           
1088#            File.seek(4096)
1089#            if not imageOnly:
1090#                print 'Read MAR CCD tiff file: ',filename
1091#            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
1092#        elif IFD[273][2][0] == 512:
1093#            tiftype = '11-ID-C'
1094#            pixy = [200,200]
1095#            File.seek(512)
1096#            if not imageOnly:
1097#                print 'Read 11-ID-C tiff file: ',filename
1098#            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)           
1099#    elif sizexy == [4096,4096]:
1100#        if IFD[273][2][0] == 8:
1101#            if IFD[258][2][0] == 16:
1102#                tifType = 'scanCCD'
1103#                pixy = (9,9)
1104#                File.seek(8)
1105#                if not imageOnly:
1106#                    print 'Read APS scanCCD tiff file: ',filename
1107#                image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
1108#        elif IFD[273][2][0] == 4096:
1109#            tifType = 'Rayonix'
1110#            pixy = (73.242,73.242)
1111#            File.seek(4096)
1112#            if not imageOnly:
1113#                print 'Read Rayonix MX300HE tiff file: ',filename
1114#            image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
1115##    elif sizexy == [960,960]:
1116##        tiftype = 'PE-BE'
1117##        pixy = (200,200)
1118##        File.seek(8)
1119##        if not imageOnly:
1120##            print 'Read Gold tiff file:',filename
1121##        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
1122#           
1123#    else:
1124#        lines = ['not a known detector tiff file',]
1125#        return lines,0,0,0
1126#       
1127#    image = np.reshape(image,(sizexy[1],sizexy[0]))
1128#    center = [pixy[0]*sizexy[0]/2000,pixy[1]*sizexy[1]/2000]
1129#    data = {'pixelSize':pixy,'wavelength':0.10,'distance':100.0,'center':center,'size':sizexy}
1130#    File.close()   
1131#    if imageOnly:
1132#        return image
1133#    else:
1134#        return head,data,Npix,image
1135#   
1136def ProjFileOpen(G2frame):
1137    'Read a GSAS-II project file and load into the G2 data tree'
1138    if not os.path.exists(G2frame.GSASprojectfile):
1139        print ('\n*** Error attempt to open project file that does not exist:\n   '+
1140               str(G2frame.GSASprojectfile))
1141        return
1142    LastSavedUsing = None
1143    file = open(G2frame.GSASprojectfile,'rb')
1144    print 'load from file: ',G2frame.GSASprojectfile
1145    G2frame.SetTitle("GSAS-II data tree: "+
1146                     os.path.split(G2frame.GSASprojectfile)[1])
1147    wx.BeginBusyCursor()
1148    try:
1149        while True:
1150            try:
1151                data = cPickle.load(file)
1152            except EOFError:
1153                break
1154            datum = data[0]
1155           
1156            Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=datum[0])
1157            if 'PWDR' in datum[0]:               
1158                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
1159                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
1160                G2frame.PatternTree.SetItemPyData(Id,datum[1][:3])  #temp. trim off junk (patch?)
1161            elif datum[0].startswith('HKLF'): 
1162                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
1163                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
1164                G2frame.PatternTree.SetItemPyData(Id,datum[1])
1165            else:
1166                G2frame.PatternTree.SetItemPyData(Id,datum[1])             
1167                if datum[0] == 'Controls' and 'LastSavedUsing' in datum[1]:
1168                    LastSavedUsing = datum[1]['LastSavedUsing']
1169                if datum[0] == 'Controls' and 'PythonVersions' in datum[1] and GSASIIpath.GetConfigValue('debug'):
1170                    print('Packages used to create .GPX file:')
1171                    if 'dict' in str(type(datum[1]['PythonVersions'])):  #patch
1172                        for p in sorted(datum[1]['PythonVersions'],key=lambda s: s.lower()):
1173                            print("{:>14s}: {:s}".format(p[0],p[1]))
1174                    else:
1175                        for p in datum[1]['PythonVersions']:
1176                            print("{:<12s} {:s}".format(p[0]+':',p[1]))
1177            for datus in data[1:]:
1178                sub = G2frame.PatternTree.AppendItem(Id,datus[0])
1179#patch
1180                if datus[0] == 'Instrument Parameters' and len(datus[1]) == 1:
1181                    if 'PWDR' in datum[0]:
1182                        datus[1] = [dict(zip(datus[1][3],zip(datus[1][0],datus[1][1],datus[1][2]))),{}]
1183                    else:
1184                        datus[1] = [dict(zip(datus[1][2],zip(datus[1][0],datus[1][1]))),{}]
1185                    for item in datus[1][0]:               #zip makes tuples - now make lists!
1186                        datus[1][0][item] = list(datus[1][0][item])
1187#end patch
1188                G2frame.PatternTree.SetItemPyData(sub,datus[1])
1189            if 'IMG' in datum[0]:                   #retrieve image default flag & data if set
1190                Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
1191                if Data['setDefault']:
1192                    G2frame.imageDefault = Data               
1193        file.close()
1194        if LastSavedUsing:
1195            print('GPX load successful. Last saved with GSAS-II version '+LastSavedUsing)
1196        else:
1197            print('project load successful')
1198        G2frame.NewPlot = True
1199    except:
1200        msg = wx.MessageDialog(G2frame,message="Error reading file "+
1201            str(G2frame.GSASprojectfile)+". This is not a GSAS-II .gpx file",
1202            caption="Load Error",style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
1203        msg.ShowModal()
1204    finally:
1205        wx.EndBusyCursor()
1206        G2frame.Status.SetStatusText('To reorder tree items, use mouse RB to drag/drop them')
1207   
1208def ProjFileSave(G2frame):
1209    'Save a GSAS-II project file'
1210    if not G2frame.PatternTree.IsEmpty():
1211        file = open(G2frame.GSASprojectfile,'wb')
1212        print 'save to file: ',G2frame.GSASprojectfile
1213        # stick the file name into the tree and version info into tree so they are saved.
1214        # (Controls should always be created at this point)
1215        try:
1216            Controls = G2frame.PatternTree.GetItemPyData(
1217                G2gd.GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
1218            Controls['LastSavedAs'] = os.path.abspath(G2frame.GSASprojectfile)
1219            Controls['LastSavedUsing'] = str(GSASIIpath.GetVersionNumber())
1220            Controls['PythonVersions'] = G2frame.PackageVersions
1221        except:
1222            pass
1223        wx.BeginBusyCursor()
1224        try:
1225            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1226            while item:
1227                data = []
1228                name = G2frame.PatternTree.GetItemText(item)
1229                data.append([name,G2frame.PatternTree.GetItemPyData(item)])
1230                item2, cookie2 = G2frame.PatternTree.GetFirstChild(item)
1231                while item2:
1232                    name = G2frame.PatternTree.GetItemText(item2)
1233                    data.append([name,G2frame.PatternTree.GetItemPyData(item2)])
1234                    item2, cookie2 = G2frame.PatternTree.GetNextChild(item, cookie2)                           
1235                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)                           
1236                cPickle.dump(data,file,1)
1237            file.close()
1238        finally:
1239            wx.EndBusyCursor()
1240        print('project save successful')
1241
1242def SaveIntegration(G2frame,PickId,data):
1243    'Save image integration results as powder pattern(s)'
1244    azms = G2frame.Integrate[1]
1245    X = G2frame.Integrate[2][:-1]
1246    N = len(X)
1247    Id = G2frame.PatternTree.GetItemParent(PickId)
1248    name = G2frame.PatternTree.GetItemText(Id)
1249    name = name.replace('IMG ',data['type']+' ')
1250    Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Comments'))
1251    if 'PWDR' in name:
1252        names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
1253        codes = [0 for i in range(11)]
1254    elif 'SASD' in name:
1255        names = ['Type','Lam','Zero','Azimuth'] 
1256        codes = [0 for i in range(4)]
1257        X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
1258    Xminmax = [X[0],X[-1]]
1259    LRazm = data['LRazimuth']
1260    Azms = []
1261    dazm = 0.
1262    if data['fullIntegrate'] and data['outAzimuths'] == 1:
1263        Azms = [45.0,]                              #a poor man's average?
1264    else:
1265        for i,azm in enumerate(azms[:-1]):
1266            if azm > 360. and azms[i+1] > 360.:
1267                Azms.append(G2img.meanAzm(azm%360.,azms[i+1]%360.))
1268            else:   
1269                Azms.append(G2img.meanAzm(azm,azms[i+1]))
1270        dazm = np.min(np.abs(np.diff(azms)))/2.
1271    G2frame.IntgOutList = []
1272    for i,azm in enumerate(azms[:-1]):
1273        Aname = name+" Azm= %.2f"%((azm+dazm)%360.)
1274        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1275        nOcc = 0
1276        while item:
1277            Name = G2frame.PatternTree.GetItemText(item)
1278            if Aname in Name:
1279                nOcc += 1
1280            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1281        if nOcc:
1282            Aname += '(%d)'%(nOcc)
1283        Sample = G2pdG.SetDefaultSample()
1284        Sample['Gonio. radius'] = data['distance']
1285        Sample['Omega'] = data['GonioAngles'][0]
1286        Sample['Chi'] = data['GonioAngles'][1]
1287        Sample['Phi'] = data['GonioAngles'][2]
1288        Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
1289        if 'PWDR' in Aname:
1290            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!
1291        elif 'SASD' in Aname:
1292            Sample['Trans'] = data['SampleAbs'][0]
1293            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
1294        Y = G2frame.Integrate[0][i]
1295        W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
1296        Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=Aname)
1297        G2frame.IntgOutList.append(Id)
1298        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
1299        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
1300        if 'PWDR' in Aname:
1301            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
1302                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1303        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
1304        for item in inst[0]:
1305            inst[0][item] = list(inst[0][item])
1306        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
1307        if 'PWDR' in Aname:
1308            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1309            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Peak List'),{'sigDict':{},'peaks':[]})
1310            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
1311            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Unit Cells List'),[])
1312            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Reflection Lists'),{})
1313        elif 'SASD' in Aname:             
1314            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1315            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1316            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1317        valuesdict = {
1318            'wtFactor':1.0,
1319            'Dummy':False,
1320            'ranId':ran.randint(0,sys.maxint),
1321            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
1322            'qPlot':False,'dPlot':False,'sqrtPlot':False
1323            }
1324        G2frame.PatternTree.SetItemPyData(
1325            Id,[valuesdict,
1326                [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
1327    return Id       #last powder pattern generated
1328           
1329# def powderFxyeSave(G2frame,exports,powderfile):
1330#     'Save a powder histogram as a GSAS FXYE file'
1331#     head,tail = ospath.split(powderfile)
1332#     name,ext = tail.split('.')
1333#     for i,export in enumerate(exports):
1334#         filename = ospath.join(head,name+'-%03d.'%(i)+ext)
1335#         prmname = filename.strip(ext)+'prm'
1336#         prm = open(prmname,'w')      #old style GSAS parm file
1337#         PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1338#         Inst = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame, \
1339#             PickId, 'Instrument Parameters'))[0]
1340#         prm.write( '            123456789012345678901234567890123456789012345678901234567890        '+'\n')
1341#         prm.write( 'INS   BANK      1                                                               '+'\n')
1342#         prm.write(('INS   HTYPE   %sR                                                              '+'\n')%(Inst['Type'][0]))
1343#         if 'Lam1' in Inst:              #Ka1 & Ka2
1344#             prm.write(('INS  1 ICONS%10.7f%10.7f    0.0000               0.990    0     0.500   '+'\n')%(Inst['Lam1'][0],Inst['Lam2'][0]))
1345#         elif 'Lam' in Inst:             #single wavelength
1346#             prm.write(('INS  1 ICONS%10.7f%10.7f    0.0000               0.990    0     0.500   '+'\n')%(Inst['Lam'][1],0.0))
1347#         prm.write( 'INS  1 IRAD     0                                                               '+'\n')
1348#         prm.write( 'INS  1I HEAD                                                                    '+'\n')
1349#         prm.write( 'INS  1I ITYP    0    0.0000  180.0000         1                                 '+'\n')
1350#         prm.write(('INS  1DETAZM%10.3f                                                          '+'\n')%(Inst['Azimuth'][0]))
1351#         prm.write( 'INS  1PRCF1     3    8   0.00100                                                '+'\n')
1352#         prm.write(('INS  1PRCF11     %15.6g%15.6g%15.6g%15.6g   '+'\n')%(Inst['U'][1],Inst['V'][1],Inst['W'][1],0.0))
1353#         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.))
1354#         prm.close()
1355#         file = open(filename,'w')
1356#         print 'save powder pattern to file: ',filename
1357#         x,y,w,yc,yb,yd = G2frame.PatternTree.GetItemPyData(PickId)[1]
1358#         file.write(powderfile+'\n')
1359#         file.write('Instrument parameter file:'+ospath.split(prmname)[1]+'\n')
1360#         file.write('BANK 1 %d %d CONS %.2f %.2f 0 0 FXYE\n'%(len(x),len(x),\
1361#             100.*x[0],100.*(x[1]-x[0])))
1362#         s = list(np.sqrt(1./np.array(w)))       
1363#         XYW = zip(x,y,s)
1364#         for X,Y,S in XYW:
1365#             file.write("%15.6g %15.6g %15.6g\n" % (100.*X,Y,max(S,1.0)))
1366#         file.close()
1367#         print 'powder pattern file '+filename+' written'
1368       
1369# def powderXyeSave(G2frame,exports,powderfile):
1370#     'Save a powder histogram as a Topas XYE file'
1371#     head,tail = ospath.split(powderfile)
1372#     name,ext = tail.split('.')
1373#     for i,export in enumerate(exports):
1374#         filename = ospath.join(head,name+'-%03d.'%(i)+ext)
1375#         PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1376#         file = open(filename,'w')
1377#         file.write('#%s\n'%(export))
1378#         print 'save powder pattern to file: ',filename
1379#         x,y,w,yc,yb,yd = G2frame.PatternTree.GetItemPyData(PickId)[1]
1380#         s = list(np.sqrt(1./np.array(w)))       
1381#         XYW = zip(x,y,s)
1382#         for X,Y,W in XYW:
1383#             file.write("%15.6g %15.6g %15.6g\n" % (X,Y,W))
1384#         file.close()
1385#         print 'powder pattern file '+filename+' written'
1386       
1387def PDFSave(G2frame,exports):
1388    'Save a PDF G(r) and S(Q) in column formats'
1389    for export in exports:
1390        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1391        SQname = 'S(Q)'+export[4:]
1392        GRname = 'G(R)'+export[4:]
1393        sqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.sq')
1394        grfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.gr')
1395        sqId = G2gd.GetPatternTreeItemId(G2frame, PickId, SQname)
1396        grId = G2gd.GetPatternTreeItemId(G2frame, PickId, GRname)
1397        sqdata = np.array(G2frame.PatternTree.GetItemPyData(sqId)[1][:2]).T
1398        grdata = np.array(G2frame.PatternTree.GetItemPyData(grId)[1][:2]).T
1399        sqfile = open(sqfilename,'w')
1400        grfile = open(grfilename,'w')
1401        sqfile.write('#T S(Q) %s\n'%(export))
1402        grfile.write('#T G(R) %s\n'%(export))
1403        sqfile.write('#L Q     S(Q)\n')
1404        grfile.write('#L R     G(R)\n')
1405        for q,sq in sqdata:
1406            sqfile.write("%15.6g %15.6g\n" % (q,sq))
1407        sqfile.close()
1408        for r,gr in grdata:
1409            grfile.write("%15.6g %15.6g\n" % (r,gr))
1410        grfile.close()
1411   
1412def PeakListSave(G2frame,file,peaks):
1413    'Save powder peaks to a data file'
1414    print 'save peak list to file: ',G2frame.peaklistfile
1415    if not peaks:
1416        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1417        try:
1418            result = dlg.ShowModal()
1419        finally:
1420            dlg.Destroy()
1421        return
1422    for peak in peaks:
1423        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
1424            (peak[0],peak[2],peak[4],peak[6]))
1425    print 'peak list saved'
1426             
1427def IndexPeakListSave(G2frame,peaks):
1428    'Save powder peaks from the indexing list'
1429    file = open(G2frame.peaklistfile,'wa')
1430    print 'save index peak list to file: ',G2frame.peaklistfile
1431    wx.BeginBusyCursor()
1432    try:
1433        if not peaks:
1434            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1435            try:
1436                result = dlg.ShowModal()
1437            finally:
1438                dlg.Destroy()
1439            return
1440        for peak in peaks:
1441            file.write("%12.6f\n" % (peak[7]))
1442        file.close()
1443    finally:
1444        wx.EndBusyCursor()
1445    print 'index peak list saved'
1446   
1447def SetNewPhase(Name='New Phase',SGData=None,cell=None,Super=None):
1448    '''Create a new phase dict with default values for various parameters
1449
1450    :param str Name: Name for new Phase
1451
1452    :param dict SGData: space group data from :func:`GSASIIspc:SpcGroup`;
1453      defaults to data for P 1
1454
1455    :param list cell: unit cell parameter list; defaults to
1456      [1.0,1.0,1.0,90.,90,90.,1.]
1457
1458    '''
1459    if SGData is None: SGData = G2spc.SpcGroup('P 1')[1]
1460    if cell is None: cell=[1.0,1.0,1.0,90.,90,90.,1.]
1461    phaseData = {
1462        'ranId':ran.randint(0,sys.maxint),
1463        'General':{
1464            'Name':Name,
1465            'Type':'nuclear',
1466            'AtomPtrs':[3,1,7,9],
1467            'SGData':SGData,
1468            'Cell':[False,]+cell,
1469            'Pawley dmin':1.0,
1470            'Data plot type':'None',
1471            'SH Texture':{
1472                'Order':0,
1473                'Model':'cylindrical',
1474                'Sample omega':[False,0.0],
1475                'Sample chi':[False,0.0],
1476                'Sample phi':[False,0.0],
1477                'SH Coeff':[False,{}],
1478                'SHShow':False,
1479                'PFhkl':[0,0,1],
1480                'PFxyz':[0,0,1],
1481                'PlotType':'Pole figure',
1482                'Penalty':[['',],0.1,False,1.0]}},
1483        'Atoms':[],
1484        'Drawing':{},
1485        'Histograms':{},
1486        'Pawley ref':[],
1487        'RBModels':{},
1488        }
1489    if Super and Super.get('Use',False):
1490        phaseData['General'].update({'Type':'modulated','Super':True,'SuperSg':Super['ssSymb']})
1491        phaseData['General']['SSGData'] = G2spc.SSpcGroup(SGData,Super['ssSymb'])
1492        phaseData['General']['SuperVec'] = [Super['ModVec'],False,Super['maxH']]
1493
1494    return phaseData
1495       
1496class MultipleChoicesDialog(wx.Dialog):
1497    '''A dialog that offers a series of choices, each with a
1498    title and a wx.Choice widget. Intended to be used Modally.
1499    typical input:
1500
1501        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1502        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1503       
1504    selections are placed in self.chosen when OK is pressed
1505    '''
1506    def __init__(self,choicelist,headinglist,
1507                 head='Select options',
1508                 title='Please select from options below',
1509                 parent=None):
1510        self.chosen = []
1511        wx.Dialog.__init__(
1512            self,parent,wx.ID_ANY,head, 
1513            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1514        panel = wx.Panel(self)
1515        mainSizer = wx.BoxSizer(wx.VERTICAL)
1516        mainSizer.Add((10,10),1)
1517        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1518        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1519        self.ChItems = []
1520        for choice,lbl in zip(choicelist,headinglist):
1521            mainSizer.Add((10,10),1)
1522            self.chosen.append(0)
1523            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1524            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1525            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1526            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1527
1528        OkBtn = wx.Button(panel,-1,"Ok")
1529        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1530        cancelBtn = wx.Button(panel,-1,"Cancel")
1531        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1532        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1533        btnSizer.Add((20,20),1)
1534        btnSizer.Add(OkBtn)
1535        btnSizer.Add((20,20),1)
1536        btnSizer.Add(cancelBtn)
1537        btnSizer.Add((20,20),1)
1538        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1539        panel.SetSizer(mainSizer)
1540        panel.Fit()
1541        self.Fit()
1542       
1543    def OnOk(self,event):
1544        parent = self.GetParent()
1545        if parent is not None: parent.Raise()
1546        # save the results from the choice widgets
1547        self.chosen = []
1548        for w in self.ChItems:
1549            self.chosen.append(w.GetSelection())
1550        self.EndModal(wx.ID_OK)             
1551           
1552    def OnCancel(self,event):
1553        parent = self.GetParent()
1554        if parent is not None: parent.Raise()
1555        self.chosen = []
1556        self.EndModal(wx.ID_CANCEL)             
1557           
1558def ExtractFileFromZip(filename, selection=None, confirmread=True,
1559                       confirmoverwrite=True, parent=None,
1560                       multipleselect=False):
1561    '''If the filename is a zip file, extract a file from that
1562    archive.
1563
1564    :param list Selection: used to predefine the name of the file
1565      to be extracted. Filename case and zip directory name are
1566      ignored in selection; the first matching file is used.
1567
1568    :param bool confirmread: if True asks the user to confirm before expanding
1569      the only file in a zip
1570
1571    :param bool confirmoverwrite: if True asks the user to confirm
1572      before overwriting if the extracted file already exists
1573
1574    :param bool multipleselect: if True allows more than one zip
1575      file to be extracted, a list of file(s) is returned.
1576      If only one file is present, do not ask which one, otherwise
1577      offer a list of choices (unless selection is used).
1578   
1579    :returns: the name of the file that has been created or a
1580      list of files (see multipleselect)
1581
1582    If the file is not a zipfile, return the name of the input file.
1583    If the zipfile is empty or no file has been selected, return None
1584    '''
1585    import zipfile # do this now, since we can save startup time by doing this only on need
1586    import shutil
1587    zloc = os.path.split(filename)[0]
1588    if not zipfile.is_zipfile(filename):
1589        #print("not zip")
1590        return filename
1591
1592    z = zipfile.ZipFile(filename,'r')
1593    zinfo = z.infolist()
1594
1595    if len(zinfo) == 0:
1596        #print('Zip has no files!')
1597        zlist = [-1]
1598    if selection:
1599        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1600        if selection.lower() in choices:
1601            zlist = [choices.index(selection.lower())]
1602        else:
1603            print('debug: file '+str(selection)+' was not found in '+str(filename))
1604            zlist = [-1]
1605    elif len(zinfo) == 1 and confirmread:
1606        result = wx.ID_NO
1607        dlg = wx.MessageDialog(
1608            parent,
1609            'Is file '+str(zinfo[0].filename)+
1610            ' what you want to extract from '+
1611            str(os.path.split(filename)[1])+'?',
1612            'Confirm file', 
1613            wx.YES_NO | wx.ICON_QUESTION)
1614        try:
1615            result = dlg.ShowModal()
1616        finally:
1617            dlg.Destroy()
1618        if result == wx.ID_NO:
1619            zlist = [-1]
1620        else:
1621            zlist = [0]
1622    elif len(zinfo) == 1:
1623        zlist = [0]
1624    elif multipleselect:
1625        # select one or more from a from list
1626        choices = [i.filename for i in zinfo]
1627        dlg = G2G.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1628            'Choose file(s)',choices)
1629        if dlg.ShowModal() == wx.ID_OK:
1630            zlist = dlg.GetSelections()
1631        else:
1632            zlist = []
1633        dlg.Destroy()
1634    else:
1635        # select one from a from list
1636        choices = [i.filename for i in zinfo]
1637        dlg = wx.SingleChoiceDialog(parent,
1638            'Select file to extract from zip file'+str(filename),'Choose file',
1639            choices,)
1640        if dlg.ShowModal() == wx.ID_OK:
1641            zlist = [dlg.GetSelection()]
1642        else:
1643            zlist = [-1]
1644        dlg.Destroy()
1645       
1646    outlist = []
1647    for zindex in zlist:
1648        if zindex >= 0:
1649            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1650            if os.path.exists(efil) and confirmoverwrite:
1651                result = wx.ID_NO
1652                dlg = wx.MessageDialog(parent,
1653                    'File '+str(efil)+' already exists. OK to overwrite it?',
1654                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1655                try:
1656                    result = dlg.ShowModal()
1657                finally:
1658                    dlg.Destroy()
1659                if result == wx.ID_NO:
1660                    zindex = -1
1661        if zindex >= 0:
1662            # extract the file to the current directory, regardless of it's original path
1663            #z.extract(zinfo[zindex],zloc)
1664            eloc,efil = os.path.split(zinfo[zindex].filename)
1665            outfile = os.path.join(zloc, efil)
1666            fpin = z.open(zinfo[zindex])
1667            fpout = file(outfile, "wb")
1668            shutil.copyfileobj(fpin, fpout)
1669            fpin.close()
1670            fpout.close()
1671            outlist.append(outfile)
1672    z.close()
1673    if multipleselect and len(outlist) >= 1:
1674        return outlist
1675    elif len(outlist) == 1:
1676        return outlist[0]
1677    else:
1678        return None
1679
1680######################################################################
1681# base classes for reading various types of data files
1682#   not used directly, only by subclassing
1683######################################################################
1684try:
1685    E,SGData = G2spc.SpcGroup('P 1') # data structure for default space group
1686except: # errors on doc build
1687    SGData = None
1688P1SGData = SGData
1689class ImportBaseclass(object):
1690    '''Defines a base class for the reading of input files (diffraction
1691    data, coordinates,...). See :ref:`Writing a Import Routine<Import_routines>`
1692    for an explanation on how to use a subclass of this class.
1693    '''
1694    class ImportException(Exception):
1695        '''Defines an Exception that is used when an import routine hits an expected error,
1696        usually in .Reader.
1697
1698        Good practice is that the Reader should define a value in self.errors that
1699        tells the user some information about what is wrong with their file.         
1700        '''
1701        pass
1702
1703    def __init__(self,
1704                 formatName,
1705                 longFormatName=None,
1706                 extensionlist=[],
1707                 strictExtension=False,
1708                 ):
1709        self.formatName = formatName # short string naming file type
1710        if longFormatName: # longer string naming file type
1711            self.longFormatName = longFormatName
1712        else:
1713            self.longFormatName = formatName
1714        # define extensions that are allowed for the file type
1715        # for windows, remove any extensions that are duplicate, as case is ignored
1716        if sys.platform == 'windows' and extensionlist:
1717            extensionlist = list(set([s.lower() for s in extensionlist]))
1718        self.extensionlist = extensionlist
1719        # If strictExtension is True, the file will not be read, unless
1720        # the extension matches one in the extensionlist
1721        self.strictExtension = strictExtension
1722        self.errors = ''
1723        self.warnings = ''
1724        # used for readers that will use multiple passes to read
1725        # more than one data block
1726        self.repeat = False
1727        self.selections = []
1728        self.repeatcount = 0
1729        self.readfilename = '?'
1730        #print 'created',self.__class__
1731
1732    def ReInitialize(self):
1733        'Reinitialize the Reader to initial settings'
1734        self.errors = ''
1735        self.warnings = ''
1736        self.repeat = False
1737        self.repeatcount = 0
1738        self.readfilename = '?'
1739
1740    def BlockSelector(self, ChoiceList, ParentFrame=None,
1741                      title='Select a block',
1742                      size=None, header='Block Selector',
1743                      useCancel=True):
1744        ''' Provide a wx dialog to select a block if the file contains more
1745        than one set of data and one must be selected
1746        '''
1747        if useCancel:
1748            dlg = wx.SingleChoiceDialog(
1749                ParentFrame,title, header,ChoiceList)
1750        else:
1751            dlg = wx.SingleChoiceDialog(
1752                ParentFrame,title, header,ChoiceList,
1753                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1754        if size: dlg.SetSize(size)
1755        dlg.CenterOnParent()
1756        if dlg.ShowModal() == wx.ID_OK:
1757            sel = dlg.GetSelection()
1758            return sel
1759        else:
1760            return None
1761        dlg.Destroy()
1762
1763    def MultipleBlockSelector(self, ChoiceList, ParentFrame=None,
1764        title='Select a block',size=None, header='Block Selector'):
1765        '''Provide a wx dialog to select a block of data if the
1766        file contains more than one set of data and one must be
1767        selected.
1768
1769        :returns: a list of the selected blocks
1770        '''
1771        dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1772            wx.CHOICEDLG_STYLE)
1773        dlg.CenterOnParent()
1774        if size: dlg.SetSize(size)
1775        if dlg.ShowModal() == wx.ID_OK:
1776            sel = dlg.GetSelections()
1777        else:
1778            return []
1779        dlg.Destroy()
1780        selected = []
1781        if len(ChoiceList) in sel:
1782            return range(len(ChoiceList))
1783        else:
1784            return sel
1785        return selected
1786
1787    def MultipleChoicesDialog(self, choicelist, headinglist, ParentFrame=None, **kwargs):
1788        '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1789        widget. Typical input:
1790       
1791           * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1792           
1793           * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1794           
1795        optional keyword parameters are: head (window title) and title
1796        returns a list of selected indicies for each choice (or None)
1797        '''
1798        result = None
1799        dlg = MultipleChoicesDialog(choicelist,headinglist,
1800            parent=ParentFrame, **kwargs)         
1801        dlg.CenterOnParent()
1802        if dlg.ShowModal() == wx.ID_OK:
1803            result = dlg.chosen
1804        dlg.Destroy()
1805        return result
1806
1807    def ShowBusy(self):
1808        wx.BeginBusyCursor()
1809#        wx.Yield() # make it happen now!
1810
1811    def DoneBusy(self):
1812        wx.EndBusyCursor()
1813        wx.Yield() # make it happen now!
1814       
1815#    def Reader(self, filename, filepointer, ParentFrame=None, **unused):
1816#        '''This method must be supplied in the child class to read the file.
1817#        if the read fails either return False or raise an Exception
1818#        preferably of type ImportException.
1819#        '''
1820#        #start reading
1821#        raise ImportException("Error occurred while...")
1822#        self.errors += "Hint for user on why the error occur
1823#        return False # if an error occurs
1824#        return True # if read OK
1825
1826    def ExtensionValidator(self, filename):
1827        '''This methods checks if the file has the correct extension
1828        Return False if this filename will not be supported by this reader
1829        Return True if the extension matches the list supplied by the reader
1830        Return None if the reader allows un-registered extensions
1831        '''
1832        if filename:
1833            ext = os.path.splitext(filename)[1]
1834            if sys.platform == 'windows': ext = ext.lower()
1835            if ext in self.extensionlist: return True
1836            if self.strictExtension: return False
1837        return None
1838
1839    def ContentsValidator(self, filepointer):
1840        '''This routine will attempt to determine if the file can be read
1841        with the current format.
1842        This will typically be overridden with a method that
1843        takes a quick scan of [some of]
1844        the file contents to do a "sanity" check if the file
1845        appears to match the selected format.
1846        Expected to be called via self.Validator()
1847        '''
1848        #filepointer.seek(0) # rewind the file pointer
1849        return True
1850
1851    def CIFValidator(self, filepointer):
1852        '''A :meth:`ContentsValidator` for use to validate CIF files.
1853        '''
1854        for i,l in enumerate(filepointer):
1855            if i >= 1000: return True
1856            '''Encountered only blank lines or comments in first 1000
1857            lines. This is unlikely, but assume it is CIF anyway, since we are
1858            even less likely to find a file with nothing but hashes and
1859            blank lines'''
1860            line = l.strip()
1861            if len(line) == 0: # ignore blank lines
1862                continue 
1863            elif line.startswith('#'): # ignore comments
1864                continue 
1865            elif line.startswith('data_'): # on the right track, accept this file
1866                return True
1867            else: # found something invalid
1868                self.errors = 'line '+str(i+1)+' contains unexpected data:\n'
1869                self.errors += '  '+str(l)
1870                self.errors += '  Note: a CIF should only have blank lines or comments before'
1871                self.errors += '        a data_ statement begins a block.'
1872                return False 
1873
1874class ImportPhase(ImportBaseclass):
1875    '''Defines a base class for the reading of files with coordinates
1876
1877    Objects constructed that subclass this (in import/G2phase_*.py etc.) will be used
1878    in :meth:`GSASII.GSASII.OnImportPhase`.
1879    See :ref:`Writing a Import Routine<Import_Routines>`
1880    for an explanation on how to use this class.
1881
1882    '''
1883    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1884        strictExtension=False,):
1885        # call parent __init__
1886        ImportBaseclass.__init__(self,formatName,longFormatName,
1887            extensionlist,strictExtension)
1888        self.Phase = None # a phase must be created with G2IO.SetNewPhase in the Reader
1889        self.Constraints = None
1890
1891    def PhaseSelector(self, ChoiceList, ParentFrame=None,
1892        title='Select a phase', size=None,header='Phase Selector'):
1893        ''' Provide a wx dialog to select a phase if the file contains more
1894        than one phase
1895        '''
1896        return self.BlockSelector(ChoiceList,ParentFrame,title,
1897            size,header)
1898
1899class ImportStructFactor(ImportBaseclass):
1900    '''Defines a base class for the reading of files with tables
1901    of structure factors.
1902
1903    Structure factors are read with a call to :meth:`GSASII.GSASII.OnImportSfact`
1904    which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`, which calls
1905    methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1906    :meth:`Reader`.
1907
1908    See :ref:`Writing a Import Routine<Import_Routines>`
1909    for an explanation on how to use import classes in general. The specifics
1910    for reading a structure factor histogram require that
1911    the ``Reader()`` routine in the import
1912    class need to do only a few things: It
1913    should load :attr:`RefDict` item ``'RefList'`` with the reflection list,
1914    and set :attr:`Parameters` with the instrument parameters
1915    (initialized with :meth:`InitParameters` and set with :meth:`UpdateParameters`).
1916    '''
1917    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1918        strictExtension=False,):
1919        ImportBaseclass.__init__(self,formatName,longFormatName,
1920            extensionlist,strictExtension)
1921
1922        # define contents of Structure Factor entry
1923        self.Parameters = []
1924        'self.Parameters is a list with two dicts for data parameter settings'
1925        self.InitParameters()
1926        self.RefDict = {'RefList':[],'FF':{},'Super':0}
1927        self.Banks = []             #for multi bank data (usually TOF)
1928        '''self.RefDict is a dict containing the reflection information, as read from the file.
1929        Item 'RefList' contains the reflection information. See the
1930        :ref:`Single Crystal Reflection Data Structure<XtalRefl_table>`
1931        for the contents of each row. Dict element 'FF'
1932        contains the form factor values for each element type; if this entry
1933        is left as initialized (an empty list) it will be initialized as needed later.
1934        '''
1935    def ReInitialize(self):
1936        'Reinitialize the Reader to initial settings'
1937        ImportBaseclass.ReInitialize(self)
1938        self.InitParameters()
1939        self.Banks = []             #for multi bank data (usually TOF)
1940        self.RefDict = {'RefList':[],'FF':{},'Super':0}
1941       
1942    def InitParameters(self):
1943        'initialize the instrument parameters structure'
1944        Lambda = 0.70926
1945        HistType = 'SXC'
1946        self.Parameters = [{'Type':[HistType,HistType], # create the structure
1947                            'Lam':[Lambda,Lambda]
1948                            }, {}]
1949        'Parameters is a list with two dicts for data parameter settings'
1950
1951    def UpdateParameters(self,Type=None,Wave=None):
1952        'Revise the instrument parameters'
1953        if Type is not None:
1954            self.Parameters[0]['Type'] = [Type,Type]
1955        if Wave is not None:
1956            self.Parameters[0]['Lam'] = [Wave,Wave]           
1957                       
1958######################################################################
1959class ImportPowderData(ImportBaseclass):
1960    '''Defines a base class for the reading of files with powder data.
1961
1962    Objects constructed that subclass this (in import/G2pwd_*.py etc.) will be used
1963    in :meth:`GSASII.GSASII.OnImportPowder`.
1964    See :ref:`Writing a Import Routine<Import_Routines>`
1965    for an explanation on how to use this class.
1966    '''
1967    def __init__(self,
1968                 formatName,
1969                 longFormatName=None,
1970                 extensionlist=[],
1971                 strictExtension=False,
1972                 ):
1973        ImportBaseclass.__init__(self,formatName,
1974                                            longFormatName,
1975                                            extensionlist,
1976                                            strictExtension)
1977        self.clockWd = None  # used in TOF
1978        self.ReInitialize()
1979       
1980    def ReInitialize(self):
1981        'Reinitialize the Reader to initial settings'
1982        ImportBaseclass.ReInitialize(self)
1983        self.powderentry = ['',None,None] #  (filename,Pos,Bank)
1984        self.powderdata = [] # Powder dataset
1985        '''A powder data set is a list with items [x,y,w,yc,yb,yd]:
1986                np.array(x), # x-axis values
1987                np.array(y), # powder pattern intensities
1988                np.array(w), # 1/sig(intensity)^2 values (weights)
1989                np.array(yc), # calc. intensities (zero)
1990                np.array(yb), # calc. background (zero)
1991                np.array(yd), # obs-calc profiles
1992        '''                           
1993        self.comments = []
1994        self.idstring = ''
1995        self.Sample = G2pdG.SetDefaultSample() # default sample parameters
1996        self.Controls = {}  # items to be placed in top-level Controls
1997        self.GSAS = None     # used in TOF
1998        self.repeat_instparm = True # Should a parm file be
1999        #                             used for multiple histograms?
2000        self.instparm = None # name hint from file of instparm to use
2001        self.instfile = '' # full path name to instrument parameter file
2002        self.instbank = '' # inst parm bank number
2003        self.instmsg = ''  # a label that gets printed to show
2004                           # where instrument parameters are from
2005        self.numbanks = 1
2006        self.instdict = {} # place items here that will be transferred to the instrument parameters
2007        self.pwdparms = {} # place parameters that are transferred directly to the tree
2008                           # here (typically from an existing GPX file)
2009######################################################################
2010class ImportSmallAngleData(ImportBaseclass):
2011    '''Defines a base class for the reading of files with small angle data.
2012    See :ref:`Writing a Import Routine<Import_Routines>`
2013    for an explanation on how to use this class.
2014    '''
2015    def __init__(self,formatName,longFormatName=None,extensionlist=[],
2016        strictExtension=False,):
2017           
2018        ImportBaseclass.__init__(self,formatName,longFormatName,extensionlist,
2019            strictExtension)
2020        self.ReInitialize()
2021       
2022    def ReInitialize(self):
2023        'Reinitialize the Reader to initial settings'
2024        ImportBaseclass.ReInitialize(self)
2025        self.smallangleentry = ['',None,None] #  (filename,Pos,Bank)
2026        self.smallangledata = [] # SASD dataset
2027        '''A small angle data set is a list with items [x,y,w,yc,yd]:
2028                np.array(x), # x-axis values
2029                np.array(y), # powder pattern intensities
2030                np.array(w), # 1/sig(intensity)^2 values (weights)
2031                np.array(yc), # calc. intensities (zero)
2032                np.array(yd), # obs-calc profiles
2033                np.array(yb), # preset bkg
2034        '''                           
2035        self.comments = []
2036        self.idstring = ''
2037        self.Sample = G2pdG.SetDefaultSample()
2038        self.GSAS = None     # used in TOF
2039        self.clockWd = None  # used in TOF
2040        self.numbanks = 1
2041        self.instdict = {} # place items here that will be transferred to the instrument parameters
2042
2043######################################################################
2044class ImportImage(ImportBaseclass):
2045    '''Defines a base class for the reading of images
2046
2047    Structure factors are read with a call to :meth:`GSASII.GSASII.OnImportImage`
2048    which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`, which calls
2049    methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
2050    :meth:`Reader`.
2051
2052    See :ref:`Writing a Import Routine<Import_Routines>`
2053    for an explanation on how to use import classes in general. The specifics
2054    for reading an image requires that the ``Reader()`` routine in the import
2055    class need to do only a few things:...
2056   
2057    (should load :attr:`RefDict` item ``'RefList'`` with the reflection list,
2058    (and set :attr:`Parameters` with the instrument parameters
2059    (initialized with :meth:`InitParameters` and set with :meth:`UpdateParameters`).
2060    '''
2061    def __init__(self,formatName,longFormatName=None,extensionlist=[],
2062        strictExtension=False,):
2063        ImportBaseclass.__init__(self,formatName,longFormatName,
2064            extensionlist,strictExtension)
2065        self.InitParameters()
2066       
2067    def ReInitialize(self):
2068        'Reinitialize the Reader to initial settings -- not used at present'
2069        ImportBaseclass.ReInitialize(self)
2070        self.InitParameters()
2071       
2072    def InitParameters(self):
2073        'initialize the instrument parameters structure'
2074        self.Comments = ['No comments']
2075        self.Data = {}
2076        self.Npix = 0
2077        self.Image = None
2078
2079    def LoadImage(self,ParentFrame,imagefile):
2080        '''Optionally, call this after reading in an image to load it into the tree.
2081        This does saves time by preventing a reread of the same information.
2082        '''
2083        if ParentFrame:
2084            ParentFrame.ImageZ = self.Image   # store the image for plotting
2085            ParentFrame.oldImagefile = imagefile # save the name of the last image file read
2086           
2087
2088######################################################################
2089class ExportBaseclass(object):
2090    '''Defines a base class for the exporting of GSAS-II results.
2091
2092    This class is subclassed in the various exports/G2export_*.py files. Those files
2093    are imported in :meth:`GSASII.GSASII._init_Exports` which defines the
2094    appropriate menu items for each one and the .Exporter method is called
2095    directly from the menu item.
2096
2097    Routines may also define a .Writer method, which is used to write a single
2098    file without invoking any GUI objects.
2099    '''
2100    def __init__(self,
2101                 G2frame,
2102                 formatName,
2103                 extension,
2104                 longFormatName=None,
2105                 ):
2106        self.G2frame = G2frame
2107        self.formatName = formatName # short string naming file type
2108        self.extension = extension
2109        if longFormatName: # longer string naming file type
2110            self.longFormatName = longFormatName
2111        else:
2112            self.longFormatName = formatName
2113        self.OverallParms = {}
2114        self.Phases = {}
2115        self.Histograms = {}
2116        self.powderDict = {}
2117        self.xtalDict = {}
2118        self.parmDict = {}
2119        self.sigDict = {}
2120        # updated in InitExport:
2121        self.currentExportType = None # type of export that has been requested
2122        # updated in ExportSelect (when used):
2123        self.phasenam = None # a list of selected phases
2124        self.histnam = None # a list of selected histograms
2125        self.filename = None # name of file to be written (single export) or template (multiple files)
2126        self.dirname = '' # name of directory where file(s) will be written
2127        self.fullpath = '' # name of file being written -- full path
2128       
2129        # items that should be defined in a subclass of this class
2130        self.exporttype = []  # defines the type(s) of exports that the class can handle.
2131        # The following types are defined: 'project', "phase", "powder", "single"
2132        self.multiple = False # set as True if the class can export multiple phases or histograms
2133        # self.multiple is ignored for "project" exports
2134
2135    def InitExport(self,event):
2136        '''Determines the type of menu that called the Exporter and
2137        misc initialization.
2138        '''
2139        self.filename = None # name of file to be written (single export)
2140        self.dirname = '' # name of file to be written (multiple export)
2141        if event:
2142            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
2143
2144    def MakePWDRfilename(self,hist):
2145        '''Make a filename root (no extension) from a PWDR histogram name
2146
2147        :param str hist: the histogram name in data tree (starts with "PWDR ")
2148        '''
2149        file0 = ''
2150        file1 = hist[5:]
2151        # replace repeated blanks
2152        while file1 != file0:
2153            file0 = file1
2154            file1 = file0.replace('  ',' ').strip()
2155        file0 = file1.replace('Azm= ','A')
2156        # if angle has unneeded decimal places on aziumuth, remove them
2157        if file0[-3:] == '.00': file0 = file0[:-3]
2158        file0 = file0.replace('.','_')
2159        file0 = file0.replace(' ','_')
2160        return file0
2161
2162    def ExportSelect(self,AskFile='ask'):
2163        '''Selects histograms or phases when needed. Sets a default file name when
2164        requested in self.filename; always sets a default directory in self.dirname.
2165
2166        :param bool AskFile: Determines how this routine processes getting a
2167          location to store the current export(s).
2168         
2169          * if AskFile is 'ask' (default option), get the name of the file to be written;
2170            self.filename and self.dirname are always set. In the case where
2171            multiple files must be generated, the export routine should do this
2172            based on self.filename as a template.
2173          * if AskFile is 'dir', get the name of the directory to be used;
2174            self.filename is not used, but self.dirname is always set. The export routine
2175            will always generate the file name.
2176          * if AskFile is 'single', get only the name of the directory to be used when
2177            multiple items will be written (as multiple files) are used
2178            *or* a complete file name is requested when a single file
2179            name is selected. self.dirname is always set and self.filename used
2180            only when a single file is selected.
2181          * if AskFile is 'default', creates a name of the file to be used from
2182            the name of the project (.gpx) file. If the project has not been saved,
2183            then the name of file is requested.
2184            self.filename and self.dirname are always set. In the case where
2185            multiple file names must be generated, the export routine should do this
2186            based on self.filename.
2187          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
2188            file. If the project has not been saved, then a directory is requested.
2189            self.filename is not used.
2190
2191        :returns: True in case of an error
2192        '''
2193       
2194        numselected = 1
2195        if self.currentExportType == 'phase':
2196            if len(self.Phases) == 0:
2197                self.G2frame.ErrorDialog(
2198                    'Empty project',
2199                    'Project does not contain any phases.')
2200                return True
2201            elif len(self.Phases) == 1:
2202                self.phasenam = self.Phases.keys()
2203            elif self.multiple: 
2204                choices = sorted(self.Phases.keys())
2205                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
2206                if phasenum is None: return True
2207                self.phasenam = [choices[i] for i in phasenum]
2208                if not self.phasenam: return True
2209                numselected = len(self.phasenam)
2210            else:
2211                choices = sorted(self.Phases.keys())
2212                phasenum = G2G.ItemSelector(choices,self.G2frame)
2213                if phasenum is None: return True
2214                self.phasenam = [choices[phasenum]]
2215                numselected = len(self.phasenam)
2216        elif self.currentExportType == 'single':
2217            if len(self.xtalDict) == 0:
2218                self.G2frame.ErrorDialog(
2219                    'Empty project',
2220                    'Project does not contain any single crystal data.')
2221                return True
2222            elif len(self.xtalDict) == 1:
2223                self.histnam = self.xtalDict.values()
2224            elif self.multiple:
2225                choices = sorted(self.xtalDict.values())
2226                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
2227                if not hnum: return True
2228                self.histnam = [choices[i] for i in hnum]
2229                numselected = len(self.histnam)
2230            else:
2231                choices = sorted(self.xtalDict.values())
2232                hnum = G2G.ItemSelector(choices,self.G2frame)
2233                if hnum is None: return True
2234                self.histnam = [choices[hnum]]
2235                numselected = len(self.histnam)
2236        elif self.currentExportType == 'powder':
2237            if len(self.powderDict) == 0:
2238                self.G2frame.ErrorDialog(
2239                    'Empty project',
2240                    'Project does not contain any powder data.')
2241                return True
2242            elif len(self.powderDict) == 1:
2243                self.histnam = self.powderDict.values()
2244            elif self.multiple:
2245                choices = sorted(self.powderDict.values())
2246                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
2247                if not hnum: return True
2248                self.histnam = [choices[i] for i in hnum]
2249                numselected = len(self.histnam)
2250            else:
2251                choices = sorted(self.powderDict.values())
2252                hnum = G2G.ItemSelector(choices,self.G2frame)
2253                if hnum is None: return True
2254                self.histnam = [choices[hnum]]
2255                numselected = len(self.histnam)
2256        elif self.currentExportType == 'image':
2257            if len(self.Histograms) == 0:
2258                self.G2frame.ErrorDialog(
2259                    'Empty project',
2260                    'Project does not contain any images.')
2261                return True
2262            elif len(self.Histograms) == 1:
2263                self.histnam = self.Histograms.keys()
2264            else:
2265                choices = sorted(self.Histograms.keys())
2266                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
2267                if self.multiple:
2268                    if not hnum: return True
2269                    self.histnam = [choices[i] for i in hnum]
2270                else:
2271                    if hnum is None: return True
2272                    self.histnam = [choices[hnum]]
2273                numselected = len(self.histnam)
2274        if self.currentExportType == 'map':
2275            # search for phases with maps
2276            mapPhases = []
2277            choices = []
2278            for phasenam in sorted(self.Phases):
2279                phasedict = self.Phases[phasenam] # pointer to current phase info           
2280                if len(phasedict['General']['Map'].get('rho',[])):
2281                    mapPhases.append(phasenam)
2282                    if phasedict['General']['Map'].get('Flip'):
2283                        choices.append('Charge flip map: '+str(phasenam))
2284                    elif phasedict['General']['Map'].get('MapType'):
2285                        choices.append(
2286                            str(phasedict['General']['Map'].get('MapType'))
2287                            + ' map: ' + str(phasenam))
2288                    else:
2289                        choices.append('unknown map: '+str(phasenam))
2290            # select a map if needed
2291            if len(mapPhases) == 0:
2292                self.G2frame.ErrorDialog(
2293                    'Empty project',
2294                    'Project does not contain any maps.')
2295                return True
2296            elif len(mapPhases) == 1:
2297                self.phasenam = mapPhases
2298            else: 
2299                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
2300                if self.multiple:
2301                    if not phasenum: return True
2302                    self.phasenam = [mapPhases[i] for i in phasenum]
2303                else:
2304                    if phasenum is None: return True
2305                    self.phasenam = [mapPhases[phasenum]]
2306            numselected = len(self.phasenam)
2307
2308        # items selected, now set self.dirname and usually self.filename
2309        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
2310            AskFile == 'default' and not self.G2frame.GSASprojectfile
2311            ):
2312            filename = self.askSaveFile()
2313            if not filename: return True
2314            self.dirname,self.filename = os.path.split(filename)
2315        elif AskFile == 'dir' or AskFile == 'single' or (
2316            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
2317            ):
2318            self.dirname = self.askSaveDirectory()
2319            if not self.dirname: return True
2320        elif AskFile == 'default-dir' or AskFile == 'default':
2321            self.dirname,self.filename = os.path.split(
2322                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
2323                )
2324        else:
2325            raise Exception('This should not happen!')
2326       
2327    def loadParmDict(self):
2328        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
2329        refined values to those from the last cycle and set the uncertainties for the
2330        refined parameters in another dict (self.sigDict).
2331
2332        Expands the parm & sig dicts to include values derived from constraints.
2333        '''
2334        self.parmDict = {}
2335        self.sigDict = {}
2336        rigidbodyDict = {}
2337        covDict = {}
2338        consDict = {}
2339        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2340        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2341        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2342        while item:
2343            name = self.G2frame.PatternTree.GetItemText(item)
2344            if name == 'Rigid bodies':
2345                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
2346            elif name == 'Covariance':
2347                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
2348            elif name == 'Constraints':
2349                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
2350            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2351        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2352        self.parmDict.update(rbDict)
2353        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2354        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,maxSSwave =  G2stIO.GetPhaseData(
2355            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
2356        self.parmDict.update(phaseDict)
2357        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
2358            Phases,Histograms,Print=False,resetRefList=False)
2359        self.parmDict.update(hapDict)
2360        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
2361        self.parmDict.update(histDict)
2362        self.parmDict.update(zip(
2363            covDict.get('varyList',[]),
2364            covDict.get('variables',[])))
2365        self.sigDict = dict(zip(
2366            covDict.get('varyList',[]),
2367            covDict.get('sig',[])))
2368        # expand to include constraints: first compile a list of constraints
2369        constList = []
2370        for item in consDict:
2371            if item.startswith('_'): continue
2372            constList += consDict[item]
2373        # now process the constraints
2374        G2mv.InitVars()
2375        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
2376        varyList = covDict.get('varyListStart')
2377        if varyList is None and len(constDict) == 0:
2378            # no constraints can use varyList
2379            varyList = covDict.get('varyList')
2380        elif varyList is None:
2381            # old GPX file from before pre-constraint varyList is saved
2382            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
2383            raise Exception(' *** Export aborted ***')
2384        else:
2385            varyList = list(varyList)
2386        try:
2387            groups,parmlist = G2mv.GroupConstraints(constDict)
2388            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,self.parmDict)
2389        except:
2390            # this really should not happen
2391            print ' *** ERROR - constraints are internally inconsistent ***'
2392            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
2393            print 'Errors',errmsg
2394            if warnmsg: print 'Warnings',warnmsg
2395            raise Exception(' *** CIF creation aborted ***')
2396        # add the constrained values to the parameter dictionary
2397        G2mv.Dict2Map(self.parmDict,varyList)
2398        # and add their uncertainties into the esd dictionary (sigDict)
2399        if covDict.get('covMatrix') is not None:
2400            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
2401
2402    def loadTree(self):
2403        '''Load the contents of the data tree into a set of dicts
2404        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
2405        & self.xtalDict)
2406       
2407        * The childrenless data tree items are overall parameters/controls for the
2408          entire project and are placed in self.OverallParms
2409        * Phase items are placed in self.Phases
2410        * Data items are placed in self.Histogram. The key for these data items
2411          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
2412        '''
2413        self.OverallParms = {}
2414        self.powderDict = {}
2415        self.xtalDict = {}
2416        self.Phases = {}
2417        self.Histograms = {}
2418        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2419        histType = None       
2420        if self.currentExportType == 'phase':
2421            # if exporting phases load them here
2422            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
2423            if not sub:
2424                print 'no phases found'
2425                return True
2426            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
2427            while item:
2428                phaseName = self.G2frame.PatternTree.GetItemText(item)
2429                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
2430                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
2431            return
2432        elif self.currentExportType == 'single':
2433            histType = 'HKLF'
2434        elif self.currentExportType == 'powder':
2435            histType = 'PWDR'
2436        elif self.currentExportType == 'image':
2437            histType = 'IMG'
2438
2439        if histType: # Loading just one kind of tree entry
2440            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2441            while item:
2442                name = self.G2frame.PatternTree.GetItemText(item)
2443                if name.startswith(histType):
2444                    if self.Histograms.get(name): # there is already an item with this name
2445                        print('Histogram name '+str(name)+' is repeated. Renaming')
2446                        if name[-1] == '9':
2447                            name = name[:-1] + '10'
2448                        elif name[-1] in '012345678':
2449                            name = name[:-1] + str(int(name[-1])+1)
2450                        else:                           
2451                            name += '-1'
2452                    self.Histograms[name] = {}
2453                    # the main info goes into Data, but the 0th
2454                    # element contains refinement results, carry
2455                    # that over too now.
2456                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
2457                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
2458                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2459                    while item2: 
2460                        child = self.G2frame.PatternTree.GetItemText(item2)
2461                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
2462                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
2463                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2464            # index powder and single crystal histograms by number
2465            for hist in self.Histograms:
2466                if hist.startswith("PWDR"): 
2467                    d = self.powderDict
2468                elif hist.startswith("HKLF"): 
2469                    d = self.xtalDict
2470                else:
2471                    return                   
2472                i = self.Histograms[hist].get('hId')
2473                if i is None and not d.keys():
2474                    i = 0
2475                elif i is None or i in d.keys():
2476                    i = max(d.keys())+1
2477                d[i] = hist
2478            return
2479        # else standard load: using all interlinked phases and histograms
2480        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2481        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2482        while item:
2483            name = self.G2frame.PatternTree.GetItemText(item)
2484            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2485            if not item2: 
2486                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
2487            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2488        # index powder and single crystal histograms
2489        for hist in self.Histograms:
2490            i = self.Histograms[hist]['hId']
2491            if hist.startswith("PWDR"): 
2492                self.powderDict[i] = hist
2493            elif hist.startswith("HKLF"): 
2494                self.xtalDict[i] = hist
2495
2496    def dumpTree(self,mode='type'):
2497        '''Print out information on the data tree dicts loaded in loadTree
2498        '''
2499        print '\nOverall'
2500        if mode == 'type':
2501            def Show(arg): return type(arg)
2502        else:
2503            def Show(arg): return arg
2504        for key in self.OverallParms:
2505            print '  ',key,Show(self.OverallParms[key])
2506        print 'Phases'
2507        for key1 in self.Phases:
2508            print '    ',key1,Show(self.Phases[key1])
2509        print 'Histogram'
2510        for key1 in self.Histograms:
2511            print '    ',key1,Show(self.Histograms[key1])
2512            for key2 in self.Histograms[key1]:
2513                print '      ',key2,Show(self.Histograms[key1][key2])
2514
2515    def defaultSaveFile(self):
2516        return os.path.abspath(
2517            os.path.splitext(self.G2frame.GSASprojectfile
2518                             )[0]+self.extension)
2519       
2520    def askSaveFile(self):
2521        '''Ask the user to supply a file name
2522
2523        :returns: a file name (str) or None if Cancel is pressed
2524        '''
2525        defnam = os.path.splitext(
2526            os.path.split(self.G2frame.GSASprojectfile)[1]
2527            )[0]+self.extension
2528        dlg = wx.FileDialog(
2529            self.G2frame, 'Input name for file to write', '.', defnam,
2530            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2531            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2532        dlg.CenterOnParent()
2533        try:
2534            if dlg.ShowModal() == wx.ID_OK:
2535                filename = dlg.GetPath()
2536                # make sure extension is correct
2537                filename = os.path.splitext(filename)[0]+self.extension
2538            else:
2539                filename = None
2540        finally:
2541            dlg.Destroy()
2542        return filename
2543
2544    def askSaveDirectory(self):
2545        '''Ask the user to supply a directory name. Path name is used as the
2546        starting point for the next export path search.
2547
2548        :returns: a directory name (str) or None if Cancel is pressed
2549        '''
2550        if self.G2frame.exportDir:
2551            startdir = self.G2frame.exportDir
2552        elif self.G2frame.GSASprojectfile:
2553            startdir = os.path.split(self.G2frame.GSASprojectfile)[0]
2554        elif self.G2frame.dirname:
2555            startdir = self.G2frame.dirname
2556        else:
2557            startdir = ''
2558        dlg = wx.DirDialog(
2559            self.G2frame, 'Input directory where file(s) will be written', startdir,
2560            wx.DD_DEFAULT_STYLE)
2561        dlg.CenterOnParent()
2562        try:
2563            if dlg.ShowModal() == wx.ID_OK:
2564                filename = dlg.GetPath()
2565                self.G2frame.exportDir = filename
2566            else:
2567                filename = None
2568        finally:
2569            dlg.Destroy()
2570        return filename
2571
2572    # Tools for file writing.
2573    def OpenFile(self,fil=None,mode='w'):
2574        '''Open the output file
2575
2576        :param str fil: The name of the file to open. If None (default)
2577          the name defaults to self.dirname + self.filename.
2578          If an extension is supplied, it is not overridded,
2579          but if not, the default extension is used.
2580        :returns: the file object opened by the routine which is also
2581          saved as self.fp
2582        '''
2583        if not fil:
2584            if not os.path.splitext(self.filename)[1]:
2585                self.filename += self.extension
2586            fil = os.path.join(self.dirname,self.filename)
2587        self.fullpath = os.path.abspath(fil)
2588        self.fp = open(self.fullpath,mode)
2589        return self.fp
2590
2591    def Write(self,line):
2592        '''write a line of output, attaching a line-end character
2593
2594        :param str line: the text to be written.
2595        '''
2596        self.fp.write(line+'\n')
2597    def CloseFile(self,fp=None):
2598        '''Close a file opened in OpenFile
2599
2600        :param file fp: the file object to be closed. If None (default)
2601          file object self.fp is closed.
2602        '''
2603        if fp is None:
2604            fp = self.fp
2605            self.fp = None
2606        fp.close()
2607    # Tools to pull information out of the data arrays
2608    def GetCell(self,phasenam):
2609        """Gets the unit cell parameters and their s.u.'s for a selected phase
2610
2611        :param str phasenam: the name for the selected phase
2612        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2613          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2614          cell values and `cellSig` has their uncertainties.
2615        """
2616        phasedict = self.Phases[phasenam] # pointer to current phase info
2617        try:
2618            pfx = str(phasedict['pId'])+'::'
2619            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2620            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
2621                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2622            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2623            return cellList,cellSig
2624        except KeyError:
2625            cell = phasedict['General']['Cell'][1:]
2626            return cell,7*[0]
2627   
2628    def GetAtoms(self,phasenam):
2629        """Gets the atoms associated with a phase. Can be used with standard
2630        or macromolecular phases
2631
2632        :param str phasenam: the name for the selected phase
2633        :returns: a list of items for eac atom where each item is a list containing:
2634          label, typ, mult, xyz, and td, where
2635
2636          * label and typ are the atom label and the scattering factor type (str)
2637          * mult is the site multiplicity (int)
2638          * xyz is contains a list with four pairs of numbers:
2639            x, y, z and fractional occupancy and
2640            their standard uncertainty (or a negative value)
2641          * td is contains a list with either one or six pairs of numbers:
2642            if one number it is U\ :sub:`iso` and with six numbers it is
2643            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2644            paired with their standard uncertainty (or a negative value)
2645        """
2646        phasedict = self.Phases[phasenam] # pointer to current phase info           
2647        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2648        cfrac = cx+3
2649        fpfx = str(phasedict['pId'])+'::Afrac:'       
2650        atomslist = []
2651        for i,at in enumerate(phasedict['Atoms']):
2652            if phasedict['General']['Type'] == 'macromolecular':
2653                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2654            else:
2655                label = at[ct-1]
2656            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2657            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2658            mult = at[cs+1]
2659            typ = at[ct]
2660            xyz = []
2661            for j,v in enumerate(('x','y','z')):
2662                val = at[cx+j]
2663                pfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2664                sig = self.sigDict.get(pfx,-0.000009)
2665                xyz.append((val,sig))
2666            xyz.append((fval,fsig))
2667            td = []
2668            if at[cia] == 'I':
2669                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2670                val = self.parmDict.get(pfx,at[cia+1])
2671                sig = self.sigDict.get(pfx,-0.0009)
2672                td.append((val,sig))
2673            else:
2674                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2675                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2676                    val = self.parmDict.get(pfx,at[cia+2+i])
2677                    sig = self.sigDict.get(pfx,-0.0009)
2678                    td.append((val,sig))
2679            atomslist.append((label,typ,mult,xyz,td))
2680        return atomslist
2681######################################################################
2682def ExportPowderList(G2frame):
2683    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
2684   
2685    :param wx.Frame G2frame: the GSAS-II main data tree window
2686    '''
2687    extList = []
2688    for obj in G2frame.exporterlist:
2689        if 'powder' in obj.exporttype:
2690            try:
2691                obj.Writer
2692                extList.append(obj.extension)
2693            except AttributeError:
2694                pass
2695    return extList
2696
2697def ExportPowder(G2frame,TreeName,fileroot,extension):
2698    '''Writes a single powder histogram using the Export routines
2699
2700    :param wx.Frame G2frame: the GSAS-II main data tree window
2701    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
2702    :param str fileroot: name for file to be written, extension ignored
2703    :param str extension: extension for file to be written (start with '.'). Must
2704      match a powder export routine that has a Writer object.
2705    '''
2706    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2707    for obj in G2frame.exporterlist:
2708        if obj.extension == extension and 'powder' in obj.exporttype:
2709            obj.currentExportType = 'powder'
2710            obj.InitExport(None)
2711            obj.loadTree() # load all histograms in tree into dicts
2712            if TreeName not in obj.Histograms:
2713                raise Exception('Histogram not found: '+str(TreeName))
2714            try:
2715                obj.Writer
2716            except AttributeError:
2717                continue
2718            try:
2719                obj.Writer(TreeName,filename)
2720                return
2721            except Exception,err:
2722                print('Export Routine for '+extension+' failed.')
2723                print err
2724    else:
2725        print('No Export routine supports extension '+extension)
2726
2727def ReadCIF(URLorFile):
2728    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2729    (from James Hester).
2730    The open routine gets confused with DOS names that begin with a letter and colon
2731    "C:\dir\" so this routine will try to open the passed name as a file and if that
2732    fails, try it as a URL
2733
2734    :param str URLorFile: string containing a URL or a file name. Code will try first
2735      to open it as a file and then as a URL.
2736
2737    :returns: a PyCifRW CIF object.
2738    '''
2739    import CifFile as cif # PyCifRW from James Hester
2740
2741    # alternate approach:
2742    #import urllib
2743    #ciffile = 'file:'+urllib.pathname2url(filename)
2744   
2745    try:
2746        fp = open(URLorFile,'r')
2747        cf = cif.ReadCif(fp)
2748        fp.close()
2749        return cf
2750    except IOError:
2751        return cif.ReadCif(URLorFile)
2752
2753if __name__ == '__main__':
2754    app = wx.PySimpleApp()
2755    frm = wx.Frame(None) # create a frame
2756    frm.Show(True)
2757    filename = '/tmp/notzip.zip'
2758    filename = '/tmp/all.zip'
2759    #filename = '/tmp/11bmb_7652.zip'
2760   
2761    #selection=None, confirmoverwrite=True, parent=None
2762    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2763    print ExtractFileFromZip(filename,multipleselect=True)
2764                             #confirmread=False, confirmoverwrite=False)
2765
2766    # choicelist=[ ('a','b','c'),
2767    #              ('test1','test2'),('no choice',)]
2768    # titles = [ 'a, b or c', 'tests', 'No option here']
2769    # dlg = MultipleChoicesDialog(
2770    #     choicelist,titles,
2771    #     parent=frm)
2772    # if dlg.ShowModal() == wx.ID_OK:
2773    #     print 'Got OK'
Note: See TracBrowser for help on using the repository browser.