source: trunk/GSASIIIO.py @ 3825

Last change on this file since 3825 was 3825, checked in by vondreele, 4 years ago

create csv exporter for small angle data - includes size distribution histogram if any.
fix formatting issue with size distribution axis label
a bit different incomm. mag str fctr. math - still wrong

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 96.9 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2019-02-15 16:43:35 +0000 (Fri, 15 Feb 2019) $
4# $Author: vondreele $
5# $Revision: 3825 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 3825 2019-02-15 16:43:35Z 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
20This module needs some work to separate wx from non-wx routines
21'''
22# If this is being used for GSASIIscriptable, don't depend on wx
23from __future__ import division, print_function
24try:
25    import wx
26except ImportError:
27    class Placeholder(object):
28        def __init__(self):
29            self.Dialog = object
30    wx = Placeholder()
31import math
32import numpy as np
33import numpy.ma as ma
34
35import copy
36import platform
37if '2' in platform.python_version_tuple()[0]:
38    import cPickle
39else:
40    import _pickle as cPickle
41import sys
42import re
43import glob
44import random as ran
45import GSASIIpath
46GSASIIpath.SetVersionNumber("$Revision: 3825 $")
47try:
48    import GSASIIdataGUI as G2gd
49except ImportError:
50    pass
51import GSASIIobj as G2obj
52import GSASIIlattice as G2lat
53import GSASIImath as G2mth
54try:
55    import GSASIIpwdGUI as G2pdG
56    import GSASIIimgGUI as G2imG
57except ImportError:
58    pass
59import GSASIIimage as G2img
60import GSASIIElem as G2el
61import GSASIIstrIO as G2stIO
62import GSASIImapvars as G2mv
63import GSASIIfiles as G2fil
64try:
65    import GSASIIctrlGUI as G2G
66except ImportError:
67    pass
68import os
69import os.path as ospath
70
71DEBUG = False       #=True for various prints
72TRANSP = False      #=true to transpose images for testing
73if GSASIIpath.GetConfigValue('Transpose'): TRANSP = True
74npsind = lambda x: np.sin(x*np.pi/180.)
75
76def sfloat(S):
77    'Convert a string to float. An empty field or a unconvertable value is treated as zero'
78    if S.strip():
79        try:
80            return float(S)
81        except ValueError:
82            pass
83    return 0.0
84
85def sint(S):
86    'Convert a string to int. An empty field is treated as zero'
87    if S.strip():
88        return int(S)
89    else:
90        return 0
91
92def trim(val):
93    '''Simplify a string containing leading and trailing spaces
94    as well as newlines, tabs, repeated spaces etc. into a shorter and
95    more simple string, by replacing all ranges of whitespace
96    characters with a single space.
97
98    :param str val: the string to be simplified
99
100    :returns: the (usually) shortened version of the string
101    '''
102    return re.sub('\s+', ' ', val).strip()
103
104def FileDlgFixExt(dlg,file):
105    'this is needed to fix a problem in linux wx.FileDialog'
106    ext = dlg.GetWildcard().split('|')[2*dlg.GetFilterIndex()+1].strip('*')
107    if ext not in file:
108        file += ext
109    return file
110       
111def GetPowderPeaks(fileName):
112    'Read powder peaks from a file'
113    sind = lambda x: math.sin(x*math.pi/180.)
114    asind = lambda x: 180.*math.asin(x)/math.pi
115    wave = 1.54052
116    File = open(fileName,'Ur')
117    Comments = []
118    peaks = []
119    S = File.readline()
120    while S:
121        if S[:1] == '#':
122            Comments.append(S[:-1])
123        else:
124            item = S.split()
125            if len(item) == 1:
126                peaks.append([float(item[0]),1.0])
127            elif len(item) > 1:
128                peaks.append([float(item[0]),float(item[0])])
129        S = File.readline()
130    File.close()
131    if Comments:
132       print ('Comments on file:')
133       for Comment in Comments: 
134            print (Comment)
135            if 'wavelength' in Comment:
136                wave = float(Comment.split('=')[1])
137    Peaks = []
138    if peaks[0][0] > peaks[-1][0]:          # d-spacings - assume CuKa
139        for peak in peaks:
140            dsp = peak[0]
141            sth = wave/(2.0*dsp)
142            if sth < 1.0:
143                tth = 2.0*asind(sth)
144            else:
145                break
146            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
147    else:                                   #2-thetas - assume Cuka (for now)
148        for peak in peaks:
149            tth = peak[0]
150            dsp = wave/(2.0*sind(tth/2.0))
151            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
152    limits = [1000.,0.]
153    for peak in Peaks:
154        limits[0] = min(limits[0],peak[0])
155        limits[1] = max(limits[1],peak[0])
156    limits[0] = max(1.,(int(limits[0]-1.)/5)*5.)
157    limits[1] = min(170.,(int(limits[1]+1.)/5)*5.)
158    return Comments,Peaks,limits,wave
159
160def GetCheckImageFile(G2frame,treeId):
161    '''Try to locate an image file if the project and image have been moved
162    together. If the image file cannot be found, request the location from
163    the user.
164
165    :param wx.Frame G2frame: main GSAS-II Frame and data object
166    :param wx.Id treeId: Id for the main tree item for the image
167    :returns: Npix,imagefile,imagetag with (Npix) number of pixels,
168       imagefile, if it exists, or the name of a file that does exist or False if the user presses Cancel
169       and (imagetag) an optional image number
170
171    '''
172    Npix,Imagefile,imagetag = G2frame.GPXtree.GetImageLoc(treeId)
173    if isinstance(Imagefile,list):
174        imagefile,imagetag = Imagefile
175    else:
176        imagefile = Imagefile
177    if not os.path.exists(imagefile):
178        print ('Image file '+imagefile+' not found')
179        fil = imagefile.replace('\\','/') # windows?!
180        # see if we can find a file with same name or in a similarly named sub-dir
181        pth,fil = os.path.split(fil)
182        prevpth = None
183        while pth and pth != prevpth:
184            prevpth = pth
185            if os.path.exists(os.path.join(G2frame.dirname,fil)):
186                print ('found image file '+os.path.join(G2frame.dirname,fil))
187                imagefile = os.path.join(G2frame.dirname,fil)
188                G2frame.GPXtree.UpdateImageLoc(treeId,imagefile)
189                return Npix,imagefile,imagetag
190            pth,enddir = os.path.split(pth)
191            fil = os.path.join(enddir,fil)
192        # not found as a subdirectory, drop common parts of path for last saved & image file names
193        #    if image was .../A/B/C/imgs/ima.ge
194        #      & GPX was  .../A/B/C/refs/fil.gpx but is now .../NEW/TEST/TEST1
195        #    will look for .../NEW/TEST/TEST1/imgs/ima.ge, .../NEW/TEST/imgs/ima.ge, .../NEW/imgs/ima.ge and so on
196        Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls'))
197        gpxPath = Controls.get('LastSavedAs','').replace('\\','/').split('/') # blank in older .GPX files
198        imgPath = imagefile.replace('\\','/').split('/')
199        for p1,p2 in zip(gpxPath,imgPath):
200            if p1 == p2:
201                gpxPath.pop(0),imgPath.pop(0)
202            else:
203                break
204        fil = os.path.join(*imgPath) # file with non-common prefix elements
205        prevpth = None
206        pth = os.path.abspath(G2frame.dirname)
207        while pth and pth != prevpth:
208            prevpth = pth
209            if os.path.exists(os.path.join(pth,fil)):
210                print ('found image file '+os.path.join(pth,fil))
211                imagefile = os.path.join(pth,fil)
212                G2frame.GPXtree.UpdateImageLoc(treeId,imagefile)
213                return Npix,imagefile,imagetag
214            pth,enddir = os.path.split(pth)
215        #GSASIIpath.IPyBreak()
216
217    if not os.path.exists(imagefile):
218        # note that this fails (at least on Mac) to get an image during the GUI initialization
219        prevnam = os.path.split(imagefile)[1]
220        prevext = os.path.splitext(imagefile)[1]
221        wildcard = 'Image format (*'+prevext+')|*'+prevext
222        dlg = wx.FileDialog(G2frame, 'Previous image file ('+prevnam+') not found; open here', '.', prevnam,
223                            wildcard,wx.FD_OPEN)
224        try:
225            dlg.SetFilename(''+ospath.split(imagefile)[1])
226            if dlg.ShowModal() == wx.ID_OK:
227                imagefile = dlg.GetPath()
228                G2frame.GPXtree.UpdateImageLoc(treeId,imagefile)
229            else:
230                imagefile = False
231        finally:
232            dlg.Destroy()
233    return Npix,imagefile,imagetag
234
235def EditImageParms(parent,Data,Comments,Image,filename):
236    dlg = wx.Dialog(parent, wx.ID_ANY, 'Edit image parameters',
237                    style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
238    def onClose(event):
239        dlg.EndModal(wx.ID_OK)
240    mainsizer = wx.BoxSizer(wx.VERTICAL)
241    h,w = Image.shape[:2]
242    mainsizer.Add(wx.StaticText(dlg,wx.ID_ANY,'File '+str(filename)+'\nImage size: '+str(h)+' x '+str(w)),
243        0,wx.ALIGN_LEFT|wx.ALL, 2)
244   
245    vsizer = wx.BoxSizer(wx.HORIZONTAL)
246    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Wavelength (\xC5) '),
247        0,wx.ALIGN_LEFT|wx.ALL, 2)
248    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'wavelength')
249    vsizer.Add(wdgt)
250    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
251
252    vsizer = wx.BoxSizer(wx.HORIZONTAL)
253    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Pixel size (\xb5m). Width '),
254        0,wx.ALIGN_LEFT|wx.ALL, 2)
255    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],0,
256                                 size=(50,-1))
257    vsizer.Add(wdgt)
258    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Height '),wx.ALIGN_LEFT|wx.ALL, 2)
259    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],1,size=(50,-1))
260    vsizer.Add(wdgt)
261    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
262
263    vsizer = wx.BoxSizer(wx.HORIZONTAL)
264    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Sample to detector (mm) '),
265        0,wx.ALIGN_LEFT|wx.ALL, 2)
266    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'distance')
267    vsizer.Add(wdgt)
268    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
269
270    vsizer = wx.BoxSizer(wx.HORIZONTAL)
271    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Beam center (pixels). X = '),
272        0,wx.ALIGN_LEFT|wx.ALL, 2)
273    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],0,size=(75,-1))
274    vsizer.Add(wdgt)
275    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Y = '),wx.ALIGN_LEFT|wx.ALL, 2)
276    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],1,size=(75,-1))
277    vsizer.Add(wdgt)
278    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
279
280    vsizer = wx.BoxSizer(wx.HORIZONTAL)
281    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Comments '),
282        0,wx.ALIGN_LEFT|wx.ALL, 2)
283    wdgt = G2G.ValidatedTxtCtrl(dlg,Comments,0,size=(250,-1))
284    vsizer.Add(wdgt)
285    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
286
287    btnsizer = wx.StdDialogButtonSizer()
288    OKbtn = wx.Button(dlg, wx.ID_OK, 'Continue')
289    OKbtn.SetDefault()
290    OKbtn.Bind(wx.EVT_BUTTON,onClose)
291    btnsizer.AddButton(OKbtn) # not sure why this is needed
292    btnsizer.Realize()
293    mainsizer.Add(btnsizer, 1, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 5)
294    dlg.SetSizer(mainsizer)
295    dlg.CenterOnParent()
296    dlg.ShowModal()
297   
298def LoadImage2Tree(imagefile,G2frame,Comments,Data,Npix,Image):
299    '''Load an image into the tree. Saves the location of the image, as well as the
300    ImageTag (where there is more than one image in the file), if defined.
301    '''
302    ImgNames = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
303    TreeLbl = 'IMG '+os.path.basename(imagefile)
304    ImageTag = Data.get('ImageTag')
305    if ImageTag:
306        TreeLbl += ' #'+'%04d'%(ImageTag)
307        imageInfo = (imagefile,ImageTag)
308    else:
309        imageInfo = imagefile
310    TreeName = G2obj.MakeUniqueLabel(TreeLbl,ImgNames)
311    Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text=TreeName)
312    G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Comments'),Comments)
313    Imax = np.amax(Image)
314    if G2frame.imageDefault:
315        Data = copy.copy(G2frame.imageDefault)
316        Data['showLines'] = True
317        Data['ring'] = []
318        Data['rings'] = []
319        Data['cutoff'] = 10.
320        Data['pixLimit'] = 20
321        Data['edgemin'] = 100000000
322        Data['calibdmin'] = 0.5
323        Data['calibskip'] = 0
324        Data['ellipses'] = []
325        Data['calibrant'] = ''
326        Data['GonioAngles'] = [0.,0.,0.]
327        Data['DetDepthRef'] = False
328    else:
329        Data['type'] = 'PWDR'
330        Data['color'] = GSASIIpath.GetConfigValue('Contour_color','Paired')
331        if 'tilt' not in Data:          #defaults if not preset in e.g. Bruker importer
332            Data['tilt'] = 0.0
333            Data['rotation'] = 0.0
334            Data['pixLimit'] = 20
335            Data['calibdmin'] = 0.5
336            Data['cutoff'] = 10.
337        Data['showLines'] = False
338        Data['calibskip'] = 0
339        Data['ring'] = []
340        Data['rings'] = []
341        Data['edgemin'] = 100000000
342        Data['ellipses'] = []
343        Data['GonioAngles'] = [0.,0.,0.]
344        Data['DetDepth'] = 0.
345        Data['DetDepthRef'] = False
346        Data['calibrant'] = ''
347        Data['IOtth'] = [5.0,50.0]
348        Data['LRazimuth'] = [0.,180.]
349        Data['azmthOff'] = 0.0
350        Data['outChannels'] = 2250
351        Data['outAzimuths'] = 1
352        Data['centerAzm'] = False
353        Data['fullIntegrate'] = GSASIIpath.GetConfigValue('fullIntegrate',True)
354        Data['setRings'] = False
355        Data['background image'] = ['',-1.0]                           
356        Data['dark image'] = ['',-1.0]
357        Data['Flat Bkg'] = 0.0
358        Data['Oblique'] = [0.5,False]
359    Data['setDefault'] = False
360    Data['range'] = [(0,Imax),[0,Imax]]
361    G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Image Controls'),Data)
362    Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(0,Imax),[0,Imax]]}
363    G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Masks'),Masks)
364    G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Stress/Strain'),
365        {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
366    G2frame.GPXtree.SetItemPyData(Id,[Npix,imageInfo])
367    G2frame.PickId = Id
368    G2frame.PickIdText = G2frame.GetTreeItemsList(G2frame.PickId)
369    G2frame.Image = Id
370
371def GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None,FormatName=''):
372    '''Read a single image with an image importer. This is called to reread an image
373    after it has already been imported with :meth:`GSASIIdataGUI.GSASII.OnImportGeneric`
374    (or :func:`ReadImages` in Auto Integration) so it is not necessary to reload metadata.
375
376    :param wx.Frame G2frame: main GSAS-II Frame and data object.
377    :param str imagefile: name of image file
378    :param bool imageOnly: If True return only the image,
379      otherwise  (default) return more (see below)
380    :param int/str ImageTag: specifies a particular image to be read from a file.
381      First image is read if None (default).
382    :param str formatName: the image reader formatName
383
384    :returns: an image as a numpy array or a list of four items:
385      Comments, Data, Npix and the Image, as selected by imageOnly
386
387    '''
388    # determine which formats are compatible with this file
389    primaryReaders = []
390    secondaryReaders = []
391    for rd in G2frame.ImportImageReaderlist:
392        flag = rd.ExtensionValidator(imagefile)
393        if flag is None: 
394            secondaryReaders.append(rd)
395        elif flag:
396            if not FormatName:
397                primaryReaders.append(rd)
398            elif FormatName == rd.formatName:
399                primaryReaders.append(rd)
400    if len(secondaryReaders) + len(primaryReaders) == 0:
401        print('Error: No matching format for file '+imagefile)
402        raise Exception('No image read')
403    errorReport = ''
404    if not imagefile:
405        return
406    for rd in primaryReaders+secondaryReaders:
407        rd.ReInitialize() # purge anything from a previous read
408        rd.errors = "" # clear out any old errors
409        if not rd.ContentsValidator(imagefile): # rejected on cursory check
410            errorReport += "\n  "+rd.formatName + ' validator error'
411            if rd.errors: 
412                errorReport += ': '+rd.errors
413                continue
414        if imageOnly:
415            ParentFrame = None # prevent GUI access on reread
416        else:
417            ParentFrame = G2frame
418        if GSASIIpath.GetConfigValue('debug'):
419            flag = rd.Reader(imagefile,ParentFrame,blocknum=ImageTag)
420        else:
421            flag = False
422            try:
423                flag = rd.Reader(imagefile,ParentFrame,blocknum=ImageTag)
424            except rd.ImportException as detail:
425                rd.errors += "\n  Read exception: "+str(detail)
426            except Exception as detail:
427                import traceback
428                rd.errors += "\n  Unhandled read exception: "+str(detail)
429                rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
430        if flag: # this read succeeded
431            if rd.Image is None:
432                raise Exception('No image read. Strange!')
433            if GSASIIpath.GetConfigValue('Transpose'):
434                print ('Transposing Image!')
435                rd.Image = rd.Image.T
436            #rd.readfilename = imagefile
437            if imageOnly:
438                return rd.Image
439            else:
440                return rd.Comments,rd.Data,rd.Npix,rd.Image
441    else:
442        print('Error reading file '+imagefile)
443        print('Error messages(s)\n'+errorReport)
444        raise Exception('No image read')   
445
446def ReadImages(G2frame,imagefile):
447    '''Read one or more images from a file and put them into the Tree
448    using image importers. Called only in :meth:`AutoIntFrame.OnTimerLoop`.
449
450    ToDo: Images are most commonly read in :meth:`GSASIIdataGUI.GSASII.OnImportGeneric`
451    which is called from :meth:`GSASIIdataGUI.GSASII.OnImportImage`
452    it would be good if these routines used a common code core so that changes need to
453    be made in only one place.
454
455    :param wx.Frame G2frame: main GSAS-II Frame and data object.
456    :param str imagefile: name of image file
457
458    :returns: a list of the id's of the IMG tree items created
459    '''
460    # determine which formats are compatible with this file
461    primaryReaders = []
462    secondaryReaders = []
463    for rd in G2frame.ImportImageReaderlist:
464        flag = rd.ExtensionValidator(imagefile)
465        if flag is None:
466            secondaryReaders.append(rd)
467        elif flag:
468            primaryReaders.append(rd)
469    if len(secondaryReaders) + len(primaryReaders) == 0:
470        print('Error: No matching format for file '+imagefile)
471        raise Exception('No image read')
472    errorReport = ''
473    rdbuffer = {} # create temporary storage for file reader
474    for rd in primaryReaders+secondaryReaders:
475        rd.ReInitialize() # purge anything from a previous read
476        rd.errors = "" # clear out any old errors
477        if not rd.ContentsValidator(imagefile): # rejected on cursory check
478            errorReport += "\n  "+rd.formatName + ' validator error'
479            if rd.errors: 
480                errorReport += ': '+rd.errors
481                continue
482        ParentFrame = G2frame
483        block = 0
484        repeat = True
485        CreatedIMGitems = []
486        while repeat: # loop if the reader asks for another pass on the file
487            block += 1
488            repeat = False
489            if GSASIIpath.GetConfigValue('debug'):
490                flag = rd.Reader(imagefile,ParentFrame,blocknum=block,Buffer=rdbuffer)
491            else:
492                flag = False
493                try:
494                    flag = rd.Reader(imagefile,ParentFrame,blocknum=block,Buffer=rdbuffer)
495                except rd.ImportException as detail:
496                    rd.errors += "\n  Read exception: "+str(detail)
497                except Exception as detail:
498                    import traceback
499                    rd.errors += "\n  Unhandled read exception: "+str(detail)
500                    rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
501            if flag: # this read succeeded
502                if rd.Image is None:
503                    raise Exception('No image read. Strange!')
504                if GSASIIpath.GetConfigValue('Transpose'):
505                    print ('Transposing Image!')
506                    rd.Image = rd.Image.T
507                rd.Data['ImageTag'] = rd.repeatcount
508                rd.readfilename = imagefile
509                # Load generic metadata, as configured
510                G2fil.GetColumnMetadata(rd)
511                LoadImage2Tree(imagefile,G2frame,rd.Comments,rd.Data,rd.Npix,rd.Image)
512                repeat = rd.repeat
513            CreatedIMGitems.append(G2frame.Image)
514        if CreatedIMGitems: return CreatedIMGitems
515    else:
516        print('Error reading file '+imagefile)
517        print('Error messages(s)\n'+errorReport)
518        return []
519        #raise Exception('No image read')   
520
521def SaveMultipleImg(G2frame):
522    if not G2frame.GPXtree.GetCount():
523        print ('no images!')
524        return
525    choices = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
526    if len(choices) == 1:
527        names = choices
528    else:
529        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices)
530        dlg.SetSelections([])
531        names = []
532        if dlg.ShowModal() == wx.ID_OK:
533            names = [choices[sel] for sel in dlg.GetSelections()]
534        dlg.Destroy()
535    if not names: return
536    for name in names:
537        Id = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, name)
538        Npix,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(Id)
539        imroot = os.path.splitext(imagefile)[0]
540        if imagetag:
541            imroot += '_' + str(imagetag)
542        Data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Image Controls'))
543        print('Writing '+imroot+'.imctrl')
544        File = open(imroot+'.imctrl','w')
545        keys = ['type','wavelength','calibrant','distance','center',
546                    'tilt','rotation','azmthOff','fullIntegrate','LRazimuth',
547                    'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
548                    'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg',
549                    'binType','SampleShape','PolaVal','SampleAbs','dark image','background image']
550        for key in keys:
551            if key not in Data: continue    #uncalibrated!
552            File.write(key+':'+str(Data[key])+'\n')
553        File.close()
554        mask = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Masks'))
555        G2imG.CleanupMasks(mask)
556        print('Writing '+imroot+'.immask')
557        File = open(imroot+'.immask','w')
558        for key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
559            File.write(key+':'+str(mask[key])+'\n')
560        File.close()
561       
562def PutG2Image(filename,Comments,Data,Npix,image):
563    'Write an image as a python pickle - might be better as an .edf file?'
564    File = open(filename,'wb')
565    cPickle.dump([Comments,Data,Npix,image],File,2)
566    File.close()
567    return
568
569objectScanIgnore = [int,bool,float,str,np.float64,np.int32,np.int64,np.ndarray,G2obj.G2VarObj,G2obj.ExpressionObj]
570try:
571    objectScanIgnore += [ma.MaskedArray] # fails in doc builds
572except AttributeError:
573    pass
574   
575if '2' in platform.python_version_tuple()[0]:
576    objectScanIgnore += [unicode,long,]
577   
578def objectScan(data,tag,indexStack=[]):
579    '''Recursively scan an object looking for unexpected data types.
580    This is used in debug mode to scan .gpx files for objects we did not
581    intend to be there.
582    '''
583    if type(data) is list or type(data) is tuple:
584        for i in range(len(data)):
585            objectScan(data[i],tag,indexStack+[i])
586    elif type(data) is dict:
587        for key in data:
588            objectScan(data[key],tag,indexStack+[key])
589    elif data is None:
590        return
591    elif type(data) in objectScanIgnore:
592        return
593    else:
594        s = 'unexpected object in '+tag
595        for i in indexStack:
596            s += "[{}]".format(i)
597        #print(s,data.__class__.__name__) # loses full name of class
598        print(s,type(data))
599        global unexpectedObject
600        unexpectedObject = True
601   
602def ProjFileOpen(G2frame,showProvenance=True):
603    'Read a GSAS-II project file and load into the G2 data tree'
604    if not os.path.exists(G2frame.GSASprojectfile):
605        print ('\n*** Error attempt to open project file that does not exist:\n   '+
606               str(G2frame.GSASprojectfile))
607        return
608    LastSavedUsing = None
609    filep = open(G2frame.GSASprojectfile,'rb')
610    if showProvenance: print ('loading from file: '+G2frame.GSASprojectfile)
611    wx.BeginBusyCursor()
612    try:
613        while True:
614            try:
615                if '2' in platform.python_version_tuple()[0]:
616                    data = cPickle.load(filep)
617                else:
618                    data = cPickle.load(filep,encoding='latin-1')
619            except EOFError:
620                break
621            datum = data[0]
622            # scan the GPX file for unexpected objects
623            if GSASIIpath.GetConfigValue('debug'):
624                global unexpectedObject
625                unexpectedObject = False               
626                objectScan(data,'tree item "{}" entry '.format(datum[0]))
627                #if unexpectedObject:
628                #    print(datum[0])
629                #    GSASIIpath.IPyBreak()
630            Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text=datum[0])
631            if datum[0].startswith('PWDR'):               
632                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
633                    datum[1][0]['ranId'] = ran.randint(0,sys.maxsize)
634                G2frame.GPXtree.SetItemPyData(Id,datum[1][:3])  #temp. trim off junk (patch?)
635            elif datum[0].startswith('HKLF'): 
636                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
637                    datum[1][0]['ranId'] = ran.randint(0,sys.maxsize)
638                G2frame.GPXtree.SetItemPyData(Id,datum[1])
639            else:
640                G2frame.GPXtree.SetItemPyData(Id,datum[1])             
641                if datum[0] == 'Controls' and 'LastSavedUsing' in datum[1]:
642                    LastSavedUsing = datum[1]['LastSavedUsing']
643                if datum[0] == 'Controls' and 'PythonVersions' in datum[1] and GSASIIpath.GetConfigValue('debug') and showProvenance:
644                    print('DBG_Packages used to create .GPX file:')
645                    if 'dict' in str(type(datum[1]['PythonVersions'])):  #patch
646                        for p in sorted(datum[1]['PythonVersions'],key=lambda s: s.lower()):
647                            print({:<14s}: {:s}".format(p[0],p[1]))
648                    else:
649                        for p in datum[1]['PythonVersions']:
650                            print({:<12s} {:s}".format(p[0]+':',p[1]))
651            oldPDF = False
652            for datus in data[1:]:
653#patch - 1/23/17 PDF cleanup
654                if datus[0][:4] in ['I(Q)','S(Q)','F(Q)','G(R)']:
655                    oldPDF = True
656                    data[1][1][datus[0][:4]] = copy.deepcopy(datus[1][:2])
657                    continue
658#end PDF cleanup
659                sub = G2frame.GPXtree.AppendItem(Id,datus[0])
660#patch
661                if datus[0] == 'Instrument Parameters' and len(datus[1]) == 1:
662                    if datum[0].startswith('PWDR'):
663                        datus[1] = [dict(zip(datus[1][3],zip(datus[1][0],datus[1][1],datus[1][2]))),{}]
664                    else:
665                        datus[1] = [dict(zip(datus[1][2],zip(datus[1][0],datus[1][1]))),{}]
666                    for item in datus[1][0]:               #zip makes tuples - now make lists!
667                        datus[1][0][item] = list(datus[1][0][item])
668#end patch
669                G2frame.GPXtree.SetItemPyData(sub,datus[1])
670            if 'PDF ' in datum[0][:4] and oldPDF:
671                sub = G2frame.GPXtree.AppendItem(Id,'PDF Peaks')
672                G2frame.GPXtree.SetItemPyData(sub,{'Limits':[1.,5.],'Background':[2,[0.,-0.2*np.pi],False],'Peaks':[]})
673            if datum[0].startswith('IMG'):                   #retrieve image default flag & data if set
674                Data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
675                if Data['setDefault']:
676                    G2frame.imageDefault = Data               
677        if LastSavedUsing:
678            print('GPX load successful. Last saved with GSAS-II revision '+LastSavedUsing)
679        else:
680            print('project load successful')
681        G2frame.NewPlot = True
682    except Exception as errmsg:
683        if GSASIIpath.GetConfigValue('debug'):
684            print('\nError reading GPX file:',errmsg)
685            import traceback
686            print (traceback.format_exc())
687        msg = wx.MessageDialog(G2frame,message="Error reading file "+
688            str(G2frame.GSASprojectfile)+". This is not a current GSAS-II .gpx file",
689            caption="Load Error",style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
690        msg.ShowModal()
691    finally:
692        filep.close()
693        wx.EndBusyCursor()
694        G2frame.Status.SetStatusText('Mouse RB drag/drop to reorder',0)
695    G2frame.SetTitleByGPX()
696   
697def ProjFileSave(G2frame):
698    'Save a GSAS-II project file'
699    if not G2frame.GPXtree.IsEmpty():
700        file = open(G2frame.GSASprojectfile,'wb')
701        print ('save to file: '+G2frame.GSASprojectfile)
702        # stick the file name into the tree and version info into tree so they are saved.
703        # (Controls should always be created at this point)
704        try:
705            Controls = G2frame.GPXtree.GetItemPyData(
706                G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls'))
707            Controls['LastSavedAs'] = os.path.abspath(G2frame.GSASprojectfile)
708            Controls['LastSavedUsing'] = str(GSASIIpath.GetVersionNumber())
709            Controls['PythonVersions'] = G2frame.PackageVersions
710        except:
711            pass
712        wx.BeginBusyCursor()
713        try:
714            item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
715            while item:
716                data = []
717                name = G2frame.GPXtree.GetItemText(item)
718                data.append([name,G2frame.GPXtree.GetItemPyData(item)])
719                item2, cookie2 = G2frame.GPXtree.GetFirstChild(item)
720                while item2:
721                    name = G2frame.GPXtree.GetItemText(item2)
722                    data.append([name,G2frame.GPXtree.GetItemPyData(item2)])
723                    item2, cookie2 = G2frame.GPXtree.GetNextChild(item, cookie2)                           
724                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)                           
725                cPickle.dump(data,file,2)
726            file.close()
727            pth = os.path.split(os.path.abspath(G2frame.GSASprojectfile))[0]
728            if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
729            G2frame.LastGPXdir = pth
730        finally:
731            wx.EndBusyCursor()
732        print('project save successful')
733
734def SaveIntegration(G2frame,PickId,data,Overwrite=False):
735    'Save image integration results as powder pattern(s)'
736    waves = {'Cu':[1.54051,1.54433],'Ti':[2.74841,2.75207],'Cr':[2.28962,2.29351],
737        'Fe':[1.93597,1.93991],'Co':[1.78892,1.79278],'Mo':[0.70926,0.713543],
738        'Ag':[0.559363,0.563775]}
739    azms = G2frame.Integrate[1]
740    X = G2frame.Integrate[2][:-1]
741    N = len(X)
742    Id = G2frame.GPXtree.GetItemParent(PickId)
743    name = G2frame.GPXtree.GetItemText(Id)
744    name = name.replace('IMG ',data['type']+' ')
745    Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Comments'))
746    Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls'))
747    if 'PWDR' in name:
748        if 'target' in data:
749            names = ['Type','Lam1','Lam2','I(L2)/I(L1)','Zero','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth'] 
750            codes = [0 for i in range(14)]
751        else:
752            names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth'] 
753            codes = [0 for i in range(12)]
754    elif 'SASD' in name:
755        names = ['Type','Lam','Zero','Azimuth'] 
756        codes = [0 for i in range(4)]
757        X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
758    Xminmax = [X[0],X[-1]]
759    Azms = []
760    dazm = 0.
761    if data['fullIntegrate'] and data['outAzimuths'] == 1:
762        Azms = [45.0,]                              #a poor man's average?
763    else:
764        for i,azm in enumerate(azms[:-1]):
765            if azm > 360. and azms[i+1] > 360.:
766                Azms.append(G2img.meanAzm(azm%360.,azms[i+1]%360.))
767            else:   
768                Azms.append(G2img.meanAzm(azm,azms[i+1]))
769        dazm = np.min(np.abs(np.diff(azms)))/2.
770    G2frame.IntgOutList = []
771    for i,azm in enumerate(azms[:-1]):
772        Aname = name+" Azm= %.2f"%((azm+dazm)%360.)
773        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
774        # if Overwrite delete any duplicate
775        if Overwrite and G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Aname):
776            print('Replacing '+Aname)
777            item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Aname)
778            G2frame.GPXtree.Delete(item)
779        else:
780            nOcc = 0
781            while item:
782                Name = G2frame.GPXtree.GetItemText(item)
783                if Aname in Name:
784                    nOcc += 1
785                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
786            if nOcc:
787                Aname += '(%d)'%(nOcc)
788        Sample = G2obj.SetDefaultSample()       #set as Debye-Scherrer
789        Sample['Gonio. radius'] = data['distance']
790        Sample['Omega'] = data['GonioAngles'][0]
791        Sample['Chi'] = data['GonioAngles'][1]
792        Sample['Phi'] = data['GonioAngles'][2]
793        Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
794        polariz = 0.99    #set default polarization for synchrotron radiation!
795        for item in Comments:
796            if 'polariz' in item:
797                try:
798                    polariz = float(item.split('=')[1])
799                except:
800                    polariz = 0.99
801            for key in ('Temperature','Pressure','Time','FreePrm1','FreePrm2','FreePrm3','Omega',
802                'Chi','Phi'):
803                if key.lower() in item.lower():
804                    try:
805                        Sample[key] = float(item.split('=')[1])
806                    except:
807                        pass
808            if 'label_prm' in item.lower():
809                for num in ('1','2','3'):
810                    if 'label_prm'+num in item.lower():
811                        Controls['FreePrm'+num] = item.split('=')[1].strip()
812        if 'PWDR' in Aname:
813            if 'target' in data:    #from lab x-ray 2D imaging data
814                wave1,wave2 = waves[data['target']]
815                parms = ['PXC',wave1,wave2,0.5,0.0,polariz,290.,-40.,30.,6.,-14.,0.0,0.0001,Azms[i]]
816            else:
817                parms = ['PXC',data['wavelength'],0.0,polariz,1.0,-0.10,0.4,0.30,1.0,0.0,0.0001,Azms[i]]
818        elif 'SASD' in Aname:
819            Sample['Trans'] = data['SampleAbs'][0]
820            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
821        Y = G2frame.Integrate[0][i]
822        Ymin = np.min(Y)
823        Ymax = np.max(Y)
824        W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
825        Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text=Aname)
826        G2frame.IntgOutList.append(Id)
827        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Comments'),Comments)                   
828        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Limits'),copy.deepcopy([tuple(Xminmax),Xminmax]))
829        if 'PWDR' in Aname:
830            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
831                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
832        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
833        for item in inst[0]:
834            inst[0][item] = list(inst[0][item])
835        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Instrument Parameters'),inst)
836        if 'PWDR' in Aname:
837            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Sample Parameters'),Sample)
838            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Peak List'),{'sigDict':{},'peaks':[]})
839            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Index Peak List'),[[],[]])
840            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Unit Cells List'),[])
841            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Reflection Lists'),{})
842        elif 'SASD' in Aname:             
843            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
844            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Sample Parameters'),Sample)
845            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
846        valuesdict = {
847            'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxsize),'Offset':[0.0,0.0],'delOffset':0.02*Ymax,
848            'refOffset':-0.1*Ymax,'refDelt':0.1*Ymax,'Yminmax':[Ymin,Ymax]}
849        G2frame.GPXtree.SetItemPyData(Id,[valuesdict,
850            [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
851    return Id       #last powder pattern generated
852   
853def XYsave(G2frame,XY,labelX='X',labelY='Y',names=[]):
854    'Save XY table data'
855    pth = G2G.GetExportPath(G2frame)
856    dlg = wx.FileDialog(
857        G2frame, 'Enter csv filename for XY table', pth, '',
858        'XY table file (*.csv)|*.csv',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
859    try:
860        if dlg.ShowModal() == wx.ID_OK:
861            filename = dlg.GetPath()
862            filename = os.path.splitext(filename)[0]+'.csv'
863            File = open(filename,'w')
864        else:
865            filename = None
866    finally:
867        dlg.Destroy()
868    if not filename:
869        return
870    for i in range(len(XY)):
871        if len(names):
872            header = '%s,%s(%s)\n'%(labelX,labelY,names[i])
873        else:
874            header = '%s,%s(%d)\n'%(labelX,labelY,i)
875        File.write(header)
876        for x,y in XY[i].T:
877            File.write('%.3f,%.3f\n'%(x,y))   
878    File.close()
879    print (' XY data saved to: '+filename)
880           
881def PDFSave(G2frame,exports,PDFsaves):
882    'Save a PDF I(Q), S(Q), F(Q) and G(r)  in column formats'
883    import scipy.interpolate as scintp
884    if len(exports) > 1:
885        dirname = G2G.askSaveDirectory(G2frame)
886        if not dirname: return
887    else:
888        defnam = exports[0].replace(' ','_')[5:]
889        filename = G2G.askSaveFile(G2frame,defnam,'.gr','G(r) file, etc.')
890        if not filename: return
891        dirname,filename = os.path.split(filename)
892        filename = os.path.splitext(filename)[0]
893    for export in exports:
894        if len(exports) > 1:
895            filename = export.replace(' ','_')[5:]
896        PickId = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, export)
897        PDFControls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, PickId,'PDF Controls'))
898        if PDFsaves[0]:     #I(Q)
899            iqfilename = ospath.join(dirname,filename+'.iq')
900            iqdata = PDFControls['I(Q)'][0]
901            iqfxn = scintp.interp1d(iqdata[0],iqdata[1],kind='linear')
902            iqfile = open(iqfilename,'w')
903            iqfile.write('#T I(Q) %s\n'%(export))
904            iqfile.write('#L Q     I(Q)\n')
905            qnew = np.arange(iqdata[0][0],iqdata[0][-1],0.005)
906            iqnew = zip(qnew,iqfxn(qnew))
907            for q,iq in iqnew:
908                iqfile.write("%15.6g %15.6g\n" % (q,iq))
909            iqfile.close()
910            print (' I(Q) saved to: '+iqfilename)
911           
912        if PDFsaves[1]:     #S(Q)
913            sqfilename = ospath.join(dirname,filename+'.sq')
914            sqdata = PDFControls['S(Q)'][1]
915            sqfxn = scintp.interp1d(sqdata[0],sqdata[1],kind='linear')
916            sqfile = open(sqfilename,'w')
917            sqfile.write('#T S(Q) %s\n'%(export))
918            sqfile.write('#L Q     S(Q)\n')
919            qnew = np.arange(sqdata[0][0],sqdata[0][-1],0.005)
920            sqnew = zip(qnew,sqfxn(qnew))
921            for q,sq in sqnew:
922                sqfile.write("%15.6g %15.6g\n" % (q,sq))
923            sqfile.close()
924            print (' S(Q) saved to: '+sqfilename)
925           
926        if PDFsaves[2]:     #F(Q)
927            fqfilename = ospath.join(dirname,filename+'.fq')
928            fqdata = PDFControls['F(Q)'][1]
929            fqfxn = scintp.interp1d(fqdata[0],fqdata[1],kind='linear')
930            fqfile = open(fqfilename,'w')
931            fqfile.write('#T F(Q) %s\n'%(export))
932            fqfile.write('#L Q     F(Q)\n')
933            qnew = np.arange(fqdata[0][0],fqdata[0][-1],0.005)
934            fqnew = zip(qnew,fqfxn(qnew))
935            for q,fq in fqnew:
936                fqfile.write("%15.6g %15.6g\n" % (q,fq))
937            fqfile.close()
938            print (' F(Q) saved to: '+fqfilename)
939           
940        if PDFsaves[3]:     #G(R)
941            grfilename = ospath.join(dirname,filename+'.gr')
942            grdata = PDFControls['G(R)'][1]
943            grfxn = scintp.interp1d(grdata[0],grdata[1],kind='linear')
944            grfile = open(grfilename,'w')
945            grfile.write('#T G(R) %s\n'%(export))
946            grfile.write('#L R     G(R)\n')
947            rnew = np.arange(grdata[0][0],grdata[0][-1],0.010)
948            grnew = zip(rnew,grfxn(rnew))
949            for r,gr in grnew:
950                grfile.write("%15.6g %15.6g\n" % (r,gr))
951            grfile.close()
952            print (' G(R) saved to: '+grfilename)
953       
954        if PDFsaves[4]: #pdfGUI file for G(R)
955            pId = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, 'PWDR'+export[4:])
956            Inst = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, pId,'Instrument Parameters'))[0]
957            Limits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, pId,'Limits'))
958            grfilename = ospath.join(dirname,filename+'.gr')
959            grdata = PDFControls['G(R)'][1]
960            qdata = PDFControls['I(Q)'][1][0]
961            grfxn = scintp.interp1d(grdata[0],grdata[1],kind='linear')
962            grfile = open(grfilename,'w')
963            rnew = np.arange(grdata[0][0],grdata[0][-1],0.010)
964            grnew = zip(rnew,grfxn(rnew))
965
966            grfile.write('[DEFAULT]\n')
967            grfile.write('\n')
968            grfile.write('version = GSAS-II-v'+str(GSASIIpath.GetVersionNumber())+'\n')
969            grfile.write('\n')
970            grfile.write('# input and output specifications\n')
971            grfile.write('dataformat = Qnm\n')
972            grfile.write('inputfile = %s\n'%(PDFControls['Sample']['Name']))
973            grfile.write('backgroundfile = %s\n'%(PDFControls['Sample Bkg.']['Name']))
974            grfile.write('outputtype = gr\n')
975            grfile.write('\n')
976            grfile.write('# PDF calculation setup\n')
977            if 'x' in Inst['Type']:
978                grfile.write('mode = %s\n'%('xray'))
979            elif 'N' in Inst['Type']:
980                grfile.write('mode = %s\n'%('neutron'))
981            wave = G2mth.getMeanWave(Inst)
982            grfile.write('wavelength = %.5f\n'%(wave))
983            formula = ''
984            for el in PDFControls['ElList']:
985                formula += el
986                num = PDFControls['ElList'][el]['FormulaNo']
987                if num == round(num):
988                    formula += '%d'%(int(num))
989                else:
990                    formula += '%.2f'%(num)
991            grfile.write('composition = %s\n'%(formula))
992            grfile.write('bgscale = %.3f\n'%(-PDFControls['Sample Bkg.']['Mult']))
993            highQ = 2.*np.pi/G2lat.Pos2dsp(Inst,Limits[1][1])
994            grfile.write('qmaxinst = %.2f\n'%(highQ))
995            grfile.write('qmin = %.5f\n'%(qdata[0]))
996            grfile.write('qmax = %.4f\n'%(qdata[-1]))
997            grfile.write('rmin = %.2f\n'%(PDFControls['Rmin']))
998            grfile.write('rmax = %.2f\n'%(PDFControls['Rmax']))
999            grfile.write('rstep = 0.01\n')
1000           
1001           
1002            grfile.write('\n')
1003            grfile.write('# End of config '+63*'-')
1004            grfile.write('\n')
1005            grfile.write('#### start data\n')
1006            grfile.write('#S 1\n')
1007            grfile.write('#L r($\AA$)  G($\AA^{-2}$)\n')           
1008            for r,gr in grnew:
1009                grfile.write("%15.2F %15.6F\n" % (r,gr))
1010            grfile.close()
1011            print (' G(R) saved to: '+grfilename)
1012   
1013def PeakListSave(G2frame,file,peaks):
1014    'Save powder peaks to a data file'
1015    print ('save peak list to file: '+G2frame.peaklistfile)
1016    if not peaks:
1017        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1018        try:
1019            dlg.ShowModal()
1020        finally:
1021            dlg.Destroy()
1022        return
1023    for peak in peaks:
1024        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
1025            (peak[0],peak[2],peak[4],peak[6]))
1026    print ('peak list saved')
1027             
1028def IndexPeakListSave(G2frame,peaks):
1029    'Save powder peaks from the indexing list'
1030    file = open(G2frame.peaklistfile,'wa')
1031    print ('save index peak list to file: '+G2frame.peaklistfile)
1032    wx.BeginBusyCursor()
1033    try:
1034        if not peaks:
1035            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1036            try:
1037                dlg.ShowModal()
1038            finally:
1039                dlg.Destroy()
1040            return
1041        for peak in peaks:
1042            file.write("%12.6f\n" % (peak[7]))
1043        file.close()
1044    finally:
1045        wx.EndBusyCursor()
1046    print ('index peak list saved')
1047   
1048class MultipleChoicesDialog(wx.Dialog):
1049    '''A dialog that offers a series of choices, each with a
1050    title and a wx.Choice widget. Intended to be used Modally.
1051    typical input:
1052
1053        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1054        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1055       
1056    selections are placed in self.chosen when OK is pressed
1057
1058    Also see GSASIIctrlGUI
1059    '''
1060    def __init__(self,choicelist,headinglist,
1061                 head='Select options',
1062                 title='Please select from options below',
1063                 parent=None):
1064        self.chosen = []
1065        wx.Dialog.__init__(
1066            self,parent,wx.ID_ANY,head, 
1067            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1068        panel = wx.Panel(self)
1069        mainSizer = wx.BoxSizer(wx.VERTICAL)
1070        mainSizer.Add((10,10),1)
1071        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1072        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1073        self.ChItems = []
1074        for choice,lbl in zip(choicelist,headinglist):
1075            mainSizer.Add((10,10),1)
1076            self.chosen.append(0)
1077            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1078            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1079            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1080            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1081
1082        OkBtn = wx.Button(panel,-1,"Ok")
1083        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1084        cancelBtn = wx.Button(panel,-1,"Cancel")
1085        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1086        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1087        btnSizer.Add((20,20),1)
1088        btnSizer.Add(OkBtn)
1089        btnSizer.Add((20,20),1)
1090        btnSizer.Add(cancelBtn)
1091        btnSizer.Add((20,20),1)
1092        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1093        panel.SetSizer(mainSizer)
1094        panel.Fit()
1095        self.Fit()
1096       
1097    def OnOk(self,event):
1098        parent = self.GetParent()
1099        if parent is not None: parent.Raise()
1100        # save the results from the choice widgets
1101        self.chosen = []
1102        for w in self.ChItems:
1103            self.chosen.append(w.GetSelection())
1104        self.EndModal(wx.ID_OK)             
1105           
1106    def OnCancel(self,event):
1107        parent = self.GetParent()
1108        if parent is not None: parent.Raise()
1109        self.chosen = []
1110        self.EndModal(wx.ID_CANCEL)             
1111           
1112def ExtractFileFromZip(filename, selection=None, confirmread=True,
1113                       confirmoverwrite=True, parent=None,
1114                       multipleselect=False):
1115    '''If the filename is a zip file, extract a file from that
1116    archive.
1117
1118    :param list Selection: used to predefine the name of the file
1119      to be extracted. Filename case and zip directory name are
1120      ignored in selection; the first matching file is used.
1121
1122    :param bool confirmread: if True asks the user to confirm before expanding
1123      the only file in a zip
1124
1125    :param bool confirmoverwrite: if True asks the user to confirm
1126      before overwriting if the extracted file already exists
1127
1128    :param bool multipleselect: if True allows more than one zip
1129      file to be extracted, a list of file(s) is returned.
1130      If only one file is present, do not ask which one, otherwise
1131      offer a list of choices (unless selection is used).
1132   
1133    :returns: the name of the file that has been created or a
1134      list of files (see multipleselect)
1135
1136    If the file is not a zipfile, return the name of the input file.
1137    If the zipfile is empty or no file has been selected, return None
1138    '''
1139    import zipfile # do this now, since we can save startup time by doing this only on need
1140    import shutil
1141    zloc = os.path.split(filename)[0]
1142    if not zipfile.is_zipfile(filename):
1143        #print("not zip")
1144        return filename
1145
1146    z = zipfile.ZipFile(filename,'r')
1147    zinfo = z.infolist()
1148
1149    if len(zinfo) == 0:
1150        #print('Zip has no files!')
1151        zlist = [-1]
1152    if selection:
1153        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1154        if selection.lower() in choices:
1155            zlist = [choices.index(selection.lower())]
1156        else:
1157            print('debug: file '+str(selection)+' was not found in '+str(filename))
1158            zlist = [-1]
1159    elif len(zinfo) == 1 and confirmread:
1160        result = wx.ID_NO
1161        dlg = wx.MessageDialog(
1162            parent,
1163            'Is file '+str(zinfo[0].filename)+
1164            ' what you want to extract from '+
1165            str(os.path.split(filename)[1])+'?',
1166            'Confirm file', 
1167            wx.YES_NO | wx.ICON_QUESTION)
1168        try:
1169            result = dlg.ShowModal()
1170        finally:
1171            dlg.Destroy()
1172        if result == wx.ID_NO:
1173            zlist = [-1]
1174        else:
1175            zlist = [0]
1176    elif len(zinfo) == 1:
1177        zlist = [0]
1178    elif multipleselect:
1179        # select one or more from a from list
1180        choices = [i.filename for i in zinfo]
1181        dlg = G2G.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1182            'Choose file(s)',choices)
1183        if dlg.ShowModal() == wx.ID_OK:
1184            zlist = dlg.GetSelections()
1185        else:
1186            zlist = []
1187        dlg.Destroy()
1188    else:
1189        # select one from a from list
1190        choices = [i.filename for i in zinfo]
1191        dlg = wx.SingleChoiceDialog(parent,
1192            'Select file to extract from zip file'+str(filename),'Choose file',
1193            choices,)
1194        if dlg.ShowModal() == wx.ID_OK:
1195            zlist = [dlg.GetSelection()]
1196        else:
1197            zlist = [-1]
1198        dlg.Destroy()
1199       
1200    outlist = []
1201    for zindex in zlist:
1202        if zindex >= 0:
1203            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1204            if os.path.exists(efil) and confirmoverwrite:
1205                result = wx.ID_NO
1206                dlg = wx.MessageDialog(parent,
1207                    'File '+str(efil)+' already exists. OK to overwrite it?',
1208                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1209                try:
1210                    result = dlg.ShowModal()
1211                finally:
1212                    dlg.Destroy()
1213                if result == wx.ID_NO:
1214                    zindex = -1
1215        if zindex >= 0:
1216            # extract the file to the current directory, regardless of it's original path
1217            #z.extract(zinfo[zindex],zloc)
1218            eloc,efil = os.path.split(zinfo[zindex].filename)
1219            outfile = os.path.join(zloc, efil)
1220            fpin = z.open(zinfo[zindex])
1221            fpout = open(outfile, "wb")
1222            shutil.copyfileobj(fpin, fpout)
1223            fpin.close()
1224            fpout.close()
1225            outlist.append(outfile)
1226    z.close()
1227    if multipleselect and len(outlist) >= 1:
1228        return outlist
1229    elif len(outlist) == 1:
1230        return outlist[0]
1231    else:
1232        return None
1233
1234######################################################################
1235# base classes for reading various types of data files
1236#   not used directly, only by subclassing
1237######################################################################
1238def BlockSelector(ChoiceList, ParentFrame=None,title='Select a block',
1239    size=None, header='Block Selector',useCancel=True):
1240    ''' Provide a wx dialog to select a block if the file contains more
1241    than one set of data and one must be selected
1242    '''
1243    if useCancel:
1244        dlg = wx.SingleChoiceDialog(
1245            ParentFrame,title, header,ChoiceList)
1246    else:
1247        dlg = wx.SingleChoiceDialog(
1248            ParentFrame,title, header,ChoiceList,
1249            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1250    if size: dlg.SetSize(size)
1251    dlg.CenterOnParent()
1252    if dlg.ShowModal() == wx.ID_OK:
1253        sel = dlg.GetSelection()
1254        return sel
1255    else:
1256        return None
1257    dlg.Destroy()
1258
1259def MultipleBlockSelector(ChoiceList, ParentFrame=None,
1260    title='Select a block',size=None, header='Block Selector'):
1261    '''Provide a wx dialog to select a block of data if the
1262    file contains more than one set of data and one must be
1263    selected.
1264
1265    :returns: a list of the selected blocks
1266    '''
1267    dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1268        wx.CHOICEDLG_STYLE)
1269    dlg.CenterOnScreen()
1270    if size: dlg.SetSize(size)
1271    if dlg.ShowModal() == wx.ID_OK:
1272        sel = dlg.GetSelections()
1273    else:
1274        return []
1275    dlg.Destroy()
1276    selected = []
1277    if len(ChoiceList) in sel:
1278        return range(len(ChoiceList))
1279    else:
1280        return sel
1281    return selected
1282
1283def MultipleChoicesSelector(choicelist, headinglist, ParentFrame=None, **kwargs):
1284    '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1285    widget. Typical input:
1286   
1287       * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1288       
1289       * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1290       
1291    optional keyword parameters are: head (window title) and title
1292    returns a list of selected indicies for each choice (or None)
1293    '''
1294    result = None
1295    dlg = MultipleChoicesDialog(choicelist,headinglist,
1296        parent=ParentFrame, **kwargs)         
1297    dlg.CenterOnParent()
1298    if dlg.ShowModal() == wx.ID_OK:
1299        result = dlg.chosen
1300    dlg.Destroy()
1301    return result
1302
1303def PhaseSelector(ChoiceList, ParentFrame=None,
1304    title='Select a phase', size=None,header='Phase Selector'):
1305    ''' Provide a wx dialog to select a phase if the file contains more
1306    than one phase
1307    '''
1308    return BlockSelector(ChoiceList,ParentFrame,title,
1309        size,header)
1310
1311######################################################################
1312def striphist(var,insChar=''):
1313    'strip a histogram number from a var name'
1314    sv = var.split(':')
1315    if len(sv) <= 1: return var
1316    if sv[1]:
1317        sv[1] = insChar
1318    return ':'.join(sv)
1319class ExportBaseclass(object):
1320    '''Defines a base class for the exporting of GSAS-II results.
1321
1322    This class is subclassed in the various exports/G2export_*.py files. Those files
1323    are imported in :meth:`GSASIIdataGUI.GSASII._init_Exports` which defines the
1324    appropriate menu items for each one and the .Exporter method is called
1325    directly from the menu item.
1326
1327    Routines may also define a .Writer method, which is used to write a single
1328    file without invoking any GUI objects.
1329    '''
1330    # TODO: review exporters producing exceptions where .Writer can't be used where G2frame is None (see CIF)
1331    # TODO: review conflicting uses of .Writer with mode (SeqRef) & elsewhere
1332    # TODO: move this class to G2fil
1333    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1334        self.G2frame = G2frame
1335        self.formatName = formatName # short string naming file type
1336        self.extension = extension
1337        if longFormatName: # longer string naming file type
1338            self.longFormatName = longFormatName
1339        else:
1340            self.longFormatName = formatName
1341        self.OverallParms = {}
1342        self.Phases = {}
1343        self.Histograms = {}
1344        self.powderDict = {}
1345        self.sasdDict = {}
1346        self.xtalDict = {}
1347        self.parmDict = {}
1348        self.sigDict = {}
1349        # updated in InitExport:
1350        self.currentExportType = None # type of export that has been requested
1351        # updated in ExportSelect (when used):
1352        self.phasenam = None # a list of selected phases
1353        self.histnam = None # a list of selected histograms
1354        self.filename = None # name of file to be written (single export) or template (multiple files)
1355        self.dirname = '' # name of directory where file(s) will be written
1356        self.fullpath = '' # name of file being written -- full path
1357       
1358        # items that should be defined in a subclass of this class
1359        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1360        # The following types are defined: 'project', "phase", "powder", "single"
1361        self.multiple = False # set as True if the class can export multiple phases or histograms
1362        # self.multiple is ignored for "project" exports
1363
1364    def InitExport(self,event):
1365        '''Determines the type of menu that called the Exporter and
1366        misc initialization.
1367        '''
1368        self.filename = None # name of file to be written (single export)
1369        self.dirname = '' # name of file to be written (multiple export)
1370        if event:
1371            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1372
1373    def MakePWDRfilename(self,hist):
1374        '''Make a filename root (no extension) from a PWDR histogram name
1375
1376        :param str hist: the histogram name in data tree (starts with "PWDR ")
1377        '''
1378        file0 = ''
1379        file1 = hist[5:]
1380        # replace repeated blanks
1381        while file1 != file0:
1382            file0 = file1
1383            file1 = file0.replace('  ',' ').strip()
1384        file0 = file1.replace('Azm= ','A')
1385        # if angle has unneeded decimal places on aziumuth, remove them
1386        if file0[-3:] == '.00': file0 = file0[:-3]
1387        file0 = file0.replace('.','_')
1388        file0 = file0.replace(' ','_')
1389        return file0
1390
1391    def ExportSelect(self,AskFile='ask'):
1392        '''Selects histograms or phases when needed. Sets a default file name when
1393        requested into self.filename; always sets a default directory in self.dirname.
1394
1395        :param bool AskFile: Determines how this routine processes getting a
1396          location to store the current export(s).
1397         
1398          * if AskFile is 'ask' (default option), get the name of the file to be written;
1399            self.filename and self.dirname are always set. In the case where
1400            multiple files must be generated, the export routine should do this
1401            based on self.filename as a template.
1402          * if AskFile is 'dir', get the name of the directory to be used;
1403            self.filename is not used, but self.dirname is always set. The export routine
1404            will always generate the file name.
1405          * if AskFile is 'single', get only the name of the directory to be used when
1406            multiple items will be written (as multiple files) are used
1407            *or* a complete file name is requested when a single file
1408            name is selected. self.dirname is always set and self.filename used
1409            only when a single file is selected. 
1410          * if AskFile is 'default', creates a name of the file to be used from
1411            the name of the project (.gpx) file. If the project has not been saved,
1412            then the name of file is requested.
1413            self.filename and self.dirname are always set. In the case where
1414            multiple file names must be generated, the export routine should do this
1415            based on self.filename.
1416          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1417            file. If the project has not been saved, then a directory is requested.
1418            self.filename is not used.
1419
1420        :returns: True in case of an error
1421        '''
1422       
1423        numselected = 1
1424        if self.currentExportType == 'phase':
1425            if len(self.Phases) == 0:
1426                self.G2frame.ErrorDialog(
1427                    'Empty project',
1428                    'Project does not contain any phases.')
1429                return True
1430            elif len(self.Phases) == 1:
1431                self.phasenam = list(self.Phases.keys())
1432            elif self.multiple: 
1433                choices = sorted(self.Phases.keys())
1434                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1435                if phasenum is None: return True
1436                self.phasenam = [choices[i] for i in phasenum]
1437                if not self.phasenam: return True
1438                numselected = len(self.phasenam)
1439            else:
1440                choices = sorted(self.Phases.keys())
1441                phasenum = G2G.ItemSelector(choices,self.G2frame)
1442                if phasenum is None: return True
1443                self.phasenam = [choices[phasenum]]
1444                numselected = len(self.phasenam)
1445        elif self.currentExportType == 'single':
1446            if len(self.xtalDict) == 0:
1447                self.G2frame.ErrorDialog(
1448                    'Empty project',
1449                    'Project does not contain any single crystal data.')
1450                return True
1451            elif len(self.xtalDict) == 1:
1452                self.histnam = self.xtalDict.values()
1453            elif self.multiple:
1454                choices = sorted(self.xtalDict.values())
1455                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1456                if not hnum: return True
1457                self.histnam = [choices[i] for i in hnum]
1458                numselected = len(self.histnam)
1459            else:
1460                choices = sorted(self.xtalDict.values())
1461                hnum = G2G.ItemSelector(choices,self.G2frame)
1462                if hnum is None: return True
1463                self.histnam = [choices[hnum]]
1464                numselected = len(self.histnam)
1465        elif self.currentExportType == 'powder':
1466            if len(self.powderDict) == 0:
1467                self.G2frame.ErrorDialog(
1468                    'Empty project',
1469                    'Project does not contain any powder data.')
1470                return True
1471            elif len(self.powderDict) == 1:
1472                self.histnam = self.powderDict.values()
1473            elif self.multiple:
1474                choices = sorted(self.powderDict.values())
1475                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1476                if not hnum: return True
1477                self.histnam = [choices[i] for i in hnum]
1478                numselected = len(self.histnam)
1479            else:
1480                choices = sorted(self.powderDict.values())
1481                hnum = G2G.ItemSelector(choices,self.G2frame)
1482                if hnum is None: return True
1483                self.histnam = [choices[hnum]]
1484                numselected = len(self.histnam)
1485        elif self.currentExportType == 'sasd':
1486            if len(self.sasdDict) == 0:
1487                self.G2frame.ErrorDialog(
1488                    'Empty project',
1489                    'Project does not contain any small angle data.')
1490                return True
1491            elif len(self.sasdDict) == 1:
1492                self.histnam = self.sasdDict.values()
1493            elif self.multiple:
1494                choices = sorted(self.sasdDict.values())
1495                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1496                if not hnum: return True
1497                self.histnam = [choices[i] for i in hnum]
1498                numselected = len(self.histnam)
1499            else:
1500                choices = sorted(self.sasdDict.values())
1501                hnum = G2G.ItemSelector(choices,self.G2frame)
1502                if hnum is None: return True
1503                self.histnam = [choices[hnum]]
1504                numselected = len(self.histnam)
1505        elif self.currentExportType == 'image':
1506            if len(self.Histograms) == 0:
1507                self.G2frame.ErrorDialog(
1508                    'Empty project',
1509                    'Project does not contain any images.')
1510                return True
1511            elif len(self.Histograms) == 1:
1512                self.histnam = list(self.Histograms.keys())
1513            else:
1514                choices = sorted(self.Histograms.keys())
1515                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1516                if self.multiple:
1517                    if not hnum: return True
1518                    self.histnam = [choices[i] for i in hnum]
1519                else:
1520                    if hnum is None: return True
1521                    self.histnam = [choices[hnum]]
1522                numselected = len(self.histnam)
1523        if self.currentExportType == 'map':
1524            # search for phases with maps
1525            mapPhases = []
1526            choices = []
1527            for phasenam in sorted(self.Phases):
1528                phasedict = self.Phases[phasenam] # pointer to current phase info           
1529                if len(phasedict['General']['Map'].get('rho',[])):
1530                    mapPhases.append(phasenam)
1531                    if phasedict['General']['Map'].get('Flip'):
1532                        choices.append('Charge flip map: '+str(phasenam))
1533                    elif phasedict['General']['Map'].get('MapType'):
1534                        choices.append(
1535                            str(phasedict['General']['Map'].get('MapType'))
1536                            + ' map: ' + str(phasenam))
1537                    else:
1538                        choices.append('unknown map: '+str(phasenam))
1539            # select a map if needed
1540            if len(mapPhases) == 0:
1541                self.G2frame.ErrorDialog(
1542                    'Empty project',
1543                    'Project does not contain any maps.')
1544                return True
1545            elif len(mapPhases) == 1:
1546                self.phasenam = mapPhases
1547            else: 
1548                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1549                if self.multiple:
1550                    if not phasenum: return True
1551                    self.phasenam = [mapPhases[i] for i in phasenum]
1552                else:
1553                    if phasenum is None: return True
1554                    self.phasenam = [mapPhases[phasenum]]
1555            numselected = len(self.phasenam)
1556
1557        # items selected, now set self.dirname and usually self.filename
1558        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
1559            AskFile == 'default' and not self.G2frame.GSASprojectfile
1560            ):
1561            filename = self.askSaveFile()
1562            if not filename: return True
1563            self.dirname,self.filename = os.path.split(filename)
1564        elif AskFile == 'dir' or AskFile == 'single' or (
1565            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
1566            ):
1567            self.dirname = self.askSaveDirectory()
1568            if not self.dirname: return True
1569        elif AskFile == 'default-dir' or AskFile == 'default':
1570            self.dirname,self.filename = os.path.split(
1571                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
1572                )
1573        else:
1574            raise Exception('This should not happen!')
1575
1576    def loadParmDict(self):
1577        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
1578        refined values to those from the last cycle and set the uncertainties for the
1579        refined parameters in another dict (self.sigDict).
1580
1581        Expands the parm & sig dicts to include values derived from constraints.
1582
1583        This could be made faster for sequential fits by reducing the histogram list to only
1584        the active histogram being exported.
1585        '''
1586        self.parmDict = {}
1587        self.sigDict = {}
1588        rigidbodyDict = {}
1589        covDict = {}
1590        consDict = {}
1591        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1592        if self.G2frame.GPXtree.IsEmpty(): return # nothing to do
1593        item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1594        while item:
1595            name = self.G2frame.GPXtree.GetItemText(item)
1596            if name == 'Rigid bodies':
1597                 rigidbodyDict = self.G2frame.GPXtree.GetItemPyData(item)
1598            elif name == 'Covariance':
1599                 covDict = self.G2frame.GPXtree.GetItemPyData(item)
1600            elif name == 'Constraints':
1601                 consDict = self.G2frame.GPXtree.GetItemPyData(item)
1602            item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1603        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1604        self.parmDict.update(rbDict)
1605        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1606        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave =  G2stIO.GetPhaseData(
1607            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
1608        self.parmDict.update(phaseDict)
1609        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
1610            Phases,Histograms,Print=False,resetRefList=False)
1611        self.parmDict.update(hapDict)
1612        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
1613        self.parmDict.update(histDict)
1614        self.parmDict.update(zip(
1615            covDict.get('varyList',[]),
1616            covDict.get('variables',[])))
1617        self.sigDict = dict(zip(
1618            covDict.get('varyList',[]),
1619            covDict.get('sig',[])))
1620        # expand to include constraints: first compile a list of constraints
1621        constList = []
1622        for item in consDict:
1623            if item.startswith('_'): continue
1624            constList += consDict[item]
1625        # now process the constraints
1626        G2mv.InitVars()
1627        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
1628        varyList = covDict.get('varyListStart')
1629        if varyList is None and len(constDict) == 0:
1630            # no constraints can use varyList
1631            varyList = covDict.get('varyList')
1632        elif varyList is None:
1633            # old GPX file from before pre-constraint varyList is saved
1634            print (' *** Old refinement: Please use Calculate/Refine to redo  ***')
1635            raise Exception(' *** Export aborted ***')
1636        else:
1637            varyList = list(varyList)
1638        # add symmetry-generated constraints
1639        rigidbodyDict = self.G2frame.GPXtree.GetItemPyData(   
1640            G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Rigid bodies'))
1641        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1642        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1643        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData(
1644            Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
1645        msg = G2mv.EvaluateMultipliers(constDict,phaseDict)
1646        if msg:
1647            print('Unable to interpret multiplier(s): '+msg)
1648            raise Exception(' *** CIF creation aborted ***')
1649        try:
1650            G2mv.GenerateConstraints(varyList,constDict,fixedList,self.parmDict)
1651            #print(G2mv.VarRemapShow(varyList))
1652        except:
1653            # this really should not happen
1654            print (' *** ERROR - constraints are internally inconsistent ***')
1655            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
1656            print ('Errors'+errmsg)
1657            if warnmsg: print ('Warnings'+warnmsg)
1658            raise Exception(' *** CIF creation aborted ***')
1659        # add the constrained values to the parameter dictionary
1660        G2mv.Dict2Map(self.parmDict,varyList)
1661        # and add their uncertainties into the esd dictionary (sigDict)
1662        if covDict.get('covMatrix') is not None:
1663            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
1664
1665    def loadTree(self):
1666        '''Load the contents of the data tree into a set of dicts
1667        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
1668        & self.xtalDict)
1669       
1670        * The childrenless data tree items are overall parameters/controls for the
1671          entire project and are placed in self.OverallParms
1672        * Phase items are placed in self.Phases
1673        * Data items are placed in self.Histogram. The key for these data items
1674          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
1675        '''
1676        self.OverallParms = {}
1677        self.powderDict = {}
1678        self.sasdDict = {}
1679        self.xtalDict = {}
1680        self.Phases = {}
1681        self.Histograms = {}
1682        self.SeqRefdata = None
1683        self.SeqRefhist = None
1684        if self.G2frame.GPXtree.IsEmpty(): return # nothing to do
1685        histType = None       
1686        if self.currentExportType == 'phase':
1687            # if exporting phases load them here
1688            sub = G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Phases')
1689            if not sub:
1690                print ('no phases found')
1691                return True
1692            item, cookie = self.G2frame.GPXtree.GetFirstChild(sub)
1693            while item:
1694                phaseName = self.G2frame.GPXtree.GetItemText(item)
1695                self.Phases[phaseName] =  self.G2frame.GPXtree.GetItemPyData(item)
1696                item, cookie = self.G2frame.GPXtree.GetNextChild(sub, cookie)
1697            return
1698        elif self.currentExportType == 'single':
1699            histType = 'HKLF'
1700        elif self.currentExportType == 'powder':
1701            histType = 'PWDR'
1702        elif self.currentExportType == 'image':
1703            histType = 'IMG'
1704        elif self.currentExportType == 'sasd':
1705            histType = 'SASD'
1706
1707        if histType: # Loading just one kind of tree entry
1708            item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1709            while item:
1710                name = self.G2frame.GPXtree.GetItemText(item)
1711                if name.startswith(histType):
1712                    if self.Histograms.get(name): # there is already an item with this name
1713                        print('Histogram name '+str(name)+' is repeated. Renaming')
1714                        if name[-1] == '9':
1715                            name = name[:-1] + '10'
1716                        elif name[-1] in '012345678':
1717                            name = name[:-1] + str(int(name[-1])+1)
1718                        else:                           
1719                            name += '-1'
1720                    self.Histograms[name] = {}
1721                    # the main info goes into Data, but the 0th
1722                    # element contains refinement results, carry
1723                    # that over too now.
1724                    self.Histograms[name]['Data'] = self.G2frame.GPXtree.GetItemPyData(item)[1]
1725                    self.Histograms[name][0] = self.G2frame.GPXtree.GetItemPyData(item)[0]
1726                    item2, cookie2 = self.G2frame.GPXtree.GetFirstChild(item)
1727                    while item2: 
1728                        child = self.G2frame.GPXtree.GetItemText(item2)
1729                        self.Histograms[name][child] = self.G2frame.GPXtree.GetItemPyData(item2)
1730                        item2, cookie2 = self.G2frame.GPXtree.GetNextChild(item, cookie2)
1731                item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1732            # index powder and single crystal histograms by number
1733            for hist in self.Histograms:
1734                if hist.startswith("PWDR"): 
1735                    d = self.powderDict
1736                elif hist.startswith("HKLF"): 
1737                    d = self.xtalDict
1738                elif hist.startswith("SASD"):
1739                    d = self.sasdDict
1740                else:
1741                    return                   
1742                i = self.Histograms[hist].get('hId')
1743                if i is None and not d.keys():
1744                    i = 0
1745                elif i is None or i in d.keys():
1746                    i = max(d.keys())+1
1747                d[i] = hist
1748            return
1749        # else standard load: using all interlinked phases and histograms
1750        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1751        item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1752        while item:
1753            name = self.G2frame.GPXtree.GetItemText(item)
1754            item2, cookie2 = self.G2frame.GPXtree.GetFirstChild(item)
1755            if not item2: 
1756                self.OverallParms[name] = self.G2frame.GPXtree.GetItemPyData(item)
1757            item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1758        # index powder and single crystal histograms
1759        for hist in self.Histograms:
1760            i = self.Histograms[hist]['hId']
1761            if hist.startswith("PWDR"): 
1762                self.powderDict[i] = hist
1763            elif hist.startswith("HKLF"): 
1764                self.xtalDict[i] = hist
1765            elif hist.startswith("SASD"):
1766                self.sasdDict[i] = hist
1767
1768    def dumpTree(self,mode='type'):
1769        '''Print out information on the data tree dicts loaded in loadTree.
1770        Used for testing only.
1771        '''
1772        if self.SeqRefdata and self.SeqRefhist:
1773            print('Note that dumpTree does not show sequential results')
1774        print ('\nOverall')
1775        if mode == 'type':
1776            def Show(arg): return type(arg)
1777        else:
1778            def Show(arg): return arg
1779        for key in self.OverallParms:
1780            print ('  '+key+Show(self.OverallParms[key]))
1781        print ('Phases')
1782        for key1 in self.Phases:
1783            print ('    '+key1+Show(self.Phases[key1]))
1784        print ('Histogram')
1785        for key1 in self.Histograms:
1786            print ('    '+key1+Show(self.Histograms[key1]))
1787            for key2 in self.Histograms[key1]:
1788                print ('      '+key2+Show(self.Histograms[key1][key2]))
1789
1790    def defaultSaveFile(self):
1791        return os.path.abspath(
1792            os.path.splitext(self.G2frame.GSASprojectfile
1793                             )[0]+self.extension)
1794       
1795    def askSaveFile(self):
1796        '''Ask the user to supply a file name
1797
1798        :returns: a file name (str) or None if Cancel is pressed
1799
1800        '''
1801        pth = G2G.GetExportPath(self.G2frame)
1802        if self.G2frame.GSASprojectfile:
1803            defnam = os.path.splitext(
1804                os.path.split(self.G2frame.GSASprojectfile)[1]
1805                )[0]+self.extension
1806        else:
1807            defnam = 'default' + self.extension
1808        return G2G.askSaveFile(self.G2frame,defnam,self.extension,self.longFormatName)
1809
1810    def askSaveDirectory(self):
1811        '''Ask the user to supply a directory name. Path name is used as the
1812        starting point for the next export path search.
1813
1814        :returns: a directory name (str) or None if Cancel is pressed
1815
1816        TODO: Can this be replaced with G2G.askSaveDirectory?
1817        '''
1818        pth = G2G.GetExportPath(self.G2frame)
1819        dlg = wx.DirDialog(
1820            self.G2frame, 'Input directory where file(s) will be written', pth,
1821            wx.DD_DEFAULT_STYLE)
1822        dlg.CenterOnParent()
1823        try:
1824            if dlg.ShowModal() == wx.ID_OK:
1825                filename = dlg.GetPath()
1826                self.G2frame.LastExportDir = filename
1827            else:
1828                filename = None
1829        finally:
1830            dlg.Destroy()
1831        return filename
1832
1833    # Tools for file writing.
1834    def OpenFile(self,fil=None,mode='w'):
1835        '''Open the output file
1836
1837        :param str fil: The name of the file to open. If None (default)
1838          the name defaults to self.dirname + self.filename.
1839          If an extension is supplied, it is not overridded,
1840          but if not, the default extension is used.
1841        :returns: the file object opened by the routine which is also
1842          saved as self.fp
1843        '''
1844        if mode == 'd': # debug mode
1845            self.fullpath = '(stdout)'
1846            self.fp = sys.stdout
1847            return
1848        if not fil:
1849            if not os.path.splitext(self.filename)[1]:
1850                self.filename += self.extension
1851            fil = os.path.join(self.dirname,self.filename)
1852        self.fullpath = os.path.abspath(fil)
1853        self.fp = open(self.fullpath,mode)
1854        return self.fp
1855
1856    def Write(self,line):
1857        '''write a line of output, attaching a line-end character
1858
1859        :param str line: the text to be written.
1860        '''
1861        self.fp.write(line+'\n')
1862       
1863    def CloseFile(self,fp=None):
1864        '''Close a file opened in OpenFile
1865
1866        :param file fp: the file object to be closed. If None (default)
1867          file object self.fp is closed.
1868        '''
1869        if self.fp == sys.stdout: return # debug mode
1870        if fp is None:
1871            fp = self.fp
1872            self.fp = None
1873        if fp is not None: fp.close()
1874       
1875    def SetSeqRef(self,data,hist):
1876        '''Set the exporter to retrieve results from a sequential refinement
1877        rather than the main tree
1878        '''
1879        self.SeqRefdata = data
1880        self.SeqRefhist = hist
1881        data_name = data[hist]
1882        for i,val in zip(data_name['varyList'],data_name['sig']):
1883            self.sigDict[i] = val
1884            self.sigDict[striphist(i)] = val
1885        for i in data_name['parmDict']:
1886            self.parmDict[striphist(i)] = data_name['parmDict'][i]
1887            self.parmDict[i] = data_name['parmDict'][i]
1888            # zero out the dA[xyz] terms, they would only bring confusion
1889            key = i.split(':')
1890            if len(key) < 3: continue
1891            if key[2].startswith('dA'):
1892                self.parmDict[i] = 0.0
1893        for i,(val,sig) in data_name.get('depParmDict',{}).items():
1894            self.parmDict[i] = val
1895            self.sigDict[i] = sig
1896        #GSASIIpath.IPyBreak()
1897
1898    def SetFromArray(self,hist,histname):
1899        '''Load a histogram into the exporter in preparation for use of the .Writer
1900        rather than the main tree. This is used in GSASIIscriptable when wx
1901        is not present.
1902        '''
1903        self.Histograms[histname] =  {}
1904        self.Histograms[histname]['Data'] = hist['data'][1]
1905        self.Histograms[histname]['Instrument Parameters'] = hist['Instrument Parameters']
1906        self.Histograms[histname]['Sample Parameters'] = hist['Sample Parameters']
1907
1908    # Tools to pull information out of the data arrays
1909    def GetCell(self,phasenam):
1910        """Gets the unit cell parameters and their s.u.'s for a selected phase
1911
1912        :param str phasenam: the name for the selected phase
1913        :returns: `cellList,cellSig` where each is a 7 element list corresponding
1914          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
1915          cell values and `cellSig` has their uncertainties.
1916        """
1917        if self.SeqRefdata and self.SeqRefhist:
1918            return self.GetSeqCell(phasenam,self.SeqRefdata[self.SeqRefhist])
1919        phasedict = self.Phases[phasenam] # pointer to current phase info
1920        try:
1921            pfx = str(phasedict['pId'])+'::'
1922            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
1923            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
1924                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
1925            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
1926            return cellList,cellSig
1927        except KeyError:
1928            cell = phasedict['General']['Cell'][1:]
1929            return cell,7*[0]
1930           
1931    def GetSeqCell(self,phasenam,data_name):
1932        """Gets the unit cell parameters and their s.u.'s for a selected phase
1933        and histogram in a sequential fit
1934
1935        :param str phasenam: the name for the selected phase
1936        :param dict data_name: the sequential refinement parameters for the selected histogram
1937        :returns: `cellList,cellSig` where each is a 7 element list corresponding
1938          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
1939          cell values and `cellSig` has their uncertainties.
1940        """
1941        phasedict = self.Phases[phasenam]
1942        SGdata = phasedict['General']['SGData']
1943        pId = phasedict['pId']
1944        RecpCellTerms = G2lat.cell2A(phasedict['General']['Cell'][1:7])
1945        ESDlookup = {}
1946        Dlookup = {}
1947        varied = [striphist(i) for i in data_name['varyList']]
1948        for item,val in data_name['newCellDict'].items():
1949            if item in varied:
1950                ESDlookup[val[0]] = item
1951                Dlookup[item] = val[0]
1952        A = RecpCellTerms[:]
1953        for i in range(6):
1954            var = str(pId)+'::A'+str(i)
1955            if var in ESDlookup:
1956                A[i] = data_name['newCellDict'][ESDlookup[var]][1] # override with refined value
1957        cellDict = dict(zip([str(pId)+'::A'+str(i) for i in range(6)],A))
1958        zeroDict = {i:0.0 for i in cellDict}
1959        A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata,cellDict,zeroDict)
1960        covData = {
1961            'varyList': [Dlookup.get(striphist(v),v) for v in data_name['varyList']],
1962            'covMatrix': data_name['covMatrix']
1963            }
1964        return list(G2lat.A2cell(A)) + [G2lat.calc_V(A)], G2stIO.getCellEsd(str(pId)+'::',SGdata,A,covData)
1965               
1966    def GetAtoms(self,phasenam):
1967        """Gets the atoms associated with a phase. Can be used with standard
1968        or macromolecular phases
1969
1970        :param str phasenam: the name for the selected phase
1971        :returns: a list of items for eac atom where each item is a list containing:
1972          label, typ, mult, xyz, and td, where
1973
1974          * label and typ are the atom label and the scattering factor type (str)
1975          * mult is the site multiplicity (int)
1976          * xyz is contains a list with four pairs of numbers:
1977            x, y, z and fractional occupancy and
1978            their standard uncertainty (or a negative value)
1979          * td is contains a list with either one or six pairs of numbers:
1980            if one number it is U\ :sub:`iso` and with six numbers it is
1981            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
1982            paired with their standard uncertainty (or a negative value)
1983        """
1984        phasedict = self.Phases[phasenam] # pointer to current phase info           
1985        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
1986        cfrac = cx+3
1987        fpfx = str(phasedict['pId'])+'::Afrac:'       
1988        atomslist = []
1989        for i,at in enumerate(phasedict['Atoms']):
1990            if phasedict['General']['Type'] == 'macromolecular':
1991                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
1992            else:
1993                label = at[ct-1]
1994            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
1995            fsig = self.sigDict.get(fpfx+str(i),-0.009)
1996            mult = at[cs+1]
1997            typ = at[ct]
1998            xyz = []
1999            for j,v in enumerate(('x','y','z')):
2000                val = at[cx+j]
2001                pfx = str(phasedict['pId']) + '::A' + v + ':' + str(i)
2002                val = self.parmDict.get(pfx, val)
2003                dpfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2004                sig = self.sigDict.get(dpfx,-0.000009)
2005                xyz.append((val,sig))
2006            xyz.append((fval,fsig))
2007            td = []
2008            if at[cia] == 'I':
2009                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2010                val = self.parmDict.get(pfx,at[cia+1])
2011                sig = self.sigDict.get(pfx,-0.0009)
2012                td.append((val,sig))
2013            else:
2014                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2015                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2016                    val = self.parmDict.get(pfx,at[cia+2+i])
2017                    sig = self.sigDict.get(pfx,-0.0009)
2018                    td.append((val,sig))
2019            atomslist.append((label,typ,mult,xyz,td))
2020        return atomslist
2021######################################################################
2022def ExportPowderList(G2frame):
2023    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
2024    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
2025   
2026    :param wx.Frame G2frame: the GSAS-II main data tree window
2027    '''
2028    extList = []
2029    for obj in G2frame.exporterlist:
2030        if 'powder' in obj.exporttype:
2031            try:
2032                obj.Writer
2033                extList.append(obj.extension)
2034            except AttributeError:
2035                pass
2036    return extList
2037
2038def ExportPowder(G2frame,TreeName,fileroot,extension):
2039    '''Writes a single powder histogram using the Export routines.
2040    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
2041
2042    :param wx.Frame G2frame: the GSAS-II main data tree window
2043    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
2044    :param str fileroot: name for file to be written, extension ignored
2045    :param str extension: extension for file to be written (start with '.'). Must
2046      match a powder export routine that has a Writer object.
2047    '''
2048    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2049    for obj in G2frame.exporterlist:
2050        if obj.extension == extension and 'powder' in obj.exporttype:
2051            obj.currentExportType = 'powder'
2052            obj.InitExport(None)
2053            obj.loadTree() # load all histograms in tree into dicts
2054            if TreeName not in obj.Histograms:
2055                raise Exception('Histogram not found: '+str(TreeName))
2056            try:
2057                obj.Writer
2058            except AttributeError:
2059                continue
2060            try:
2061                obj.Writer(TreeName,filename)
2062                print('wrote file '+filename)
2063                return
2064            except Exception:
2065                print('Export Routine for '+extension+' failed.')
2066    else:
2067        print('No Export routine supports extension '+extension)
2068
2069def ExportSequential(G2frame,data,obj,exporttype):
2070    '''
2071    Used to export from every phase/dataset in a sequential refinement using
2072    a .Writer method for either projects or phases. Prompts to select histograms
2073    and for phase exports, which phase(s).
2074
2075    :param wx.Frame G2frame: the GSAS-II main data tree window
2076    :param dict data: the sequential refinement data object
2077    :param str exporttype: indicates the type of export ('project' or 'phase')
2078    '''
2079    if len(data['histNames']) == 0:
2080        G2G.G2MessageBox(G2frame,'There are no sequential histograms','Warning')
2081    obj.InitExport(None)
2082    obj.loadTree()
2083    obj.loadParmDict()
2084    if len(data['histNames']) == 1:
2085        histlist = data['histNames']
2086    else:
2087        dlg = G2G.G2MultiChoiceDialog(G2frame,'Select histograms to export from list',
2088                                 'Select histograms',data['histNames'])
2089        if dlg.ShowModal() == wx.ID_OK:
2090            histlist = [data['histNames'][l] for l in dlg.GetSelections()]
2091            dlg.Destroy()
2092        else:
2093            dlg.Destroy()
2094            return
2095    if exporttype == 'Phase':
2096        phaselist = list(obj.Phases.keys())
2097        if len(obj.Phases) == 0:
2098            G2G.G2MessageBox(G2frame,'There are no phases in sequential ref.','Warning')
2099            return
2100        elif len(obj.Phases) > 1:
2101            dlg = G2G.G2MultiChoiceDialog(G2frame,'Select phases to export from list',
2102                                    'Select phases', phaselist)
2103            if dlg.ShowModal() == wx.ID_OK:
2104                phaselist = [phaselist[l] for l in dlg.GetSelections()]
2105                dlg.Destroy()
2106            else:
2107                dlg.Destroy()
2108                return
2109        filename = obj.askSaveFile()
2110        if not filename: return True
2111        obj.dirname,obj.filename = os.path.split(filename)
2112        print('Writing output to file '+str(obj.filename)+"...")
2113        mode = 'w'
2114        for p in phaselist:
2115            for h in histlist:
2116                obj.SetSeqRef(data,h)
2117                #GSASIIpath.IPyBreak()
2118                obj.Writer(h,phasenam=p,mode=mode)
2119                mode = 'a'
2120        print('...done')
2121    elif exporttype == 'Project':  # note that the CIF exporter is not yet ready for this
2122        filename = obj.askSaveFile()
2123        if not filename: return True
2124        obj.dirname,obj.filename = os.path.split(filename)
2125        print('Writing output to file '+str(obj.filename)+"...")
2126        mode = 'w'
2127        for h in histlist:
2128            obj.SetSeqRef(data,h)
2129            obj.Writer(h,mode=mode)
2130            print('\t'+str(h)+' written')
2131            mode = 'a'
2132        print('...done')
2133    elif exporttype == 'Powder':
2134        filename = obj.askSaveFile()
2135        if not filename: return True
2136        obj.dirname,obj.filename = os.path.split(filename)
2137        print('Writing output to file '+str(obj.filename)+"...")
2138        mode = 'w'
2139        for h in histlist:
2140            obj.SetSeqRef(data,h)
2141            obj.Writer(h,mode=mode)
2142            print('\t'+str(h)+' written')
2143            mode = 'a'
2144        print('...done')
2145
2146def ReadDIFFaX(DIFFaXfile):
2147    print ('read '+DIFFaXfile)
2148    Layer = {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],'Width':[[10.,10.],[False,False]],
2149        'Layers':[],'Stacking':[],'Transitions':[],'Toler':0.01,'AtInfo':{}}
2150    df = open(DIFFaXfile,'r')
2151    lines = df.readlines()
2152    df.close()
2153    struct = False
2154    Struct = []
2155    stack = False
2156    Stack = []
2157    trans = False
2158    Trans = []
2159    for diff in lines:
2160        diff = diff[:-1].lower()
2161        if '!'  in diff:
2162            continue
2163        while '}' in diff: #strip comments
2164            iB = diff.index('{')
2165            iF = diff.index('}')+1
2166            if iB:
2167                diff = diff[:iB]
2168            else:
2169                diff = diff[iF:]
2170        if not diff:
2171            continue
2172        if diff.strip() == 'instrumental':
2173            continue
2174        if diff.strip() == 'structural':
2175            struct = True
2176            continue
2177        elif diff.strip() == 'stacking':
2178            struct = False
2179            stack = True
2180            continue
2181        elif diff.strip() == 'transitions':
2182            stack = False
2183            trans = True
2184            continue
2185        diff = diff.strip()
2186        if struct:
2187            if diff:
2188                Struct.append(diff)
2189        elif stack:
2190            if diff:
2191                Stack.append(diff)
2192        elif trans:
2193            if diff:
2194                Trans.append(diff)
2195   
2196#STRUCTURE records
2197    laueRec = Struct[1].split()
2198    Layer['Laue'] = laueRec[0]
2199    if Layer['Laue'] == 'unknown' and len(laueRec) > 1:
2200        Layer['Toler'] = float(laueRec[1])    #tolerance for 'unknown'?
2201    if Layer['Laue'] == '2/m(1)': Layer['Laue'] = '2/m(c)'
2202    if Layer['Laue'] == '2/m(2)': Layer['Laue'] = '2/m(ab)'
2203    cell = Struct[0].split()
2204    Layer['Cell'] = [False,float(cell[0]),float(cell[1]),float(cell[2]),90.,90.,float(cell[3]),1.0]
2205    nLayers = int(Struct[2])
2206    N = 3
2207    if 'layer' not in Struct[3]:
2208        N = 4
2209        if Struct[3] != 'infinite':
2210            width = Struct[3].split()
2211            Layer['Width'][0] = [float(width[0]),float(width[1])]
2212    for nL in range(nLayers):
2213        if '=' in Struct[N]:
2214            name = Struct[N].split('=')
2215            sameas = int(name[1])-1
2216            Layer['Layers'].append({'Name':name[0],'SameAs':Layer['Layers'][sameas]['Name'],'Symm':'None','Atoms':[]})
2217            N += 1
2218            continue
2219        Symm = 'None'
2220        if 'centro' in Struct[N+1]: Symm = '-1'
2221        Layer['Layers'].append({'Name':Struct[N],'SameAs':'','Symm':Symm,'Atoms':[]})
2222        N += 2
2223        while 'layer' not in Struct[N]:
2224            atom = Struct[N][4:].split()
2225            atomType = G2el.FixValence(Struct[N][:4].replace(' ','').strip().capitalize())
2226            if atomType not in Layer['AtInfo']:
2227                Layer['AtInfo'][atomType] = G2el.GetAtomInfo(atomType)
2228            atomName = '%s(%s)'%(atomType,atom[0])
2229            newVals = []
2230            for val in atom[1:6]:
2231                if '/' in val:
2232                    newVals.append(eval(val+'.'))
2233                else:
2234                    newVals.append(float(val))               
2235            atomRec = [atomName,atomType,newVals[0],newVals[1],newVals[2],newVals[4],newVals[3]/78.9568]
2236            Layer['Layers'][-1]['Atoms'].append(atomRec)
2237            N += 1
2238            if N > len(Struct)-1:
2239                break
2240#TRANSITIONS records
2241    transArray = []
2242    N = 0
2243    for i in range(nLayers):
2244        transArray.append([])
2245        for j in range(nLayers):
2246            vals = Trans[N].split()
2247            newVals = []
2248            for val in vals[:4]:
2249                if '/' in val:
2250                    newVals.append(eval(val+'.'))
2251                else:
2252                    newVals.append(float(val))
2253            transArray[-1].append(newVals+['',False])
2254            N += 1
2255    Layer['Transitions'] = transArray
2256#STACKING records
2257    Layer['Stacking'] = [Stack[0],'']
2258    if Stack[0] == 'recursive':
2259        Layer['Stacking'][1] = Stack[1]
2260    elif Stack[0] == 'explicit':
2261        if Stack[1] == 'random':
2262            Layer['Stacking'][1] = Stack[1]
2263        else:
2264            Layer['Stacking'][1] = 'list'
2265            Layer['Stacking'].append('')
2266            for stack in Stack[2:]:
2267                Layer['Stacking'][2] += ' '+stack
2268    return Layer
2269
2270if __name__ == '__main__':
2271    import GSASIIdataGUI
2272    application = GSASIIdataGUI.GSASIImain(0)
2273    G2frame = application.main
2274    #app = wx.PySimpleApp()
2275    #G2frame = wx.Frame(None) # create a frame
2276    #frm.Show(True)
2277    #filename = '/tmp/notzip.zip'
2278    #filename = '/tmp/all.zip'
2279    #filename = '/tmp/11bmb_7652.zip'
2280   
2281    #selection=None, confirmoverwrite=True, parent=None
2282    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2283    #print ExtractFileFromZip(filename,multipleselect=True)
2284    #                         #confirmread=False, confirmoverwrite=False)
2285
2286    # choicelist=[ ('a','b','c'),
2287    #              ('test1','test2'),('no choice',)]
2288    # titles = [ 'a, b or c', 'tests', 'No option here']
2289    # dlg = MultipleChoicesDialog(
2290    #     choicelist,titles,
2291    #     parent=frm)
2292    # if dlg.ShowModal() == wx.ID_OK:
2293    #     print 'Got OK'
2294    imagefile = '/tmp/NDC5_00237_3.ge3'
2295    Comments, Data, Npix, Image = GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None)
2296
2297    print("\n\nResults loaded to Comments, Data, Npix and Image\n\n")
2298
2299    GSASIIpath.IPyBreak_base()
Note: See TracBrowser for help on using the repository browser.