source: trunk/GSASIIIO.py @ 1961

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

save package info in GPX; display only in Debug mode

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