source: trunk/GSASIIIO.py @ 2003

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

Make Transpose a config opt; new routine to read images with import routine (G2IO.ReadImageData?) -- used in Debug mode; In debug mode, import exceptions not caught.

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