source: trunk/GSASIIIO.py @ 4317

Last change on this file since 4317 was 4317, checked in by vondreele, 20 months ago

make esd multiplier for spot mask search a float
add timing to it

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