source: trunk/GSASIIIO.py @ 2710

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

improve ExportPDF with G2file selector & allow selection of I(Q), S(Q), F(Q) & G(R)

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