source: trunk/GSASIIIO.py @ 2659

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

make lab data (2 x-ray wavelengths) instrument default 'Bragg-Brentano', all others 'Debye-Scherrer'
refactor PDF stuff to show PDF Controls & (new) PDF Peaks on G2 tree (removing I(Q)...).
Old gpx files with I(Q)... updated automatically to new scheme
Add new tree item for PDF Peaks - does nothing yet.
Fix FWHM calc for TOF so bins/FWHM on peak fitting make sense.

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