source: trunk/GSASIIIO.py @ 2703

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

fix problem with default Sample parms for lab data default selection

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 112.0 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2017-02-14 15:52:37 +0000 (Tue, 14 Feb 2017) $
4# $Author: vondreele $
5# $Revision: 2703 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 2703 2017-02-14 15:52:37Z 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: 2703 $")
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            oldPDF = False
787            for datus in data[1:]:
788#patch - 1/23/17 PDF cleanup
789                if datus[0][:4] in ['I(Q)','S(Q)','F(Q)','G(R)']:
790                    oldPDF = True
791                    data[1][1][datus[0][:4]] = copy.deepcopy(datus[1][:2])
792                    continue
793#end PDF cleanup
794                sub = G2frame.PatternTree.AppendItem(Id,datus[0])
795#patch
796                if datus[0] == 'Instrument Parameters' and len(datus[1]) == 1:
797                    if datum[0].startswith('PWDR'):
798                        datus[1] = [dict(zip(datus[1][3],zip(datus[1][0],datus[1][1],datus[1][2]))),{}]
799                    else:
800                        datus[1] = [dict(zip(datus[1][2],zip(datus[1][0],datus[1][1]))),{}]
801                    for item in datus[1][0]:               #zip makes tuples - now make lists!
802                        datus[1][0][item] = list(datus[1][0][item])
803#end patch
804                G2frame.PatternTree.SetItemPyData(sub,datus[1])
805            if 'PDF ' in datum[0][:4] and oldPDF:
806                sub = G2frame.PatternTree.AppendItem(Id,'PDF Peaks')
807                G2frame.PatternTree.SetItemPyData(sub,{'Limits':[1.,5.],'Background':[2,[0.,-0.2*np.pi],False],'Peaks':[]})
808            if datum[0].startswith('IMG'):                   #retrieve image default flag & data if set
809                Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
810                if Data['setDefault']:
811                    G2frame.imageDefault = Data               
812        file.close()
813        if LastSavedUsing:
814            print('GPX load successful. Last saved with GSAS-II revision '+LastSavedUsing)
815        else:
816            print('project load successful')
817        G2frame.NewPlot = True
818    except:
819        msg = wx.MessageDialog(G2frame,message="Error reading file "+
820            str(G2frame.GSASprojectfile)+". This is not a GSAS-II .gpx file",
821            caption="Load Error",style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
822        msg.ShowModal()
823    finally:
824        wx.EndBusyCursor()
825        G2frame.Status.SetStatusText('Mouse RB click item to refresh/raise; RB drag/drop to reorder')
826   
827def ProjFileSave(G2frame):
828    'Save a GSAS-II project file'
829    if not G2frame.PatternTree.IsEmpty():
830        file = open(G2frame.GSASprojectfile,'wb')
831        print 'save to file: ',G2frame.GSASprojectfile
832        # stick the file name into the tree and version info into tree so they are saved.
833        # (Controls should always be created at this point)
834        try:
835            Controls = G2frame.PatternTree.GetItemPyData(
836                G2gd.GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
837            Controls['LastSavedAs'] = os.path.abspath(G2frame.GSASprojectfile)
838            Controls['LastSavedUsing'] = str(GSASIIpath.GetVersionNumber())
839            Controls['PythonVersions'] = G2frame.PackageVersions
840        except:
841            pass
842        wx.BeginBusyCursor()
843        try:
844            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
845            while item:
846                data = []
847                name = G2frame.PatternTree.GetItemText(item)
848                data.append([name,G2frame.PatternTree.GetItemPyData(item)])
849                item2, cookie2 = G2frame.PatternTree.GetFirstChild(item)
850                while item2:
851                    name = G2frame.PatternTree.GetItemText(item2)
852                    data.append([name,G2frame.PatternTree.GetItemPyData(item2)])
853                    item2, cookie2 = G2frame.PatternTree.GetNextChild(item, cookie2)                           
854                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)                           
855                cPickle.dump(data,file,1)
856            file.close()
857            pth = os.path.split(os.path.abspath(G2frame.GSASprojectfile))[0]
858            if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
859            G2frame.LastGPXdir = pth
860        finally:
861            wx.EndBusyCursor()
862        print('project save successful')
863
864def SaveIntegration(G2frame,PickId,data,Overwrite=False):
865    'Save image integration results as powder pattern(s)'
866    azms = G2frame.Integrate[1]
867    X = G2frame.Integrate[2][:-1]
868    N = len(X)
869    Id = G2frame.PatternTree.GetItemParent(PickId)
870    name = G2frame.PatternTree.GetItemText(Id)
871    name = name.replace('IMG ',data['type']+' ')
872    Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Comments'))
873    if 'PWDR' in name:
874        names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
875        codes = [0 for i in range(11)]
876    elif 'SASD' in name:
877        names = ['Type','Lam','Zero','Azimuth'] 
878        codes = [0 for i in range(4)]
879        X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
880    Xminmax = [X[0],X[-1]]
881    Azms = []
882    dazm = 0.
883    if data['fullIntegrate'] and data['outAzimuths'] == 1:
884        Azms = [45.0,]                              #a poor man's average?
885    else:
886        for i,azm in enumerate(azms[:-1]):
887            if azm > 360. and azms[i+1] > 360.:
888                Azms.append(G2img.meanAzm(azm%360.,azms[i+1]%360.))
889            else:   
890                Azms.append(G2img.meanAzm(azm,azms[i+1]))
891        dazm = np.min(np.abs(np.diff(azms)))/2.
892    G2frame.IntgOutList = []
893    for i,azm in enumerate(azms[:-1]):
894        Aname = name+" Azm= %.2f"%((azm+dazm)%360.)
895        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
896        # if Overwrite delete any duplicate
897        if Overwrite and G2gd.GetPatternTreeItemId(G2frame,G2frame.root,Aname):
898            print('Replacing '+Aname)
899            item = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,Aname)
900            G2frame.PatternTree.Delete(item)
901        else:
902            nOcc = 0
903            while item:
904                Name = G2frame.PatternTree.GetItemText(item)
905                if Aname in Name:
906                    nOcc += 1
907                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
908            if nOcc:
909                Aname += '(%d)'%(nOcc)
910        Sample = G2pdG.SetDefaultSample()       #set as Debye-Scherrer
911        Sample['Gonio. radius'] = data['distance']
912        Sample['Omega'] = data['GonioAngles'][0]
913        Sample['Chi'] = data['GonioAngles'][1]
914        Sample['Phi'] = data['GonioAngles'][2]
915        Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
916        polariz = 0.99    #set default polarization for synchrotron radiation!
917        for item in Comments:
918            if 'polariz' in item:
919                try:
920                    polariz = float(item.split('=')[1])
921                except:
922                    polariz = 0.99
923        if 'PWDR' in Aname:
924            parms = ['PXC',data['wavelength'],0.0,polariz,1.0,-0.10,0.4,0.30,1.0,0.0001,Azms[i]]
925        elif 'SASD' in Aname:
926            Sample['Trans'] = data['SampleAbs'][0]
927            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
928        Y = G2frame.Integrate[0][i]
929        Ymin = np.min(Y)
930        Ymax = np.max(Y)
931        W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
932        Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=Aname)
933        G2frame.IntgOutList.append(Id)
934        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
935        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
936        if 'PWDR' in Aname:
937            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
938                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
939        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
940        for item in inst[0]:
941            inst[0][item] = list(inst[0][item])
942        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
943        if 'PWDR' in Aname:
944            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
945            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Peak List'),{'sigDict':{},'peaks':[]})
946            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
947            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Unit Cells List'),[])
948            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Reflection Lists'),{})
949        elif 'SASD' in Aname:             
950            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
951            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
952            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
953        valuesdict = {
954            'wtFactor':1.0,
955            'Dummy':False,
956            'ranId':ran.randint(0,sys.maxint),
957            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-0.1*Ymax,'refDelt':0.1*Ymax,
958            'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
959            }
960        G2frame.PatternTree.SetItemPyData(
961            Id,[valuesdict,
962                [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
963    return Id       #last powder pattern generated
964   
965def XYsave(G2frame,XY,labelX='X',labelY='Y',names=None):
966    'Save XY table data'
967    pth = G2G.GetExportPath(G2frame)
968    dlg = wx.FileDialog(
969        G2frame, 'Enter csv filename for XY table', pth, '',
970        'XY table file (*.csv)|*.csv',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
971    try:
972        if dlg.ShowModal() == wx.ID_OK:
973            filename = dlg.GetPath()
974            filename = os.path.splitext(filename)[0]+'.csv'
975            File = open(filename,'w')
976        else:
977            filename = None
978    finally:
979        dlg.Destroy()
980    if not filename:
981        return
982    for i in range(len(XY)):
983        if names != None:
984            header = '%s,%s(%s)\n'%(labelX,labelY,names[i])
985        else:
986            header = '%s,%s(%d)\n'%(labelX,labelY,i)
987        File.write(header)
988        for x,y in XY[i].T:
989            File.write('%.3f,%.3f\n'%(x,y))   
990    File.close()
991    print ' XY data saved to: ',filename
992           
993def PDFSave(G2frame,exports):
994    'Save a PDF G(r) and F(Q), S(Q) in column formats'
995    import scipy.interpolate as scintp
996    for export in exports:
997        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
998        PDFControls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame, PickId,'PDF Controls'))
999        sqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.sq')
1000        fqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.fq')
1001        grfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.gr')
1002        sqdata = PDFControls['S(Q)'][1]
1003        sqfxn = scintp.interp1d(sqdata[0],sqdata[1],kind='linear')
1004        fqdata = PDFControls['F(Q)'][1]
1005        fqfxn = scintp.interp1d(fqdata[0],fqdata[1],kind='linear')
1006        grdata = PDFControls['G(R)'][1]
1007        grfxn = scintp.interp1d(grdata[0],grdata[1],kind='linear')
1008        sqfile = open(sqfilename,'w')
1009        fqfile = open(sqfilename,'w')
1010        grfile = open(grfilename,'w')
1011        sqfile.write('#T S(Q) %s\n'%(export))
1012        fqfile.write('#T F(Q) %s\n'%(export))
1013        grfile.write('#T G(R) %s\n'%(export))
1014        sqfile.write('#L Q     S(Q)\n')
1015        fqfile.write('#L Q     F(Q)\n')
1016        grfile.write('#L R     G(R)\n')
1017        qnew = np.arange(sqdata[0][0],sqdata[0][-1],0.005)
1018        rnew = np.arange(grdata[0][0],grdata[0][-1],0.010)
1019        sqnew = zip(qnew,sqfxn(qnew))
1020        for q,sq in sqnew:
1021            sqfile.write("%15.6g %15.6g\n" % (q,sq))
1022        sqfile.close()
1023        print ' S(Q) saved to: ',sqfilename
1024        fqnew = zip(qnew,fqfxn(qnew))
1025        for q,fq in fqnew:
1026            fqfile.write("%15.6g %15.6g\n" % (q,fq))
1027        fqfile.close()
1028        print ' F(Q) saved to: ',fqfilename
1029        grnew = zip(rnew,grfxn(rnew))
1030        for r,gr in grnew:
1031            grfile.write("%15.6g %15.6g\n" % (r,gr))
1032        grfile.close()
1033        print ' G)R) saved to: ',grfilename
1034   
1035def PeakListSave(G2frame,file,peaks):
1036    'Save powder peaks to a data file'
1037    print 'save peak list to file: ',G2frame.peaklistfile
1038    if not peaks:
1039        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1040        try:
1041            dlg.ShowModal()
1042        finally:
1043            dlg.Destroy()
1044        return
1045    for peak in peaks:
1046        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
1047            (peak[0],peak[2],peak[4],peak[6]))
1048    print 'peak list saved'
1049             
1050def IndexPeakListSave(G2frame,peaks):
1051    'Save powder peaks from the indexing list'
1052    file = open(G2frame.peaklistfile,'wa')
1053    print 'save index peak list to file: ',G2frame.peaklistfile
1054    wx.BeginBusyCursor()
1055    try:
1056        if not peaks:
1057            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1058            try:
1059                dlg.ShowModal()
1060            finally:
1061                dlg.Destroy()
1062            return
1063        for peak in peaks:
1064            file.write("%12.6f\n" % (peak[7]))
1065        file.close()
1066    finally:
1067        wx.EndBusyCursor()
1068    print 'index peak list saved'
1069   
1070def SetNewPhase(Name='New Phase',SGData=None,cell=None,Super=None):
1071    '''Create a new phase dict with default values for various parameters
1072
1073    :param str Name: Name for new Phase
1074
1075    :param dict SGData: space group data from :func:`GSASIIspc:SpcGroup`;
1076      defaults to data for P 1
1077
1078    :param list cell: unit cell parameter list; defaults to
1079      [1.0,1.0,1.0,90.,90,90.,1.]
1080
1081    '''
1082    if SGData is None: SGData = G2spc.SpcGroup('P 1')[1]
1083    if cell is None: cell=[1.0,1.0,1.0,90.,90,90.,1.]
1084    phaseData = {
1085        'ranId':ran.randint(0,sys.maxint),
1086        'General':{
1087            'Name':Name,
1088            'Type':'nuclear',
1089            'Modulated':False,
1090            'AtomPtrs':[3,1,7,9],
1091            'SGData':SGData,
1092            'Cell':[False,]+cell,
1093            'Pawley dmin':1.0,
1094            'Data plot type':'None',
1095            'SH Texture':{
1096                'Order':0,
1097                'Model':'cylindrical',
1098                'Sample omega':[False,0.0],
1099                'Sample chi':[False,0.0],
1100                'Sample phi':[False,0.0],
1101                'SH Coeff':[False,{}],
1102                'SHShow':False,
1103                'PFhkl':[0,0,1],
1104                'PFxyz':[0,0,1],
1105                'PlotType':'Pole figure',
1106                'Penalty':[['',],0.1,False,1.0]}},
1107        'Atoms':[],
1108        'Drawing':{},
1109        'Histograms':{},
1110        'Pawley ref':[],
1111        'RBModels':{},
1112        }
1113    if Super and Super.get('Use',False):
1114        phaseData['General'].update({'Modulated':True,'Super':True,'SuperSg':Super['ssSymb']})
1115        phaseData['General']['SSGData'] = G2spc.SSpcGroup(SGData,Super['ssSymb'])
1116        phaseData['General']['SuperVec'] = [Super['ModVec'],False,Super['maxH']]
1117
1118    return phaseData
1119       
1120class MultipleChoicesDialog(wx.Dialog):
1121    '''A dialog that offers a series of choices, each with a
1122    title and a wx.Choice widget. Intended to be used Modally.
1123    typical input:
1124
1125        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1126        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1127       
1128    selections are placed in self.chosen when OK is pressed
1129
1130    Also see GSASIIctrls
1131    '''
1132    def __init__(self,choicelist,headinglist,
1133                 head='Select options',
1134                 title='Please select from options below',
1135                 parent=None):
1136        self.chosen = []
1137        wx.Dialog.__init__(
1138            self,parent,wx.ID_ANY,head, 
1139            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1140        panel = wx.Panel(self)
1141        mainSizer = wx.BoxSizer(wx.VERTICAL)
1142        mainSizer.Add((10,10),1)
1143        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1144        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1145        self.ChItems = []
1146        for choice,lbl in zip(choicelist,headinglist):
1147            mainSizer.Add((10,10),1)
1148            self.chosen.append(0)
1149            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1150            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1151            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1152            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1153
1154        OkBtn = wx.Button(panel,-1,"Ok")
1155        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1156        cancelBtn = wx.Button(panel,-1,"Cancel")
1157        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1158        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1159        btnSizer.Add((20,20),1)
1160        btnSizer.Add(OkBtn)
1161        btnSizer.Add((20,20),1)
1162        btnSizer.Add(cancelBtn)
1163        btnSizer.Add((20,20),1)
1164        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1165        panel.SetSizer(mainSizer)
1166        panel.Fit()
1167        self.Fit()
1168       
1169    def OnOk(self,event):
1170        parent = self.GetParent()
1171        if parent is not None: parent.Raise()
1172        # save the results from the choice widgets
1173        self.chosen = []
1174        for w in self.ChItems:
1175            self.chosen.append(w.GetSelection())
1176        self.EndModal(wx.ID_OK)             
1177           
1178    def OnCancel(self,event):
1179        parent = self.GetParent()
1180        if parent is not None: parent.Raise()
1181        self.chosen = []
1182        self.EndModal(wx.ID_CANCEL)             
1183           
1184def ExtractFileFromZip(filename, selection=None, confirmread=True,
1185                       confirmoverwrite=True, parent=None,
1186                       multipleselect=False):
1187    '''If the filename is a zip file, extract a file from that
1188    archive.
1189
1190    :param list Selection: used to predefine the name of the file
1191      to be extracted. Filename case and zip directory name are
1192      ignored in selection; the first matching file is used.
1193
1194    :param bool confirmread: if True asks the user to confirm before expanding
1195      the only file in a zip
1196
1197    :param bool confirmoverwrite: if True asks the user to confirm
1198      before overwriting if the extracted file already exists
1199
1200    :param bool multipleselect: if True allows more than one zip
1201      file to be extracted, a list of file(s) is returned.
1202      If only one file is present, do not ask which one, otherwise
1203      offer a list of choices (unless selection is used).
1204   
1205    :returns: the name of the file that has been created or a
1206      list of files (see multipleselect)
1207
1208    If the file is not a zipfile, return the name of the input file.
1209    If the zipfile is empty or no file has been selected, return None
1210    '''
1211    import zipfile # do this now, since we can save startup time by doing this only on need
1212    import shutil
1213    zloc = os.path.split(filename)[0]
1214    if not zipfile.is_zipfile(filename):
1215        #print("not zip")
1216        return filename
1217
1218    z = zipfile.ZipFile(filename,'r')
1219    zinfo = z.infolist()
1220
1221    if len(zinfo) == 0:
1222        #print('Zip has no files!')
1223        zlist = [-1]
1224    if selection:
1225        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1226        if selection.lower() in choices:
1227            zlist = [choices.index(selection.lower())]
1228        else:
1229            print('debug: file '+str(selection)+' was not found in '+str(filename))
1230            zlist = [-1]
1231    elif len(zinfo) == 1 and confirmread:
1232        result = wx.ID_NO
1233        dlg = wx.MessageDialog(
1234            parent,
1235            'Is file '+str(zinfo[0].filename)+
1236            ' what you want to extract from '+
1237            str(os.path.split(filename)[1])+'?',
1238            'Confirm file', 
1239            wx.YES_NO | wx.ICON_QUESTION)
1240        try:
1241            result = dlg.ShowModal()
1242        finally:
1243            dlg.Destroy()
1244        if result == wx.ID_NO:
1245            zlist = [-1]
1246        else:
1247            zlist = [0]
1248    elif len(zinfo) == 1:
1249        zlist = [0]
1250    elif multipleselect:
1251        # select one or more from a from list
1252        choices = [i.filename for i in zinfo]
1253        dlg = G2G.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1254            'Choose file(s)',choices)
1255        if dlg.ShowModal() == wx.ID_OK:
1256            zlist = dlg.GetSelections()
1257        else:
1258            zlist = []
1259        dlg.Destroy()
1260    else:
1261        # select one from a from list
1262        choices = [i.filename for i in zinfo]
1263        dlg = wx.SingleChoiceDialog(parent,
1264            'Select file to extract from zip file'+str(filename),'Choose file',
1265            choices,)
1266        if dlg.ShowModal() == wx.ID_OK:
1267            zlist = [dlg.GetSelection()]
1268        else:
1269            zlist = [-1]
1270        dlg.Destroy()
1271       
1272    outlist = []
1273    for zindex in zlist:
1274        if zindex >= 0:
1275            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1276            if os.path.exists(efil) and confirmoverwrite:
1277                result = wx.ID_NO
1278                dlg = wx.MessageDialog(parent,
1279                    'File '+str(efil)+' already exists. OK to overwrite it?',
1280                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1281                try:
1282                    result = dlg.ShowModal()
1283                finally:
1284                    dlg.Destroy()
1285                if result == wx.ID_NO:
1286                    zindex = -1
1287        if zindex >= 0:
1288            # extract the file to the current directory, regardless of it's original path
1289            #z.extract(zinfo[zindex],zloc)
1290            eloc,efil = os.path.split(zinfo[zindex].filename)
1291            outfile = os.path.join(zloc, efil)
1292            fpin = z.open(zinfo[zindex])
1293            fpout = file(outfile, "wb")
1294            shutil.copyfileobj(fpin, fpout)
1295            fpin.close()
1296            fpout.close()
1297            outlist.append(outfile)
1298    z.close()
1299    if multipleselect and len(outlist) >= 1:
1300        return outlist
1301    elif len(outlist) == 1:
1302        return outlist[0]
1303    else:
1304        return None
1305
1306######################################################################
1307# base classes for reading various types of data files
1308#   not used directly, only by subclassing
1309######################################################################
1310try:
1311    E,SGData = G2spc.SpcGroup('P 1') # data structure for default space group
1312except: # errors on doc build
1313    SGData = None
1314P1SGData = SGData
1315######################################################################
1316class ImportBaseclass(object):
1317    '''Defines a base class for the reading of input files (diffraction
1318    data, coordinates,...). See :ref:`Writing a Import Routine<Import_routines>`
1319    for an explanation on how to use a subclass of this class.
1320    '''
1321    class ImportException(Exception):
1322        '''Defines an Exception that is used when an import routine hits an expected error,
1323        usually in .Reader.
1324
1325        Good practice is that the Reader should define a value in self.errors that
1326        tells the user some information about what is wrong with their file.         
1327        '''
1328        pass
1329   
1330    UseReader = True  # in __init__ set value of self.UseReader to False to skip use of current importer
1331    def __init__(self,formatName,longFormatName=None,
1332                 extensionlist=[],strictExtension=False,):
1333        self.formatName = formatName # short string naming file type
1334        if longFormatName: # longer string naming file type
1335            self.longFormatName = longFormatName
1336        else:
1337            self.longFormatName = formatName
1338        # define extensions that are allowed for the file type
1339        # for windows, remove any extensions that are duplicate, as case is ignored
1340        if sys.platform == 'windows' and extensionlist:
1341            extensionlist = list(set([s.lower() for s in extensionlist]))
1342        self.extensionlist = extensionlist
1343        # If strictExtension is True, the file will not be read, unless
1344        # the extension matches one in the extensionlist
1345        self.strictExtension = strictExtension
1346        self.errors = ''
1347        self.warnings = ''
1348        # used for readers that will use multiple passes to read
1349        # more than one data block
1350        self.repeat = False
1351        self.selections = []
1352        self.repeatcount = 0
1353        self.readfilename = '?'
1354        #print 'created',self.__class__
1355
1356    def ReInitialize(self):
1357        'Reinitialize the Reader to initial settings'
1358        self.errors = ''
1359        self.warnings = ''
1360        self.repeat = False
1361        self.repeatcount = 0
1362        self.readfilename = '?'
1363
1364    def BlockSelector(self, ChoiceList, ParentFrame=None,title='Select a block',
1365        size=None, header='Block Selector',useCancel=True):
1366        ''' Provide a wx dialog to select a block if the file contains more
1367        than one set of data and one must be selected
1368        '''
1369        if useCancel:
1370            dlg = wx.SingleChoiceDialog(
1371                ParentFrame,title, header,ChoiceList)
1372        else:
1373            dlg = wx.SingleChoiceDialog(
1374                ParentFrame,title, header,ChoiceList,
1375                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1376        if size: dlg.SetSize(size)
1377        dlg.CenterOnParent()
1378        if dlg.ShowModal() == wx.ID_OK:
1379            sel = dlg.GetSelection()
1380            return sel
1381        else:
1382            return None
1383        dlg.Destroy()
1384
1385    def MultipleBlockSelector(self, ChoiceList, ParentFrame=None,
1386        title='Select a block',size=None, header='Block Selector'):
1387        '''Provide a wx dialog to select a block of data if the
1388        file contains more than one set of data and one must be
1389        selected.
1390
1391        :returns: a list of the selected blocks
1392        '''
1393        dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1394            wx.CHOICEDLG_STYLE)
1395        dlg.CenterOnScreen()
1396        if size: dlg.SetSize(size)
1397        if dlg.ShowModal() == wx.ID_OK:
1398            sel = dlg.GetSelections()
1399        else:
1400            return []
1401        dlg.Destroy()
1402        selected = []
1403        if len(ChoiceList) in sel:
1404            return range(len(ChoiceList))
1405        else:
1406            return sel
1407        return selected
1408
1409    def MultipleChoicesDialog(self, choicelist, headinglist, ParentFrame=None, **kwargs):
1410        '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1411        widget. Typical input:
1412       
1413           * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1414           
1415           * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1416           
1417        optional keyword parameters are: head (window title) and title
1418        returns a list of selected indicies for each choice (or None)
1419        '''
1420        result = None
1421        dlg = MultipleChoicesDialog(choicelist,headinglist,
1422            parent=ParentFrame, **kwargs)         
1423        dlg.CenterOnParent()
1424        if dlg.ShowModal() == wx.ID_OK:
1425            result = dlg.chosen
1426        dlg.Destroy()
1427        return result
1428
1429    def ShowBusy(self):
1430        wx.BeginBusyCursor()
1431#        wx.Yield() # make it happen now!
1432
1433    def DoneBusy(self):
1434        wx.EndBusyCursor()
1435        wx.Yield() # make it happen now!
1436       
1437#    def Reader(self, filename, filepointer, ParentFrame=None, **unused):
1438#        '''This method must be supplied in the child class to read the file.
1439#        if the read fails either return False or raise an Exception
1440#        preferably of type ImportException.
1441#        '''
1442#        #start reading
1443#        raise ImportException("Error occurred while...")
1444#        self.errors += "Hint for user on why the error occur
1445#        return False # if an error occurs
1446#        return True # if read OK
1447
1448    def ExtensionValidator(self, filename):
1449        '''This methods checks if the file has the correct extension
1450        Return False if this filename will not be supported by this reader
1451        Return True if the extension matches the list supplied by the reader
1452        Return None if the reader allows un-registered extensions
1453        '''
1454        if filename:
1455            ext = os.path.splitext(filename)[1]
1456            if sys.platform == 'windows': ext = ext.lower()
1457            if ext in self.extensionlist: return True
1458            if self.strictExtension: return False
1459        return None
1460
1461    def ContentsValidator(self, filepointer):
1462        '''This routine will attempt to determine if the file can be read
1463        with the current format.
1464        This will typically be overridden with a method that
1465        takes a quick scan of [some of]
1466        the file contents to do a "sanity" check if the file
1467        appears to match the selected format.
1468        Expected to be called via self.Validator()
1469        '''
1470        #filepointer.seek(0) # rewind the file pointer
1471        return True
1472
1473    def CIFValidator(self, filepointer):
1474        '''A :meth:`ContentsValidator` for use to validate CIF files.
1475        '''
1476        for i,l in enumerate(filepointer):
1477            if i >= 1000: return True
1478            '''Encountered only blank lines or comments in first 1000
1479            lines. This is unlikely, but assume it is CIF anyway, since we are
1480            even less likely to find a file with nothing but hashes and
1481            blank lines'''
1482            line = l.strip()
1483            if len(line) == 0: # ignore blank lines
1484                continue 
1485            elif line.startswith('#'): # ignore comments
1486                continue 
1487            elif line.startswith('data_'): # on the right track, accept this file
1488                return True
1489            else: # found something invalid
1490                self.errors = 'line '+str(i+1)+' contains unexpected data:\n'
1491                if all([ord(c) < 128 and ord(c) != 0 for c in str(l)]): # show only if ASCII
1492                    self.errors += '  '+str(l)
1493                else: 
1494                    self.errors += '  (binary)'
1495                self.errors += '\n  Note: a CIF should only have blank lines or comments before'
1496                self.errors += '\n        a data_ statement begins a block.'
1497                return False 
1498
1499######################################################################
1500class ImportPhase(ImportBaseclass):
1501    '''Defines a base class for the reading of files with coordinates
1502
1503    Objects constructed that subclass this (in import/G2phase_*.py etc.) will be used
1504    in :meth:`GSASII.GSASII.OnImportPhase`.
1505    See :ref:`Writing a Import Routine<Import_Routines>`
1506    for an explanation on how to use this class.
1507
1508    '''
1509    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1510        strictExtension=False,):
1511        # call parent __init__
1512        ImportBaseclass.__init__(self,formatName,longFormatName,
1513            extensionlist,strictExtension)
1514        self.Phase = None # a phase must be created with G2IO.SetNewPhase in the Reader
1515        self.Constraints = None
1516
1517    def PhaseSelector(self, ChoiceList, ParentFrame=None,
1518        title='Select a phase', size=None,header='Phase Selector'):
1519        ''' Provide a wx dialog to select a phase if the file contains more
1520        than one phase
1521        '''
1522        return self.BlockSelector(ChoiceList,ParentFrame,title,
1523            size,header)
1524
1525######################################################################
1526class ImportStructFactor(ImportBaseclass):
1527    '''Defines a base class for the reading of files with tables
1528    of structure factors.
1529
1530    Structure factors are read with a call to :meth:`GSASII.GSASII.OnImportSfact`
1531    which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`, which calls
1532    methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1533    :meth:`Reader`.
1534
1535    See :ref:`Writing a Import Routine<Import_Routines>`
1536    for an explanation on how to use import classes in general. The specifics
1537    for reading a structure factor histogram require that
1538    the ``Reader()`` routine in the import
1539    class need to do only a few things: It
1540    should load :attr:`RefDict` item ``'RefList'`` with the reflection list,
1541    and set :attr:`Parameters` with the instrument parameters
1542    (initialized with :meth:`InitParameters` and set with :meth:`UpdateParameters`).
1543    '''
1544    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1545        strictExtension=False,):
1546        ImportBaseclass.__init__(self,formatName,longFormatName,
1547            extensionlist,strictExtension)
1548
1549        # define contents of Structure Factor entry
1550        self.Parameters = []
1551        'self.Parameters is a list with two dicts for data parameter settings'
1552        self.InitParameters()
1553        self.RefDict = {'RefList':[],'FF':{},'Super':0}
1554        self.Banks = []             #for multi bank data (usually TOF)
1555        '''self.RefDict is a dict containing the reflection information, as read from the file.
1556        Item 'RefList' contains the reflection information. See the
1557        :ref:`Single Crystal Reflection Data Structure<XtalRefl_table>`
1558        for the contents of each row. Dict element 'FF'
1559        contains the form factor values for each element type; if this entry
1560        is left as initialized (an empty list) it will be initialized as needed later.
1561        '''
1562    def ReInitialize(self):
1563        'Reinitialize the Reader to initial settings'
1564        ImportBaseclass.ReInitialize(self)
1565        self.InitParameters()
1566        self.Banks = []             #for multi bank data (usually TOF)
1567        self.RefDict = {'RefList':[],'FF':{},'Super':0}
1568       
1569    def InitParameters(self):
1570        'initialize the instrument parameters structure'
1571        Lambda = 0.70926
1572        HistType = 'SXC'
1573        self.Parameters = [{'Type':[HistType,HistType], # create the structure
1574                            'Lam':[Lambda,Lambda]
1575                            }, {}]
1576        'Parameters is a list with two dicts for data parameter settings'
1577
1578    def UpdateParameters(self,Type=None,Wave=None):
1579        'Revise the instrument parameters'
1580        if Type is not None:
1581            self.Parameters[0]['Type'] = [Type,Type]
1582        if Wave is not None:
1583            self.Parameters[0]['Lam'] = [Wave,Wave]           
1584                       
1585######################################################################
1586class ImportPowderData(ImportBaseclass):
1587    '''Defines a base class for the reading of files with powder data.
1588
1589    Objects constructed that subclass this (in import/G2pwd_*.py etc.) will be used
1590    in :meth:`GSASII.GSASII.OnImportPowder`.
1591    See :ref:`Writing a Import Routine<Import_Routines>`
1592    for an explanation on how to use this class.
1593    '''
1594    def __init__(self,formatName,longFormatName=None,
1595        extensionlist=[],strictExtension=False,):
1596        ImportBaseclass.__init__(self,formatName,longFormatName,
1597            extensionlist,strictExtension)
1598        self.clockWd = None  # used in TOF
1599        self.ReInitialize()
1600       
1601    def ReInitialize(self):
1602        'Reinitialize the Reader to initial settings'
1603        ImportBaseclass.ReInitialize(self)
1604        self.powderentry = ['',None,None] #  (filename,Pos,Bank)
1605        self.powderdata = [] # Powder dataset
1606        '''A powder data set is a list with items [x,y,w,yc,yb,yd]:
1607                np.array(x), # x-axis values
1608                np.array(y), # powder pattern intensities
1609                np.array(w), # 1/sig(intensity)^2 values (weights)
1610                np.array(yc), # calc. intensities (zero)
1611                np.array(yb), # calc. background (zero)
1612                np.array(yd), # obs-calc profiles
1613        '''                           
1614        self.comments = []
1615        self.idstring = ''
1616        self.Sample = G2pdG.SetDefaultSample() # default sample parameters
1617        self.Controls = {}  # items to be placed in top-level Controls
1618        self.GSAS = None     # used in TOF
1619        self.repeat_instparm = True # Should a parm file be
1620        #                             used for multiple histograms?
1621        self.instparm = None # name hint from file of instparm to use
1622        self.instfile = '' # full path name to instrument parameter file
1623        self.instbank = '' # inst parm bank number
1624        self.instmsg = ''  # a label that gets printed to show
1625                           # where instrument parameters are from
1626        self.numbanks = 1
1627        self.instdict = {} # place items here that will be transferred to the instrument parameters
1628        self.pwdparms = {} # place parameters that are transferred directly to the tree
1629                           # here (typically from an existing GPX file)
1630######################################################################
1631class ImportSmallAngleData(ImportBaseclass):
1632    '''Defines a base class for the reading of files with small angle data.
1633    See :ref:`Writing a Import Routine<Import_Routines>`
1634    for an explanation on how to use this class.
1635    '''
1636    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1637        strictExtension=False,):
1638           
1639        ImportBaseclass.__init__(self,formatName,longFormatName,extensionlist,
1640            strictExtension)
1641        self.ReInitialize()
1642       
1643    def ReInitialize(self):
1644        'Reinitialize the Reader to initial settings'
1645        ImportBaseclass.ReInitialize(self)
1646        self.smallangleentry = ['',None,None] #  (filename,Pos,Bank)
1647        self.smallangledata = [] # SASD dataset
1648        '''A small angle data set is a list with items [x,y,w,yc,yd]:
1649                np.array(x), # x-axis values
1650                np.array(y), # powder pattern intensities
1651                np.array(w), # 1/sig(intensity)^2 values (weights)
1652                np.array(yc), # calc. intensities (zero)
1653                np.array(yd), # obs-calc profiles
1654                np.array(yb), # preset bkg
1655        '''                           
1656        self.comments = []
1657        self.idstring = ''
1658        self.Sample = G2pdG.SetDefaultSample()
1659        self.GSAS = None     # used in TOF
1660        self.clockWd = None  # used in TOF
1661        self.numbanks = 1
1662        self.instdict = {} # place items here that will be transferred to the instrument parameters
1663
1664######################################################################
1665class ImportImage(ImportBaseclass):
1666    '''Defines a base class for the reading of images
1667
1668    Images are read in only these places:
1669   
1670      * Initial reading is typically done from a menu item
1671        with a call to :meth:`GSASII.GSASII.OnImportImage`
1672        which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`. That calls
1673        methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1674        :meth:`Reader`. This returns a list of reader objects for each read image.
1675
1676      * Images are read alternatively in :func:`GSASIIIO.ReadImages`, which puts image info
1677        directly into the data tree.
1678
1679      * Images are reloaded with :func:`GSASIIIO.GetImageData`.
1680
1681    .. _Image_import_routines:
1682
1683    When reading an image, the ``Reader()`` routine in the ImportImage class
1684    should set:
1685   
1686      * :attr:`Comments`: a list of strings (str),
1687      * :attr:`Npix`: the number of pixels in the image (int),
1688      * :attr:`Image`: the actual image as a numpy array (np.array)
1689      * :attr:`Data`: a dict defining image parameters (dict). Within this dict the following
1690        data items are needed:
1691       
1692         * 'pixelSize': size of each pixel in microns (such as ``[200,200]``.
1693         * 'wavelength': wavelength in Angstoms.
1694         * 'distance': distance of detector from sample in cm.
1695         * 'center': uncalibrated center of beam on detector (such as ``[204.8,204.8]``.
1696         * 'size': size of image (such as ``[2048,2048]``).
1697         * 'ImageTag': image number or other keyword used to retrieve image from
1698           a multi-image data file (defaults to ``1`` if not specified).
1699         * 'sumfile': holds sum image file name if a sum was produced from a multi image file
1700
1701    optional data items:
1702   
1703      * :attr:`repeat`: set to True if there are additional images to
1704        read in the file, False otherwise
1705      * :attr:`repeatcount`: set to the number of the image.
1706     
1707    Note that the above is initialized with :meth:`InitParameters`.
1708    (Also see :ref:`Writing a Import Routine<Import_Routines>`
1709    for an explanation on how to use import classes in general.)
1710    '''
1711    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1712        strictExtension=False,):
1713        ImportBaseclass.__init__(self,formatName,longFormatName,
1714            extensionlist,strictExtension)
1715        self.InitParameters()
1716       
1717    def ReInitialize(self):
1718        'Reinitialize the Reader to initial settings -- not used at present'
1719        ImportBaseclass.ReInitialize(self)
1720        self.InitParameters()
1721       
1722    def InitParameters(self):
1723        'initialize the instrument parameters structure'
1724        self.Comments = ['No comments']
1725        self.Data = {}
1726        self.Npix = 0
1727        self.Image = None
1728        self.repeat = False
1729        self.repeatcount = 1
1730        self.sumfile = ''
1731
1732    def LoadImage(self,ParentFrame,imagefile,imagetag=None):
1733        '''Optionally, call this after reading in an image to load it into the tree.
1734        This saves time by preventing a reread of the same information.
1735        '''
1736        if ParentFrame:
1737            ParentFrame.ImageZ = self.Image   # store the image for plotting
1738            ParentFrame.oldImagefile = imagefile # save the name of the last image file read
1739            ParentFrame.oldImageTag = imagetag   # save the tag of the last image file read           
1740
1741######################################################################
1742def striphist(var,insChar=''):
1743    'strip a histogram number from a var name'
1744    sv = var.split(':')
1745    if len(sv) <= 1: return var
1746    if sv[1]:
1747        sv[1] = insChar
1748    return ':'.join(sv)
1749class ExportBaseclass(object):
1750    '''Defines a base class for the exporting of GSAS-II results.
1751
1752    This class is subclassed in the various exports/G2export_*.py files. Those files
1753    are imported in :meth:`GSASII.GSASII._init_Exports` which defines the
1754    appropriate menu items for each one and the .Exporter method is called
1755    directly from the menu item.
1756
1757    Routines may also define a .Writer method, which is used to write a single
1758    file without invoking any GUI objects.
1759    '''
1760    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1761        self.G2frame = G2frame
1762        self.formatName = formatName # short string naming file type
1763        self.extension = extension
1764        if longFormatName: # longer string naming file type
1765            self.longFormatName = longFormatName
1766        else:
1767            self.longFormatName = formatName
1768        self.OverallParms = {}
1769        self.Phases = {}
1770        self.Histograms = {}
1771        self.powderDict = {}
1772        self.xtalDict = {}
1773        self.parmDict = {}
1774        self.sigDict = {}
1775        # updated in InitExport:
1776        self.currentExportType = None # type of export that has been requested
1777        # updated in ExportSelect (when used):
1778        self.phasenam = None # a list of selected phases
1779        self.histnam = None # a list of selected histograms
1780        self.filename = None # name of file to be written (single export) or template (multiple files)
1781        self.dirname = '' # name of directory where file(s) will be written
1782        self.fullpath = '' # name of file being written -- full path
1783       
1784        # items that should be defined in a subclass of this class
1785        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1786        # The following types are defined: 'project', "phase", "powder", "single"
1787        self.multiple = False # set as True if the class can export multiple phases or histograms
1788        # self.multiple is ignored for "project" exports
1789
1790    def InitExport(self,event):
1791        '''Determines the type of menu that called the Exporter and
1792        misc initialization.
1793        '''
1794        self.filename = None # name of file to be written (single export)
1795        self.dirname = '' # name of file to be written (multiple export)
1796        if event:
1797            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1798
1799    def MakePWDRfilename(self,hist):
1800        '''Make a filename root (no extension) from a PWDR histogram name
1801
1802        :param str hist: the histogram name in data tree (starts with "PWDR ")
1803        '''
1804        file0 = ''
1805        file1 = hist[5:]
1806        # replace repeated blanks
1807        while file1 != file0:
1808            file0 = file1
1809            file1 = file0.replace('  ',' ').strip()
1810        file0 = file1.replace('Azm= ','A')
1811        # if angle has unneeded decimal places on aziumuth, remove them
1812        if file0[-3:] == '.00': file0 = file0[:-3]
1813        file0 = file0.replace('.','_')
1814        file0 = file0.replace(' ','_')
1815        return file0
1816
1817    def ExportSelect(self,AskFile='ask'):
1818        '''Selects histograms or phases when needed. Sets a default file name when
1819        requested into self.filename; always sets a default directory in self.dirname.
1820
1821        :param bool AskFile: Determines how this routine processes getting a
1822          location to store the current export(s).
1823         
1824          * if AskFile is 'ask' (default option), get the name of the file to be written;
1825            self.filename and self.dirname are always set. In the case where
1826            multiple files must be generated, the export routine should do this
1827            based on self.filename as a template.
1828          * if AskFile is 'dir', get the name of the directory to be used;
1829            self.filename is not used, but self.dirname is always set. The export routine
1830            will always generate the file name.
1831          * if AskFile is 'single', get only the name of the directory to be used when
1832            multiple items will be written (as multiple files) are used
1833            *or* a complete file name is requested when a single file
1834            name is selected. self.dirname is always set and self.filename used
1835            only when a single file is selected.
1836          * if AskFile is 'default', creates a name of the file to be used from
1837            the name of the project (.gpx) file. If the project has not been saved,
1838            then the name of file is requested.
1839            self.filename and self.dirname are always set. In the case where
1840            multiple file names must be generated, the export routine should do this
1841            based on self.filename.
1842          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1843            file. If the project has not been saved, then a directory is requested.
1844            self.filename is not used.
1845
1846        :returns: True in case of an error
1847        '''
1848       
1849        numselected = 1
1850        if self.currentExportType == 'phase':
1851            if len(self.Phases) == 0:
1852                self.G2frame.ErrorDialog(
1853                    'Empty project',
1854                    'Project does not contain any phases.')
1855                return True
1856            elif len(self.Phases) == 1:
1857                self.phasenam = self.Phases.keys()
1858            elif self.multiple: 
1859                choices = sorted(self.Phases.keys())
1860                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1861                if phasenum is None: return True
1862                self.phasenam = [choices[i] for i in phasenum]
1863                if not self.phasenam: return True
1864                numselected = len(self.phasenam)
1865            else:
1866                choices = sorted(self.Phases.keys())
1867                phasenum = G2G.ItemSelector(choices,self.G2frame)
1868                if phasenum is None: return True
1869                self.phasenam = [choices[phasenum]]
1870                numselected = len(self.phasenam)
1871        elif self.currentExportType == 'single':
1872            if len(self.xtalDict) == 0:
1873                self.G2frame.ErrorDialog(
1874                    'Empty project',
1875                    'Project does not contain any single crystal data.')
1876                return True
1877            elif len(self.xtalDict) == 1:
1878                self.histnam = self.xtalDict.values()
1879            elif self.multiple:
1880                choices = sorted(self.xtalDict.values())
1881                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1882                if not hnum: return True
1883                self.histnam = [choices[i] for i in hnum]
1884                numselected = len(self.histnam)
1885            else:
1886                choices = sorted(self.xtalDict.values())
1887                hnum = G2G.ItemSelector(choices,self.G2frame)
1888                if hnum is None: return True
1889                self.histnam = [choices[hnum]]
1890                numselected = len(self.histnam)
1891        elif self.currentExportType == 'powder':
1892            if len(self.powderDict) == 0:
1893                self.G2frame.ErrorDialog(
1894                    'Empty project',
1895                    'Project does not contain any powder data.')
1896                return True
1897            elif len(self.powderDict) == 1:
1898                self.histnam = self.powderDict.values()
1899            elif self.multiple:
1900                choices = sorted(self.powderDict.values())
1901                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1902                if not hnum: return True
1903                self.histnam = [choices[i] for i in hnum]
1904                numselected = len(self.histnam)
1905            else:
1906                choices = sorted(self.powderDict.values())
1907                hnum = G2G.ItemSelector(choices,self.G2frame)
1908                if hnum is None: return True
1909                self.histnam = [choices[hnum]]
1910                numselected = len(self.histnam)
1911        elif self.currentExportType == 'image':
1912            if len(self.Histograms) == 0:
1913                self.G2frame.ErrorDialog(
1914                    'Empty project',
1915                    'Project does not contain any images.')
1916                return True
1917            elif len(self.Histograms) == 1:
1918                self.histnam = self.Histograms.keys()
1919            else:
1920                choices = sorted(self.Histograms.keys())
1921                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1922                if self.multiple:
1923                    if not hnum: return True
1924                    self.histnam = [choices[i] for i in hnum]
1925                else:
1926                    if hnum is None: return True
1927                    self.histnam = [choices[hnum]]
1928                numselected = len(self.histnam)
1929        if self.currentExportType == 'map':
1930            # search for phases with maps
1931            mapPhases = []
1932            choices = []
1933            for phasenam in sorted(self.Phases):
1934                phasedict = self.Phases[phasenam] # pointer to current phase info           
1935                if len(phasedict['General']['Map'].get('rho',[])):
1936                    mapPhases.append(phasenam)
1937                    if phasedict['General']['Map'].get('Flip'):
1938                        choices.append('Charge flip map: '+str(phasenam))
1939                    elif phasedict['General']['Map'].get('MapType'):
1940                        choices.append(
1941                            str(phasedict['General']['Map'].get('MapType'))
1942                            + ' map: ' + str(phasenam))
1943                    else:
1944                        choices.append('unknown map: '+str(phasenam))
1945            # select a map if needed
1946            if len(mapPhases) == 0:
1947                self.G2frame.ErrorDialog(
1948                    'Empty project',
1949                    'Project does not contain any maps.')
1950                return True
1951            elif len(mapPhases) == 1:
1952                self.phasenam = mapPhases
1953            else: 
1954                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1955                if self.multiple:
1956                    if not phasenum: return True
1957                    self.phasenam = [mapPhases[i] for i in phasenum]
1958                else:
1959                    if phasenum is None: return True
1960                    self.phasenam = [mapPhases[phasenum]]
1961            numselected = len(self.phasenam)
1962
1963        # items selected, now set self.dirname and usually self.filename
1964        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
1965            AskFile == 'default' and not self.G2frame.GSASprojectfile
1966            ):
1967            filename = self.askSaveFile()
1968            if not filename: return True
1969            self.dirname,self.filename = os.path.split(filename)
1970        elif AskFile == 'dir' or AskFile == 'single' or (
1971            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
1972            ):
1973            self.dirname = self.askSaveDirectory()
1974            if not self.dirname: return True
1975        elif AskFile == 'default-dir' or AskFile == 'default':
1976            self.dirname,self.filename = os.path.split(
1977                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
1978                )
1979        else:
1980            raise Exception('This should not happen!')
1981
1982    def loadParmDict(self):
1983        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
1984        refined values to those from the last cycle and set the uncertainties for the
1985        refined parameters in another dict (self.sigDict).
1986
1987        Expands the parm & sig dicts to include values derived from constraints.
1988        '''
1989        self.parmDict = {}
1990        self.sigDict = {}
1991        rigidbodyDict = {}
1992        covDict = {}
1993        consDict = {}
1994        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1995        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
1996        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
1997        while item:
1998            name = self.G2frame.PatternTree.GetItemText(item)
1999            if name == 'Rigid bodies':
2000                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
2001            elif name == 'Covariance':
2002                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
2003            elif name == 'Constraints':
2004                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
2005            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2006        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2007        self.parmDict.update(rbDict)
2008        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2009        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave =  G2stIO.GetPhaseData(
2010            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
2011        self.parmDict.update(phaseDict)
2012        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
2013            Phases,Histograms,Print=False,resetRefList=False)
2014        self.parmDict.update(hapDict)
2015        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
2016        self.parmDict.update(histDict)
2017        self.parmDict.update(zip(
2018            covDict.get('varyList',[]),
2019            covDict.get('variables',[])))
2020        self.sigDict = dict(zip(
2021            covDict.get('varyList',[]),
2022            covDict.get('sig',[])))
2023        # expand to include constraints: first compile a list of constraints
2024        constList = []
2025        for item in consDict:
2026            if item.startswith('_'): continue
2027            constList += consDict[item]
2028        # now process the constraints
2029        G2mv.InitVars()
2030        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
2031        varyList = covDict.get('varyListStart')
2032        if varyList is None and len(constDict) == 0:
2033            # no constraints can use varyList
2034            varyList = covDict.get('varyList')
2035        elif varyList is None:
2036            # old GPX file from before pre-constraint varyList is saved
2037            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
2038            raise Exception(' *** Export aborted ***')
2039        else:
2040            varyList = list(varyList)
2041        try:
2042            groups,parmlist = G2mv.GroupConstraints(constDict)
2043            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,self.parmDict)
2044        except:
2045            # this really should not happen
2046            print ' *** ERROR - constraints are internally inconsistent ***'
2047            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
2048            print 'Errors',errmsg
2049            if warnmsg: print 'Warnings',warnmsg
2050            raise Exception(' *** CIF creation aborted ***')
2051        # add the constrained values to the parameter dictionary
2052        G2mv.Dict2Map(self.parmDict,varyList)
2053        # and add their uncertainties into the esd dictionary (sigDict)
2054        if covDict.get('covMatrix') is not None:
2055            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
2056
2057    def loadTree(self):
2058        '''Load the contents of the data tree into a set of dicts
2059        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
2060        & self.xtalDict)
2061       
2062        * The childrenless data tree items are overall parameters/controls for the
2063          entire project and are placed in self.OverallParms
2064        * Phase items are placed in self.Phases
2065        * Data items are placed in self.Histogram. The key for these data items
2066          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
2067        '''
2068        self.OverallParms = {}
2069        self.powderDict = {}
2070        self.xtalDict = {}
2071        self.Phases = {}
2072        self.Histograms = {}
2073        self.SeqRefdata = None
2074        self.SeqRefhist = None
2075        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2076        histType = None       
2077        if self.currentExportType == 'phase':
2078            # if exporting phases load them here
2079            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
2080            if not sub:
2081                print 'no phases found'
2082                return True
2083            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
2084            while item:
2085                phaseName = self.G2frame.PatternTree.GetItemText(item)
2086                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
2087                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
2088            return
2089        elif self.currentExportType == 'single':
2090            histType = 'HKLF'
2091        elif self.currentExportType == 'powder':
2092            histType = 'PWDR'
2093        elif self.currentExportType == 'image':
2094            histType = 'IMG'
2095
2096        if histType: # Loading just one kind of tree entry
2097            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2098            while item:
2099                name = self.G2frame.PatternTree.GetItemText(item)
2100                if name.startswith(histType):
2101                    if self.Histograms.get(name): # there is already an item with this name
2102                        print('Histogram name '+str(name)+' is repeated. Renaming')
2103                        if name[-1] == '9':
2104                            name = name[:-1] + '10'
2105                        elif name[-1] in '012345678':
2106                            name = name[:-1] + str(int(name[-1])+1)
2107                        else:                           
2108                            name += '-1'
2109                    self.Histograms[name] = {}
2110                    # the main info goes into Data, but the 0th
2111                    # element contains refinement results, carry
2112                    # that over too now.
2113                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
2114                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
2115                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2116                    while item2: 
2117                        child = self.G2frame.PatternTree.GetItemText(item2)
2118                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
2119                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
2120                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2121            # index powder and single crystal histograms by number
2122            for hist in self.Histograms:
2123                if hist.startswith("PWDR"): 
2124                    d = self.powderDict
2125                elif hist.startswith("HKLF"): 
2126                    d = self.xtalDict
2127                else:
2128                    return                   
2129                i = self.Histograms[hist].get('hId')
2130                if i is None and not d.keys():
2131                    i = 0
2132                elif i is None or i in d.keys():
2133                    i = max(d.keys())+1
2134                d[i] = hist
2135            return
2136        # else standard load: using all interlinked phases and histograms
2137        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2138        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2139        while item:
2140            name = self.G2frame.PatternTree.GetItemText(item)
2141            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2142            if not item2: 
2143                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
2144            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2145        # index powder and single crystal histograms
2146        for hist in self.Histograms:
2147            i = self.Histograms[hist]['hId']
2148            if hist.startswith("PWDR"): 
2149                self.powderDict[i] = hist
2150            elif hist.startswith("HKLF"): 
2151                self.xtalDict[i] = hist
2152
2153    def dumpTree(self,mode='type'):
2154        '''Print out information on the data tree dicts loaded in loadTree.
2155        Used for testing only.
2156        '''
2157        if self.SeqRefdata and self.SeqRefhist:
2158            print('Note that dumpTree does not show sequential results')
2159        print '\nOverall'
2160        if mode == 'type':
2161            def Show(arg): return type(arg)
2162        else:
2163            def Show(arg): return arg
2164        for key in self.OverallParms:
2165            print '  ',key,Show(self.OverallParms[key])
2166        print 'Phases'
2167        for key1 in self.Phases:
2168            print '    ',key1,Show(self.Phases[key1])
2169        print 'Histogram'
2170        for key1 in self.Histograms:
2171            print '    ',key1,Show(self.Histograms[key1])
2172            for key2 in self.Histograms[key1]:
2173                print '      ',key2,Show(self.Histograms[key1][key2])
2174
2175    def defaultSaveFile(self):
2176        return os.path.abspath(
2177            os.path.splitext(self.G2frame.GSASprojectfile
2178                             )[0]+self.extension)
2179       
2180    def askSaveFile(self):
2181        '''Ask the user to supply a file name
2182
2183        :returns: a file name (str) or None if Cancel is pressed
2184        '''
2185       
2186        pth = G2G.GetExportPath(self.G2frame)
2187        defnam = os.path.splitext(
2188            os.path.split(self.G2frame.GSASprojectfile)[1]
2189            )[0]+self.extension
2190        dlg = wx.FileDialog(
2191            self.G2frame, 'Input name for file to write', pth, defnam,
2192            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2193            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2194        dlg.CenterOnParent()
2195        try:
2196            if dlg.ShowModal() == wx.ID_OK:
2197                filename = dlg.GetPath()
2198                self.G2frame.LastExportDir = os.path.split(filename)[0]
2199                filename = os.path.splitext(filename)[0]+self.extension # make sure extension is correct
2200            else:
2201                filename = None
2202        finally:
2203            dlg.Destroy()
2204        return filename
2205
2206    def askSaveDirectory(self):
2207        '''Ask the user to supply a directory name. Path name is used as the
2208        starting point for the next export path search.
2209
2210        :returns: a directory name (str) or None if Cancel is pressed
2211        '''
2212        pth = G2G.GetExportPath(self.G2frame)
2213        dlg = wx.DirDialog(
2214            self.G2frame, 'Input directory where file(s) will be written', pth,
2215            wx.DD_DEFAULT_STYLE)
2216        dlg.CenterOnParent()
2217        try:
2218            if dlg.ShowModal() == wx.ID_OK:
2219                filename = dlg.GetPath()
2220                self.G2frame.LastExportDir = filename
2221            else:
2222                filename = None
2223        finally:
2224            dlg.Destroy()
2225        return filename
2226
2227    # Tools for file writing.
2228    def OpenFile(self,fil=None,mode='w'):
2229        '''Open the output file
2230
2231        :param str fil: The name of the file to open. If None (default)
2232          the name defaults to self.dirname + self.filename.
2233          If an extension is supplied, it is not overridded,
2234          but if not, the default extension is used.
2235        :returns: the file object opened by the routine which is also
2236          saved as self.fp
2237        '''
2238        if mode == 'd': # debug mode
2239            self.fullpath = '(stdout)'
2240            self.fp = sys.stdout
2241            return
2242        if not fil:
2243            if not os.path.splitext(self.filename)[1]:
2244                self.filename += self.extension
2245            fil = os.path.join(self.dirname,self.filename)
2246        self.fullpath = os.path.abspath(fil)
2247        self.fp = open(self.fullpath,mode)
2248        return self.fp
2249
2250    def Write(self,line):
2251        '''write a line of output, attaching a line-end character
2252
2253        :param str line: the text to be written.
2254        '''
2255        self.fp.write(line+'\n')
2256       
2257    def CloseFile(self,fp=None):
2258        '''Close a file opened in OpenFile
2259
2260        :param file fp: the file object to be closed. If None (default)
2261          file object self.fp is closed.
2262        '''
2263        if self.fp == sys.stdout: return # debug mode
2264        if fp is None:
2265            fp = self.fp
2266            self.fp = None
2267        fp.close()
2268       
2269    def SetSeqRef(self,data,hist):
2270        '''Set the exporter to retrieve results from a sequential refinement
2271        rather than the main tree
2272        '''
2273        self.SeqRefdata = data
2274        self.SeqRefhist = hist
2275        data_name = data[hist]
2276        for i,val in zip(data_name['varyList'],data_name['sig']):
2277            self.sigDict[i] = val
2278            self.sigDict[striphist(i)] = val
2279        for i in data_name['parmDict']:
2280            self.parmDict[striphist(i)] = data_name['parmDict'][i]
2281            self.parmDict[i] = data_name['parmDict'][i]
2282            # zero out the dA[xyz] terms, they would only bring confusion
2283            key = i.split(':')
2284            if len(key) < 3: continue
2285            if key[2].startswith('dA'):
2286                self.parmDict[i] = 0.0
2287        for i,(val,sig) in data_name.get('depParmDict',{}).iteritems():
2288            self.parmDict[i] = val
2289            self.sigDict[i] = sig
2290        #GSASIIpath.IPyBreak()
2291               
2292    # Tools to pull information out of the data arrays
2293    def GetCell(self,phasenam):
2294        """Gets the unit cell parameters and their s.u.'s for a selected phase
2295
2296        :param str phasenam: the name for the selected phase
2297        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2298          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2299          cell values and `cellSig` has their uncertainties.
2300        """
2301        if self.SeqRefdata and self.SeqRefhist:
2302            return self.GetSeqCell(phasenam,self.SeqRefdata[self.SeqRefhist])
2303        phasedict = self.Phases[phasenam] # pointer to current phase info
2304        try:
2305            pfx = str(phasedict['pId'])+'::'
2306            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2307            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
2308                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2309            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2310            return cellList,cellSig
2311        except KeyError:
2312            cell = phasedict['General']['Cell'][1:]
2313            return cell,7*[0]
2314           
2315    def GetSeqCell(self,phasenam,data_name):
2316        """Gets the unit cell parameters and their s.u.'s for a selected phase
2317        and histogram in a sequential fit
2318
2319        :param str phasenam: the name for the selected phase
2320        :param dict data_name: the sequential refinement parameters for the selected histogram
2321        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2322          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2323          cell values and `cellSig` has their uncertainties.
2324        """
2325        phasedict = self.Phases[phasenam]
2326        SGdata = phasedict['General']['SGData']
2327        pId = phasedict['pId']
2328        RecpCellTerms = G2lat.cell2A(phasedict['General']['Cell'][1:7])
2329        ESDlookup = {}
2330        Dlookup = {}
2331        varied = [striphist(i) for i in data_name['varyList']]
2332        for item,val in data_name['newCellDict'].iteritems():
2333            if item in varied:
2334                ESDlookup[val[0]] = item
2335                Dlookup[item] = val[0]
2336        A = RecpCellTerms[:]
2337        for i in range(6):
2338            var = str(pId)+'::A'+str(i)
2339            if var in ESDlookup:
2340                A[i] = data_name['newCellDict'][ESDlookup[var]][1] # override with refined value
2341        cellDict = dict(zip([str(pId)+'::A'+str(i) for i in range(6)],A))
2342        zeroDict = {i:0.0 for i in cellDict}
2343        A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata,cellDict,zeroDict)
2344        covData = {
2345            'varyList': [Dlookup.get(striphist(v),v) for v in data_name['varyList']],
2346            'covMatrix': data_name['covMatrix']
2347            }
2348        return list(G2lat.A2cell(A)) + [G2lat.calc_V(A)], G2stIO.getCellEsd(str(pId)+'::',SGdata,A,covData)
2349               
2350    def GetAtoms(self,phasenam):
2351        """Gets the atoms associated with a phase. Can be used with standard
2352        or macromolecular phases
2353
2354        :param str phasenam: the name for the selected phase
2355        :returns: a list of items for eac atom where each item is a list containing:
2356          label, typ, mult, xyz, and td, where
2357
2358          * label and typ are the atom label and the scattering factor type (str)
2359          * mult is the site multiplicity (int)
2360          * xyz is contains a list with four pairs of numbers:
2361            x, y, z and fractional occupancy and
2362            their standard uncertainty (or a negative value)
2363          * td is contains a list with either one or six pairs of numbers:
2364            if one number it is U\ :sub:`iso` and with six numbers it is
2365            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2366            paired with their standard uncertainty (or a negative value)
2367        """
2368        phasedict = self.Phases[phasenam] # pointer to current phase info           
2369        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2370        cfrac = cx+3
2371        fpfx = str(phasedict['pId'])+'::Afrac:'       
2372        atomslist = []
2373        for i,at in enumerate(phasedict['Atoms']):
2374            if phasedict['General']['Type'] == 'macromolecular':
2375                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2376            else:
2377                label = at[ct-1]
2378            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2379            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2380            mult = at[cs+1]
2381            typ = at[ct]
2382            xyz = []
2383            for j,v in enumerate(('x','y','z')):
2384                val = at[cx+j]
2385                pfx = str(phasedict['pId']) + '::A' + v + ':' + str(i)
2386                val = self.parmDict.get(pfx, val)
2387                dpfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2388                sig = self.sigDict.get(dpfx,-0.000009)
2389                xyz.append((val,sig))
2390            xyz.append((fval,fsig))
2391            td = []
2392            if at[cia] == 'I':
2393                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2394                val = self.parmDict.get(pfx,at[cia+1])
2395                sig = self.sigDict.get(pfx,-0.0009)
2396                td.append((val,sig))
2397            else:
2398                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2399                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2400                    val = self.parmDict.get(pfx,at[cia+2+i])
2401                    sig = self.sigDict.get(pfx,-0.0009)
2402                    td.append((val,sig))
2403            atomslist.append((label,typ,mult,xyz,td))
2404        return atomslist
2405######################################################################
2406def ExportPowderList(G2frame):
2407    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
2408    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
2409   
2410    :param wx.Frame G2frame: the GSAS-II main data tree window
2411    '''
2412    extList = []
2413    for obj in G2frame.exporterlist:
2414        if 'powder' in obj.exporttype:
2415            try:
2416                obj.Writer
2417                extList.append(obj.extension)
2418            except AttributeError:
2419                pass
2420    return extList
2421
2422def ExportPowder(G2frame,TreeName,fileroot,extension):
2423    '''Writes a single powder histogram using the Export routines.
2424    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
2425
2426    :param wx.Frame G2frame: the GSAS-II main data tree window
2427    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
2428    :param str fileroot: name for file to be written, extension ignored
2429    :param str extension: extension for file to be written (start with '.'). Must
2430      match a powder export routine that has a Writer object.
2431    '''
2432    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2433    for obj in G2frame.exporterlist:
2434        if obj.extension == extension and 'powder' in obj.exporttype:
2435            obj.currentExportType = 'powder'
2436            obj.InitExport(None)
2437            obj.loadTree() # load all histograms in tree into dicts
2438            if TreeName not in obj.Histograms:
2439                raise Exception('Histogram not found: '+str(TreeName))
2440            try:
2441                obj.Writer
2442            except AttributeError:
2443                continue
2444            try:
2445                obj.Writer(TreeName,filename)
2446                return
2447            except Exception,err:
2448                print('Export Routine for '+extension+' failed.')
2449                print err
2450    else:
2451        print('No Export routine supports extension '+extension)
2452
2453def ExportSequential(G2frame,data,obj,exporttype):
2454    '''
2455    Used to export from every phase/dataset in a sequential refinement using
2456    a .Writer method for either projects or phases. Prompts to select histograms
2457    and for phase exports, which phase(s).
2458
2459    :param wx.Frame G2frame: the GSAS-II main data tree window
2460    :param dict data: the sequential refinement data object
2461    :param str exporttype: indicates the type of export ('project' or 'phase')
2462    '''
2463    if len(data['histNames']) == 0:
2464        G2G.G2MessageBox(G2frame,'There are no sequential histograms','Warning')
2465    obj.InitExport(None)
2466    obj.loadTree()
2467    obj.loadParmDict()
2468    if len(data['histNames']) == 1:
2469        histlist = data['histNames']
2470    else:
2471        dlg = G2G.G2MultiChoiceDialog(G2frame,'Select histograms to export from list',
2472                                 'Select histograms',data['histNames'])
2473        if dlg.ShowModal() == wx.ID_OK:
2474            histlist = [data['histNames'][l] for l in dlg.GetSelections()]
2475            dlg.Destroy()
2476        else:
2477            dlg.Destroy()
2478            return
2479    if exporttype == 'Phase':
2480        phaselist = obj.Phases.keys()
2481        if len(obj.Phases) == 0:
2482            G2G.G2MessageBox(G2frame,'There are no phases in sequential ref.','Warning')
2483            return
2484        elif len(obj.Phases) > 1:
2485            dlg = G2G.G2MultiChoiceDialog(G2frame,'Select phases to export from list',
2486                                    'Select phases', phaselist)
2487            if dlg.ShowModal() == wx.ID_OK:
2488                phaselist = [phaselist[l] for l in dlg.GetSelections()]
2489                dlg.Destroy()
2490            else:
2491                dlg.Destroy()
2492                return
2493        filename = obj.askSaveFile()
2494        if not filename: return True
2495        obj.dirname,obj.filename = os.path.split(filename)
2496        print('Writing output to file '+str(obj.filename)+"...")
2497        mode = 'w'
2498        for p in phaselist:
2499            for h in histlist:
2500                obj.SetSeqRef(data,h)
2501                #GSASIIpath.IPyBreak()
2502                obj.Writer(h,phasenam=p,mode=mode)
2503                mode = 'a'
2504        print('...done')
2505    elif exporttype == 'Project':
2506        filename = obj.askSaveFile()
2507        if not filename: return True
2508        obj.dirname,obj.filename = os.path.split(filename)
2509        print('Writing output to file '+str(obj.filename)+"...")
2510        mode = 'w'
2511        for h in histlist:
2512            obj.SetSeqRef(data,h)
2513            obj.Writer(h,mode=mode)
2514            print('\t'+str(h)+' written')
2515            mode = 'a'
2516        print('...done')
2517
2518def ReadDIFFaX(DIFFaXfile):
2519    print 'read ',DIFFaXfile
2520    Layer = {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],'Width':[[10.,10.],[False,False]],
2521        'Layers':[],'Stacking':[],'Transitions':[],'Toler':0.01,'AtInfo':{}}
2522    df = open(DIFFaXfile,'r')
2523    lines = df.readlines()
2524    df.close()
2525    struct = False
2526    Struct = []
2527    stack = False
2528    Stack = []
2529    trans = False
2530    Trans = []
2531    for diff in lines:
2532        diff = diff[:-1].lower()
2533        if '!'  in diff:
2534            continue
2535        while '}' in diff: #strip comments
2536            iB = diff.index('{')
2537            iF = diff.index('}')+1
2538            if iB:
2539                diff = diff[:iB]
2540            else:
2541                diff = diff[iF:]
2542        if not diff:
2543            continue
2544        if diff.strip() == 'instrumental':
2545            continue
2546        if diff.strip() == 'structural':
2547            struct = True
2548            continue
2549        elif diff.strip() == 'stacking':
2550            struct = False
2551            stack = True
2552            continue
2553        elif diff.strip() == 'transitions':
2554            stack = False
2555            trans = True
2556            continue
2557        diff = diff.strip()
2558        if struct:
2559            if diff:
2560                Struct.append(diff)
2561        elif stack:
2562            if diff:
2563                Stack.append(diff)
2564        elif trans:
2565            if diff:
2566                Trans.append(diff)
2567   
2568#STRUCTURE records
2569    laueRec = Struct[1].split()
2570    Layer['Laue'] = laueRec[0]
2571    if Layer['Laue'] == 'unknown' and len(laueRec) > 1:
2572        Layer['Toler'] = float(laueRec[1])    #tolerance for 'unknown'?
2573    if Layer['Laue'] == '2/m(1)': Layer['Laue'] = '2/m(c)'
2574    if Layer['Laue'] == '2/m(2)': Layer['Laue'] = '2/m(ab)'
2575    cell = Struct[0].split()
2576    Layer['Cell'] = [False,float(cell[0]),float(cell[1]),float(cell[2]),90.,90.,float(cell[3]),1.0]
2577    nLayers = int(Struct[2])
2578    N = 3
2579    if 'layer' not in Struct[3]:
2580        N = 4
2581        if Struct[3] != 'infinite':
2582            width = Struct[3].split()
2583            Layer['Width'][0] = [float(width[0]),float(width[1])]
2584    for nL in range(nLayers):
2585        if '=' in Struct[N]:
2586            name = Struct[N].split('=')
2587            sameas = int(name[1])-1
2588            Layer['Layers'].append({'Name':name[0],'SameAs':Layer['Layers'][sameas]['Name'],'Symm':'None','Atoms':[]})
2589            N += 1
2590            continue
2591        Symm = 'None'
2592        if 'centro' in Struct[N+1]: Symm = '-1'
2593        Layer['Layers'].append({'Name':Struct[N],'SameAs':'','Symm':Symm,'Atoms':[]})
2594        N += 2
2595        while 'layer' not in Struct[N]:
2596            atom = Struct[N][4:].split()
2597            atomType = G2el.FixValence(Struct[N][:4].replace(' ','').strip().capitalize())
2598            if atomType not in Layer['AtInfo']:
2599                Layer['AtInfo'][atomType] = G2el.GetAtomInfo(atomType)
2600            atomName = '%s(%s)'%(atomType,atom[0])
2601            newVals = []
2602            for val in atom[1:6]:
2603                if '/' in val:
2604                    newVals.append(eval(val+'.'))
2605                else:
2606                    newVals.append(float(val))               
2607            atomRec = [atomName,atomType,newVals[0],newVals[1],newVals[2],newVals[4],newVals[3]/78.9568]
2608            Layer['Layers'][-1]['Atoms'].append(atomRec)
2609            N += 1
2610            if N > len(Struct)-1:
2611                break
2612#TRANSITIONS records
2613    transArray = []
2614    N = 0
2615    for i in range(nLayers):
2616        transArray.append([])
2617        for j in range(nLayers):
2618            vals = Trans[N].split()
2619            newVals = []
2620            for val in vals[:4]:
2621                if '/' in val:
2622                    newVals.append(eval(val+'.'))
2623                else:
2624                    newVals.append(float(val))
2625            transArray[-1].append(newVals+['',False])
2626            N += 1
2627    Layer['Transitions'] = transArray
2628#STACKING records
2629    Layer['Stacking'] = [Stack[0],'']
2630    if Stack[0] == 'recursive':
2631        Layer['Stacking'][1] = Stack[1]
2632    elif Stack[0] == 'explicit':
2633        if Stack[1] == 'random':
2634            Layer['Stacking'][1] = Stack[1]
2635        else:
2636            Layer['Stacking'][1] = 'list'
2637            Layer['Stacking'].append('')
2638            for stack in Stack[2:]:
2639                Layer['Stacking'][2] += ' '+stack
2640    return Layer
2641
2642def ReadCIF(URLorFile):
2643    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2644    (from James Hester).
2645    The open routine gets confused with DOS names that begin with a letter and colon
2646    "C:\dir\" so this routine will try to open the passed name as a file and if that
2647    fails, try it as a URL
2648
2649    :param str URLorFile: string containing a URL or a file name. Code will try first
2650      to open it as a file and then as a URL.
2651
2652    :returns: a PyCifRW CIF object.
2653    '''
2654    import CifFile as cif # PyCifRW from James Hester
2655
2656    # alternate approach:
2657    #import urllib
2658    #ciffile = 'file:'+urllib.pathname2url(filename)
2659   
2660    try:
2661        fp = open(URLorFile,'r')
2662        cf = cif.ReadCif(fp)
2663        fp.close()
2664        return cf
2665    except IOError:
2666        return cif.ReadCif(URLorFile)
2667
2668if __name__ == '__main__':
2669    import GSASII
2670    application = GSASII.GSASIImain(0)
2671    G2frame = application.main
2672    #app = wx.PySimpleApp()
2673    #G2frame = wx.Frame(None) # create a frame
2674    #frm.Show(True)
2675    #filename = '/tmp/notzip.zip'
2676    #filename = '/tmp/all.zip'
2677    #filename = '/tmp/11bmb_7652.zip'
2678   
2679    #selection=None, confirmoverwrite=True, parent=None
2680    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2681    #print ExtractFileFromZip(filename,multipleselect=True)
2682    #                         #confirmread=False, confirmoverwrite=False)
2683
2684    # choicelist=[ ('a','b','c'),
2685    #              ('test1','test2'),('no choice',)]
2686    # titles = [ 'a, b or c', 'tests', 'No option here']
2687    # dlg = MultipleChoicesDialog(
2688    #     choicelist,titles,
2689    #     parent=frm)
2690    # if dlg.ShowModal() == wx.ID_OK:
2691    #     print 'Got OK'
2692    imagefile = '/tmp/NDC5_00237_3.ge3'
2693    Comments, Data, Npix, Image = GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None)
2694
2695    print("\n\nResults loaded to Comments, Data, Npix and Image\n\n")
2696
2697    GSASIIpath.IPyBreak_base()
Note: See TracBrowser for help on using the repository browser.