source: trunk/GSASIIIO.py @ 3924

Last change on this file since 3924 was 3887, checked in by toby, 6 years ago

Fix restore of seq results after crash

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 100.0 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2019-04-10 22:52:53 +0000 (Wed, 10 Apr 2019) $
4# $Author: toby $
5# $Revision: 3887 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 3887 2019-04-10 22:52:53Z toby $
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: 3887 $")
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 = None # was 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'] = 2500
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 cPickleLoad(fp):
603    if '2' in platform.python_version_tuple()[0]:
604        return cPickle.load(fp)
605    else:
606       return cPickle.load(fp,encoding='latin-1')
607
608def ProjFileOpen(G2frame,showProvenance=True):
609    'Read a GSAS-II project file and load into the G2 data tree'
610    if not os.path.exists(G2frame.GSASprojectfile):
611        print ('\n*** Error attempt to open project file that does not exist:\n   '+
612               str(G2frame.GSASprojectfile))
613        return
614    LastSavedUsing = None
615    filep = open(G2frame.GSASprojectfile,'rb')
616    if showProvenance: print ('loading from file: '+G2frame.GSASprojectfile)
617    GPXphase = os.path.splitext(G2frame.GSASprojectfile)[0]+'.seqPhase'
618    GPXhist = os.path.splitext(G2frame.GSASprojectfile)[0]+'.seqHist'
619    deleteSeq = False
620    hist = None
621    tmpHistIndex = {}
622    updateFromSeq = False
623    if os.path.exists(GPXphase) and os.path.exists(GPXhist):
624        dlg = wx.MessageDialog(G2frame,
625            'Load results from crashed sequential fit?\nNo deletes the files!', 'Recover partial sequential fit?', wx.YES | wx.NO | wx.CANCEL)
626        dlg.CenterOnParent()
627        try:
628            result = dlg.ShowModal()
629            deleteSeq = result != wx.ID_CANCEL
630            if result == wx.ID_YES:
631                updateFromSeq = True
632                fp = open(GPXphase,'rb')
633                data = cPickleLoad(fp) # first block in file should be Phases
634                if data[0][0] != 'Phases':
635                    raise Exception('Unexpected block in {} file. How did this happen?'
636                            .format(GPXphase))
637                Phases = {}
638                for name,vals in data[1:]:
639                    Phases[name] = vals
640                name,CovData = cPickleLoad(fp)[0] # 2nd block in file should be Covariance
641                name,RigidBodies = cPickleLoad(fp)[0] # 3rd block in file should be Rigid Bodies
642                fp.close()
643                # index the histogram updates
644                hist = open(GPXhist,'rb')
645                try:
646                    while True:
647                        loc = hist.tell()
648                        datum = cPickleLoad(hist)[0]
649                        tmpHistIndex[datum[0]] = loc
650                except EOFError:
651                    pass
652        finally:
653            dlg.Destroy()
654    wx.BeginBusyCursor()
655    try:
656        while True:
657            try:
658                data = cPickleLoad(filep)
659            except EOFError:
660                break
661            datum = data[0]
662            # scan the GPX file for unexpected objects
663            if GSASIIpath.GetConfigValue('debug'):
664                global unexpectedObject
665                unexpectedObject = False               
666                objectScan(data,'tree item "{}" entry '.format(datum[0]))
667                #if unexpectedObject:
668                #    print(datum[0])
669                #    GSASIIpath.IPyBreak()
670            Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text=datum[0])
671            if updateFromSeq and datum[0] == 'Phases':
672                for pdata in data[1:]:
673                    if pdata[0] in Phases:
674                        pdata[1].update(Phases[pdata[0]])
675            elif updateFromSeq and datum[0] == 'Covariance':
676                data[0][1] = CovData
677            elif updateFromSeq and datum[0] == 'Rigid bodies':
678                data[0][1] = RigidBodies
679            elif updateFromSeq and datum[0] in tmpHistIndex:
680                hist.seek(tmpHistIndex[datum[0]])
681                hdata = cPickleLoad(hist)
682                if data[0][0] != hdata[0][0]:
683                    print('Error! Updating {} with {}'.format(data[0][0],hdata[0][0]))
684                datum = hdata[0]
685                xferItems = ['Background','Instrument Parameters','Sample Parameters','Reflection Lists']
686                hItems = {name:j+1 for j,(name,val) in enumerate(hdata[1:]) if name in xferItems}
687                for j,(name,val) in enumerate(data[1:]):
688                    if name not in xferItems: continue
689                    data[j+1][1] = hdata[hItems[name]][1]
690            if datum[0].startswith('PWDR'):               
691                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
692                    datum[1][0]['ranId'] = ran.randint(0,sys.maxsize)
693                G2frame.GPXtree.SetItemPyData(Id,datum[1][:3])  #temp. trim off junk (patch?)
694            elif datum[0].startswith('HKLF'): 
695                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
696                    datum[1][0]['ranId'] = ran.randint(0,sys.maxsize)
697                G2frame.GPXtree.SetItemPyData(Id,datum[1])
698            else:
699                G2frame.GPXtree.SetItemPyData(Id,datum[1])             
700                if datum[0] == 'Controls' and 'LastSavedUsing' in datum[1]:
701                    LastSavedUsing = datum[1]['LastSavedUsing']
702                if datum[0] == 'Controls' and 'PythonVersions' in datum[1] and GSASIIpath.GetConfigValue('debug') and showProvenance:
703                    print('DBG_Packages used to create .GPX file:')
704                    if 'dict' in str(type(datum[1]['PythonVersions'])):  #patch
705                        for p in sorted(datum[1]['PythonVersions'],key=lambda s: s.lower()):
706                            print({:<14s}: {:s}".format(p[0],p[1]))
707                    else:
708                        for p in datum[1]['PythonVersions']:
709                            print({:<12s} {:s}".format(p[0]+':',p[1]))
710            oldPDF = False
711            for datus in data[1:]:
712#patch - 1/23/17 PDF cleanup
713                if datus[0][:4] in ['I(Q)','S(Q)','F(Q)','G(R)']:
714                    oldPDF = True
715                    data[1][1][datus[0][:4]] = copy.deepcopy(datus[1][:2])
716                    continue
717#end PDF cleanup
718                sub = G2frame.GPXtree.AppendItem(Id,datus[0])
719#patch
720                if datus[0] == 'Instrument Parameters' and len(datus[1]) == 1:
721                    if datum[0].startswith('PWDR'):
722                        datus[1] = [dict(zip(datus[1][3],zip(datus[1][0],datus[1][1],datus[1][2]))),{}]
723                    else:
724                        datus[1] = [dict(zip(datus[1][2],zip(datus[1][0],datus[1][1]))),{}]
725                    for item in datus[1][0]:               #zip makes tuples - now make lists!
726                        datus[1][0][item] = list(datus[1][0][item])
727#end patch
728                G2frame.GPXtree.SetItemPyData(sub,datus[1])
729            if 'PDF ' in datum[0][:4] and oldPDF:
730                sub = G2frame.GPXtree.AppendItem(Id,'PDF Peaks')
731                G2frame.GPXtree.SetItemPyData(sub,{'Limits':[1.,5.],'Background':[2,[0.,-0.2*np.pi],False],'Peaks':[]})
732            if datum [0].startswith('IMG'):                   #retrieve image default flag & data if set
733                Data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
734                if Data['setDefault']:
735                    G2frame.imageDefault = Data               
736        if LastSavedUsing:
737            print('GPX load successful. Last saved with GSAS-II revision '+LastSavedUsing)
738        else:
739            print('project load successful')
740        G2frame.NewPlot = True
741    except Exception as errmsg:
742        if GSASIIpath.GetConfigValue('debug'):
743            print('\nError reading GPX file:',errmsg)
744            import traceback
745            print (traceback.format_exc())
746        msg = wx.MessageDialog(G2frame,message="Error reading file "+
747            str(G2frame.GSASprojectfile)+". This is not a current GSAS-II .gpx file",
748            caption="Load Error",style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
749        msg.ShowModal()
750    finally:
751        filep.close()
752        wx.EndBusyCursor()
753        G2frame.Status.SetStatusText('Mouse RB drag/drop to reorder',0)
754    if deleteSeq:
755        if hist: hist.close()
756        try:
757            os.remove(GPXphase)
758        except:
759            print('Warning: unable to delete {}'.format(GPXphase))
760        try:
761            os.remove(GPXhist)
762        except:
763            print('Warning: unable to delete {}'.format(GPXhist))
764    G2frame.SetTitleByGPX()
765   
766def ProjFileSave(G2frame):
767    'Save a GSAS-II project file'
768    if not G2frame.GPXtree.IsEmpty():
769        file = open(G2frame.GSASprojectfile,'wb')
770        print ('save to file: '+G2frame.GSASprojectfile)
771        # stick the file name into the tree and version info into tree so they are saved.
772        # (Controls should always be created at this point)
773        try:
774            Controls = G2frame.GPXtree.GetItemPyData(
775                G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls'))
776            Controls['LastSavedAs'] = os.path.abspath(G2frame.GSASprojectfile)
777            Controls['LastSavedUsing'] = str(GSASIIpath.GetVersionNumber())
778            Controls['PythonVersions'] = G2frame.PackageVersions
779        except:
780            pass
781        wx.BeginBusyCursor()
782        try:
783            item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
784            while item:
785                data = []
786                name = G2frame.GPXtree.GetItemText(item)
787                data.append([name,G2frame.GPXtree.GetItemPyData(item)])
788                item2, cookie2 = G2frame.GPXtree.GetFirstChild(item)
789                while item2:
790                    name = G2frame.GPXtree.GetItemText(item2)
791                    data.append([name,G2frame.GPXtree.GetItemPyData(item2)])
792                    item2, cookie2 = G2frame.GPXtree.GetNextChild(item, cookie2)                           
793                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)                           
794                cPickle.dump(data,file,2)
795            file.close()
796            pth = os.path.split(os.path.abspath(G2frame.GSASprojectfile))[0]
797            if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
798            G2frame.LastGPXdir = pth
799        finally:
800            wx.EndBusyCursor()
801        print('project save successful')
802
803def SaveIntegration(G2frame,PickId,data,Overwrite=False):
804    'Save image integration results as powder pattern(s)'
805    waves = {'Cu':[1.54051,1.54433],'Ti':[2.74841,2.75207],'Cr':[2.28962,2.29351],
806        'Fe':[1.93597,1.93991],'Co':[1.78892,1.79278],'Mo':[0.70926,0.713543],
807        'Ag':[0.559363,0.563775]}
808    azms = G2frame.Integrate[1]
809    X = G2frame.Integrate[2][:-1]
810    N = len(X)
811    Id = G2frame.GPXtree.GetItemParent(PickId)
812    name = G2frame.GPXtree.GetItemText(Id)
813    name = name.replace('IMG ',data['type']+' ')
814    Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Comments'))
815    Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls'))
816    if 'PWDR' in name:
817        if 'target' in data:
818            names = ['Type','Lam1','Lam2','I(L2)/I(L1)','Zero','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth'] 
819            codes = [0 for i in range(14)]
820        else:
821            names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth'] 
822            codes = [0 for i in range(12)]
823    elif 'SASD' in name:
824        names = ['Type','Lam','Zero','Azimuth'] 
825        codes = [0 for i in range(4)]
826        X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
827    Xminmax = [X[0],X[-1]]
828    Azms = []
829    dazm = 0.
830    if data['fullIntegrate'] and data['outAzimuths'] == 1:
831        Azms = [45.0,]                              #a poor man's average?
832    else:
833        for i,azm in enumerate(azms[:-1]):
834            if azm > 360. and azms[i+1] > 360.:
835                Azms.append(G2img.meanAzm(azm%360.,azms[i+1]%360.))
836            else:   
837                Azms.append(G2img.meanAzm(azm,azms[i+1]))
838        dazm = np.min(np.abs(np.diff(azms)))/2.
839    G2frame.IntgOutList = []
840    for i,azm in enumerate(azms[:-1]):
841        Aname = name+" Azm= %.2f"%((azm+dazm)%360.)
842        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
843        # if Overwrite delete any duplicate
844        if Overwrite and G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Aname):
845            print('Replacing '+Aname)
846            item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Aname)
847            G2frame.GPXtree.Delete(item)
848        else:
849            nOcc = 0
850            while item:
851                Name = G2frame.GPXtree.GetItemText(item)
852                if Aname in Name:
853                    nOcc += 1
854                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
855            if nOcc:
856                Aname += '(%d)'%(nOcc)
857        Sample = G2obj.SetDefaultSample()       #set as Debye-Scherrer
858        Sample['Gonio. radius'] = data['distance']
859        Sample['Omega'] = data['GonioAngles'][0]
860        Sample['Chi'] = data['GonioAngles'][1]
861        Sample['Phi'] = data['GonioAngles'][2]
862        Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
863        polariz = 0.99    #set default polarization for synchrotron radiation!
864        for item in Comments:
865            if 'polariz' in item:
866                try:
867                    polariz = float(item.split('=')[1])
868                except:
869                    polariz = 0.99
870            for key in ('Temperature','Pressure','Time','FreePrm1','FreePrm2','FreePrm3','Omega',
871                'Chi','Phi'):
872                if key.lower() in item.lower():
873                    try:
874                        Sample[key] = float(item.split('=')[1])
875                    except:
876                        pass
877            if 'label_prm' in item.lower():
878                for num in ('1','2','3'):
879                    if 'label_prm'+num in item.lower():
880                        Controls['FreePrm'+num] = item.split('=')[1].strip()
881        if 'PWDR' in Aname:
882            if 'target' in data:    #from lab x-ray 2D imaging data
883                wave1,wave2 = waves[data['target']]
884                parms = ['PXC',wave1,wave2,0.5,0.0,polariz,290.,-40.,30.,6.,-14.,0.0,0.0001,Azms[i]]
885            else:
886                parms = ['PXC',data['wavelength'],0.0,polariz,1.0,-0.10,0.4,0.30,1.0,0.0,0.0001,Azms[i]]
887        elif 'SASD' in Aname:
888            Sample['Trans'] = data['SampleAbs'][0]
889            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
890        Y = G2frame.Integrate[0][i]
891        Ymin = np.min(Y)
892        Ymax = np.max(Y)
893        W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
894        Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text=Aname)
895        G2frame.IntgOutList.append(Id)
896        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Comments'),Comments)                   
897        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Limits'),copy.deepcopy([tuple(Xminmax),Xminmax]))
898        if 'PWDR' in Aname:
899            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
900                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
901        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
902        for item in inst[0]:
903            inst[0][item] = list(inst[0][item])
904        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Instrument Parameters'),inst)
905        if 'PWDR' in Aname:
906            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Sample Parameters'),Sample)
907            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Peak List'),{'sigDict':{},'peaks':[]})
908            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Index Peak List'),[[],[]])
909            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Unit Cells List'),[])
910            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Reflection Lists'),{})
911        elif 'SASD' in Aname:             
912            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
913            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Sample Parameters'),Sample)
914            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
915        valuesdict = {
916            'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxsize),'Offset':[0.0,0.0],'delOffset':0.02*Ymax,
917            'refOffset':-0.1*Ymax,'refDelt':0.1*Ymax,'Yminmax':[Ymin,Ymax]}
918        G2frame.GPXtree.SetItemPyData(Id,[valuesdict,
919            [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
920    return Id       #last powder pattern generated
921   
922def XYsave(G2frame,XY,labelX='X',labelY='Y',names=[]):
923    'Save XY table data'
924    pth = G2G.GetExportPath(G2frame)
925    dlg = wx.FileDialog(
926        G2frame, 'Enter csv filename for XY table', pth, '',
927        'XY table file (*.csv)|*.csv',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
928    try:
929        if dlg.ShowModal() == wx.ID_OK:
930            filename = dlg.GetPath()
931            filename = os.path.splitext(filename)[0]+'.csv'
932            File = open(filename,'w')
933        else:
934            filename = None
935    finally:
936        dlg.Destroy()
937    if not filename:
938        return
939    for i in range(len(XY)):
940        if len(names):
941            header = '%s,%s(%s)\n'%(labelX,labelY,names[i])
942        else:
943            header = '%s,%s(%d)\n'%(labelX,labelY,i)
944        File.write(header)
945        for x,y in XY[i].T:
946            File.write('%.3f,%.3f\n'%(x,y))   
947    File.close()
948    print (' XY data saved to: '+filename)
949           
950def PDFSave(G2frame,exports,PDFsaves):
951    'Save a PDF I(Q), S(Q), F(Q) and G(r)  in column formats'
952    import scipy.interpolate as scintp
953    if len(exports) > 1:
954        dirname = G2G.askSaveDirectory(G2frame)
955        if not dirname: return
956    else:
957        defnam = exports[0].replace(' ','_')[5:]
958        filename = G2G.askSaveFile(G2frame,defnam,'.gr','G(r) file, etc.')
959        if not filename: return
960        dirname,filename = os.path.split(filename)
961        filename = os.path.splitext(filename)[0]
962    for export in exports:
963        if len(exports) > 1:
964            filename = export.replace(' ','_')[5:]
965        PickId = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, export)
966        PDFControls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, PickId,'PDF Controls'))
967        if PDFsaves[0]:     #I(Q)
968            iqfilename = ospath.join(dirname,filename+'.iq')
969            iqdata = PDFControls['I(Q)'][0]
970            iqfxn = scintp.interp1d(iqdata[0],iqdata[1],kind='linear')
971            iqfile = open(iqfilename,'w')
972            iqfile.write('#T I(Q) %s\n'%(export))
973            iqfile.write('#L Q     I(Q)\n')
974            qnew = np.arange(iqdata[0][0],iqdata[0][-1],0.005)
975            iqnew = zip(qnew,iqfxn(qnew))
976            for q,iq in iqnew:
977                iqfile.write("%15.6g %15.6g\n" % (q,iq))
978            iqfile.close()
979            print (' I(Q) saved to: '+iqfilename)
980           
981        if PDFsaves[1]:     #S(Q)
982            sqfilename = ospath.join(dirname,filename+'.sq')
983            sqdata = PDFControls['S(Q)'][1]
984            sqfxn = scintp.interp1d(sqdata[0],sqdata[1],kind='linear')
985            sqfile = open(sqfilename,'w')
986            sqfile.write('#T S(Q) %s\n'%(export))
987            sqfile.write('#L Q     S(Q)\n')
988            qnew = np.arange(sqdata[0][0],sqdata[0][-1],0.005)
989            sqnew = zip(qnew,sqfxn(qnew))
990            for q,sq in sqnew:
991                sqfile.write("%15.6g %15.6g\n" % (q,sq))
992            sqfile.close()
993            print (' S(Q) saved to: '+sqfilename)
994           
995        if PDFsaves[2]:     #F(Q)
996            fqfilename = ospath.join(dirname,filename+'.fq')
997            fqdata = PDFControls['F(Q)'][1]
998            fqfxn = scintp.interp1d(fqdata[0],fqdata[1],kind='linear')
999            fqfile = open(fqfilename,'w')
1000            fqfile.write('#T F(Q) %s\n'%(export))
1001            fqfile.write('#L Q     F(Q)\n')
1002            qnew = np.arange(fqdata[0][0],fqdata[0][-1],0.005)
1003            fqnew = zip(qnew,fqfxn(qnew))
1004            for q,fq in fqnew:
1005                fqfile.write("%15.6g %15.6g\n" % (q,fq))
1006            fqfile.close()
1007            print (' F(Q) saved to: '+fqfilename)
1008           
1009        if PDFsaves[3]:     #G(R)
1010            grfilename = ospath.join(dirname,filename+'.gr')
1011            grdata = PDFControls['G(R)'][1]
1012            grfxn = scintp.interp1d(grdata[0],grdata[1],kind='linear')
1013            grfile = open(grfilename,'w')
1014            grfile.write('#T G(R) %s\n'%(export))
1015            grfile.write('#L R     G(R)\n')
1016            rnew = np.arange(grdata[0][0],grdata[0][-1],0.010)
1017            grnew = zip(rnew,grfxn(rnew))
1018            for r,gr in grnew:
1019                grfile.write("%15.6g %15.6g\n" % (r,gr))
1020            grfile.close()
1021            print (' G(R) saved to: '+grfilename)
1022       
1023        if PDFsaves[4]: #pdfGUI file for G(R)
1024            pId = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, 'PWDR'+export[4:])
1025            Inst = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, pId,'Instrument Parameters'))[0]
1026            Limits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, pId,'Limits'))
1027            grfilename = ospath.join(dirname,filename+'.gr')
1028            grdata = PDFControls['G(R)'][1]
1029            qdata = PDFControls['I(Q)'][1][0]
1030            grfxn = scintp.interp1d(grdata[0],grdata[1],kind='linear')
1031            grfile = open(grfilename,'w')
1032            rnew = np.arange(grdata[0][0],grdata[0][-1],0.010)
1033            grnew = zip(rnew,grfxn(rnew))
1034
1035            grfile.write('[DEFAULT]\n')
1036            grfile.write('\n')
1037            grfile.write('version = GSAS-II-v'+str(GSASIIpath.GetVersionNumber())+'\n')
1038            grfile.write('\n')
1039            grfile.write('# input and output specifications\n')
1040            grfile.write('dataformat = Qnm\n')
1041            grfile.write('inputfile = %s\n'%(PDFControls['Sample']['Name']))
1042            grfile.write('backgroundfile = %s\n'%(PDFControls['Sample Bkg.']['Name']))
1043            grfile.write('outputtype = gr\n')
1044            grfile.write('\n')
1045            grfile.write('# PDF calculation setup\n')
1046            if 'x' in Inst['Type']:
1047                grfile.write('mode = %s\n'%('xray'))
1048            elif 'N' in Inst['Type']:
1049                grfile.write('mode = %s\n'%('neutron'))
1050            wave = G2mth.getMeanWave(Inst)
1051            grfile.write('wavelength = %.5f\n'%(wave))
1052            formula = ''
1053            for el in PDFControls['ElList']:
1054                formula += el
1055                num = PDFControls['ElList'][el]['FormulaNo']
1056                if num == round(num):
1057                    formula += '%d'%(int(num))
1058                else:
1059                    formula += '%.2f'%(num)
1060            grfile.write('composition = %s\n'%(formula))
1061            grfile.write('bgscale = %.3f\n'%(-PDFControls['Sample Bkg.']['Mult']))
1062            highQ = 2.*np.pi/G2lat.Pos2dsp(Inst,Limits[1][1])
1063            grfile.write('qmaxinst = %.2f\n'%(highQ))
1064            grfile.write('qmin = %.5f\n'%(qdata[0]))
1065            grfile.write('qmax = %.4f\n'%(qdata[-1]))
1066            grfile.write('rmin = %.2f\n'%(PDFControls['Rmin']))
1067            grfile.write('rmax = %.2f\n'%(PDFControls['Rmax']))
1068            grfile.write('rstep = 0.01\n')
1069           
1070           
1071            grfile.write('\n')
1072            grfile.write('# End of config '+63*'-')
1073            grfile.write('\n')
1074            grfile.write('#### start data\n')
1075            grfile.write('#S 1\n')
1076            grfile.write('#L r($\AA$)  G($\AA^{-2}$)\n')           
1077            for r,gr in grnew:
1078                grfile.write("%15.2F %15.6F\n" % (r,gr))
1079            grfile.close()
1080            print (' G(R) saved to: '+grfilename)
1081   
1082def PeakListSave(G2frame,file,peaks):
1083    'Save powder peaks to a data file'
1084    print ('save peak list to file: '+G2frame.peaklistfile)
1085    if not peaks:
1086        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1087        try:
1088            dlg.ShowModal()
1089        finally:
1090            dlg.Destroy()
1091        return
1092    for peak in peaks:
1093        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
1094            (peak[0],peak[2],peak[4],peak[6]))
1095    print ('peak list saved')
1096             
1097def IndexPeakListSave(G2frame,peaks):
1098    'Save powder peaks from the indexing list'
1099    file = open(G2frame.peaklistfile,'wa')
1100    print ('save index peak list to file: '+G2frame.peaklistfile)
1101    wx.BeginBusyCursor()
1102    try:
1103        if not peaks:
1104            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1105            try:
1106                dlg.ShowModal()
1107            finally:
1108                dlg.Destroy()
1109            return
1110        for peak in peaks:
1111            file.write("%12.6f\n" % (peak[7]))
1112        file.close()
1113    finally:
1114        wx.EndBusyCursor()
1115    print ('index peak list saved')
1116   
1117class MultipleChoicesDialog(wx.Dialog):
1118    '''A dialog that offers a series of choices, each with a
1119    title and a wx.Choice widget. Intended to be used Modally.
1120    typical input:
1121
1122        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1123        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1124       
1125    selections are placed in self.chosen when OK is pressed
1126
1127    Also see GSASIIctrlGUI
1128    '''
1129    def __init__(self,choicelist,headinglist,
1130                 head='Select options',
1131                 title='Please select from options below',
1132                 parent=None):
1133        self.chosen = []
1134        wx.Dialog.__init__(
1135            self,parent,wx.ID_ANY,head, 
1136            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1137        panel = wx.Panel(self)
1138        mainSizer = wx.BoxSizer(wx.VERTICAL)
1139        mainSizer.Add((10,10),1)
1140        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1141        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1142        self.ChItems = []
1143        for choice,lbl in zip(choicelist,headinglist):
1144            mainSizer.Add((10,10),1)
1145            self.chosen.append(0)
1146            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1147            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1148            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1149            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1150
1151        OkBtn = wx.Button(panel,-1,"Ok")
1152        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1153        cancelBtn = wx.Button(panel,-1,"Cancel")
1154        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1155        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1156        btnSizer.Add((20,20),1)
1157        btnSizer.Add(OkBtn)
1158        btnSizer.Add((20,20),1)
1159        btnSizer.Add(cancelBtn)
1160        btnSizer.Add((20,20),1)
1161        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1162        panel.SetSizer(mainSizer)
1163        panel.Fit()
1164        self.Fit()
1165       
1166    def OnOk(self,event):
1167        parent = self.GetParent()
1168        if parent is not None: parent.Raise()
1169        # save the results from the choice widgets
1170        self.chosen = []
1171        for w in self.ChItems:
1172            self.chosen.append(w.GetSelection())
1173        self.EndModal(wx.ID_OK)             
1174           
1175    def OnCancel(self,event):
1176        parent = self.GetParent()
1177        if parent is not None: parent.Raise()
1178        self.chosen = []
1179        self.EndModal(wx.ID_CANCEL)             
1180           
1181def ExtractFileFromZip(filename, selection=None, confirmread=True,
1182                       confirmoverwrite=True, parent=None,
1183                       multipleselect=False):
1184    '''If the filename is a zip file, extract a file from that
1185    archive.
1186
1187    :param list Selection: used to predefine the name of the file
1188      to be extracted. Filename case and zip directory name are
1189      ignored in selection; the first matching file is used.
1190
1191    :param bool confirmread: if True asks the user to confirm before expanding
1192      the only file in a zip
1193
1194    :param bool confirmoverwrite: if True asks the user to confirm
1195      before overwriting if the extracted file already exists
1196
1197    :param bool multipleselect: if True allows more than one zip
1198      file to be extracted, a list of file(s) is returned.
1199      If only one file is present, do not ask which one, otherwise
1200      offer a list of choices (unless selection is used).
1201   
1202    :returns: the name of the file that has been created or a
1203      list of files (see multipleselect)
1204
1205    If the file is not a zipfile, return the name of the input file.
1206    If the zipfile is empty or no file has been selected, return None
1207    '''
1208    import zipfile # do this now, since we can save startup time by doing this only on need
1209    import shutil
1210    zloc = os.path.split(filename)[0]
1211    if not zipfile.is_zipfile(filename):
1212        #print("not zip")
1213        return filename
1214
1215    z = zipfile.ZipFile(filename,'r')
1216    zinfo = z.infolist()
1217
1218    if len(zinfo) == 0:
1219        #print('Zip has no files!')
1220        zlist = [-1]
1221    if selection:
1222        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1223        if selection.lower() in choices:
1224            zlist = [choices.index(selection.lower())]
1225        else:
1226            print('debug: file '+str(selection)+' was not found in '+str(filename))
1227            zlist = [-1]
1228    elif len(zinfo) == 1 and confirmread:
1229        result = wx.ID_NO
1230        dlg = wx.MessageDialog(
1231            parent,
1232            'Is file '+str(zinfo[0].filename)+
1233            ' what you want to extract from '+
1234            str(os.path.split(filename)[1])+'?',
1235            'Confirm file', 
1236            wx.YES_NO | wx.ICON_QUESTION)
1237        try:
1238            result = dlg.ShowModal()
1239        finally:
1240            dlg.Destroy()
1241        if result == wx.ID_NO:
1242            zlist = [-1]
1243        else:
1244            zlist = [0]
1245    elif len(zinfo) == 1:
1246        zlist = [0]
1247    elif multipleselect:
1248        # select one or more from a from list
1249        choices = [i.filename for i in zinfo]
1250        dlg = G2G.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1251            'Choose file(s)',choices)
1252        if dlg.ShowModal() == wx.ID_OK:
1253            zlist = dlg.GetSelections()
1254        else:
1255            zlist = []
1256        dlg.Destroy()
1257    else:
1258        # select one from a from list
1259        choices = [i.filename for i in zinfo]
1260        dlg = wx.SingleChoiceDialog(parent,
1261            'Select file to extract from zip file'+str(filename),'Choose file',
1262            choices,)
1263        if dlg.ShowModal() == wx.ID_OK:
1264            zlist = [dlg.GetSelection()]
1265        else:
1266            zlist = [-1]
1267        dlg.Destroy()
1268       
1269    outlist = []
1270    for zindex in zlist:
1271        if zindex >= 0:
1272            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1273            if os.path.exists(efil) and confirmoverwrite:
1274                result = wx.ID_NO
1275                dlg = wx.MessageDialog(parent,
1276                    'File '+str(efil)+' already exists. OK to overwrite it?',
1277                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1278                try:
1279                    result = dlg.ShowModal()
1280                finally:
1281                    dlg.Destroy()
1282                if result == wx.ID_NO:
1283                    zindex = -1
1284        if zindex >= 0:
1285            # extract the file to the current directory, regardless of it's original path
1286            #z.extract(zinfo[zindex],zloc)
1287            eloc,efil = os.path.split(zinfo[zindex].filename)
1288            outfile = os.path.join(zloc, efil)
1289            fpin = z.open(zinfo[zindex])
1290            fpout = open(outfile, "wb")
1291            shutil.copyfileobj(fpin, fpout)
1292            fpin.close()
1293            fpout.close()
1294            outlist.append(outfile)
1295    z.close()
1296    if multipleselect and len(outlist) >= 1:
1297        return outlist
1298    elif len(outlist) == 1:
1299        return outlist[0]
1300    else:
1301        return None
1302
1303######################################################################
1304# base classes for reading various types of data files
1305#   not used directly, only by subclassing
1306######################################################################
1307def BlockSelector(ChoiceList, ParentFrame=None,title='Select a block',
1308    size=None, header='Block Selector',useCancel=True):
1309    ''' Provide a wx dialog to select a block if the file contains more
1310    than one set of data and one must be selected
1311    '''
1312    if useCancel:
1313        dlg = wx.SingleChoiceDialog(
1314            ParentFrame,title, header,ChoiceList)
1315    else:
1316        dlg = wx.SingleChoiceDialog(
1317            ParentFrame,title, header,ChoiceList,
1318            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1319    if size: dlg.SetSize(size)
1320    dlg.CenterOnParent()
1321    if dlg.ShowModal() == wx.ID_OK:
1322        sel = dlg.GetSelection()
1323        return sel
1324    else:
1325        return None
1326    dlg.Destroy()
1327
1328def MultipleBlockSelector(ChoiceList, ParentFrame=None,
1329    title='Select a block',size=None, header='Block Selector'):
1330    '''Provide a wx dialog to select a block of data if the
1331    file contains more than one set of data and one must be
1332    selected.
1333
1334    :returns: a list of the selected blocks
1335    '''
1336    dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1337        wx.CHOICEDLG_STYLE)
1338    dlg.CenterOnScreen()
1339    if size: dlg.SetSize(size)
1340    if dlg.ShowModal() == wx.ID_OK:
1341        sel = dlg.GetSelections()
1342    else:
1343        return []
1344    dlg.Destroy()
1345    selected = []
1346    if len(ChoiceList) in sel:
1347        return range(len(ChoiceList))
1348    else:
1349        return sel
1350    return selected
1351
1352def MultipleChoicesSelector(choicelist, headinglist, ParentFrame=None, **kwargs):
1353    '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1354    widget. Typical input:
1355   
1356       * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1357       
1358       * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1359       
1360    optional keyword parameters are: head (window title) and title
1361    returns a list of selected indicies for each choice (or None)
1362    '''
1363    result = None
1364    dlg = MultipleChoicesDialog(choicelist,headinglist,
1365        parent=ParentFrame, **kwargs)         
1366    dlg.CenterOnParent()
1367    if dlg.ShowModal() == wx.ID_OK:
1368        result = dlg.chosen
1369    dlg.Destroy()
1370    return result
1371
1372def PhaseSelector(ChoiceList, ParentFrame=None,
1373    title='Select a phase', size=None,header='Phase Selector'):
1374    ''' Provide a wx dialog to select a phase if the file contains more
1375    than one phase
1376    '''
1377    return BlockSelector(ChoiceList,ParentFrame,title,
1378        size,header)
1379
1380######################################################################
1381def striphist(var,insChar=''):
1382    'strip a histogram number from a var name'
1383    sv = var.split(':')
1384    if len(sv) <= 1: return var
1385    if sv[1]:
1386        sv[1] = insChar
1387    return ':'.join(sv)
1388class ExportBaseclass(object):
1389    '''Defines a base class for the exporting of GSAS-II results.
1390
1391    This class is subclassed in the various exports/G2export_*.py files. Those files
1392    are imported in :meth:`GSASIIdataGUI.GSASII._init_Exports` which defines the
1393    appropriate menu items for each one and the .Exporter method is called
1394    directly from the menu item.
1395
1396    Routines may also define a .Writer method, which is used to write a single
1397    file without invoking any GUI objects.
1398    '''
1399    # TODO: review exporters producing exceptions where .Writer can't be used where G2frame is None (see CIF)
1400    # TODO: review conflicting uses of .Writer with mode (SeqRef) & elsewhere
1401    # TODO: move this class to G2fil
1402    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1403        self.G2frame = G2frame
1404        self.formatName = formatName # short string naming file type
1405        self.extension = extension
1406        if longFormatName: # longer string naming file type
1407            self.longFormatName = longFormatName
1408        else:
1409            self.longFormatName = formatName
1410        self.OverallParms = {}
1411        self.Phases = {}
1412        self.Histograms = {}
1413        self.powderDict = {}
1414        self.sasdDict = {}
1415        self.xtalDict = {}
1416        self.parmDict = {}
1417        self.sigDict = {}
1418        # updated in InitExport:
1419        self.currentExportType = None # type of export that has been requested
1420        # updated in ExportSelect (when used):
1421        self.phasenam = None # a list of selected phases
1422        self.histnam = None # a list of selected histograms
1423        self.filename = None # name of file to be written (single export) or template (multiple files)
1424        self.dirname = '' # name of directory where file(s) will be written
1425        self.fullpath = '' # name of file being written -- full path
1426       
1427        # items that should be defined in a subclass of this class
1428        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1429        # The following types are defined: 'project', "phase", "powder", "single"
1430        self.multiple = False # set as True if the class can export multiple phases or histograms
1431        # self.multiple is ignored for "project" exports
1432
1433    def InitExport(self,event):
1434        '''Determines the type of menu that called the Exporter and
1435        misc initialization.
1436        '''
1437        self.filename = None # name of file to be written (single export)
1438        self.dirname = '' # name of file to be written (multiple export)
1439        if event:
1440            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1441
1442    def MakePWDRfilename(self,hist):
1443        '''Make a filename root (no extension) from a PWDR histogram name
1444
1445        :param str hist: the histogram name in data tree (starts with "PWDR ")
1446        '''
1447        file0 = ''
1448        file1 = hist[5:]
1449        # replace repeated blanks
1450        while file1 != file0:
1451            file0 = file1
1452            file1 = file0.replace('  ',' ').strip()
1453        file0 = file1.replace('Azm= ','A')
1454        # if angle has unneeded decimal places on aziumuth, remove them
1455        if file0[-3:] == '.00': file0 = file0[:-3]
1456        file0 = file0.replace('.','_')
1457        file0 = file0.replace(' ','_')
1458        return file0
1459
1460    def ExportSelect(self,AskFile='ask'):
1461        '''Selects histograms or phases when needed. Sets a default file name when
1462        requested into self.filename; always sets a default directory in self.dirname.
1463
1464        :param bool AskFile: Determines how this routine processes getting a
1465          location to store the current export(s).
1466         
1467          * if AskFile is 'ask' (default option), get the name of the file to be written;
1468            self.filename and self.dirname are always set. In the case where
1469            multiple files must be generated, the export routine should do this
1470            based on self.filename as a template.
1471          * if AskFile is 'dir', get the name of the directory to be used;
1472            self.filename is not used, but self.dirname is always set. The export routine
1473            will always generate the file name.
1474          * if AskFile is 'single', get only the name of the directory to be used when
1475            multiple items will be written (as multiple files) are used
1476            *or* a complete file name is requested when a single file
1477            name is selected. self.dirname is always set and self.filename used
1478            only when a single file is selected. 
1479          * if AskFile is 'default', creates a name of the file to be used from
1480            the name of the project (.gpx) file. If the project has not been saved,
1481            then the name of file is requested.
1482            self.filename and self.dirname are always set. In the case where
1483            multiple file names must be generated, the export routine should do this
1484            based on self.filename.
1485          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1486            file. If the project has not been saved, then a directory is requested.
1487            self.filename is not used.
1488
1489        :returns: True in case of an error
1490        '''
1491       
1492        numselected = 1
1493        if self.currentExportType == 'phase':
1494            if len(self.Phases) == 0:
1495                self.G2frame.ErrorDialog(
1496                    'Empty project',
1497                    'Project does not contain any phases.')
1498                return True
1499            elif len(self.Phases) == 1:
1500                self.phasenam = list(self.Phases.keys())
1501            elif self.multiple: 
1502                choices = sorted(self.Phases.keys())
1503                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1504                if phasenum is None: return True
1505                self.phasenam = [choices[i] for i in phasenum]
1506                if not self.phasenam: return True
1507                numselected = len(self.phasenam)
1508            else:
1509                choices = sorted(self.Phases.keys())
1510                phasenum = G2G.ItemSelector(choices,self.G2frame)
1511                if phasenum is None: return True
1512                self.phasenam = [choices[phasenum]]
1513                numselected = len(self.phasenam)
1514        elif self.currentExportType == 'single':
1515            if len(self.xtalDict) == 0:
1516                self.G2frame.ErrorDialog(
1517                    'Empty project',
1518                    'Project does not contain any single crystal data.')
1519                return True
1520            elif len(self.xtalDict) == 1:
1521                self.histnam = self.xtalDict.values()
1522            elif self.multiple:
1523                choices = sorted(self.xtalDict.values())
1524                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1525                if not hnum: return True
1526                self.histnam = [choices[i] for i in hnum]
1527                numselected = len(self.histnam)
1528            else:
1529                choices = sorted(self.xtalDict.values())
1530                hnum = G2G.ItemSelector(choices,self.G2frame)
1531                if hnum is None: return True
1532                self.histnam = [choices[hnum]]
1533                numselected = len(self.histnam)
1534        elif self.currentExportType == 'powder':
1535            if len(self.powderDict) == 0:
1536                self.G2frame.ErrorDialog(
1537                    'Empty project',
1538                    'Project does not contain any powder data.')
1539                return True
1540            elif len(self.powderDict) == 1:
1541                self.histnam = self.powderDict.values()
1542            elif self.multiple:
1543                choices = sorted(self.powderDict.values())
1544                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1545                if not hnum: return True
1546                self.histnam = [choices[i] for i in hnum]
1547                numselected = len(self.histnam)
1548            else:
1549                choices = sorted(self.powderDict.values())
1550                hnum = G2G.ItemSelector(choices,self.G2frame)
1551                if hnum is None: return True
1552                self.histnam = [choices[hnum]]
1553                numselected = len(self.histnam)
1554        elif self.currentExportType == 'sasd':
1555            if len(self.sasdDict) == 0:
1556                self.G2frame.ErrorDialog(
1557                    'Empty project',
1558                    'Project does not contain any small angle data.')
1559                return True
1560            elif len(self.sasdDict) == 1:
1561                self.histnam = self.sasdDict.values()
1562            elif self.multiple:
1563                choices = sorted(self.sasdDict.values())
1564                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1565                if not hnum: return True
1566                self.histnam = [choices[i] for i in hnum]
1567                numselected = len(self.histnam)
1568            else:
1569                choices = sorted(self.sasdDict.values())
1570                hnum = G2G.ItemSelector(choices,self.G2frame)
1571                if hnum is None: return True
1572                self.histnam = [choices[hnum]]
1573                numselected = len(self.histnam)
1574        elif self.currentExportType == 'image':
1575            if len(self.Histograms) == 0:
1576                self.G2frame.ErrorDialog(
1577                    'Empty project',
1578                    'Project does not contain any images.')
1579                return True
1580            elif len(self.Histograms) == 1:
1581                self.histnam = list(self.Histograms.keys())
1582            else:
1583                choices = sorted(self.Histograms.keys())
1584                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1585                if self.multiple:
1586                    if not hnum: return True
1587                    self.histnam = [choices[i] for i in hnum]
1588                else:
1589                    if hnum is None: return True
1590                    self.histnam = [choices[hnum]]
1591                numselected = len(self.histnam)
1592        if self.currentExportType == 'map':
1593            # search for phases with maps
1594            mapPhases = []
1595            choices = []
1596            for phasenam in sorted(self.Phases):
1597                phasedict = self.Phases[phasenam] # pointer to current phase info           
1598                if len(phasedict['General']['Map'].get('rho',[])):
1599                    mapPhases.append(phasenam)
1600                    if phasedict['General']['Map'].get('Flip'):
1601                        choices.append('Charge flip map: '+str(phasenam))
1602                    elif phasedict['General']['Map'].get('MapType'):
1603                        choices.append(
1604                            str(phasedict['General']['Map'].get('MapType'))
1605                            + ' map: ' + str(phasenam))
1606                    else:
1607                        choices.append('unknown map: '+str(phasenam))
1608            # select a map if needed
1609            if len(mapPhases) == 0:
1610                self.G2frame.ErrorDialog(
1611                    'Empty project',
1612                    'Project does not contain any maps.')
1613                return True
1614            elif len(mapPhases) == 1:
1615                self.phasenam = mapPhases
1616            else: 
1617                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1618                if self.multiple:
1619                    if not phasenum: return True
1620                    self.phasenam = [mapPhases[i] for i in phasenum]
1621                else:
1622                    if phasenum is None: return True
1623                    self.phasenam = [mapPhases[phasenum]]
1624            numselected = len(self.phasenam)
1625
1626        # items selected, now set self.dirname and usually self.filename
1627        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
1628            AskFile == 'default' and not self.G2frame.GSASprojectfile
1629            ):
1630            filename = self.askSaveFile()
1631            if not filename: return True
1632            self.dirname,self.filename = os.path.split(filename)
1633        elif AskFile == 'dir' or AskFile == 'single' or (
1634            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
1635            ):
1636            self.dirname = self.askSaveDirectory()
1637            if not self.dirname: return True
1638        elif AskFile == 'default-dir' or AskFile == 'default':
1639            self.dirname,self.filename = os.path.split(
1640                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
1641                )
1642        else:
1643            raise Exception('This should not happen!')
1644
1645    def loadParmDict(self):
1646        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
1647        refined values to those from the last cycle and set the uncertainties for the
1648        refined parameters in another dict (self.sigDict).
1649
1650        Expands the parm & sig dicts to include values derived from constraints.
1651
1652        This could be made faster for sequential fits by reducing the histogram list to only
1653        the active histogram being exported.
1654        '''
1655        self.parmDict = {}
1656        self.sigDict = {}
1657        rigidbodyDict = {}
1658        covDict = {}
1659        consDict = {}
1660        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1661        if self.G2frame.GPXtree.IsEmpty(): return # nothing to do
1662        item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1663        while item:
1664            name = self.G2frame.GPXtree.GetItemText(item)
1665            if name == 'Rigid bodies':
1666                 rigidbodyDict = self.G2frame.GPXtree.GetItemPyData(item)
1667            elif name == 'Covariance':
1668                 covDict = self.G2frame.GPXtree.GetItemPyData(item)
1669            elif name == 'Constraints':
1670                 consDict = self.G2frame.GPXtree.GetItemPyData(item)
1671            item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1672        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1673        self.parmDict.update(rbDict)
1674        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1675        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave =  G2stIO.GetPhaseData(
1676            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
1677        self.parmDict.update(phaseDict)
1678        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
1679            Phases,Histograms,Print=False,resetRefList=False)
1680        self.parmDict.update(hapDict)
1681        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
1682        self.parmDict.update(histDict)
1683        self.parmDict.update(zip(
1684            covDict.get('varyList',[]),
1685            covDict.get('variables',[])))
1686        self.sigDict = dict(zip(
1687            covDict.get('varyList',[]),
1688            covDict.get('sig',[])))
1689        # expand to include constraints: first compile a list of constraints
1690        constList = []
1691        for item in consDict:
1692            if item.startswith('_'): continue
1693            constList += consDict[item]
1694        # now process the constraints
1695        G2mv.InitVars()
1696        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
1697        varyList = covDict.get('varyListStart')
1698        if varyList is None and len(constDict) == 0:
1699            # no constraints can use varyList
1700            varyList = covDict.get('varyList')
1701        elif varyList is None:
1702            # old GPX file from before pre-constraint varyList is saved
1703            print (' *** Old refinement: Please use Calculate/Refine to redo  ***')
1704            raise Exception(' *** Export aborted ***')
1705        else:
1706            varyList = list(varyList)
1707        # add symmetry-generated constraints
1708        rigidbodyDict = self.G2frame.GPXtree.GetItemPyData(   
1709            G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Rigid bodies'))
1710        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1711        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1712        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData(
1713            Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
1714        msg = G2mv.EvaluateMultipliers(constDict,phaseDict)
1715        if msg:
1716            print('Unable to interpret multiplier(s): '+msg)
1717            raise Exception(' *** CIF creation aborted ***')
1718        try:
1719            G2mv.GenerateConstraints(varyList,constDict,fixedList,self.parmDict)
1720            #print(G2mv.VarRemapShow(varyList))
1721        except:
1722            # this really should not happen
1723            print (' *** ERROR - constraints are internally inconsistent ***')
1724            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
1725            print ('Errors'+errmsg)
1726            if warnmsg: print ('Warnings'+warnmsg)
1727            raise Exception(' *** CIF creation aborted ***')
1728        # add the constrained values to the parameter dictionary
1729        G2mv.Dict2Map(self.parmDict,varyList)
1730        # and add their uncertainties into the esd dictionary (sigDict)
1731        if covDict.get('covMatrix') is not None:
1732            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
1733
1734    def loadTree(self):
1735        '''Load the contents of the data tree into a set of dicts
1736        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
1737        & self.xtalDict)
1738       
1739        * The childrenless data tree items are overall parameters/controls for the
1740          entire project and are placed in self.OverallParms
1741        * Phase items are placed in self.Phases
1742        * Data items are placed in self.Histogram. The key for these data items
1743          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
1744        '''
1745        self.OverallParms = {}
1746        self.powderDict = {}
1747        self.sasdDict = {}
1748        self.xtalDict = {}
1749        self.Phases = {}
1750        self.Histograms = {}
1751        self.SeqRefdata = None
1752        self.SeqRefhist = None
1753        if self.G2frame.GPXtree.IsEmpty(): return # nothing to do
1754        histType = None       
1755        if self.currentExportType == 'phase':
1756            # if exporting phases load them here
1757            sub = G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Phases')
1758            if not sub:
1759                print ('no phases found')
1760                return True
1761            item, cookie = self.G2frame.GPXtree.GetFirstChild(sub)
1762            while item:
1763                phaseName = self.G2frame.GPXtree.GetItemText(item)
1764                self.Phases[phaseName] =  self.G2frame.GPXtree.GetItemPyData(item)
1765                item, cookie = self.G2frame.GPXtree.GetNextChild(sub, cookie)
1766            return
1767        elif self.currentExportType == 'single':
1768            histType = 'HKLF'
1769        elif self.currentExportType == 'powder':
1770            histType = 'PWDR'
1771        elif self.currentExportType == 'image':
1772            histType = 'IMG'
1773        elif self.currentExportType == 'sasd':
1774            histType = 'SASD'
1775
1776        if histType: # Loading just one kind of tree entry
1777            item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1778            while item:
1779                name = self.G2frame.GPXtree.GetItemText(item)
1780                if name.startswith(histType):
1781                    if self.Histograms.get(name): # there is already an item with this name
1782                        print('Histogram name '+str(name)+' is repeated. Renaming')
1783                        if name[-1] == '9':
1784                            name = name[:-1] + '10'
1785                        elif name[-1] in '012345678':
1786                            name = name[:-1] + str(int(name[-1])+1)
1787                        else:                           
1788                            name += '-1'
1789                    self.Histograms[name] = {}
1790                    # the main info goes into Data, but the 0th
1791                    # element contains refinement results, carry
1792                    # that over too now.
1793                    self.Histograms[name]['Data'] = self.G2frame.GPXtree.GetItemPyData(item)[1]
1794                    self.Histograms[name][0] = self.G2frame.GPXtree.GetItemPyData(item)[0]
1795                    item2, cookie2 = self.G2frame.GPXtree.GetFirstChild(item)
1796                    while item2: 
1797                        child = self.G2frame.GPXtree.GetItemText(item2)
1798                        self.Histograms[name][child] = self.G2frame.GPXtree.GetItemPyData(item2)
1799                        item2, cookie2 = self.G2frame.GPXtree.GetNextChild(item, cookie2)
1800                item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1801            # index powder and single crystal histograms by number
1802            for hist in self.Histograms:
1803                if hist.startswith("PWDR"): 
1804                    d = self.powderDict
1805                elif hist.startswith("HKLF"): 
1806                    d = self.xtalDict
1807                elif hist.startswith("SASD"):
1808                    d = self.sasdDict
1809                else:
1810                    return                   
1811                i = self.Histograms[hist].get('hId')
1812                if i is None and not d.keys():
1813                    i = 0
1814                elif i is None or i in d.keys():
1815                    i = max(d.keys())+1
1816                d[i] = hist
1817            return
1818        # else standard load: using all interlinked phases and histograms
1819        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1820        item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1821        while item:
1822            name = self.G2frame.GPXtree.GetItemText(item)
1823            item2, cookie2 = self.G2frame.GPXtree.GetFirstChild(item)
1824            if not item2: 
1825                self.OverallParms[name] = self.G2frame.GPXtree.GetItemPyData(item)
1826            item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1827        # index powder and single crystal histograms
1828        for hist in self.Histograms:
1829            i = self.Histograms[hist]['hId']
1830            if hist.startswith("PWDR"): 
1831                self.powderDict[i] = hist
1832            elif hist.startswith("HKLF"): 
1833                self.xtalDict[i] = hist
1834            elif hist.startswith("SASD"):
1835                self.sasdDict[i] = hist
1836
1837    def dumpTree(self,mode='type'):
1838        '''Print out information on the data tree dicts loaded in loadTree.
1839        Used for testing only.
1840        '''
1841        if self.SeqRefdata and self.SeqRefhist:
1842            print('Note that dumpTree does not show sequential results')
1843        print ('\nOverall')
1844        if mode == 'type':
1845            def Show(arg): return type(arg)
1846        else:
1847            def Show(arg): return arg
1848        for key in self.OverallParms:
1849            print ('  '+key+Show(self.OverallParms[key]))
1850        print ('Phases')
1851        for key1 in self.Phases:
1852            print ('    '+key1+Show(self.Phases[key1]))
1853        print ('Histogram')
1854        for key1 in self.Histograms:
1855            print ('    '+key1+Show(self.Histograms[key1]))
1856            for key2 in self.Histograms[key1]:
1857                print ('      '+key2+Show(self.Histograms[key1][key2]))
1858
1859    def defaultSaveFile(self):
1860        return os.path.abspath(
1861            os.path.splitext(self.G2frame.GSASprojectfile
1862                             )[0]+self.extension)
1863       
1864    def askSaveFile(self):
1865        '''Ask the user to supply a file name
1866
1867        :returns: a file name (str) or None if Cancel is pressed
1868
1869        '''
1870        pth = G2G.GetExportPath(self.G2frame)
1871        if self.G2frame.GSASprojectfile:
1872            defnam = os.path.splitext(
1873                os.path.split(self.G2frame.GSASprojectfile)[1]
1874                )[0]+self.extension
1875        else:
1876            defnam = 'default' + self.extension
1877        return G2G.askSaveFile(self.G2frame,defnam,self.extension,self.longFormatName)
1878
1879    def askSaveDirectory(self):
1880        '''Ask the user to supply a directory name. Path name is used as the
1881        starting point for the next export path search.
1882
1883        :returns: a directory name (str) or None if Cancel is pressed
1884
1885        TODO: Can this be replaced with G2G.askSaveDirectory?
1886        '''
1887        pth = G2G.GetExportPath(self.G2frame)
1888        dlg = wx.DirDialog(
1889            self.G2frame, 'Input directory where file(s) will be written', pth,
1890            wx.DD_DEFAULT_STYLE)
1891        dlg.CenterOnParent()
1892        try:
1893            if dlg.ShowModal() == wx.ID_OK:
1894                filename = dlg.GetPath()
1895                self.G2frame.LastExportDir = filename
1896            else:
1897                filename = None
1898        finally:
1899            dlg.Destroy()
1900        return filename
1901
1902    # Tools for file writing.
1903    def OpenFile(self,fil=None,mode='w'):
1904        '''Open the output file
1905
1906        :param str fil: The name of the file to open. If None (default)
1907          the name defaults to self.dirname + self.filename.
1908          If an extension is supplied, it is not overridded,
1909          but if not, the default extension is used.
1910        :returns: the file object opened by the routine which is also
1911          saved as self.fp
1912        '''
1913        if mode == 'd': # debug mode
1914            self.fullpath = '(stdout)'
1915            self.fp = sys.stdout
1916            return
1917        if not fil:
1918            if not os.path.splitext(self.filename)[1]:
1919                self.filename += self.extension
1920            fil = os.path.join(self.dirname,self.filename)
1921        self.fullpath = os.path.abspath(fil)
1922        self.fp = open(self.fullpath,mode)
1923        return self.fp
1924
1925    def Write(self,line):
1926        '''write a line of output, attaching a line-end character
1927
1928        :param str line: the text to be written.
1929        '''
1930        self.fp.write(line+'\n')
1931       
1932    def CloseFile(self,fp=None):
1933        '''Close a file opened in OpenFile
1934
1935        :param file fp: the file object to be closed. If None (default)
1936          file object self.fp is closed.
1937        '''
1938        if self.fp == sys.stdout: return # debug mode
1939        if fp is None:
1940            fp = self.fp
1941            self.fp = None
1942        if fp is not None: fp.close()
1943       
1944    def SetSeqRef(self,data,hist):
1945        '''Set the exporter to retrieve results from a sequential refinement
1946        rather than the main tree
1947        '''
1948        self.SeqRefdata = data
1949        self.SeqRefhist = hist
1950        data_name = data[hist]
1951        for i,val in zip(data_name['varyList'],data_name['sig']):
1952            self.sigDict[i] = val
1953            self.sigDict[striphist(i)] = val
1954        for i in data_name['parmDict']:
1955            self.parmDict[striphist(i)] = data_name['parmDict'][i]
1956            self.parmDict[i] = data_name['parmDict'][i]
1957            # zero out the dA[xyz] terms, they would only bring confusion
1958            key = i.split(':')
1959            if len(key) < 3: continue
1960            if key[2].startswith('dA'):
1961                self.parmDict[i] = 0.0
1962        for i,(val,sig) in data_name.get('depParmDict',{}).items():
1963            self.parmDict[i] = val
1964            self.sigDict[i] = sig
1965        #GSASIIpath.IPyBreak()
1966
1967    def SetFromArray(self,hist,histname):
1968        '''Load a histogram into the exporter in preparation for use of the .Writer
1969        rather than the main tree. This is used in GSASIIscriptable when wx
1970        is not present.
1971        '''
1972        self.Histograms[histname] =  {}
1973        self.Histograms[histname]['Data'] = hist['data'][1]
1974        self.Histograms[histname]['Instrument Parameters'] = hist['Instrument Parameters']
1975        self.Histograms[histname]['Sample Parameters'] = hist['Sample Parameters']
1976
1977    # Tools to pull information out of the data arrays
1978    def GetCell(self,phasenam):
1979        """Gets the unit cell parameters and their s.u.'s for a selected phase
1980
1981        :param str phasenam: the name for the selected phase
1982        :returns: `cellList,cellSig` where each is a 7 element list corresponding
1983          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
1984          cell values and `cellSig` has their uncertainties.
1985        """
1986        if self.SeqRefdata and self.SeqRefhist:
1987            return self.GetSeqCell(phasenam,self.SeqRefdata[self.SeqRefhist])
1988        phasedict = self.Phases[phasenam] # pointer to current phase info
1989        try:
1990            pfx = str(phasedict['pId'])+'::'
1991            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
1992            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
1993                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
1994            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
1995            return cellList,cellSig
1996        except KeyError:
1997            cell = phasedict['General']['Cell'][1:]
1998            return cell,7*[0]
1999           
2000    def GetSeqCell(self,phasenam,data_name):
2001        """Gets the unit cell parameters and their s.u.'s for a selected phase
2002        and histogram in a sequential fit
2003
2004        :param str phasenam: the name for the selected phase
2005        :param dict data_name: the sequential refinement parameters for the selected histogram
2006        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2007          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2008          cell values and `cellSig` has their uncertainties.
2009        """
2010        phasedict = self.Phases[phasenam]
2011        SGdata = phasedict['General']['SGData']
2012        pId = phasedict['pId']
2013        RecpCellTerms = G2lat.cell2A(phasedict['General']['Cell'][1:7])
2014        ESDlookup = {}
2015        Dlookup = {}
2016        varied = [striphist(i) for i in data_name['varyList']]
2017        for item,val in data_name['newCellDict'].items():
2018            if item in varied:
2019                ESDlookup[val[0]] = item
2020                Dlookup[item] = val[0]
2021        A = RecpCellTerms[:]
2022        for i in range(6):
2023            var = str(pId)+'::A'+str(i)
2024            if var in ESDlookup:
2025                A[i] = data_name['newCellDict'][ESDlookup[var]][1] # override with refined value
2026        cellDict = dict(zip([str(pId)+'::A'+str(i) for i in range(6)],A))
2027        zeroDict = {i:0.0 for i in cellDict}
2028        A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata,cellDict,zeroDict)
2029        covData = {
2030            'varyList': [Dlookup.get(striphist(v),v) for v in data_name['varyList']],
2031            'covMatrix': data_name['covMatrix']
2032            }
2033        return list(G2lat.A2cell(A)) + [G2lat.calc_V(A)], G2stIO.getCellEsd(str(pId)+'::',SGdata,A,covData)
2034               
2035    def GetAtoms(self,phasenam):
2036        """Gets the atoms associated with a phase. Can be used with standard
2037        or macromolecular phases
2038
2039        :param str phasenam: the name for the selected phase
2040        :returns: a list of items for eac atom where each item is a list containing:
2041          label, typ, mult, xyz, and td, where
2042
2043          * label and typ are the atom label and the scattering factor type (str)
2044          * mult is the site multiplicity (int)
2045          * xyz is contains a list with four pairs of numbers:
2046            x, y, z and fractional occupancy and
2047            their standard uncertainty (or a negative value)
2048          * td is contains a list with either one or six pairs of numbers:
2049            if one number it is U\ :sub:`iso` and with six numbers it is
2050            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2051            paired with their standard uncertainty (or a negative value)
2052        """
2053        phasedict = self.Phases[phasenam] # pointer to current phase info           
2054        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2055        cfrac = cx+3
2056        fpfx = str(phasedict['pId'])+'::Afrac:'       
2057        atomslist = []
2058        for i,at in enumerate(phasedict['Atoms']):
2059            if phasedict['General']['Type'] == 'macromolecular':
2060                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2061            else:
2062                label = at[ct-1]
2063            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2064            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2065            mult = at[cs+1]
2066            typ = at[ct]
2067            xyz = []
2068            for j,v in enumerate(('x','y','z')):
2069                val = at[cx+j]
2070                pfx = str(phasedict['pId']) + '::A' + v + ':' + str(i)
2071                val = self.parmDict.get(pfx, val)
2072                dpfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2073                sig = self.sigDict.get(dpfx,-0.000009)
2074                xyz.append((val,sig))
2075            xyz.append((fval,fsig))
2076            td = []
2077            if at[cia] == 'I':
2078                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2079                val = self.parmDict.get(pfx,at[cia+1])
2080                sig = self.sigDict.get(pfx,-0.0009)
2081                td.append((val,sig))
2082            else:
2083                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2084                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2085                    val = self.parmDict.get(pfx,at[cia+2+i])
2086                    sig = self.sigDict.get(pfx,-0.0009)
2087                    td.append((val,sig))
2088            atomslist.append((label,typ,mult,xyz,td))
2089        return atomslist
2090######################################################################
2091def ExportPowderList(G2frame):
2092    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
2093    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
2094   
2095    :param wx.Frame G2frame: the GSAS-II main data tree window
2096    '''
2097    extList = []
2098    for obj in G2frame.exporterlist:
2099        if 'powder' in obj.exporttype:
2100            try:
2101                obj.Writer
2102                extList.append(obj.extension)
2103            except AttributeError:
2104                pass
2105    return extList
2106
2107def ExportPowder(G2frame,TreeName,fileroot,extension):
2108    '''Writes a single powder histogram using the Export routines.
2109    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
2110
2111    :param wx.Frame G2frame: the GSAS-II main data tree window
2112    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
2113    :param str fileroot: name for file to be written, extension ignored
2114    :param str extension: extension for file to be written (start with '.'). Must
2115      match a powder export routine that has a Writer object.
2116    '''
2117    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2118    for obj in G2frame.exporterlist:
2119        if obj.extension == extension and 'powder' in obj.exporttype:
2120            obj.currentExportType = 'powder'
2121            obj.InitExport(None)
2122            obj.loadTree() # load all histograms in tree into dicts
2123            if TreeName not in obj.Histograms:
2124                raise Exception('Histogram not found: '+str(TreeName))
2125            try:
2126                obj.Writer
2127            except AttributeError:
2128                continue
2129            try:
2130                obj.Writer(TreeName,filename)
2131                print('wrote file '+filename)
2132                return
2133            except Exception:
2134                print('Export Routine for '+extension+' failed.')
2135    else:
2136        print('No Export routine supports extension '+extension)
2137
2138def ExportSequential(G2frame,data,obj,exporttype):
2139    '''
2140    Used to export from every phase/dataset in a sequential refinement using
2141    a .Writer method for either projects or phases. Prompts to select histograms
2142    and for phase exports, which phase(s).
2143
2144    :param wx.Frame G2frame: the GSAS-II main data tree window
2145    :param dict data: the sequential refinement data object
2146    :param str exporttype: indicates the type of export ('project' or 'phase')
2147    '''
2148    if len(data['histNames']) == 0:
2149        G2G.G2MessageBox(G2frame,'There are no sequential histograms','Warning')
2150    obj.InitExport(None)
2151    obj.loadTree()
2152    obj.loadParmDict()
2153    if len(data['histNames']) == 1:
2154        histlist = data['histNames']
2155    else:
2156        dlg = G2G.G2MultiChoiceDialog(G2frame,'Select histograms to export from list',
2157                                 'Select histograms',data['histNames'])
2158        if dlg.ShowModal() == wx.ID_OK:
2159            histlist = [data['histNames'][l] for l in dlg.GetSelections()]
2160            dlg.Destroy()
2161        else:
2162            dlg.Destroy()
2163            return
2164    if exporttype == 'Phase':
2165        phaselist = list(obj.Phases.keys())
2166        if len(obj.Phases) == 0:
2167            G2G.G2MessageBox(G2frame,'There are no phases in sequential ref.','Warning')
2168            return
2169        elif len(obj.Phases) > 1:
2170            dlg = G2G.G2MultiChoiceDialog(G2frame,'Select phases to export from list',
2171                                    'Select phases', phaselist)
2172            if dlg.ShowModal() == wx.ID_OK:
2173                phaselist = [phaselist[l] for l in dlg.GetSelections()]
2174                dlg.Destroy()
2175            else:
2176                dlg.Destroy()
2177                return
2178        filename = obj.askSaveFile()
2179        if not filename: return True
2180        obj.dirname,obj.filename = os.path.split(filename)
2181        print('Writing output to file '+str(obj.filename)+"...")
2182        mode = 'w'
2183        for p in phaselist:
2184            for h in histlist:
2185                obj.SetSeqRef(data,h)
2186                #GSASIIpath.IPyBreak()
2187                obj.Writer(h,phasenam=p,mode=mode)
2188                mode = 'a'
2189        print('...done')
2190    elif exporttype == 'Project':  # note that the CIF exporter is not yet ready for this
2191        filename = obj.askSaveFile()
2192        if not filename: return True
2193        obj.dirname,obj.filename = os.path.split(filename)
2194        print('Writing output to file '+str(obj.filename)+"...")
2195        mode = 'w'
2196        for h in histlist:
2197            obj.SetSeqRef(data,h)
2198            obj.Writer(h,mode=mode)
2199            print('\t'+str(h)+' written')
2200            mode = 'a'
2201        print('...done')
2202    elif exporttype == 'Powder':
2203        filename = obj.askSaveFile()
2204        if not filename: return True
2205        obj.dirname,obj.filename = os.path.split(filename)
2206        print('Writing output to file '+str(obj.filename)+"...")
2207        mode = 'w'
2208        for h in histlist:
2209            obj.SetSeqRef(data,h)
2210            obj.Writer(h,mode=mode)
2211            print('\t'+str(h)+' written')
2212            mode = 'a'
2213        print('...done')
2214
2215def ReadDIFFaX(DIFFaXfile):
2216    print ('read '+DIFFaXfile)
2217    Layer = {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],'Width':[[10.,10.],[False,False]],
2218        'Layers':[],'Stacking':[],'Transitions':[],'Toler':0.01,'AtInfo':{}}
2219    df = open(DIFFaXfile,'r')
2220    lines = df.readlines()
2221    df.close()
2222    struct = False
2223    Struct = []
2224    stack = False
2225    Stack = []
2226    trans = False
2227    Trans = []
2228    for diff in lines:
2229        diff = diff[:-1].lower()
2230        if '!'  in diff:
2231            continue
2232        while '}' in diff: #strip comments
2233            iB = diff.index('{')
2234            iF = diff.index('}')+1
2235            if iB:
2236                diff = diff[:iB]
2237            else:
2238                diff = diff[iF:]
2239        if not diff:
2240            continue
2241        if diff.strip() == 'instrumental':
2242            continue
2243        if diff.strip() == 'structural':
2244            struct = True
2245            continue
2246        elif diff.strip() == 'stacking':
2247            struct = False
2248            stack = True
2249            continue
2250        elif diff.strip() == 'transitions':
2251            stack = False
2252            trans = True
2253            continue
2254        diff = diff.strip()
2255        if struct:
2256            if diff:
2257                Struct.append(diff)
2258        elif stack:
2259            if diff:
2260                Stack.append(diff)
2261        elif trans:
2262            if diff:
2263                Trans.append(diff)
2264   
2265#STRUCTURE records
2266    laueRec = Struct[1].split()
2267    Layer['Laue'] = laueRec[0]
2268    if Layer['Laue'] == 'unknown' and len(laueRec) > 1:
2269        Layer['Toler'] = float(laueRec[1])    #tolerance for 'unknown'?
2270    if Layer['Laue'] == '2/m(1)': Layer['Laue'] = '2/m(c)'
2271    if Layer['Laue'] == '2/m(2)': Layer['Laue'] = '2/m(ab)'
2272    cell = Struct[0].split()
2273    Layer['Cell'] = [False,float(cell[0]),float(cell[1]),float(cell[2]),90.,90.,float(cell[3]),1.0]
2274    nLayers = int(Struct[2])
2275    N = 3
2276    if 'layer' not in Struct[3]:
2277        N = 4
2278        if Struct[3] != 'infinite':
2279            width = Struct[3].split()
2280            Layer['Width'][0] = [float(width[0]),float(width[1])]
2281    for nL in range(nLayers):
2282        if '=' in Struct[N]:
2283            name = Struct[N].split('=')
2284            sameas = int(name[1])-1
2285            Layer['Layers'].append({'Name':name[0],'SameAs':Layer['Layers'][sameas]['Name'],'Symm':'None','Atoms':[]})
2286            N += 1
2287            continue
2288        Symm = 'None'
2289        if 'centro' in Struct[N+1]: Symm = '-1'
2290        Layer['Layers'].append({'Name':Struct[N],'SameAs':'','Symm':Symm,'Atoms':[]})
2291        N += 2
2292        while 'layer' not in Struct[N]:
2293            atom = Struct[N][4:].split()
2294            atomType = G2el.FixValence(Struct[N][:4].replace(' ','').strip().capitalize())
2295            if atomType not in Layer['AtInfo']:
2296                Layer['AtInfo'][atomType] = G2el.GetAtomInfo(atomType)
2297            atomName = '%s(%s)'%(atomType,atom[0])
2298            newVals = []
2299            for val in atom[1:6]:
2300                if '/' in val:
2301                    newVals.append(eval(val+'.'))
2302                else:
2303                    newVals.append(float(val))               
2304            atomRec = [atomName,atomType,newVals[0],newVals[1],newVals[2],newVals[4],newVals[3]/78.9568]
2305            Layer['Layers'][-1]['Atoms'].append(atomRec)
2306            N += 1
2307            if N > len(Struct)-1:
2308                break
2309#TRANSITIONS records
2310    transArray = []
2311    N = 0
2312    for i in range(nLayers):
2313        transArray.append([])
2314        for j in range(nLayers):
2315            vals = Trans[N].split()
2316            newVals = []
2317            for val in vals[:4]:
2318                if '/' in val:
2319                    newVals.append(eval(val+'.'))
2320                else:
2321                    newVals.append(float(val))
2322            transArray[-1].append(newVals+['',False])
2323            N += 1
2324    Layer['Transitions'] = transArray
2325#STACKING records
2326    Layer['Stacking'] = [Stack[0],'']
2327    if Stack[0] == 'recursive':
2328        Layer['Stacking'][1] = Stack[1]
2329    elif Stack[0] == 'explicit':
2330        if Stack[1] == 'random':
2331            Layer['Stacking'][1] = Stack[1]
2332        else:
2333            Layer['Stacking'][1] = 'list'
2334            Layer['Stacking'].append('')
2335            for stack in Stack[2:]:
2336                Layer['Stacking'][2] += ' '+stack
2337    return Layer
2338
2339if __name__ == '__main__':
2340    import GSASIIdataGUI
2341    application = GSASIIdataGUI.GSASIImain(0)
2342    G2frame = application.main
2343    #app = wx.PySimpleApp()
2344    #G2frame = wx.Frame(None) # create a frame
2345    #frm.Show(True)
2346    #filename = '/tmp/notzip.zip'
2347    #filename = '/tmp/all.zip'
2348    #filename = '/tmp/11bmb_7652.zip'
2349   
2350    #selection=None, confirmoverwrite=True, parent=None
2351    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2352    #print ExtractFileFromZip(filename,multipleselect=True)
2353    #                         #confirmread=False, confirmoverwrite=False)
2354
2355    # choicelist=[ ('a','b','c'),
2356    #              ('test1','test2'),('no choice',)]
2357    # titles = [ 'a, b or c', 'tests', 'No option here']
2358    # dlg = MultipleChoicesDialog(
2359    #     choicelist,titles,
2360    #     parent=frm)
2361    # if dlg.ShowModal() == wx.ID_OK:
2362    #     print 'Got OK'
2363    imagefile = '/tmp/NDC5_00237_3.ge3'
2364    Comments, Data, Npix, Image = GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None)
2365
2366    print("\n\nResults loaded to Comments, Data, Npix and Image\n\n")
2367
2368    GSASIIpath.IPyBreak_base()
Note: See TracBrowser for help on using the repository browser.