source: trunk/GSASIIIO.py @ 4538

Last change on this file since 4538 was 4538, checked in by vondreele, 3 years ago

fix integer divide issue in ComputeArc? in G2plot
remove Spotmask is None message (Spotmask is missing was removed earlier)
in SaveIntegration? get polarization from dataPolaVal? instead of from Comments

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