source: trunk/GSASIIIO.py @ 4361

Last change on this file since 4361 was 4361, checked in by toby, 3 years ago

fix hist CIF export, remove dev. ver of IntPDFtool

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