source: trunk/GSASIIIO.py @ 2012

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

belated check in

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