source: trunk/GSASIIIO.py @ 2013

Last change on this file since 2013 was 2013, checked in by vondreele, 6 years ago

remove two stray debug prints

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