source: trunk/GSASIIIO.py

Last change on this file was 5663, checked in by vondreele, 2 days ago

Implement 1st draft of atom deformation refinement - functions from Phil Coppens. Functions apparently correct; derivatives need work. GUI interface done, constraints available for all deformation parameters.
New ORBtables available for orbital form factor coefficients
Add factor sizer to bond (only) control - was only for bond & angle control
new routine GetHistogramTypes? - from tree in G2dataGUI

Add optional comment line to G2ScrolledGrid - shows below table
Move Parameter Impact menu item to be under Compute partials

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