source: trunk/GSASIIIO.py @ 1916

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

now read mar2560 image files

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