source: trunk/GSASIIIO.py @ 1770

Last change on this file since 1770 was 1770, checked in by vondreele, 7 years ago

moved MultipleChoicesDialog? and SingleChoiceDialog? from G2grid to G2ctrls

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