source: trunk/GSASIIIO.py @ 2056

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

peak list read/list/indexing now fixed

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