source: trunk/GSASIIIO.py @ 2563

Last change on this file since 2563 was 2563, checked in by vondreele, 5 years ago

Speed up image integration on Mac by reducing ProgressBar? calls from 3*nBlk*nBlk+3 to Nblk+3
Allow PDF setup to include chemical formula from tif macrofile
fix contour bug for G(R), etc.
fix copy error for masks

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