source: trunk/GSASIIIO.py @ 3246

Last change on this file since 3246 was 3246, checked in by toby, 5 years ago

cif export: use sig for x,x,x positions etc; column order for seq table atom positions; misc Py3 fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 114.5 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2018-01-29 02:56:19 +0000 (Mon, 29 Jan 2018) $
4# $Author: toby $
5# $Revision: 3246 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 3246 2018-01-29 02:56:19Z 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 copy
34import platform
35if '2' in platform.python_version_tuple()[0]:
36    import cPickle
37else:
38    import _pickle as cPickle
39import sys
40import re
41import glob
42import random as ran
43import GSASIIpath
44GSASIIpath.SetVersionNumber("$Revision: 3246 $")
45try:
46    import GSASIIdataGUI as G2gd
47except ImportError:
48    pass
49import GSASIIobj as G2obj
50import GSASIIlattice as G2lat
51import GSASIImath as G2mth
52try:
53    import GSASIIpwdGUI as G2pdG
54    import GSASIIimgGUI as G2imG
55except ImportError:
56    pass
57import GSASIIimage as G2img
58import GSASIIElem as G2el
59import GSASIIstrIO as G2stIO
60import GSASIImapvars as G2mv
61try:
62    import GSASIIctrlGUI as G2G
63except ImportError:
64    pass
65import os
66import os.path as ospath
67
68DEBUG = False       #=True for various prints
69TRANSP = False      #=true to transpose images for testing
70if GSASIIpath.GetConfigValue('Transpose'): TRANSP = True
71npsind = lambda x: np.sin(x*np.pi/180.)
72
73def sfloat(S):
74    'Convert a string to float. An empty field or a unconvertable value is treated as zero'
75    if S.strip():
76        try:
77            return float(S)
78        except ValueError:
79            pass
80    return 0.0
81
82def sint(S):
83    'Convert a string to int. An empty field is treated as zero'
84    if S.strip():
85        return int(S)
86    else:
87        return 0
88
89def trim(val):
90    '''Simplify a string containing leading and trailing spaces
91    as well as newlines, tabs, repeated spaces etc. into a shorter and
92    more simple string, by replacing all ranges of whitespace
93    characters with a single space.
94
95    :param str val: the string to be simplified
96
97    :returns: the (usually) shortened version of the string
98    '''
99    return re.sub('\s+', ' ', val).strip()
100
101def FileDlgFixExt(dlg,file):
102    'this is needed to fix a problem in linux wx.FileDialog'
103    ext = dlg.GetWildcard().split('|')[2*dlg.GetFilterIndex()+1].strip('*')
104    if ext not in file:
105        file += ext
106    return file
107       
108def GetPowderPeaks(fileName):
109    'Read powder peaks from a file'
110    sind = lambda x: math.sin(x*math.pi/180.)
111    asind = lambda x: 180.*math.asin(x)/math.pi
112    wave = 1.54052
113    File = open(fileName,'Ur')
114    Comments = []
115    peaks = []
116    S = File.readline()
117    while S:
118        if S[:1] == '#':
119            Comments.append(S[:-1])
120        else:
121            item = S.split()
122            if len(item) == 1:
123                peaks.append([float(item[0]),1.0])
124            elif len(item) > 1:
125                peaks.append([float(item[0]),float(item[0])])
126        S = File.readline()
127    File.close()
128    if Comments:
129       print ('Comments on file:')
130       for Comment in Comments: 
131            print (Comment)
132            if 'wavelength' in Comment:
133                wave = float(Comment.split('=')[1])
134    Peaks = []
135    if peaks[0][0] > peaks[-1][0]:          # d-spacings - assume CuKa
136        for peak in peaks:
137            dsp = peak[0]
138            sth = wave/(2.0*dsp)
139            if sth < 1.0:
140                tth = 2.0*asind(sth)
141            else:
142                break
143            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
144    else:                                   #2-thetas - assume Cuka (for now)
145        for peak in peaks:
146            tth = peak[0]
147            dsp = wave/(2.0*sind(tth/2.0))
148            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
149    limits = [1000.,0.]
150    for peak in Peaks:
151        limits[0] = min(limits[0],peak[0])
152        limits[1] = max(limits[1],peak[0])
153    limits[0] = max(1.,(int(limits[0]-1.)/5)*5.)
154    limits[1] = min(170.,(int(limits[1]+1.)/5)*5.)
155    return Comments,Peaks,limits,wave
156
157def GetCheckImageFile(G2frame,treeId):
158    '''Try to locate an image file if the project and image have been moved
159    together. If the image file cannot be found, request the location from
160    the user.
161
162    :param wx.Frame G2frame: main GSAS-II Frame and data object
163    :param wx.Id treeId: Id for the main tree item for the image
164    :returns: Npix,imagefile,imagetag with (Npix) number of pixels,
165       imagefile, if it exists, or the name of a file that does exist or False if the user presses Cancel
166       and (imagetag) an optional image number
167
168    '''
169    Npix,Imagefile,imagetag = G2frame.GPXtree.GetImageLoc(treeId)
170    if isinstance(Imagefile,list):
171        imagefile,imagetag = Imagefile
172    else:
173        imagefile = Imagefile
174    if not os.path.exists(imagefile):
175        print ('Image file '+imagefile+' not found')
176        fil = imagefile.replace('\\','/') # windows?!
177        # see if we can find a file with same name or in a similarly named sub-dir
178        pth,fil = os.path.split(fil)
179        prevpth = None
180        while pth and pth != prevpth:
181            prevpth = pth
182            if os.path.exists(os.path.join(G2frame.dirname,fil)):
183                print ('found image file '+os.path.join(G2frame.dirname,fil))
184                imagefile = os.path.join(G2frame.dirname,fil)
185                G2frame.GPXtree.UpdateImageLoc(treeId,imagefile)
186                return Npix,imagefile,imagetag
187            pth,enddir = os.path.split(pth)
188            fil = os.path.join(enddir,fil)
189        # not found as a subdirectory, drop common parts of path for last saved & image file names
190        #    if image was .../A/B/C/imgs/ima.ge
191        #      & GPX was  .../A/B/C/refs/fil.gpx but is now .../NEW/TEST/TEST1
192        #    will look for .../NEW/TEST/TEST1/imgs/ima.ge, .../NEW/TEST/imgs/ima.ge, .../NEW/imgs/ima.ge and so on
193        Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls'))
194        gpxPath = Controls.get('LastSavedAs','').replace('\\','/').split('/') # blank in older .GPX files
195        imgPath = imagefile.replace('\\','/').split('/')
196        for p1,p2 in zip(gpxPath,imgPath):
197            if p1 == p2:
198                gpxPath.pop(0),imgPath.pop(0)
199            else:
200                break
201        fil = os.path.join(*imgPath) # file with non-common prefix elements
202        prevpth = None
203        pth = os.path.abspath(G2frame.dirname)
204        while pth and pth != prevpth:
205            prevpth = pth
206            if os.path.exists(os.path.join(pth,fil)):
207                print ('found image file '+os.path.join(pth,fil))
208                imagefile = os.path.join(pth,fil)
209                G2frame.GPXtree.UpdateImageLoc(treeId,imagefile)
210                return Npix,imagefile,imagetag
211            pth,enddir = os.path.split(pth)
212        #GSASIIpath.IPyBreak()
213
214    if not os.path.exists(imagefile):
215        # note that this fails (at least on Mac) to get an image during the GUI initialization
216        prevnam = os.path.split(imagefile)[1]
217        prevext = os.path.splitext(imagefile)[1]
218        wildcard = 'Image format (*'+prevext+')|*'+prevext
219        dlg = wx.FileDialog(G2frame, 'Previous image file ('+prevnam+') not found; open here', '.', prevnam,
220                            wildcard,wx.FD_OPEN)
221        try:
222            dlg.SetFilename(''+ospath.split(imagefile)[1])
223            if dlg.ShowModal() == wx.ID_OK:
224                imagefile = dlg.GetPath()
225                G2frame.GPXtree.UpdateImageLoc(treeId,imagefile)
226            else:
227                imagefile = False
228        finally:
229            dlg.Destroy()
230    return Npix,imagefile,imagetag
231
232def EditImageParms(parent,Data,Comments,Image,filename):
233    dlg = wx.Dialog(parent, wx.ID_ANY, 'Edit image parameters',
234                    style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
235    def onClose(event):
236        dlg.EndModal(wx.ID_OK)
237    mainsizer = wx.BoxSizer(wx.VERTICAL)
238    h,w = Image.shape[:2]
239    mainsizer.Add(wx.StaticText(dlg,wx.ID_ANY,'File '+str(filename)+'\nImage size: '+str(h)+' x '+str(w)),
240        0,wx.ALIGN_LEFT|wx.ALL, 2)
241   
242    vsizer = wx.BoxSizer(wx.HORIZONTAL)
243    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Wavelength (\xC5) '),
244        0,wx.ALIGN_LEFT|wx.ALL, 2)
245    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'wavelength')
246    vsizer.Add(wdgt)
247    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
248
249    vsizer = wx.BoxSizer(wx.HORIZONTAL)
250    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Pixel size (\xb5m). Width '),
251        0,wx.ALIGN_LEFT|wx.ALL, 2)
252    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],0,
253                                 size=(50,-1))
254    vsizer.Add(wdgt)
255    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Height '),wx.ALIGN_LEFT|wx.ALL, 2)
256    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],1,size=(50,-1))
257    vsizer.Add(wdgt)
258    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
259
260    vsizer = wx.BoxSizer(wx.HORIZONTAL)
261    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Sample to detector (mm) '),
262        0,wx.ALIGN_LEFT|wx.ALL, 2)
263    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'distance')
264    vsizer.Add(wdgt)
265    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
266
267    vsizer = wx.BoxSizer(wx.HORIZONTAL)
268    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Beam center (pixels). X = '),
269        0,wx.ALIGN_LEFT|wx.ALL, 2)
270    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],0,size=(75,-1))
271    vsizer.Add(wdgt)
272    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Y = '),wx.ALIGN_LEFT|wx.ALL, 2)
273    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],1,size=(75,-1))
274    vsizer.Add(wdgt)
275    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
276
277    vsizer = wx.BoxSizer(wx.HORIZONTAL)
278    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Comments '),
279        0,wx.ALIGN_LEFT|wx.ALL, 2)
280    wdgt = G2G.ValidatedTxtCtrl(dlg,Comments,0,size=(250,-1))
281    vsizer.Add(wdgt)
282    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
283
284    btnsizer = wx.StdDialogButtonSizer()
285    OKbtn = wx.Button(dlg, wx.ID_OK, 'Continue')
286    OKbtn.SetDefault()
287    OKbtn.Bind(wx.EVT_BUTTON,onClose)
288    btnsizer.AddButton(OKbtn) # not sure why this is needed
289    btnsizer.Realize()
290    mainsizer.Add(btnsizer, 1, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 5)
291    dlg.SetSizer(mainsizer)
292    dlg.CenterOnParent()
293    dlg.ShowModal()
294   
295def LoadImage2Tree(imagefile,G2frame,Comments,Data,Npix,Image):
296    '''Load an image into the tree. Saves the location of the image, as well as the
297    ImageTag (where there is more than one image in the file), if defined.
298    '''
299    ImgNames = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
300    TreeLbl = 'IMG '+os.path.basename(imagefile)
301    ImageTag = Data.get('ImageTag')
302    if ImageTag:
303        TreeLbl += ' #'+'%04d'%(ImageTag)
304        imageInfo = (imagefile,ImageTag)
305    else:
306        imageInfo = imagefile
307    TreeName = G2obj.MakeUniqueLabel(TreeLbl,ImgNames)
308    Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text=TreeName)
309    G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Comments'),Comments)
310    Imax = np.amax(Image)
311    if G2frame.imageDefault:
312        Data = copy.copy(G2frame.imageDefault)
313        Data['showLines'] = True
314        Data['ring'] = []
315        Data['rings'] = []
316        Data['cutoff'] = 10
317        Data['pixLimit'] = 20
318        Data['edgemin'] = 100000000
319        Data['calibdmin'] = 0.5
320        Data['calibskip'] = 0
321        Data['ellipses'] = []
322        Data['calibrant'] = ''
323        Data['GonioAngles'] = [0.,0.,0.]
324        Data['DetDepthRef'] = False
325    else:
326        Data['type'] = 'PWDR'
327        Data['color'] = GSASIIpath.GetConfigValue('Contour_color','Paired')
328        Data['tilt'] = 0.0
329        Data['rotation'] = 0.0
330        Data['showLines'] = False
331        Data['ring'] = []
332        Data['rings'] = []
333        Data['cutoff'] = 10
334        Data['pixLimit'] = 20
335        Data['calibdmin'] = 0.5
336        Data['calibskip'] = 0
337        Data['edgemin'] = 100000000
338        Data['ellipses'] = []
339        Data['GonioAngles'] = [0.,0.,0.]
340        Data['DetDepth'] = 0.
341        Data['DetDepthRef'] = False
342        Data['calibrant'] = ''
343        Data['IOtth'] = [2.0,5.0]
344        Data['LRazimuth'] = [0.,180.]
345        Data['azmthOff'] = 0.0
346        Data['outChannels'] = 2500
347        Data['outAzimuths'] = 1
348        Data['centerAzm'] = False
349        Data['fullIntegrate'] = False
350        Data['setRings'] = False
351        Data['background image'] = ['',-1.0]                           
352        Data['dark image'] = ['',-1.0]
353        Data['Flat Bkg'] = 0.0
354    Data['setDefault'] = False
355    Data['range'] = [(0,Imax),[0,Imax]]
356    G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Image Controls'),Data)
357    Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(0,Imax),[0,Imax]]}
358    G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Masks'),Masks)
359    G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Stress/Strain'),
360        {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
361    G2frame.GPXtree.SetItemPyData(Id,[Npix,imageInfo])
362    G2frame.PickId = Id
363    G2frame.PickIdText = G2frame.GetTreeItemsList(G2frame.PickId)
364    G2frame.Image = Id
365
366def GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None,FormatName=''):
367    '''Read a single image with an image importer. This is called to reread an image
368    after it has already been imported with :meth:`GSASIIdataGUI.GSASII.OnImportGeneric`
369    (or :func:`ReadImages` in Auto Integration) so it is not necessary to reload metadata.
370
371    :param wx.Frame G2frame: main GSAS-II Frame and data object.
372    :param str imagefile: name of image file
373    :param bool imageOnly: If True return only the image,
374      otherwise  (default) return more (see below)
375    :param int/str ImageTag: specifies a particular image to be read from a file.
376      First image is read if None (default).
377    :param str formatName: the image reader formatName
378
379    :returns: an image as a numpy array or a list of four items:
380      Comments, Data, Npix and the Image, as selected by imageOnly
381
382    '''
383    # determine which formats are compatible with this file
384    primaryReaders = []
385    secondaryReaders = []
386    for rd in G2frame.ImportImageReaderlist:
387        flag = rd.ExtensionValidator(imagefile)
388        if flag is None: 
389            secondaryReaders.append(rd)
390        elif flag:
391            if not FormatName:
392                primaryReaders.append(rd)
393            elif FormatName == rd.formatName:
394                primaryReaders.append(rd)
395    if len(secondaryReaders) + len(primaryReaders) == 0:
396        print('Error: No matching format for file '+imagefile)
397        raise Exception('No image read')
398    errorReport = ''
399    if not imagefile:
400        return
401    for rd in primaryReaders+secondaryReaders:
402        rd.ReInitialize() # purge anything from a previous read
403        rd.errors = "" # clear out any old errors
404        if not rd.ContentsValidator(imagefile): # rejected on cursory check
405            errorReport += "\n  "+rd.formatName + ' validator error'
406            if rd.errors: 
407                errorReport += ': '+rd.errors
408                continue
409        if imageOnly:
410            ParentFrame = None # prevent GUI access on reread
411        else:
412            ParentFrame = G2frame
413        if GSASIIpath.GetConfigValue('debug'):
414            flag = rd.Reader(imagefile,ParentFrame,blocknum=ImageTag)
415        else:
416            flag = False
417            try:
418                flag = rd.Reader(imagefile,ParentFrame,blocknum=ImageTag)
419            except rd.ImportException as detail:
420                rd.errors += "\n  Read exception: "+str(detail)
421            except Exception as detail:
422                import traceback
423                rd.errors += "\n  Unhandled read exception: "+str(detail)
424                rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
425        if flag: # this read succeeded
426            if rd.Image is None:
427                raise Exception('No image read. Strange!')
428            if GSASIIpath.GetConfigValue('Transpose'):
429                print ('Transposing Image!')
430                rd.Image = rd.Image.T
431            #rd.readfilename = imagefile
432            if imageOnly:
433                return rd.Image
434            else:
435                return rd.Comments,rd.Data,rd.Npix,rd.Image
436    else:
437        print('Error reading file '+imagefile)
438        print('Error messages(s)\n'+errorReport)
439        raise Exception('No image read')   
440
441def ReadImages(G2frame,imagefile):
442    '''Read one or more images from a file and put them into the Tree
443    using image importers. Called only in :meth:`AutoIntFrame.OnTimerLoop`.
444
445    ToDo: Images are most commonly read in :meth:`GSASIIdataGUI.GSASII.OnImportGeneric`
446    which is called from :meth:`GSASIIdataGUI.GSASII.OnImportImage`
447    it would be good if these routines used a common code core so that changes need to
448    be made in only one place.
449
450    :param wx.Frame G2frame: main GSAS-II Frame and data object.
451    :param str imagefile: name of image file
452
453    :returns: a list of the id's of the IMG tree items created
454    '''
455    # determine which formats are compatible with this file
456    primaryReaders = []
457    secondaryReaders = []
458    for rd in G2frame.ImportImageReaderlist:
459        flag = rd.ExtensionValidator(imagefile)
460        if flag is None:
461            secondaryReaders.append(rd)
462        elif flag:
463            primaryReaders.append(rd)
464    if len(secondaryReaders) + len(primaryReaders) == 0:
465        print('Error: No matching format for file '+imagefile)
466        raise Exception('No image read')
467    errorReport = ''
468    rdbuffer = {} # create temporary storage for file reader
469    for rd in primaryReaders+secondaryReaders:
470        rd.ReInitialize() # purge anything from a previous read
471        rd.errors = "" # clear out any old errors
472        if not rd.ContentsValidator(imagefile): # rejected on cursory check
473            errorReport += "\n  "+rd.formatName + ' validator error'
474            if rd.errors: 
475                errorReport += ': '+rd.errors
476                continue
477        ParentFrame = G2frame
478        block = 0
479        repeat = True
480        CreatedIMGitems = []
481        while repeat: # loop if the reader asks for another pass on the file
482            block += 1
483            repeat = False
484            if GSASIIpath.GetConfigValue('debug'):
485                flag = rd.Reader(imagefile,ParentFrame,blocknum=block,Buffer=rdbuffer)
486            else:
487                flag = False
488                try:
489                    flag = rd.Reader(imagefile,ParentFrame,blocknum=block,Buffer=rdbuffer)
490                except rd.ImportException as detail:
491                    rd.errors += "\n  Read exception: "+str(detail)
492                except Exception as detail:
493                    import traceback
494                    rd.errors += "\n  Unhandled read exception: "+str(detail)
495                    rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
496            if flag: # this read succeeded
497                if rd.Image is None:
498                    raise Exception('No image read. Strange!')
499                if GSASIIpath.GetConfigValue('Transpose'):
500                    print ('Transposing Image!')
501                    rd.Image = rd.Image.T
502                rd.Data['ImageTag'] = rd.repeatcount
503                rd.readfilename = imagefile
504                # Load generic metadata, as configured
505                GetColumnMetadata(rd)
506                LoadImage2Tree(imagefile,G2frame,rd.Comments,rd.Data,rd.Npix,rd.Image)
507                repeat = rd.repeat
508            CreatedIMGitems.append(G2frame.Image)
509        if CreatedIMGitems: return CreatedIMGitems
510    else:
511        print('Error reading file '+imagefile)
512        print('Error messages(s)\n'+errorReport)
513        return []
514        #raise Exception('No image read')   
515
516def SaveMultipleImg(G2frame):
517    if not G2frame.GPXtree.GetCount():
518        print ('no images!')
519        return
520    choices = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
521    if len(choices) == 1:
522        names = choices
523    else:
524        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices)
525        dlg.SetSelections([])
526        names = []
527        if dlg.ShowModal() == wx.ID_OK:
528            names = [choices[sel] for sel in dlg.GetSelections()]
529        dlg.Destroy()
530    if not names: return
531    for name in names:
532        Id = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, name)
533        Npix,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(Id)
534        imroot = os.path.splitext(imagefile)[0]
535        if imagetag:
536            imroot += '_' + str(imagetag)
537        Data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Image Controls'))
538        print('Writing '+imroot+'.imctrl')
539        File = open(imroot+'.imctrl','w')
540        keys = ['type','wavelength','calibrant','distance','center',
541                    'tilt','rotation','azmthOff','fullIntegrate','LRazimuth',
542                    'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
543                    'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg',
544                    'binType','SampleShape','PolaVal','SampleAbs','dark image','background image']
545        for key in keys:
546            if key not in Data: continue    #uncalibrated!
547            File.write(key+':'+str(Data[key])+'\n')
548        File.close()
549        mask = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Masks'))
550        G2imG.CleanupMasks(mask)
551        print('Writing '+imroot+'.immask')
552        File = open(imroot+'.immask','w')
553        for key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
554            File.write(key+':'+str(mask[key])+'\n')
555        File.close()
556       
557def PutG2Image(filename,Comments,Data,Npix,image):
558    'Write an image as a python pickle - might be better as an .edf file?'
559    File = open(filename,'wb')
560    cPickle.dump([Comments,Data,Npix,image],File,2)
561    File.close()
562    return
563   
564def ProjFileOpen(G2frame,showProvenance=True):
565    'Read a GSAS-II project file and load into the G2 data tree'
566    if not os.path.exists(G2frame.GSASprojectfile):
567        print ('\n*** Error attempt to open project file that does not exist:\n   '+
568               str(G2frame.GSASprojectfile))
569        return
570    LastSavedUsing = None
571    file = open(G2frame.GSASprojectfile,'rb')
572    if showProvenance: print ('loading from file: '+G2frame.GSASprojectfile)
573    #G2frame.SetTitle("GSAS-II data tree: "+
574    #                 os.path.split(G2frame.GSASprojectfile)[1])
575    G2frame.SetTitle("GSAS-II data tree: "+
576        os.path.split(G2frame.GSASprojectfile)[1],1)
577    wx.BeginBusyCursor()
578    try:
579        while True:
580            try:
581                if '2' in platform.python_version_tuple()[0]:
582                    data = cPickle.load(file)
583                else:
584                    data = cPickle.load(file,encoding='latin-1')
585            except EOFError:
586                break
587            datum = data[0]
588           
589            Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text=datum[0])
590            if datum[0].startswith('PWDR'):               
591                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
592                    datum[1][0]['ranId'] = ran.randint(0,sys.maxsize)
593                G2frame.GPXtree.SetItemPyData(Id,datum[1][:3])  #temp. trim off junk (patch?)
594            elif datum[0].startswith('HKLF'): 
595                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
596                    datum[1][0]['ranId'] = ran.randint(0,sys.maxsize)
597                G2frame.GPXtree.SetItemPyData(Id,datum[1])
598            else:
599                G2frame.GPXtree.SetItemPyData(Id,datum[1])             
600                if datum[0] == 'Controls' and 'LastSavedUsing' in datum[1]:
601                    LastSavedUsing = datum[1]['LastSavedUsing']
602                if datum[0] == 'Controls' and 'PythonVersions' in datum[1] and GSASIIpath.GetConfigValue('debug') and showProvenance:
603                    print('Packages used to create .GPX file:')
604                    if 'dict' in str(type(datum[1]['PythonVersions'])):  #patch
605                        for p in sorted(datum[1]['PythonVersions'],key=lambda s: s.lower()):
606                            print({:<14s}: {:s}".format(p[0],p[1]))
607                    else:
608                        for p in datum[1]['PythonVersions']:
609                            print({:<12s} {:s}".format(p[0]+':',p[1]))
610            oldPDF = False
611            for datus in data[1:]:
612#patch - 1/23/17 PDF cleanup
613                if datus[0][:4] in ['I(Q)','S(Q)','F(Q)','G(R)']:
614                    oldPDF = True
615                    data[1][1][datus[0][:4]] = copy.deepcopy(datus[1][:2])
616                    continue
617#end PDF cleanup
618                sub = G2frame.GPXtree.AppendItem(Id,datus[0])
619#patch
620                if datus[0] == 'Instrument Parameters' and len(datus[1]) == 1:
621                    if datum[0].startswith('PWDR'):
622                        datus[1] = [dict(zip(datus[1][3],zip(datus[1][0],datus[1][1],datus[1][2]))),{}]
623                    else:
624                        datus[1] = [dict(zip(datus[1][2],zip(datus[1][0],datus[1][1]))),{}]
625                    for item in datus[1][0]:               #zip makes tuples - now make lists!
626                        datus[1][0][item] = list(datus[1][0][item])
627#end patch
628                G2frame.GPXtree.SetItemPyData(sub,datus[1])
629            if 'PDF ' in datum[0][:4] and oldPDF:
630                sub = G2frame.GPXtree.AppendItem(Id,'PDF Peaks')
631                G2frame.GPXtree.SetItemPyData(sub,{'Limits':[1.,5.],'Background':[2,[0.,-0.2*np.pi],False],'Peaks':[]})
632            if datum[0].startswith('IMG'):                   #retrieve image default flag & data if set
633                Data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
634                if Data['setDefault']:
635                    G2frame.imageDefault = Data               
636        file.close()
637        if LastSavedUsing:
638            print('GPX load successful. Last saved with GSAS-II revision '+LastSavedUsing)
639        else:
640            print('project load successful')
641        G2frame.NewPlot = True
642    except:
643        msg = wx.MessageDialog(G2frame,message="Error reading file "+
644            str(G2frame.GSASprojectfile)+". This is not a GSAS-II .gpx file",
645            caption="Load Error",style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
646        msg.ShowModal()
647    finally:
648        wx.EndBusyCursor()
649        G2frame.Status.SetStatusText('Mouse RB drag/drop to reorder',0)
650   
651def ProjFileSave(G2frame):
652    'Save a GSAS-II project file'
653    if not G2frame.GPXtree.IsEmpty():
654        file = open(G2frame.GSASprojectfile,'wb')
655        print ('save to file: '+G2frame.GSASprojectfile)
656        # stick the file name into the tree and version info into tree so they are saved.
657        # (Controls should always be created at this point)
658        try:
659            Controls = G2frame.GPXtree.GetItemPyData(
660                G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls'))
661            Controls['LastSavedAs'] = os.path.abspath(G2frame.GSASprojectfile)
662            Controls['LastSavedUsing'] = str(GSASIIpath.GetVersionNumber())
663            Controls['PythonVersions'] = G2frame.PackageVersions
664        except:
665            pass
666        wx.BeginBusyCursor()
667        try:
668            item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
669            while item:
670                data = []
671                name = G2frame.GPXtree.GetItemText(item)
672                data.append([name,G2frame.GPXtree.GetItemPyData(item)])
673                item2, cookie2 = G2frame.GPXtree.GetFirstChild(item)
674                while item2:
675                    name = G2frame.GPXtree.GetItemText(item2)
676                    data.append([name,G2frame.GPXtree.GetItemPyData(item2)])
677                    item2, cookie2 = G2frame.GPXtree.GetNextChild(item, cookie2)                           
678                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)                           
679                cPickle.dump(data,file,2)
680            file.close()
681            pth = os.path.split(os.path.abspath(G2frame.GSASprojectfile))[0]
682            if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
683            G2frame.LastGPXdir = pth
684        finally:
685            wx.EndBusyCursor()
686        print('project save successful')
687
688def SaveIntegration(G2frame,PickId,data,Overwrite=False):
689    'Save image integration results as powder pattern(s)'
690    azms = G2frame.Integrate[1]
691    X = G2frame.Integrate[2][:-1]
692    N = len(X)
693    Id = G2frame.GPXtree.GetItemParent(PickId)
694    name = G2frame.GPXtree.GetItemText(Id)
695    name = name.replace('IMG ',data['type']+' ')
696    Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Comments'))
697    Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls'))
698    if 'PWDR' in name:
699        names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth'] 
700        codes = [0 for i in range(12)]
701    elif 'SASD' in name:
702        names = ['Type','Lam','Zero','Azimuth'] 
703        codes = [0 for i in range(4)]
704        X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
705    Xminmax = [X[0],X[-1]]
706    Azms = []
707    dazm = 0.
708    if data['fullIntegrate'] and data['outAzimuths'] == 1:
709        Azms = [45.0,]                              #a poor man's average?
710    else:
711        for i,azm in enumerate(azms[:-1]):
712            if azm > 360. and azms[i+1] > 360.:
713                Azms.append(G2img.meanAzm(azm%360.,azms[i+1]%360.))
714            else:   
715                Azms.append(G2img.meanAzm(azm,azms[i+1]))
716        dazm = np.min(np.abs(np.diff(azms)))/2.
717    G2frame.IntgOutList = []
718    for i,azm in enumerate(azms[:-1]):
719        Aname = name+" Azm= %.2f"%((azm+dazm)%360.)
720        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
721        # if Overwrite delete any duplicate
722        if Overwrite and G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Aname):
723            print('Replacing '+Aname)
724            item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Aname)
725            G2frame.GPXtree.Delete(item)
726        else:
727            nOcc = 0
728            while item:
729                Name = G2frame.GPXtree.GetItemText(item)
730                if Aname in Name:
731                    nOcc += 1
732                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
733            if nOcc:
734                Aname += '(%d)'%(nOcc)
735        Sample = G2obj.SetDefaultSample()       #set as Debye-Scherrer
736        Sample['Gonio. radius'] = data['distance']
737        Sample['Omega'] = data['GonioAngles'][0]
738        Sample['Chi'] = data['GonioAngles'][1]
739        Sample['Phi'] = data['GonioAngles'][2]
740        Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
741        polariz = 0.99    #set default polarization for synchrotron radiation!
742        for item in Comments:
743            if 'polariz' in item:
744                try:
745                    polariz = float(item.split('=')[1])
746                except:
747                    polariz = 0.99
748            for key in ('Temperature','Pressure','Time','FreePrm1','FreePrm2','FreePrm3','Omega',
749                'Chi','Phi'):
750                if key.lower() in item.lower():
751                    try:
752                        Sample[key] = float(item.split('=')[1])
753                    except:
754                        pass
755            if 'label_prm' in item.lower():
756                for num in ('1','2','3'):
757                    if 'label_prm'+num in item.lower():
758                        Controls['FreePrm'+num] = item.split('=')[1].strip()
759        if 'PWDR' in Aname:
760            parms = ['PXC',data['wavelength'],0.0,polariz,1.0,-0.10,0.4,0.30,1.0,0.0,0.0001,Azms[i]]
761        elif 'SASD' in Aname:
762            Sample['Trans'] = data['SampleAbs'][0]
763            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
764        Y = G2frame.Integrate[0][i]
765        Ymin = np.min(Y)
766        Ymax = np.max(Y)
767        W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
768        Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text=Aname)
769        G2frame.IntgOutList.append(Id)
770        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Comments'),Comments)                   
771        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Limits'),copy.deepcopy([tuple(Xminmax),Xminmax]))
772        if 'PWDR' in Aname:
773            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
774                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
775        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
776        for item in inst[0]:
777            inst[0][item] = list(inst[0][item])
778        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Instrument Parameters'),inst)
779        if 'PWDR' in Aname:
780            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Sample Parameters'),Sample)
781            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Peak List'),{'sigDict':{},'peaks':[]})
782            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Index Peak List'),[[],[]])
783            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Unit Cells List'),[])
784            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Reflection Lists'),{})
785        elif 'SASD' in Aname:             
786            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
787            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Sample Parameters'),Sample)
788            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
789        valuesdict = {
790            'wtFactor':1.0,
791            'Dummy':False,
792            'ranId':ran.randint(0,sys.maxsize),
793            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-0.1*Ymax,'refDelt':0.1*Ymax,
794            'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
795            }
796        G2frame.GPXtree.SetItemPyData(
797            Id,[valuesdict,
798                [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
799    return Id       #last powder pattern generated
800   
801def XYsave(G2frame,XY,labelX='X',labelY='Y',names=None):
802    'Save XY table data'
803    pth = G2G.GetExportPath(G2frame)
804    dlg = wx.FileDialog(
805        G2frame, 'Enter csv filename for XY table', pth, '',
806        'XY table file (*.csv)|*.csv',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
807    try:
808        if dlg.ShowModal() == wx.ID_OK:
809            filename = dlg.GetPath()
810            filename = os.path.splitext(filename)[0]+'.csv'
811            File = open(filename,'w')
812        else:
813            filename = None
814    finally:
815        dlg.Destroy()
816    if not filename:
817        return
818    for i in range(len(XY)):
819        if names != None:
820            header = '%s,%s(%s)\n'%(labelX,labelY,names[i])
821        else:
822            header = '%s,%s(%d)\n'%(labelX,labelY,i)
823        File.write(header)
824        for x,y in XY[i].T:
825            File.write('%.3f,%.3f\n'%(x,y))   
826    File.close()
827    print (' XY data saved to: '+filename)
828           
829def askSaveDirectory(G2frame):
830    '''Ask the user to supply a directory name. Path name is used as the
831    starting point for the next export path search.
832
833    :returns: a directory name (str) or None if Cancel is pressed
834    '''
835    pth = G2G.GetExportPath(G2frame)
836    dlg = wx.DirDialog(
837            G2frame, 'Input directory where file(s) will be written', pth,
838            wx.DD_DEFAULT_STYLE)
839    dlg.CenterOnParent()
840    try:
841        if dlg.ShowModal() == wx.ID_OK:
842            filename = dlg.GetPath()
843            G2frame.LastExportDir = filename
844        else:
845            filename = None
846    finally:
847        dlg.Destroy()
848    return filename
849
850def askSaveFile(G2frame,defnam,extension,longFormatName):
851    '''Ask the user to supply a file name
852
853    :returns: a file name (str) or None if Cancel is pressed
854    '''
855
856    pth = G2G.GetExportPath(G2frame)
857    dlg = wx.FileDialog(
858        G2frame, 'Input name for file to write', pth, defnam,
859        longFormatName+' (*'+extension+')|*'+extension,
860        wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
861    dlg.CenterOnParent()
862    try:
863        if dlg.ShowModal() == wx.ID_OK:
864            filename = dlg.GetPath()
865            G2frame.LastExportDir = os.path.split(filename)[0]
866            filename = os.path.splitext(filename)[0]+extension # make sure extension is correct
867        else:
868            filename = None
869    finally:
870        dlg.Destroy()
871    return filename
872
873def PDFSave(G2frame,exports,PDFsaves):
874    'Save a PDF I(Q), S(Q), F(Q) and G(r)  in column formats'
875    import scipy.interpolate as scintp
876    if len(exports) > 1:
877        dirname = askSaveDirectory(G2frame)
878        if not dirname: return
879    else:
880        defnam = exports[0].replace(' ','_')[5:]
881        filename = askSaveFile(G2frame,defnam,'.gr','G(r) file, etc.')
882        if not filename: return
883        dirname,filename = os.path.split(filename)
884        filename = os.path.splitext(filename)[0]
885    for export in exports:
886        if len(exports) > 1:
887            filename = export.replace(' ','_')[5:]
888        PickId = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, export)
889        PDFControls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, PickId,'PDF Controls'))
890        if PDFsaves[0]:     #I(Q)
891            iqfilename = ospath.join(dirname,filename+'.iq')
892            iqdata = PDFControls['I(Q)'][0]
893            iqfxn = scintp.interp1d(iqdata[0],iqdata[1],kind='linear')
894            iqfile = open(iqfilename,'w')
895            iqfile.write('#T I(Q) %s\n'%(export))
896            iqfile.write('#L Q     I(Q)\n')
897            qnew = np.arange(iqdata[0][0],iqdata[0][-1],0.005)
898            iqnew = zip(qnew,iqfxn(qnew))
899            for q,iq in iqnew:
900                iqfile.write("%15.6g %15.6g\n" % (q,iq))
901            iqfile.close()
902            print (' I(Q) saved to: '+iqfilename)
903           
904        if PDFsaves[1]:     #S(Q)
905            sqfilename = ospath.join(dirname,filename+'.sq')
906            sqdata = PDFControls['S(Q)'][1]
907            sqfxn = scintp.interp1d(sqdata[0],sqdata[1],kind='linear')
908            sqfile = open(sqfilename,'w')
909            sqfile.write('#T S(Q) %s\n'%(export))
910            sqfile.write('#L Q     S(Q)\n')
911            qnew = np.arange(sqdata[0][0],sqdata[0][-1],0.005)
912            sqnew = zip(qnew,sqfxn(qnew))
913            for q,sq in sqnew:
914                sqfile.write("%15.6g %15.6g\n" % (q,sq))
915            sqfile.close()
916            print (' S(Q) saved to: '+sqfilename)
917           
918        if PDFsaves[2]:     #F(Q)
919            fqfilename = ospath.join(dirname,filename+'.fq')
920            fqdata = PDFControls['F(Q)'][1]
921            fqfxn = scintp.interp1d(fqdata[0],fqdata[1],kind='linear')
922            fqfile = open(fqfilename,'w')
923            fqfile.write('#T F(Q) %s\n'%(export))
924            fqfile.write('#L Q     F(Q)\n')
925            qnew = np.arange(fqdata[0][0],fqdata[0][-1],0.005)
926            fqnew = zip(qnew,fqfxn(qnew))
927            for q,fq in fqnew:
928                fqfile.write("%15.6g %15.6g\n" % (q,fq))
929            fqfile.close()
930            print (' F(Q) saved to: '+fqfilename)
931           
932        if PDFsaves[3]:     #G(R)
933            grfilename = ospath.join(dirname,filename+'.gr')
934            grdata = PDFControls['G(R)'][1]
935            grfxn = scintp.interp1d(grdata[0],grdata[1],kind='linear')
936            grfile = open(grfilename,'w')
937            grfile.write('#T G(R) %s\n'%(export))
938            grfile.write('#L R     G(R)\n')
939            rnew = np.arange(grdata[0][0],grdata[0][-1],0.010)
940            grnew = zip(rnew,grfxn(rnew))
941            for r,gr in grnew:
942                grfile.write("%15.6g %15.6g\n" % (r,gr))
943            grfile.close()
944            print (' G(R) saved to: '+grfilename)
945       
946        if PDFsaves[4]: #pdfGUI file for G(R)
947            pId = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, 'PWDR'+export[4:])
948            Inst = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, pId,'Instrument Parameters'))[0]
949            Limits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, pId,'Limits'))
950            grfilename = ospath.join(dirname,filename+'.gr')
951            grdata = PDFControls['G(R)'][1]
952            qdata = PDFControls['I(Q)'][1][0]
953            grfxn = scintp.interp1d(grdata[0],grdata[1],kind='linear')
954            grfile = open(grfilename,'w')
955            rnew = np.arange(grdata[0][0],grdata[0][-1],0.010)
956            grnew = zip(rnew,grfxn(rnew))
957
958            grfile.write('[DEFAULT]\n')
959            grfile.write('\n')
960            grfile.write('version = GSAS-II-v'+str(GSASIIpath.GetVersionNumber())+'\n')
961            grfile.write('\n')
962            grfile.write('# input and output specifications\n')
963            grfile.write('dataformat = Qnm\n')
964            grfile.write('inputfile = %s\n'%(PDFControls['Sample']['Name']))
965            grfile.write('backgroundfile = %s\n'%(PDFControls['Sample Bkg.']['Name']))
966            grfile.write('outputtype = gr\n')
967            grfile.write('\n')
968            grfile.write('# PDF calculation setup\n')
969            if 'x' in Inst['Type']:
970                grfile.write('mode = %s\n'%('xray'))
971            elif 'N' in Inst['Type']:
972                grfile.write('mode = %s\n'%('neutron'))
973            wave = G2mth.getMeanWave(Inst)
974            grfile.write('wavelength = %.5f\n'%(wave))
975            formula = ''
976            for el in PDFControls['ElList']:
977                formula += el
978                num = PDFControls['ElList'][el]['FormulaNo']
979                if num == round(num):
980                    formula += '%d'%(int(num))
981                else:
982                    formula += '%.2f'%(num)
983            grfile.write('composition = %s\n'%(formula))
984            grfile.write('bgscale = %.3f\n'%(-PDFControls['Sample Bkg.']['Mult']))
985            highQ = 2.*np.pi/G2lat.Pos2dsp(Inst,Limits[1][1])
986            grfile.write('qmaxinst = %.2f\n'%(highQ))
987            grfile.write('qmin = %.5f\n'%(qdata[0]))
988            grfile.write('qmax = %.4f\n'%(qdata[-1]))
989            grfile.write('rmin = %.2f\n'%(PDFControls['Rmin']))
990            grfile.write('rmax = %.2f\n'%(PDFControls['Rmax']))
991            grfile.write('rstep = 0.01\n')
992           
993           
994            grfile.write('\n')
995            grfile.write('# End of config '+63*'-')
996            grfile.write('\n')
997            grfile.write('#### start data\n')
998            grfile.write('#S 1\n')
999            grfile.write('#L r($\AA$)  G($\AA^{-2}$)\n')           
1000            for r,gr in grnew:
1001                grfile.write("%15.2F %15.6F\n" % (r,gr))
1002            grfile.close()
1003            print (' G(R) saved to: '+grfilename)
1004   
1005def PeakListSave(G2frame,file,peaks):
1006    'Save powder peaks to a data file'
1007    print ('save peak list to file: '+G2frame.peaklistfile)
1008    if not peaks:
1009        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1010        try:
1011            dlg.ShowModal()
1012        finally:
1013            dlg.Destroy()
1014        return
1015    for peak in peaks:
1016        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
1017            (peak[0],peak[2],peak[4],peak[6]))
1018    print ('peak list saved')
1019             
1020def IndexPeakListSave(G2frame,peaks):
1021    'Save powder peaks from the indexing list'
1022    file = open(G2frame.peaklistfile,'wa')
1023    print ('save index peak list to file: '+G2frame.peaklistfile)
1024    wx.BeginBusyCursor()
1025    try:
1026        if not peaks:
1027            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1028            try:
1029                dlg.ShowModal()
1030            finally:
1031                dlg.Destroy()
1032            return
1033        for peak in peaks:
1034            file.write("%12.6f\n" % (peak[7]))
1035        file.close()
1036    finally:
1037        wx.EndBusyCursor()
1038    print ('index peak list saved')
1039   
1040class MultipleChoicesDialog(wx.Dialog):
1041    '''A dialog that offers a series of choices, each with a
1042    title and a wx.Choice widget. Intended to be used Modally.
1043    typical input:
1044
1045        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1046        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1047       
1048    selections are placed in self.chosen when OK is pressed
1049
1050    Also see GSASIIctrlGUI
1051    '''
1052    def __init__(self,choicelist,headinglist,
1053                 head='Select options',
1054                 title='Please select from options below',
1055                 parent=None):
1056        self.chosen = []
1057        wx.Dialog.__init__(
1058            self,parent,wx.ID_ANY,head, 
1059            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1060        panel = wx.Panel(self)
1061        mainSizer = wx.BoxSizer(wx.VERTICAL)
1062        mainSizer.Add((10,10),1)
1063        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1064        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1065        self.ChItems = []
1066        for choice,lbl in zip(choicelist,headinglist):
1067            mainSizer.Add((10,10),1)
1068            self.chosen.append(0)
1069            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1070            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1071            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1072            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1073
1074        OkBtn = wx.Button(panel,-1,"Ok")
1075        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1076        cancelBtn = wx.Button(panel,-1,"Cancel")
1077        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1078        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1079        btnSizer.Add((20,20),1)
1080        btnSizer.Add(OkBtn)
1081        btnSizer.Add((20,20),1)
1082        btnSizer.Add(cancelBtn)
1083        btnSizer.Add((20,20),1)
1084        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1085        panel.SetSizer(mainSizer)
1086        panel.Fit()
1087        self.Fit()
1088       
1089    def OnOk(self,event):
1090        parent = self.GetParent()
1091        if parent is not None: parent.Raise()
1092        # save the results from the choice widgets
1093        self.chosen = []
1094        for w in self.ChItems:
1095            self.chosen.append(w.GetSelection())
1096        self.EndModal(wx.ID_OK)             
1097           
1098    def OnCancel(self,event):
1099        parent = self.GetParent()
1100        if parent is not None: parent.Raise()
1101        self.chosen = []
1102        self.EndModal(wx.ID_CANCEL)             
1103           
1104def ExtractFileFromZip(filename, selection=None, confirmread=True,
1105                       confirmoverwrite=True, parent=None,
1106                       multipleselect=False):
1107    '''If the filename is a zip file, extract a file from that
1108    archive.
1109
1110    :param list Selection: used to predefine the name of the file
1111      to be extracted. Filename case and zip directory name are
1112      ignored in selection; the first matching file is used.
1113
1114    :param bool confirmread: if True asks the user to confirm before expanding
1115      the only file in a zip
1116
1117    :param bool confirmoverwrite: if True asks the user to confirm
1118      before overwriting if the extracted file already exists
1119
1120    :param bool multipleselect: if True allows more than one zip
1121      file to be extracted, a list of file(s) is returned.
1122      If only one file is present, do not ask which one, otherwise
1123      offer a list of choices (unless selection is used).
1124   
1125    :returns: the name of the file that has been created or a
1126      list of files (see multipleselect)
1127
1128    If the file is not a zipfile, return the name of the input file.
1129    If the zipfile is empty or no file has been selected, return None
1130    '''
1131    import zipfile # do this now, since we can save startup time by doing this only on need
1132    import shutil
1133    zloc = os.path.split(filename)[0]
1134    if not zipfile.is_zipfile(filename):
1135        #print("not zip")
1136        return filename
1137
1138    z = zipfile.ZipFile(filename,'r')
1139    zinfo = z.infolist()
1140
1141    if len(zinfo) == 0:
1142        #print('Zip has no files!')
1143        zlist = [-1]
1144    if selection:
1145        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1146        if selection.lower() in choices:
1147            zlist = [choices.index(selection.lower())]
1148        else:
1149            print('debug: file '+str(selection)+' was not found in '+str(filename))
1150            zlist = [-1]
1151    elif len(zinfo) == 1 and confirmread:
1152        result = wx.ID_NO
1153        dlg = wx.MessageDialog(
1154            parent,
1155            'Is file '+str(zinfo[0].filename)+
1156            ' what you want to extract from '+
1157            str(os.path.split(filename)[1])+'?',
1158            'Confirm file', 
1159            wx.YES_NO | wx.ICON_QUESTION)
1160        try:
1161            result = dlg.ShowModal()
1162        finally:
1163            dlg.Destroy()
1164        if result == wx.ID_NO:
1165            zlist = [-1]
1166        else:
1167            zlist = [0]
1168    elif len(zinfo) == 1:
1169        zlist = [0]
1170    elif multipleselect:
1171        # select one or more from a from list
1172        choices = [i.filename for i in zinfo]
1173        dlg = G2G.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1174            'Choose file(s)',choices)
1175        if dlg.ShowModal() == wx.ID_OK:
1176            zlist = dlg.GetSelections()
1177        else:
1178            zlist = []
1179        dlg.Destroy()
1180    else:
1181        # select one from a from list
1182        choices = [i.filename for i in zinfo]
1183        dlg = wx.SingleChoiceDialog(parent,
1184            'Select file to extract from zip file'+str(filename),'Choose file',
1185            choices,)
1186        if dlg.ShowModal() == wx.ID_OK:
1187            zlist = [dlg.GetSelection()]
1188        else:
1189            zlist = [-1]
1190        dlg.Destroy()
1191       
1192    outlist = []
1193    for zindex in zlist:
1194        if zindex >= 0:
1195            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1196            if os.path.exists(efil) and confirmoverwrite:
1197                result = wx.ID_NO
1198                dlg = wx.MessageDialog(parent,
1199                    'File '+str(efil)+' already exists. OK to overwrite it?',
1200                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1201                try:
1202                    result = dlg.ShowModal()
1203                finally:
1204                    dlg.Destroy()
1205                if result == wx.ID_NO:
1206                    zindex = -1
1207        if zindex >= 0:
1208            # extract the file to the current directory, regardless of it's original path
1209            #z.extract(zinfo[zindex],zloc)
1210            eloc,efil = os.path.split(zinfo[zindex].filename)
1211            outfile = os.path.join(zloc, efil)
1212            fpin = z.open(zinfo[zindex])
1213            fpout = file(outfile, "wb")
1214            shutil.copyfileobj(fpin, fpout)
1215            fpin.close()
1216            fpout.close()
1217            outlist.append(outfile)
1218    z.close()
1219    if multipleselect and len(outlist) >= 1:
1220        return outlist
1221    elif len(outlist) == 1:
1222        return outlist[0]
1223    else:
1224        return None
1225
1226######################################################################
1227# base classes for reading various types of data files
1228#   not used directly, only by subclassing
1229######################################################################
1230def BlockSelector(ChoiceList, ParentFrame=None,title='Select a block',
1231    size=None, header='Block Selector',useCancel=True):
1232    ''' Provide a wx dialog to select a block if the file contains more
1233    than one set of data and one must be selected
1234    '''
1235    if useCancel:
1236        dlg = wx.SingleChoiceDialog(
1237            ParentFrame,title, header,ChoiceList)
1238    else:
1239        dlg = wx.SingleChoiceDialog(
1240            ParentFrame,title, header,ChoiceList,
1241            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1242    if size: dlg.SetSize(size)
1243    dlg.CenterOnParent()
1244    if dlg.ShowModal() == wx.ID_OK:
1245        sel = dlg.GetSelection()
1246        return sel
1247    else:
1248        return None
1249    dlg.Destroy()
1250
1251def MultipleBlockSelector(ChoiceList, ParentFrame=None,
1252    title='Select a block',size=None, header='Block Selector'):
1253    '''Provide a wx dialog to select a block of data if the
1254    file contains more than one set of data and one must be
1255    selected.
1256
1257    :returns: a list of the selected blocks
1258    '''
1259    dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1260        wx.CHOICEDLG_STYLE)
1261    dlg.CenterOnScreen()
1262    if size: dlg.SetSize(size)
1263    if dlg.ShowModal() == wx.ID_OK:
1264        sel = dlg.GetSelections()
1265    else:
1266        return []
1267    dlg.Destroy()
1268    selected = []
1269    if len(ChoiceList) in sel:
1270        return range(len(ChoiceList))
1271    else:
1272        return sel
1273    return selected
1274
1275def MultipleChoicesSelector(choicelist, headinglist, ParentFrame=None, **kwargs):
1276    '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1277    widget. Typical input:
1278   
1279       * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1280       
1281       * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1282       
1283    optional keyword parameters are: head (window title) and title
1284    returns a list of selected indicies for each choice (or None)
1285    '''
1286    result = None
1287    dlg = MultipleChoicesDialog(choicelist,headinglist,
1288        parent=ParentFrame, **kwargs)         
1289    dlg.CenterOnParent()
1290    if dlg.ShowModal() == wx.ID_OK:
1291        result = dlg.chosen
1292    dlg.Destroy()
1293    return result
1294
1295def PhaseSelector(ChoiceList, ParentFrame=None,
1296    title='Select a phase', size=None,header='Phase Selector'):
1297    ''' Provide a wx dialog to select a phase if the file contains more
1298    than one phase
1299    '''
1300    return BlockSelector(ChoiceList,ParentFrame,title,
1301        size,header)
1302
1303######################################################################
1304def striphist(var,insChar=''):
1305    'strip a histogram number from a var name'
1306    sv = var.split(':')
1307    if len(sv) <= 1: return var
1308    if sv[1]:
1309        sv[1] = insChar
1310    return ':'.join(sv)
1311class ExportBaseclass(object):
1312    '''Defines a base class for the exporting of GSAS-II results.
1313
1314    This class is subclassed in the various exports/G2export_*.py files. Those files
1315    are imported in :meth:`GSASIIdataGUI.GSASII._init_Exports` which defines the
1316    appropriate menu items for each one and the .Exporter method is called
1317    directly from the menu item.
1318
1319    Routines may also define a .Writer method, which is used to write a single
1320    file without invoking any GUI objects.
1321    '''
1322    # TODO: review exporters producing exceptions where .Writer can't be used where G2frame is None (see CIF)
1323    # TODO: review conflicting uses of .Writer with mode (SeqRef) & elsewhere
1324    # TODO: move this class to G2fil
1325    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1326        self.G2frame = G2frame
1327        self.formatName = formatName # short string naming file type
1328        self.extension = extension
1329        if longFormatName: # longer string naming file type
1330            self.longFormatName = longFormatName
1331        else:
1332            self.longFormatName = formatName
1333        self.OverallParms = {}
1334        self.Phases = {}
1335        self.Histograms = {}
1336        self.powderDict = {}
1337        self.xtalDict = {}
1338        self.parmDict = {}
1339        self.sigDict = {}
1340        # updated in InitExport:
1341        self.currentExportType = None # type of export that has been requested
1342        # updated in ExportSelect (when used):
1343        self.phasenam = None # a list of selected phases
1344        self.histnam = None # a list of selected histograms
1345        self.filename = None # name of file to be written (single export) or template (multiple files)
1346        self.dirname = '' # name of directory where file(s) will be written
1347        self.fullpath = '' # name of file being written -- full path
1348       
1349        # items that should be defined in a subclass of this class
1350        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1351        # The following types are defined: 'project', "phase", "powder", "single"
1352        self.multiple = False # set as True if the class can export multiple phases or histograms
1353        # self.multiple is ignored for "project" exports
1354
1355    def InitExport(self,event):
1356        '''Determines the type of menu that called the Exporter and
1357        misc initialization.
1358        '''
1359        self.filename = None # name of file to be written (single export)
1360        self.dirname = '' # name of file to be written (multiple export)
1361        if event:
1362            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1363
1364    def MakePWDRfilename(self,hist):
1365        '''Make a filename root (no extension) from a PWDR histogram name
1366
1367        :param str hist: the histogram name in data tree (starts with "PWDR ")
1368        '''
1369        file0 = ''
1370        file1 = hist[5:]
1371        # replace repeated blanks
1372        while file1 != file0:
1373            file0 = file1
1374            file1 = file0.replace('  ',' ').strip()
1375        file0 = file1.replace('Azm= ','A')
1376        # if angle has unneeded decimal places on aziumuth, remove them
1377        if file0[-3:] == '.00': file0 = file0[:-3]
1378        file0 = file0.replace('.','_')
1379        file0 = file0.replace(' ','_')
1380        return file0
1381
1382    def ExportSelect(self,AskFile='ask'):
1383        '''Selects histograms or phases when needed. Sets a default file name when
1384        requested into self.filename; always sets a default directory in self.dirname.
1385
1386        :param bool AskFile: Determines how this routine processes getting a
1387          location to store the current export(s).
1388         
1389          * if AskFile is 'ask' (default option), get the name of the file to be written;
1390            self.filename and self.dirname are always set. In the case where
1391            multiple files must be generated, the export routine should do this
1392            based on self.filename as a template.
1393          * if AskFile is 'dir', get the name of the directory to be used;
1394            self.filename is not used, but self.dirname is always set. The export routine
1395            will always generate the file name.
1396          * if AskFile is 'single', get only the name of the directory to be used when
1397            multiple items will be written (as multiple files) are used
1398            *or* a complete file name is requested when a single file
1399            name is selected. self.dirname is always set and self.filename used
1400            only when a single file is selected. 
1401          * if AskFile is 'default', creates a name of the file to be used from
1402            the name of the project (.gpx) file. If the project has not been saved,
1403            then the name of file is requested.
1404            self.filename and self.dirname are always set. In the case where
1405            multiple file names must be generated, the export routine should do this
1406            based on self.filename.
1407          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1408            file. If the project has not been saved, then a directory is requested.
1409            self.filename is not used.
1410
1411        :returns: True in case of an error
1412        '''
1413       
1414        numselected = 1
1415        if self.currentExportType == 'phase':
1416            if len(self.Phases) == 0:
1417                self.G2frame.ErrorDialog(
1418                    'Empty project',
1419                    'Project does not contain any phases.')
1420                return True
1421            elif len(self.Phases) == 1:
1422                self.phasenam = list(self.Phases.keys())
1423            elif self.multiple: 
1424                choices = sorted(self.Phases.keys())
1425                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1426                if phasenum is None: return True
1427                self.phasenam = [choices[i] for i in phasenum]
1428                if not self.phasenam: return True
1429                numselected = len(self.phasenam)
1430            else:
1431                choices = sorted(self.Phases.keys())
1432                phasenum = G2G.ItemSelector(choices,self.G2frame)
1433                if phasenum is None: return True
1434                self.phasenam = [choices[phasenum]]
1435                numselected = len(self.phasenam)
1436        elif self.currentExportType == 'single':
1437            if len(self.xtalDict) == 0:
1438                self.G2frame.ErrorDialog(
1439                    'Empty project',
1440                    'Project does not contain any single crystal data.')
1441                return True
1442            elif len(self.xtalDict) == 1:
1443                self.histnam = self.xtalDict.values()
1444            elif self.multiple:
1445                choices = sorted(self.xtalDict.values())
1446                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1447                if not hnum: return True
1448                self.histnam = [choices[i] for i in hnum]
1449                numselected = len(self.histnam)
1450            else:
1451                choices = sorted(self.xtalDict.values())
1452                hnum = G2G.ItemSelector(choices,self.G2frame)
1453                if hnum is None: return True
1454                self.histnam = [choices[hnum]]
1455                numselected = len(self.histnam)
1456        elif self.currentExportType == 'powder':
1457            if len(self.powderDict) == 0:
1458                self.G2frame.ErrorDialog(
1459                    'Empty project',
1460                    'Project does not contain any powder data.')
1461                return True
1462            elif len(self.powderDict) == 1:
1463                self.histnam = self.powderDict.values()
1464            elif self.multiple:
1465                choices = sorted(self.powderDict.values())
1466                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1467                if not hnum: return True
1468                self.histnam = [choices[i] for i in hnum]
1469                numselected = len(self.histnam)
1470            else:
1471                choices = sorted(self.powderDict.values())
1472                hnum = G2G.ItemSelector(choices,self.G2frame)
1473                if hnum is None: return True
1474                self.histnam = [choices[hnum]]
1475                numselected = len(self.histnam)
1476        elif self.currentExportType == 'image':
1477            if len(self.Histograms) == 0:
1478                self.G2frame.ErrorDialog(
1479                    'Empty project',
1480                    'Project does not contain any images.')
1481                return True
1482            elif len(self.Histograms) == 1:
1483                self.histnam = list(self.Histograms.keys())
1484            else:
1485                choices = sorted(self.Histograms.keys())
1486                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1487                if self.multiple:
1488                    if not hnum: return True
1489                    self.histnam = [choices[i] for i in hnum]
1490                else:
1491                    if hnum is None: return True
1492                    self.histnam = [choices[hnum]]
1493                numselected = len(self.histnam)
1494        if self.currentExportType == 'map':
1495            # search for phases with maps
1496            mapPhases = []
1497            choices = []
1498            for phasenam in sorted(self.Phases):
1499                phasedict = self.Phases[phasenam] # pointer to current phase info           
1500                if len(phasedict['General']['Map'].get('rho',[])):
1501                    mapPhases.append(phasenam)
1502                    if phasedict['General']['Map'].get('Flip'):
1503                        choices.append('Charge flip map: '+str(phasenam))
1504                    elif phasedict['General']['Map'].get('MapType'):
1505                        choices.append(
1506                            str(phasedict['General']['Map'].get('MapType'))
1507                            + ' map: ' + str(phasenam))
1508                    else:
1509                        choices.append('unknown map: '+str(phasenam))
1510            # select a map if needed
1511            if len(mapPhases) == 0:
1512                self.G2frame.ErrorDialog(
1513                    'Empty project',
1514                    'Project does not contain any maps.')
1515                return True
1516            elif len(mapPhases) == 1:
1517                self.phasenam = mapPhases
1518            else: 
1519                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1520                if self.multiple:
1521                    if not phasenum: return True
1522                    self.phasenam = [mapPhases[i] for i in phasenum]
1523                else:
1524                    if phasenum is None: return True
1525                    self.phasenam = [mapPhases[phasenum]]
1526            numselected = len(self.phasenam)
1527
1528        # items selected, now set self.dirname and usually self.filename
1529        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
1530            AskFile == 'default' and not self.G2frame.GSASprojectfile
1531            ):
1532            filename = self.askSaveFile()
1533            if not filename: return True
1534            self.dirname,self.filename = os.path.split(filename)
1535        elif AskFile == 'dir' or AskFile == 'single' or (
1536            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
1537            ):
1538            self.dirname = self.askSaveDirectory()
1539            if not self.dirname: return True
1540        elif AskFile == 'default-dir' or AskFile == 'default':
1541            self.dirname,self.filename = os.path.split(
1542                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
1543                )
1544        else:
1545            raise Exception('This should not happen!')
1546
1547    def loadParmDict(self):
1548        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
1549        refined values to those from the last cycle and set the uncertainties for the
1550        refined parameters in another dict (self.sigDict).
1551
1552        Expands the parm & sig dicts to include values derived from constraints.
1553        '''
1554        self.parmDict = {}
1555        self.sigDict = {}
1556        rigidbodyDict = {}
1557        covDict = {}
1558        consDict = {}
1559        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1560        if self.G2frame.GPXtree.IsEmpty(): return # nothing to do
1561        item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1562        while item:
1563            name = self.G2frame.GPXtree.GetItemText(item)
1564            if name == 'Rigid bodies':
1565                 rigidbodyDict = self.G2frame.GPXtree.GetItemPyData(item)
1566            elif name == 'Covariance':
1567                 covDict = self.G2frame.GPXtree.GetItemPyData(item)
1568            elif name == 'Constraints':
1569                 consDict = self.G2frame.GPXtree.GetItemPyData(item)
1570            item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1571        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1572        self.parmDict.update(rbDict)
1573        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1574        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave =  G2stIO.GetPhaseData(
1575            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
1576        self.parmDict.update(phaseDict)
1577        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
1578            Phases,Histograms,Print=False,resetRefList=False)
1579        self.parmDict.update(hapDict)
1580        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
1581        self.parmDict.update(histDict)
1582        self.parmDict.update(zip(
1583            covDict.get('varyList',[]),
1584            covDict.get('variables',[])))
1585        self.sigDict = dict(zip(
1586            covDict.get('varyList',[]),
1587            covDict.get('sig',[])))
1588        # expand to include constraints: first compile a list of constraints
1589        constList = []
1590        for item in consDict:
1591            if item.startswith('_'): continue
1592            constList += consDict[item]
1593        # now process the constraints
1594        G2mv.InitVars()
1595        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
1596        varyList = covDict.get('varyListStart')
1597        if varyList is None and len(constDict) == 0:
1598            # no constraints can use varyList
1599            varyList = covDict.get('varyList')
1600        elif varyList is None:
1601            # old GPX file from before pre-constraint varyList is saved
1602            print (' *** Old refinement: Please use Calculate/Refine to redo  ***')
1603            raise Exception(' *** Export aborted ***')
1604        else:
1605            varyList = list(varyList)
1606        # add symmetry-generated constraints
1607        rigidbodyDict = self.G2frame.GPXtree.GetItemPyData(   
1608            G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Rigid bodies'))
1609        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1610        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1611        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData(
1612            Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
1613        try:
1614            groups,parmlist = G2mv.GroupConstraints(constDict)
1615            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,self.parmDict)
1616            #print(G2mv.VarRemapShow(varyList))
1617        except:
1618            # this really should not happen
1619            print (' *** ERROR - constraints are internally inconsistent ***')
1620            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
1621            print ('Errors'+errmsg)
1622            if warnmsg: print ('Warnings'+warnmsg)
1623            raise Exception(' *** CIF creation aborted ***')
1624        # add the constrained values to the parameter dictionary
1625        G2mv.Dict2Map(self.parmDict,varyList)
1626        # and add their uncertainties into the esd dictionary (sigDict)
1627        if covDict.get('covMatrix') is not None:
1628            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
1629
1630    def loadTree(self):
1631        '''Load the contents of the data tree into a set of dicts
1632        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
1633        & self.xtalDict)
1634       
1635        * The childrenless data tree items are overall parameters/controls for the
1636          entire project and are placed in self.OverallParms
1637        * Phase items are placed in self.Phases
1638        * Data items are placed in self.Histogram. The key for these data items
1639          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
1640        '''
1641        self.OverallParms = {}
1642        self.powderDict = {}
1643        self.xtalDict = {}
1644        self.Phases = {}
1645        self.Histograms = {}
1646        self.SeqRefdata = None
1647        self.SeqRefhist = None
1648        if self.G2frame.GPXtree.IsEmpty(): return # nothing to do
1649        histType = None       
1650        if self.currentExportType == 'phase':
1651            # if exporting phases load them here
1652            sub = G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Phases')
1653            if not sub:
1654                print ('no phases found')
1655                return True
1656            item, cookie = self.G2frame.GPXtree.GetFirstChild(sub)
1657            while item:
1658                phaseName = self.G2frame.GPXtree.GetItemText(item)
1659                self.Phases[phaseName] =  self.G2frame.GPXtree.GetItemPyData(item)
1660                item, cookie = self.G2frame.GPXtree.GetNextChild(sub, cookie)
1661            return
1662        elif self.currentExportType == 'single':
1663            histType = 'HKLF'
1664        elif self.currentExportType == 'powder':
1665            histType = 'PWDR'
1666        elif self.currentExportType == 'image':
1667            histType = 'IMG'
1668
1669        if histType: # Loading just one kind of tree entry
1670            item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1671            while item:
1672                name = self.G2frame.GPXtree.GetItemText(item)
1673                if name.startswith(histType):
1674                    if self.Histograms.get(name): # there is already an item with this name
1675                        print('Histogram name '+str(name)+' is repeated. Renaming')
1676                        if name[-1] == '9':
1677                            name = name[:-1] + '10'
1678                        elif name[-1] in '012345678':
1679                            name = name[:-1] + str(int(name[-1])+1)
1680                        else:                           
1681                            name += '-1'
1682                    self.Histograms[name] = {}
1683                    # the main info goes into Data, but the 0th
1684                    # element contains refinement results, carry
1685                    # that over too now.
1686                    self.Histograms[name]['Data'] = self.G2frame.GPXtree.GetItemPyData(item)[1]
1687                    self.Histograms[name][0] = self.G2frame.GPXtree.GetItemPyData(item)[0]
1688                    item2, cookie2 = self.G2frame.GPXtree.GetFirstChild(item)
1689                    while item2: 
1690                        child = self.G2frame.GPXtree.GetItemText(item2)
1691                        self.Histograms[name][child] = self.G2frame.GPXtree.GetItemPyData(item2)
1692                        item2, cookie2 = self.G2frame.GPXtree.GetNextChild(item, cookie2)
1693                item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1694            # index powder and single crystal histograms by number
1695            for hist in self.Histograms:
1696                if hist.startswith("PWDR"): 
1697                    d = self.powderDict
1698                elif hist.startswith("HKLF"): 
1699                    d = self.xtalDict
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
1726    def dumpTree(self,mode='type'):
1727        '''Print out information on the data tree dicts loaded in loadTree.
1728        Used for testing only.
1729        '''
1730        if self.SeqRefdata and self.SeqRefhist:
1731            print('Note that dumpTree does not show sequential results')
1732        print ('\nOverall')
1733        if mode == 'type':
1734            def Show(arg): return type(arg)
1735        else:
1736            def Show(arg): return arg
1737        for key in self.OverallParms:
1738            print ('  '+key+Show(self.OverallParms[key]))
1739        print ('Phases')
1740        for key1 in self.Phases:
1741            print ('    '+key1+Show(self.Phases[key1]))
1742        print ('Histogram')
1743        for key1 in self.Histograms:
1744            print ('    '+key1+Show(self.Histograms[key1]))
1745            for key2 in self.Histograms[key1]:
1746                print ('      '+key2+Show(self.Histograms[key1][key2]))
1747
1748    def defaultSaveFile(self):
1749        return os.path.abspath(
1750            os.path.splitext(self.G2frame.GSASprojectfile
1751                             )[0]+self.extension)
1752       
1753    def askSaveFile(self):
1754        '''Ask the user to supply a file name
1755
1756        :returns: a file name (str) or None if Cancel is pressed
1757        '''
1758       
1759        pth = G2G.GetExportPath(self.G2frame)
1760        defnam = os.path.splitext(
1761            os.path.split(self.G2frame.GSASprojectfile)[1]
1762            )[0]+self.extension
1763        dlg = wx.FileDialog(
1764            self.G2frame, 'Input name for file to write', pth, defnam,
1765            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
1766            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1767        dlg.CenterOnParent()
1768        try:
1769            if dlg.ShowModal() == wx.ID_OK:
1770                filename = dlg.GetPath()
1771                self.G2frame.LastExportDir = os.path.split(filename)[0]
1772                filename = os.path.splitext(filename)[0]+self.extension # make sure extension is correct
1773            else:
1774                filename = None
1775        finally:
1776            dlg.Destroy()
1777        return filename
1778
1779    def askSaveDirectory(self):
1780        '''Ask the user to supply a directory name. Path name is used as the
1781        starting point for the next export path search.
1782
1783        :returns: a directory name (str) or None if Cancel is pressed
1784        '''
1785        pth = G2G.GetExportPath(self.G2frame)
1786        dlg = wx.DirDialog(
1787            self.G2frame, 'Input directory where file(s) will be written', pth,
1788            wx.DD_DEFAULT_STYLE)
1789        dlg.CenterOnParent()
1790        try:
1791            if dlg.ShowModal() == wx.ID_OK:
1792                filename = dlg.GetPath()
1793                self.G2frame.LastExportDir = filename
1794            else:
1795                filename = None
1796        finally:
1797            dlg.Destroy()
1798        return filename
1799
1800    # Tools for file writing.
1801    def OpenFile(self,fil=None,mode='w'):
1802        '''Open the output file
1803
1804        :param str fil: The name of the file to open. If None (default)
1805          the name defaults to self.dirname + self.filename.
1806          If an extension is supplied, it is not overridded,
1807          but if not, the default extension is used.
1808        :returns: the file object opened by the routine which is also
1809          saved as self.fp
1810        '''
1811        if mode == 'd': # debug mode
1812            self.fullpath = '(stdout)'
1813            self.fp = sys.stdout
1814            return
1815        if not fil:
1816            if not os.path.splitext(self.filename)[1]:
1817                self.filename += self.extension
1818            fil = os.path.join(self.dirname,self.filename)
1819        self.fullpath = os.path.abspath(fil)
1820        self.fp = open(self.fullpath,mode)
1821        return self.fp
1822
1823    def Write(self,line):
1824        '''write a line of output, attaching a line-end character
1825
1826        :param str line: the text to be written.
1827        '''
1828        self.fp.write(line+'\n')
1829       
1830    def CloseFile(self,fp=None):
1831        '''Close a file opened in OpenFile
1832
1833        :param file fp: the file object to be closed. If None (default)
1834          file object self.fp is closed.
1835        '''
1836        if self.fp == sys.stdout: return # debug mode
1837        if fp is None:
1838            fp = self.fp
1839            self.fp = None
1840        if fp is not None: fp.close()
1841       
1842    def SetSeqRef(self,data,hist):
1843        '''Set the exporter to retrieve results from a sequential refinement
1844        rather than the main tree
1845        '''
1846        self.SeqRefdata = data
1847        self.SeqRefhist = hist
1848        data_name = data[hist]
1849        for i,val in zip(data_name['varyList'],data_name['sig']):
1850            self.sigDict[i] = val
1851            self.sigDict[striphist(i)] = val
1852        for i in data_name['parmDict']:
1853            self.parmDict[striphist(i)] = data_name['parmDict'][i]
1854            self.parmDict[i] = data_name['parmDict'][i]
1855            # zero out the dA[xyz] terms, they would only bring confusion
1856            key = i.split(':')
1857            if len(key) < 3: continue
1858            if key[2].startswith('dA'):
1859                self.parmDict[i] = 0.0
1860        for i,(val,sig) in data_name.get('depParmDict',{}).iteritems():
1861            self.parmDict[i] = val
1862            self.sigDict[i] = sig
1863        #GSASIIpath.IPyBreak()
1864
1865    def SetFromArray(self,hist,histname):
1866        '''Load a histogram into the exporter in preparation for use of the .Writer
1867        rather than the main tree. This is used in GSASIIscriptable when wx
1868        is not present.
1869        '''
1870        self.Histograms[histname] =  {}
1871        self.Histograms[histname]['Data'] = hist['data'][1]
1872        self.Histograms[histname]['Instrument Parameters'] = hist['Instrument Parameters']
1873        self.Histograms[histname]['Sample Parameters'] = hist['Sample Parameters']
1874
1875    # Tools to pull information out of the data arrays
1876    def GetCell(self,phasenam):
1877        """Gets the unit cell parameters and their s.u.'s for a selected phase
1878
1879        :param str phasenam: the name for the selected phase
1880        :returns: `cellList,cellSig` where each is a 7 element list corresponding
1881          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
1882          cell values and `cellSig` has their uncertainties.
1883        """
1884        if self.SeqRefdata and self.SeqRefhist:
1885            return self.GetSeqCell(phasenam,self.SeqRefdata[self.SeqRefhist])
1886        phasedict = self.Phases[phasenam] # pointer to current phase info
1887        try:
1888            pfx = str(phasedict['pId'])+'::'
1889            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
1890            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
1891                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
1892            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
1893            return cellList,cellSig
1894        except KeyError:
1895            cell = phasedict['General']['Cell'][1:]
1896            return cell,7*[0]
1897           
1898    def GetSeqCell(self,phasenam,data_name):
1899        """Gets the unit cell parameters and their s.u.'s for a selected phase
1900        and histogram in a sequential fit
1901
1902        :param str phasenam: the name for the selected phase
1903        :param dict data_name: the sequential refinement parameters for the selected histogram
1904        :returns: `cellList,cellSig` where each is a 7 element list corresponding
1905          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
1906          cell values and `cellSig` has their uncertainties.
1907        """
1908        phasedict = self.Phases[phasenam]
1909        SGdata = phasedict['General']['SGData']
1910        pId = phasedict['pId']
1911        RecpCellTerms = G2lat.cell2A(phasedict['General']['Cell'][1:7])
1912        ESDlookup = {}
1913        Dlookup = {}
1914        varied = [striphist(i) for i in data_name['varyList']]
1915        for item,val in data_name['newCellDict'].iteritems():
1916            if item in varied:
1917                ESDlookup[val[0]] = item
1918                Dlookup[item] = val[0]
1919        A = RecpCellTerms[:]
1920        for i in range(6):
1921            var = str(pId)+'::A'+str(i)
1922            if var in ESDlookup:
1923                A[i] = data_name['newCellDict'][ESDlookup[var]][1] # override with refined value
1924        cellDict = dict(zip([str(pId)+'::A'+str(i) for i in range(6)],A))
1925        zeroDict = {i:0.0 for i in cellDict}
1926        A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata,cellDict,zeroDict)
1927        covData = {
1928            'varyList': [Dlookup.get(striphist(v),v) for v in data_name['varyList']],
1929            'covMatrix': data_name['covMatrix']
1930            }
1931        return list(G2lat.A2cell(A)) + [G2lat.calc_V(A)], G2stIO.getCellEsd(str(pId)+'::',SGdata,A,covData)
1932               
1933    def GetAtoms(self,phasenam):
1934        """Gets the atoms associated with a phase. Can be used with standard
1935        or macromolecular phases
1936
1937        :param str phasenam: the name for the selected phase
1938        :returns: a list of items for eac atom where each item is a list containing:
1939          label, typ, mult, xyz, and td, where
1940
1941          * label and typ are the atom label and the scattering factor type (str)
1942          * mult is the site multiplicity (int)
1943          * xyz is contains a list with four pairs of numbers:
1944            x, y, z and fractional occupancy and
1945            their standard uncertainty (or a negative value)
1946          * td is contains a list with either one or six pairs of numbers:
1947            if one number it is U\ :sub:`iso` and with six numbers it is
1948            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
1949            paired with their standard uncertainty (or a negative value)
1950        """
1951        phasedict = self.Phases[phasenam] # pointer to current phase info           
1952        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
1953        cfrac = cx+3
1954        fpfx = str(phasedict['pId'])+'::Afrac:'       
1955        atomslist = []
1956        for i,at in enumerate(phasedict['Atoms']):
1957            if phasedict['General']['Type'] == 'macromolecular':
1958                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
1959            else:
1960                label = at[ct-1]
1961            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
1962            fsig = self.sigDict.get(fpfx+str(i),-0.009)
1963            mult = at[cs+1]
1964            typ = at[ct]
1965            xyz = []
1966            for j,v in enumerate(('x','y','z')):
1967                val = at[cx+j]
1968                pfx = str(phasedict['pId']) + '::A' + v + ':' + str(i)
1969                val = self.parmDict.get(pfx, val)
1970                dpfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
1971                sig = self.sigDict.get(dpfx,-0.000009)
1972                xyz.append((val,sig))
1973            xyz.append((fval,fsig))
1974            td = []
1975            if at[cia] == 'I':
1976                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
1977                val = self.parmDict.get(pfx,at[cia+1])
1978                sig = self.sigDict.get(pfx,-0.0009)
1979                td.append((val,sig))
1980            else:
1981                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
1982                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
1983                    val = self.parmDict.get(pfx,at[cia+2+i])
1984                    sig = self.sigDict.get(pfx,-0.0009)
1985                    td.append((val,sig))
1986            atomslist.append((label,typ,mult,xyz,td))
1987        return atomslist
1988######################################################################
1989def ExportPowderList(G2frame):
1990    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
1991    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
1992   
1993    :param wx.Frame G2frame: the GSAS-II main data tree window
1994    '''
1995    extList = []
1996    for obj in G2frame.exporterlist:
1997        if 'powder' in obj.exporttype:
1998            try:
1999                obj.Writer
2000                extList.append(obj.extension)
2001            except AttributeError:
2002                pass
2003    return extList
2004
2005def ExportPowder(G2frame,TreeName,fileroot,extension):
2006    '''Writes a single powder histogram using the Export routines.
2007    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
2008
2009    :param wx.Frame G2frame: the GSAS-II main data tree window
2010    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
2011    :param str fileroot: name for file to be written, extension ignored
2012    :param str extension: extension for file to be written (start with '.'). Must
2013      match a powder export routine that has a Writer object.
2014    '''
2015    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2016    for obj in G2frame.exporterlist:
2017        if obj.extension == extension and 'powder' in obj.exporttype:
2018            obj.currentExportType = 'powder'
2019            obj.InitExport(None)
2020            obj.loadTree() # load all histograms in tree into dicts
2021            if TreeName not in obj.Histograms:
2022                raise Exception('Histogram not found: '+str(TreeName))
2023            try:
2024                obj.Writer
2025            except AttributeError:
2026                continue
2027            try:
2028                obj.Writer(TreeName,filename)
2029                print('wrote file '+filename)
2030                return
2031            except Exception:
2032                print('Export Routine for '+extension+' failed.')
2033    else:
2034        print('No Export routine supports extension '+extension)
2035
2036def ExportSequential(G2frame,data,obj,exporttype):
2037    '''
2038    Used to export from every phase/dataset in a sequential refinement using
2039    a .Writer method for either projects or phases. Prompts to select histograms
2040    and for phase exports, which phase(s).
2041
2042    :param wx.Frame G2frame: the GSAS-II main data tree window
2043    :param dict data: the sequential refinement data object
2044    :param str exporttype: indicates the type of export ('project' or 'phase')
2045    '''
2046    if len(data['histNames']) == 0:
2047        G2G.G2MessageBox(G2frame,'There are no sequential histograms','Warning')
2048    obj.InitExport(None)
2049    obj.loadTree()
2050    obj.loadParmDict()
2051    if len(data['histNames']) == 1:
2052        histlist = data['histNames']
2053    else:
2054        dlg = G2G.G2MultiChoiceDialog(G2frame,'Select histograms to export from list',
2055                                 'Select histograms',data['histNames'])
2056        if dlg.ShowModal() == wx.ID_OK:
2057            histlist = [data['histNames'][l] for l in dlg.GetSelections()]
2058            dlg.Destroy()
2059        else:
2060            dlg.Destroy()
2061            return
2062    if exporttype == 'Phase':
2063        phaselist = list(obj.Phases.keys())
2064        if len(obj.Phases) == 0:
2065            G2G.G2MessageBox(G2frame,'There are no phases in sequential ref.','Warning')
2066            return
2067        elif len(obj.Phases) > 1:
2068            dlg = G2G.G2MultiChoiceDialog(G2frame,'Select phases to export from list',
2069                                    'Select phases', phaselist)
2070            if dlg.ShowModal() == wx.ID_OK:
2071                phaselist = [phaselist[l] for l in dlg.GetSelections()]
2072                dlg.Destroy()
2073            else:
2074                dlg.Destroy()
2075                return
2076        filename = obj.askSaveFile()
2077        if not filename: return True
2078        obj.dirname,obj.filename = os.path.split(filename)
2079        print('Writing output to file '+str(obj.filename)+"...")
2080        mode = 'w'
2081        for p in phaselist:
2082            for h in histlist:
2083                obj.SetSeqRef(data,h)
2084                #GSASIIpath.IPyBreak()
2085                obj.Writer(h,phasenam=p,mode=mode)
2086                mode = 'a'
2087        print('...done')
2088    elif exporttype == 'Project':  # note that the CIF exporter is not yet ready for this
2089        filename = obj.askSaveFile()
2090        if not filename: return True
2091        obj.dirname,obj.filename = os.path.split(filename)
2092        print('Writing output to file '+str(obj.filename)+"...")
2093        mode = 'w'
2094        for h in histlist:
2095            obj.SetSeqRef(data,h)
2096            obj.Writer(h,mode=mode)
2097            print('\t'+str(h)+' written')
2098            mode = 'a'
2099        print('...done')
2100    elif exporttype == 'Powder':
2101        filename = obj.askSaveFile()
2102        if not filename: return True
2103        obj.dirname,obj.filename = os.path.split(filename)
2104        print('Writing output to file '+str(obj.filename)+"...")
2105        mode = 'w'
2106        for h in histlist:
2107            obj.SetSeqRef(data,h)
2108            obj.Writer(h,mode=mode)
2109            print('\t'+str(h)+' written')
2110            mode = 'a'
2111        print('...done')
2112
2113def ReadDIFFaX(DIFFaXfile):
2114    print ('read '+DIFFaXfile)
2115    Layer = {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],'Width':[[10.,10.],[False,False]],
2116        'Layers':[],'Stacking':[],'Transitions':[],'Toler':0.01,'AtInfo':{}}
2117    df = open(DIFFaXfile,'r')
2118    lines = df.readlines()
2119    df.close()
2120    struct = False
2121    Struct = []
2122    stack = False
2123    Stack = []
2124    trans = False
2125    Trans = []
2126    for diff in lines:
2127        diff = diff[:-1].lower()
2128        if '!'  in diff:
2129            continue
2130        while '}' in diff: #strip comments
2131            iB = diff.index('{')
2132            iF = diff.index('}')+1
2133            if iB:
2134                diff = diff[:iB]
2135            else:
2136                diff = diff[iF:]
2137        if not diff:
2138            continue
2139        if diff.strip() == 'instrumental':
2140            continue
2141        if diff.strip() == 'structural':
2142            struct = True
2143            continue
2144        elif diff.strip() == 'stacking':
2145            struct = False
2146            stack = True
2147            continue
2148        elif diff.strip() == 'transitions':
2149            stack = False
2150            trans = True
2151            continue
2152        diff = diff.strip()
2153        if struct:
2154            if diff:
2155                Struct.append(diff)
2156        elif stack:
2157            if diff:
2158                Stack.append(diff)
2159        elif trans:
2160            if diff:
2161                Trans.append(diff)
2162   
2163#STRUCTURE records
2164    laueRec = Struct[1].split()
2165    Layer['Laue'] = laueRec[0]
2166    if Layer['Laue'] == 'unknown' and len(laueRec) > 1:
2167        Layer['Toler'] = float(laueRec[1])    #tolerance for 'unknown'?
2168    if Layer['Laue'] == '2/m(1)': Layer['Laue'] = '2/m(c)'
2169    if Layer['Laue'] == '2/m(2)': Layer['Laue'] = '2/m(ab)'
2170    cell = Struct[0].split()
2171    Layer['Cell'] = [False,float(cell[0]),float(cell[1]),float(cell[2]),90.,90.,float(cell[3]),1.0]
2172    nLayers = int(Struct[2])
2173    N = 3
2174    if 'layer' not in Struct[3]:
2175        N = 4
2176        if Struct[3] != 'infinite':
2177            width = Struct[3].split()
2178            Layer['Width'][0] = [float(width[0]),float(width[1])]
2179    for nL in range(nLayers):
2180        if '=' in Struct[N]:
2181            name = Struct[N].split('=')
2182            sameas = int(name[1])-1
2183            Layer['Layers'].append({'Name':name[0],'SameAs':Layer['Layers'][sameas]['Name'],'Symm':'None','Atoms':[]})
2184            N += 1
2185            continue
2186        Symm = 'None'
2187        if 'centro' in Struct[N+1]: Symm = '-1'
2188        Layer['Layers'].append({'Name':Struct[N],'SameAs':'','Symm':Symm,'Atoms':[]})
2189        N += 2
2190        while 'layer' not in Struct[N]:
2191            atom = Struct[N][4:].split()
2192            atomType = G2el.FixValence(Struct[N][:4].replace(' ','').strip().capitalize())
2193            if atomType not in Layer['AtInfo']:
2194                Layer['AtInfo'][atomType] = G2el.GetAtomInfo(atomType)
2195            atomName = '%s(%s)'%(atomType,atom[0])
2196            newVals = []
2197            for val in atom[1:6]:
2198                if '/' in val:
2199                    newVals.append(eval(val+'.'))
2200                else:
2201                    newVals.append(float(val))               
2202            atomRec = [atomName,atomType,newVals[0],newVals[1],newVals[2],newVals[4],newVals[3]/78.9568]
2203            Layer['Layers'][-1]['Atoms'].append(atomRec)
2204            N += 1
2205            if N > len(Struct)-1:
2206                break
2207#TRANSITIONS records
2208    transArray = []
2209    N = 0
2210    for i in range(nLayers):
2211        transArray.append([])
2212        for j in range(nLayers):
2213            vals = Trans[N].split()
2214            newVals = []
2215            for val in vals[:4]:
2216                if '/' in val:
2217                    newVals.append(eval(val+'.'))
2218                else:
2219                    newVals.append(float(val))
2220            transArray[-1].append(newVals+['',False])
2221            N += 1
2222    Layer['Transitions'] = transArray
2223#STACKING records
2224    Layer['Stacking'] = [Stack[0],'']
2225    if Stack[0] == 'recursive':
2226        Layer['Stacking'][1] = Stack[1]
2227    elif Stack[0] == 'explicit':
2228        if Stack[1] == 'random':
2229            Layer['Stacking'][1] = Stack[1]
2230        else:
2231            Layer['Stacking'][1] = 'list'
2232            Layer['Stacking'].append('')
2233            for stack in Stack[2:]:
2234                Layer['Stacking'][2] += ' '+stack
2235    return Layer
2236
2237def readColMetadata(imagefile):
2238    '''Reads image metadata from a column-oriented metadata table
2239    (1-ID style .par file). Called by :func:`GetColumnMetadata`
2240   
2241    The .par file has any number of columns separated by spaces.
2242    The directory for the file must be specified in
2243    Config variable ``Column_Metadata_directory``.
2244    As an index to the .par file a second "label file" must be specified with the
2245    same file root name as the .par file but the extension must be .XXX_lbls (where
2246    .XXX is the extension of the image) or if that is not present extension
2247    .lbls.
2248
2249    :param str imagefile: the full name of the image file (with extension, directory optional)
2250
2251    :returns: a dict with parameter values. Named parameters will have the type based on
2252       the specified Python function, named columns will be character strings
2253   
2254    The contents of the label file will look like this::
2255   
2256        # define keywords
2257        filename:lambda x,y: "{}_{:0>6}".format(x,y)|33,34
2258        distance: float | 75
2259        wavelength:lambda keV: 12.398425/float(keV)|9
2260        pixelSize:lambda x: [74.8, 74.8]|0
2261        ISOlikeDate: lambda dow,m,d,t,y:"{}-{}-{}T{} ({})".format(y,m,d,t,dow)|0,1,2,3,4
2262        Temperature: float|53
2263        FreePrm2: int | 34 | Free Parm2 Label
2264        # define other variables
2265        0:day
2266        1:month
2267        2:date
2268        3:time
2269        4:year
2270        7:I_ring
2271
2272    This file contains three types of lines in any order.
2273     * Named parameters are evaluated with user-supplied Python code (see
2274       subsequent information). Specific named parameters are used
2275       to determine values that are used for image interpretation (see table,
2276       below). Any others are copied to the Comments subsection of the Image
2277       tree item.
2278     * Column labels are defined with a column number (integer) followed by
2279       a colon (:) and a label to be assigned to that column. All labeled
2280       columns are copied to the Image's Comments subsection.
2281     * Comments are any line that does not contain a colon.
2282
2283    Note that columns are numbered starting at zero.
2284
2285    Any named parameter may be defined provided it is not a valid integer,
2286    but the named parameters in the table have special meanings, as descibed.
2287    The parameter name is followed by a colon. After the colon, specify
2288    Python code that defines or specifies a function that will be called to
2289    generate a value for that parameter.
2290
2291    Note that several keywords, if defined in the Comments, will be found and
2292    placed in the appropriate section of the powder histogram(s)'s Sample
2293    Parameters after an integration: ``Temperature``,``Pressure``,``Time``,
2294    ``FreePrm1``,``FreePrm2``,``FreePrm3``,``Omega``,``Chi``, and ``Phi``.
2295
2296    After the Python code, supply a vertical bar (|) and then a list of one
2297    more more columns that will be supplied as arguments to that function.
2298
2299    Note that the labels for the three FreePrm items can be changed by
2300    including that label as a third item with an additional vertical bar. Labels
2301    will be ignored for any other named parameters.
2302   
2303    The examples above are discussed here:
2304
2305    ``filename:lambda x,y: "{}_{:0>6}".format(x,y)|33,34``
2306        Here the function to be used is defined with a lambda statement::
2307       
2308          lambda x,y: "{}_{:0>6}".format(x,y)
2309
2310        This function will use the format function to create a file name from the
2311        contents of columns 33 and 34. The first parameter (x, col. 33) is inserted directly into
2312        the file name, followed by a underscore (_), followed by the second parameter (y, col. 34),
2313        which will be left-padded with zeros to six characters (format directive ``:0>6``).
2314
2315        When there will be more than one image generated per line in the .par file, an alternate way to
2316        generate list of file names takes into account the number of images generated::
2317
2318          lambda x,y,z: ["{}_{:0>6}".format(x,int(y)+i) for i in range(int(z))]
2319
2320        Here a third parameter is used to specify the number of images generated, where
2321        the image number is incremented for each image.
2322         
2323    ``distance: float | 75``
2324        Here the contents of column 75 will be converted to a floating point number
2325        by calling float on it. Note that the spaces here are ignored.
2326       
2327    ``wavelength:lambda keV: 12.398425/float(keV)|9``
2328        Here we define an algebraic expression to convert an energy in keV to a
2329        wavelength and pass the contents of column 9 as that input energy
2330       
2331    ``pixelSize:lambda x: [74.8, 74.8]|0``
2332        In this case the pixel size is a constant (a list of two numbers). The first
2333        column is passed as an argument as at least one argument is required, but that
2334        value is not used in the expression.
2335
2336    ``ISOlikeDate: lambda dow,m,d,t,y:"{}-{}-{}T{} ({})".format(y,m,d,t,dow)|0,1,2,3,4``
2337        This example defines a parameter that takes items in the first five columns
2338        and formats them in a different way. This parameter is not one of the pre-defined
2339        parameter names below. Some external code could be used to change the month string
2340        (argument ``m``) to a integer from 1 to 12.
2341       
2342    ``FreePrm2: int | 34 | Free Parm2 Label``
2343        In this example, the contents of column 34 will be converted to an integer and
2344        placed as the second free-named parameter in the Sample Parameters after an
2345        integration. The label for this parameter will be changed to "Free Parm2 Label".
2346           
2347    **Pre-defined parameter names**
2348   
2349    =============  =========  ========  =====================================================
2350     keyword       required    type      Description
2351    =============  =========  ========  =====================================================
2352       filename    yes         str or   generates the file name prefix for the matching image
2353                               list     file (MyImage001 for file /tmp/MyImage001.tif) or
2354                                        a list of file names.
2355     polarization  no         float     generates the polarization expected based on the
2356                                        monochromator angle, defaults to 0.99.
2357       center      no         list of   generates the approximate beam center on the detector
2358                              2 floats  in mm, such as [204.8, 204.8].
2359       distance    yes        float     generates the distance from the sample to the detector
2360                                        in mm
2361       pixelSize   no         list of   generates the size of the pixels in microns such as
2362                              2 floats  [200.0, 200.0].
2363       wavelength  yes        float     generates the wavelength in Angstroms
2364    =============  =========  ========  =====================================================
2365   
2366    '''
2367    dir,fil = os.path.split(os.path.abspath(imagefile))
2368    imageName,ext = os.path.splitext(fil)
2369    if not GSASIIpath.GetConfigValue('Column_Metadata_directory'): return
2370    parfiles = glob.glob(os.path.join(GSASIIpath.GetConfigValue('Column_Metadata_directory'),'*.par'))
2371    if len(parfiles) == 0:
2372        print('Sorry, No Column metadata (.par) file found in '+
2373              GSASIIpath.GetConfigValue('Column_Metadata_directory'))
2374        return {}
2375    for parFil in parfiles: # loop over all .par files (hope just 1) in image dir until image is found
2376        parRoot = os.path.splitext(parFil)[0]
2377        for e in (ext+'_lbls','.lbls'):
2378            if os.path.exists(parRoot+e):
2379                lblFil = parRoot+e
2380                break
2381        else:
2382            print('Warning: No labels definitions found for '+parFil)
2383            continue
2384        labels,lbldict,keyCols,keyExp,errors = readColMetadataLabels(lblFil)
2385        if errors:
2386            print('Errors in labels file '+lblFil)
2387            for i in errors: print('  '+i)
2388            continue
2389        else:
2390            print('Read '+lblFil)
2391        # scan through each line in this .par file, looking for the matching image rootname
2392        fp = open(parFil,'Ur')
2393        for iline,line in enumerate(fp):
2394            items = line.strip().split(' ')
2395            nameList = keyExp['filename'](*[items[j] for j in keyCols['filename']])
2396            if type(nameList) is str:
2397                if nameList != imageName: continue
2398                name = nameList
2399            else:
2400                for name in nameList:
2401                    if name == imageName: break # got a match
2402                else:
2403                    continue
2404            # parse the line and finish
2405            metadata = evalColMetadataDicts(items,labels,lbldict,keyCols,keyExp)
2406            metadata['par file'] = parFil
2407            metadata['lbls file'] = lblFil
2408            print("Metadata read from {} line {}".format(parFil,iline+1))
2409            fp.close()
2410            return metadata
2411        else:
2412            print("Image {} not found in {}".format(imageName,parFil))
2413            fp.close()
2414            continue
2415        fp.close()
2416    else:
2417        print("Warning: No .par metadata for image {}".format(imageName))
2418        return {}
2419
2420def readColMetadataLabels(lblFil):
2421    '''Read the .*lbls file and setup for metadata assignments
2422    '''
2423    lbldict = {}
2424    keyExp = {}
2425    keyCols = {}
2426    labels = {}
2427    errors = []
2428    fp = open(lblFil,'Ur')         # read column labels
2429    for iline,line in enumerate(fp): # read label definitions
2430        line = line.strip()
2431        if not line or line[0] == '#': continue # comments
2432        items = line.split(':')
2433        if len(items) < 2: continue # lines with no colon are also comments
2434        # does this line a definition for a named parameter?
2435        key = items[0]
2436        try: 
2437            int(key)
2438        except ValueError: # try as named parameter since not a valid number
2439            items = line.split(':',1)[1].split('|')
2440            try:
2441                f = eval(items[0]) # compile the expression
2442                if not callable(f):
2443                    errors += ['Expression "{}" for key {} is not a function (line {})'.
2444                           format(items[0],key,iline)]
2445                    continue
2446                keyExp[key] = f
2447            except Exception as msg:
2448                errors += ['Expression "{}" for key {} is not valid (line {})'.
2449                           format(items[0],key,iline)]
2450                errors += [str(msg)]
2451                continue
2452            keyCols[key] = [int(i) for i in items[1].strip().split(',')]
2453            if key.lower().startswith('freeprm') and len(items) > 2:
2454                labels[key] = items[2]
2455            continue
2456        if len(items) == 2: # simple column definition
2457            lbldict[int(items[0])] = items[1]
2458    fp.close()
2459    if 'filename' not in keyExp:
2460        errors += ["File {} is invalid. No valid filename expression.".format(lblFil)]
2461    return labels,lbldict,keyCols,keyExp,errors
2462
2463def evalColMetadataDicts(items,labels,lbldict,keyCols,keyExp,ShowError=False):
2464    '''Evaluate the metadata for a line in the .par file
2465    '''
2466    metadata = {lbldict[j]:items[j] for j in lbldict}
2467    named = {}
2468    for key in keyExp:
2469        try:
2470            res = keyExp[key](*[items[j] for j in keyCols[key]])
2471        except:
2472            if ShowError:
2473                res = "*** error ***"
2474            else:
2475                continue
2476        named[key] = res
2477    metadata.update(named)
2478    for lbl in labels: # add labels for FreePrm's
2479        metadata['label_'+lbl[4:].lower()] = labels[lbl]
2480    return metadata
2481
2482def GetColumnMetadata(reader):
2483    '''Add metadata to an image from a column-type metadata file
2484    using :func:`readColMetadata`
2485   
2486    :param reader: a reader object from reading an image
2487   
2488    '''
2489    if not GSASIIpath.GetConfigValue('Column_Metadata_directory'): return
2490    parParms = readColMetadata(reader.readfilename)
2491    if not parParms: return # check for read failure
2492    specialKeys = ('filename',"polarization", "center", "distance", "pixelSize", "wavelength",)
2493    reader.Comments = ['Metadata from {} assigned by {}'.format(parParms['par file'],parParms['lbls file'])]
2494    for key in parParms:
2495        if key in specialKeys+('par file','lbls file'): continue
2496        reader.Comments += ["{} = {}".format(key,parParms[key])]
2497    if "polarization" in parParms:
2498        reader.Data['PolaVal'][0] = parParms["polarization"]
2499    else:
2500        reader.Data['PolaVal'][0] = 0.99
2501    if "center" in parParms:
2502        reader.Data['center'] = parParms["center"]
2503    if "pixelSize" in parParms:
2504        reader.Data['pixelSize'] = parParms["pixelSize"]
2505    if "wavelength" in parParms:
2506        reader.Data['wavelength'] = parParms['wavelength']
2507    else:
2508        print('Error: wavelength not defined in {}'.format(parParms['lbls file']))
2509    if "distance" in parParms:
2510        reader.Data['distance'] = parParms['distance']
2511        reader.Data['setdist'] = parParms['distance']
2512    else:
2513        print('Error: distance not defined in {}'.format(parParms['lbls file']))
2514
2515def testColumnMetadata(G2frame):
2516    '''Test the column-oriented metadata parsing, as implemented at 1-ID, by showing results
2517    when using a .par and .lbls pair.
2518   
2519     * Select a .par file, if more than one in selected dir.
2520     * Select the .*lbls file, if more than one matching .par file.
2521     * Parse the .lbls file, showing errors if encountered; loop until errors are fixed.
2522     * Search for an image or a line in the .par file and show the results when interpreted
2523     
2524    See :func:`readColMetadata` for more details.
2525    '''
2526    if not GSASIIpath.GetConfigValue('Column_Metadata_directory'):
2527        G2G.G2MessageBox(G2frame,'The configuration option for I-ID Metadata is not set.\n'+
2528                         'Please use the File/Preferences menu to set Column_Metadata_directory',
2529                         'Warning')
2530        return
2531    parFiles = glob.glob(os.path.join(GSASIIpath.GetConfigValue('Column_Metadata_directory'),'*.par'))
2532    if not parFiles: 
2533        G2G.G2MessageBox(G2frame,'No .par files found in directory {}. '
2534                         .format(GSASIIpath.GetConfigValue('Column_Metadata_directory'))+
2535                         '\nThis is set by config variable Column_Metadata_directory '+
2536                         '(Set in File/Preferences menu).',
2537                         'Warning')
2538        return
2539    parList = []
2540    for parFile in parFiles:
2541        lblList = []
2542        parRoot = os.path.splitext(parFile)[0]
2543        for f in glob.glob(parRoot+'.*lbls'):
2544            if os.path.exists(f): lblList.append(f)
2545        if not len(lblList):
2546            continue
2547        parList.append(parFile)
2548    if len(parList) == 0:
2549        G2G.G2MessageBox(G2frame,'No .lbls or .EXT_lbls file found for .par file(s) in directory {}. '
2550                         .format(GSASIIpath.GetConfigValue('Column_Metadata_directory'))+
2551                         '\nThis is set by config variable Column_Metadata_directory '+
2552                         '(Set in File/Preferences menu).',
2553                         'Warning')
2554        return
2555    elif len(parList) == 1:
2556        parFile = parList[0]
2557    else:
2558        dlg = G2G.G2SingleChoiceDialog(G2frame,
2559                'More than 1 .par file found. (Better if only 1!). Choose the one to test in '+
2560                GSASIIpath.GetConfigValue('Column_Metadata_directory'),
2561                'Choose .par file', [os.path.split(i)[1] for i in parList])
2562        if dlg.ShowModal() == wx.ID_OK:
2563            parFile = parList[dlg.GetSelection()]
2564            dlg.Destroy()
2565        else:
2566            dlg.Destroy()
2567            return
2568    # got .par file; now work on .*lbls file
2569    lblList = []
2570    parRoot = os.path.splitext(parFile)[0]
2571    for f in glob.glob(parRoot+'.*lbls'):
2572        if os.path.exists(f): lblList.append(f)
2573    if not len(lblList):
2574        raise Exception('How did this happen! No .*lbls files for '+parFile)
2575    elif len(lblList) == 1:
2576        lblFile = lblList[0]
2577    else:
2578        dlg = G2G.G2SingleChoiceDialog(G2frame,
2579                'Select label file for .par file '+parFile,
2580                'Choose label file', [os.path.split(i)[1] for i in lblList])
2581        if dlg.ShowModal() == wx.ID_OK:
2582            lblFile = lblList[dlg.GetSelection()]
2583            dlg.Destroy()
2584        else:
2585            dlg.Destroy()
2586            return
2587    # parse the labels file
2588    errors = True
2589    while errors:
2590        labels,lbldict,keyCols,keyExp,errors = readColMetadataLabels(lblFile)
2591        if errors:
2592            t = "Error reading file "+lblFile
2593            for l in errors:
2594                t += '\n'
2595                t += l
2596            t += "\n\nPlease edit the file and press OK (or Cancel to quit)"
2597            dlg = wx.MessageDialog(G2frame,message=t,
2598                caption="Read Error",style=wx.ICON_ERROR| wx.OK|wx.STAY_ON_TOP|wx.CANCEL)
2599            if dlg.ShowModal() != wx.ID_OK: return           
2600    # request a line number, read that line
2601    dlg = G2G.SingleStringDialog(G2frame,'Read what',
2602                                 'Enter a line number or an image file name (-1=last line)',
2603                                 '-1',size=(400,-1))
2604    if dlg.Show():
2605        fileorline = dlg.GetValue()
2606        dlg.Destroy()
2607    else:
2608        dlg.Destroy()
2609        return
2610    # and report the generated key pairs in metadata dict
2611    linenum = None
2612    try:
2613        linenum = int(fileorline)
2614    except:
2615        imageName = os.path.splitext(os.path.split(fileorline)[1])[0]
2616
2617    fp = open(parFile,'Ur')
2618    for iline,line in enumerate(fp):
2619        if linenum is not None:
2620            if iline == linenum:
2621                items = line.strip().split(' ')
2622                n = "Line {}".format(iline)
2623                break
2624            else:
2625                continue
2626        else:
2627            items = line.strip().split(' ')
2628            nameList = keyExp['filename'](*[items[j] for j in keyCols['filename']])
2629            if type(nameList) is str:
2630                if nameList != imageName: continue
2631                name = nameList
2632                break
2633            else:
2634                for name in nameList:
2635                    print (name,name == imageName)
2636                    if name == imageName:
2637                        n = "Image {} found in line {}".format(imageName,iline)
2638                        break # got a match
2639                else:
2640                    continue
2641                break
2642    else:
2643        if linenum is not None:
2644            n = "Line {}".format(iline)
2645        else:
2646            n = "Image {} not found. Reporting line {}".format(imageName,iline)
2647        items = line.strip().split(' ')
2648    fp.close()
2649    metadata = evalColMetadataDicts(items,labels,lbldict,keyCols,keyExp,True)
2650    title = "Results: ("+n+")"
2651    t = ['Files: '+parFile,lblFile,' ']
2652    n = ["Named parameters:"]
2653    l = ['',"Labeled columns:"]
2654    for key in sorted(metadata):
2655        if key == "filename" or key.startswith('label_prm'): continue
2656        if key in keyCols:
2657            n += [{} = {}".format(key,metadata[key])]
2658        elif key in lbldict.values():
2659            l += [{} = {}".format(key,metadata[key])]
2660        else:
2661            t += ["** Unexpected:  {}".format(key,metadata[key])]
2662    if type(metadata['filename']) is str:
2663        l += ["","Filename: "+ metadata['filename']]
2664    else:
2665        l += ["","Filename(s): "]
2666        for i,j in enumerate(metadata['filename']):
2667            if i: l[-1] += ', '
2668            l[-1] += j
2669    t += n + l + ['','Unused columns:']
2670    usedCols = list(lbldict.keys())
2671    for i in keyCols.values(): usedCols += i
2672    for i in range(len(items)):
2673        if i in usedCols: continue
2674        t += [{}: {}".format(i,items[i])]
2675    dlg = G2G.G2SingleChoiceDialog(None,title,'Column metadata parse results',t,
2676                                   monoFont=True,filterBox=False,size=(400,600),
2677                                   style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE|wx.OK)
2678    dlg.ShowModal()
2679
2680if __name__ == '__main__':
2681    import GSASIIdataGUI
2682    application = GSASIIdataGUI.GSASIImain(0)
2683    G2frame = application.main
2684    #app = wx.PySimpleApp()
2685    #G2frame = wx.Frame(None) # create a frame
2686    #frm.Show(True)
2687    #filename = '/tmp/notzip.zip'
2688    #filename = '/tmp/all.zip'
2689    #filename = '/tmp/11bmb_7652.zip'
2690   
2691    #selection=None, confirmoverwrite=True, parent=None
2692    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2693    #print ExtractFileFromZip(filename,multipleselect=True)
2694    #                         #confirmread=False, confirmoverwrite=False)
2695
2696    # choicelist=[ ('a','b','c'),
2697    #              ('test1','test2'),('no choice',)]
2698    # titles = [ 'a, b or c', 'tests', 'No option here']
2699    # dlg = MultipleChoicesDialog(
2700    #     choicelist,titles,
2701    #     parent=frm)
2702    # if dlg.ShowModal() == wx.ID_OK:
2703    #     print 'Got OK'
2704    imagefile = '/tmp/NDC5_00237_3.ge3'
2705    Comments, Data, Npix, Image = GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None)
2706
2707    print("\n\nResults loaded to Comments, Data, Npix and Image\n\n")
2708
2709    GSASIIpath.IPyBreak_base()
Note: See TracBrowser for help on using the repository browser.