source: trunk/GSASIIIO.py

Last change on this file was 5309, checked in by vondreele, 3 months ago

Add EFtable to list of returned items from G2strIO.GetPhaseData? every where - I had missed a few adding SEC data type
A little progress on Cluster Analysis

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