source: trunk/GSASIIIO.py @ 2042

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

move TIF code; correct bug; get rid of old GetImageData?, next step in migration to new import image code

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