source: trunk/GSASIIIO.py @ 2625

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

fix problem with using dark/background images - GetCheckImageFile? needed to refer to treeId rather than G2frame.Image
fix Progress bar problem in integration should be destroyed after each use

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