source: trunk/GSASIIIO.py @ 4084

Last change on this file since 4084 was 4032, checked in by vondreele, 6 years ago

fix 'Resolution' in old gpx to 'GridStep?'
fix to bad logical operator in MakeFrameMask?
apply polarization pixel by pixel in integration; then put back on for azm=0 for powder diffraction
remove a wasted busycursor
add Xlines & Ylines to masks to mask whole row/columns of pixels (not quite complete)
make picktype = None for unknown picks ('?' causes crash)

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