source: trunk/GSASIIIO.py @ 4573

Last change on this file since 4573 was 4573, checked in by toby, 13 months ago

RB extensions and minor bug fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 92.1 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2020-09-21 20:43:06 +0000 (Mon, 21 Sep 2020) $
4# $Author: toby $
5# $Revision: 4573 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 4573 2020-09-21 20:43:06Z toby $
8########### SVN repository information ###################
9'''
10*GSASIIIO: Misc I/O routines*
11=============================
12
13Module with miscellaneous routines for input and output. Many
14are GUI routines to interact with user.
15
16Includes support for image reading.
17
18Also includes base 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: 4573 $")
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['outAzimuths'] > 1:
854        dazm = np.min(np.abs(np.diff(azms)))/2.
855    G2frame.IntgOutList = []
856    for i,azm in enumerate(azms[:-1]):
857        Aname = name+" Azm= %.2f"%((azm+dazm)%360.)
858        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
859        # if Overwrite delete any duplicate
860        if Overwrite and G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Aname):
861            print('Replacing '+Aname)
862            item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Aname)
863            G2frame.GPXtree.Delete(item)
864        else:
865            nOcc = 0
866            while item:
867                Name = G2frame.GPXtree.GetItemText(item)
868                if Aname in Name:
869                    nOcc += 1
870                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
871            if nOcc:
872                Aname += '(%d)'%(nOcc)
873        Sample = G2obj.SetDefaultSample()       #set as Debye-Scherrer
874        Sample['Gonio. radius'] = data['distance']
875        Sample['Omega'] = data['GonioAngles'][0]
876        Sample['Chi'] = data['GonioAngles'][1]
877        Sample['Phi'] = data['GonioAngles'][2]
878        Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
879        polariz = data['PolaVal'][0]
880        for item in Comments:
881            for key in ('Temperature','Pressure','Time','FreePrm1','FreePrm2','FreePrm3','Omega',
882                'Chi','Phi'):
883                if key.lower() in item.lower():
884                    try:
885                        Sample[key] = float(item.split('=')[1])
886                    except:
887                        pass
888            if 'label_prm' in item.lower():
889                for num in ('1','2','3'):
890                    if 'label_prm'+num in item.lower():
891                        Controls['FreePrm'+num] = item.split('=')[1].strip()
892        if 'PWDR' in Aname:
893            if 'target' in data:    #from lab x-ray 2D imaging data
894                wave1,wave2 = waves[data['target']]
895                parms = ['PXC',wave1,wave2,0.5,0.0,polariz,290.,-40.,30.,6.,-14.,0.0,0.0001,Azms[i]]
896            else:
897                parms = ['PXC',data['wavelength'],0.0,polariz,1.0,-0.10,0.4,0.30,1.0,0.0,0.0001,Azms[i]]
898        elif 'SASD' in Aname:
899            Sample['Trans'] = data['SampleAbs'][0]
900            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
901        Y = G2frame.Integrate[0][i]
902        Ymin = np.min(Y)
903        Ymax = np.max(Y)
904        W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
905        Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text=Aname)
906        G2frame.IntgOutList.append(Id)
907        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Comments'),Comments)                   
908        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Limits'),copy.deepcopy([tuple(Xminmax),Xminmax]))
909        if 'PWDR' in Aname:
910            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Background'),[['chebyschev-1',1,3,1.0,0.0,0.0],
911                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
912        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
913        for item in inst[0]:
914            inst[0][item] = list(inst[0][item])
915        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Instrument Parameters'),inst)
916        if 'PWDR' in Aname:
917            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Sample Parameters'),Sample)
918            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Peak List'),{'sigDict':{},'peaks':[]})
919            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Index Peak List'),[[],[]])
920            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Unit Cells List'),[])
921            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Reflection Lists'),{})
922        elif 'SASD' in Aname:             
923            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
924            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Sample Parameters'),Sample)
925            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
926        valuesdict = {
927            'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxsize),'Offset':[0.0,0.0],'delOffset':0.02*Ymax,
928            'refOffset':-0.1*Ymax,'refDelt':0.1*Ymax,'Yminmax':[Ymin,Ymax]}
929        G2frame.GPXtree.SetItemPyData(Id,[valuesdict,
930            [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
931    return Id       #last powder pattern generated
932   
933def XYsave(G2frame,XY,labelX='X',labelY='Y',names=[]):
934    'Save XY table data'
935    pth = G2G.GetExportPath(G2frame)
936    dlg = wx.FileDialog(
937        G2frame, 'Enter csv filename for XY table', pth, '',
938        'XY table file (*.csv)|*.csv',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
939    try:
940        if dlg.ShowModal() == wx.ID_OK:
941            filename = dlg.GetPath()
942            filename = os.path.splitext(filename)[0]+'.csv'
943            File = open(filename,'w')
944        else:
945            filename = None
946    finally:
947        dlg.Destroy()
948    if not filename:
949        return
950    for i in range(len(XY)):
951        if len(names):
952            header = '%s,%s(%s)\n'%(labelX,labelY,names[i])
953        else:
954            header = '%s,%s(%d)\n'%(labelX,labelY,i)
955        File.write(header)
956        for x,y in XY[i].T:
957            File.write('%.3f,%.3f\n'%(x,y))   
958    File.close()
959    print (' XY data saved to: '+filename)                       
960   
961def PeakListSave(G2frame,file,peaks):
962    'Save powder peaks to a data file'
963    print ('save peak list to file: '+G2frame.peaklistfile)
964    if not peaks:
965        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
966        try:
967            dlg.ShowModal()
968        finally:
969            dlg.Destroy()
970        return
971    for peak in peaks:
972        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
973            (peak[0],peak[2],peak[4],peak[6]))
974    print ('peak list saved')
975             
976def IndexPeakListSave(G2frame,peaks):
977    'Save powder peaks from the indexing list'
978    file = open(G2frame.peaklistfile,'wa')
979    print ('save index peak list to file: '+G2frame.peaklistfile)
980    wx.BeginBusyCursor()
981    try:
982        if not peaks:
983            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
984            try:
985                dlg.ShowModal()
986            finally:
987                dlg.Destroy()
988            return
989        for peak in peaks:
990            file.write("%12.6f\n" % (peak[7]))
991        file.close()
992    finally:
993        wx.EndBusyCursor()
994    print ('index peak list saved')
995               
996def ExtractFileFromZip(filename, selection=None, confirmread=True,
997                       confirmoverwrite=True, parent=None,
998                       multipleselect=False):
999    '''If the filename is a zip file, extract a file from that
1000    archive.
1001
1002    :param list Selection: used to predefine the name of the file
1003      to be extracted. Filename case and zip directory name are
1004      ignored in selection; the first matching file is used.
1005
1006    :param bool confirmread: if True asks the user to confirm before expanding
1007      the only file in a zip
1008
1009    :param bool confirmoverwrite: if True asks the user to confirm
1010      before overwriting if the extracted file already exists
1011
1012    :param bool multipleselect: if True allows more than one zip
1013      file to be extracted, a list of file(s) is returned.
1014      If only one file is present, do not ask which one, otherwise
1015      offer a list of choices (unless selection is used).
1016   
1017    :returns: the name of the file that has been created or a
1018      list of files (see multipleselect)
1019
1020    If the file is not a zipfile, return the name of the input file.
1021    If the zipfile is empty or no file has been selected, return None
1022    '''
1023    import zipfile # do this now, since we can save startup time by doing this only on need
1024    import shutil
1025    zloc = os.path.split(filename)[0]
1026    if not zipfile.is_zipfile(filename):
1027        #print("not zip")
1028        return filename
1029
1030    z = zipfile.ZipFile(filename,'r')
1031    zinfo = z.infolist()
1032
1033    if len(zinfo) == 0:
1034        #print('Zip has no files!')
1035        zlist = [-1]
1036    if selection:
1037        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1038        if selection.lower() in choices:
1039            zlist = [choices.index(selection.lower())]
1040        else:
1041            print('debug: file '+str(selection)+' was not found in '+str(filename))
1042            zlist = [-1]
1043    elif len(zinfo) == 1 and confirmread:
1044        result = wx.ID_NO
1045        dlg = wx.MessageDialog(
1046            parent,
1047            'Is file '+str(zinfo[0].filename)+
1048            ' what you want to extract from '+
1049            str(os.path.split(filename)[1])+'?',
1050            'Confirm file', 
1051            wx.YES_NO | wx.ICON_QUESTION)
1052        try:
1053            result = dlg.ShowModal()
1054        finally:
1055            dlg.Destroy()
1056        if result == wx.ID_NO:
1057            zlist = [-1]
1058        else:
1059            zlist = [0]
1060    elif len(zinfo) == 1:
1061        zlist = [0]
1062    elif multipleselect:
1063        # select one or more from a from list
1064        choices = [i.filename for i in zinfo]
1065        dlg = G2G.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1066            'Choose file(s)',choices)
1067        if dlg.ShowModal() == wx.ID_OK:
1068            zlist = dlg.GetSelections()
1069        else:
1070            zlist = []
1071        dlg.Destroy()
1072    else:
1073        # select one from a from list
1074        choices = [i.filename for i in zinfo]
1075        dlg = wx.SingleChoiceDialog(parent,
1076            'Select file to extract from zip file'+str(filename),'Choose file',
1077            choices,)
1078        if dlg.ShowModal() == wx.ID_OK:
1079            zlist = [dlg.GetSelection()]
1080        else:
1081            zlist = [-1]
1082        dlg.Destroy()
1083       
1084    outlist = []
1085    for zindex in zlist:
1086        if zindex >= 0:
1087            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1088            if os.path.exists(efil) and confirmoverwrite:
1089                result = wx.ID_NO
1090                dlg = wx.MessageDialog(parent,
1091                    'File '+str(efil)+' already exists. OK to overwrite it?',
1092                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1093                try:
1094                    result = dlg.ShowModal()
1095                finally:
1096                    dlg.Destroy()
1097                if result == wx.ID_NO:
1098                    zindex = -1
1099        if zindex >= 0:
1100            # extract the file to the current directory, regardless of it's original path
1101            #z.extract(zinfo[zindex],zloc)
1102            eloc,efil = os.path.split(zinfo[zindex].filename)
1103            outfile = os.path.join(zloc, efil)
1104            fpin = z.open(zinfo[zindex])
1105            fpout = open(outfile, "wb")
1106            shutil.copyfileobj(fpin, fpout)
1107            fpin.close()
1108            fpout.close()
1109            outlist.append(outfile)
1110    z.close()
1111    if multipleselect and len(outlist) >= 1:
1112        return outlist
1113    elif len(outlist) == 1:
1114        return outlist[0]
1115    else:
1116        return None
1117
1118def striphist(var,insChar=''):
1119    'strip a histogram number from a var name'
1120    sv = var.split(':')
1121    if len(sv) <= 1: return var
1122    if sv[1]:
1123        sv[1] = insChar
1124    return ':'.join(sv)
1125
1126######################################################################
1127# base classes for reading various types of data files
1128#   not used directly, only by subclassing
1129######################################################################
1130class ExportBaseclass(object):
1131    '''Defines a base class for the exporting of GSAS-II results.
1132
1133    This class is subclassed in the various exports/G2export_*.py files. Those files
1134    are imported in :meth:`GSASIIdataGUI.GSASII._init_Exports` which defines the
1135    appropriate menu items for each one and the .Exporter method is called
1136    directly from the menu item.
1137
1138    Routines may also define a .Writer method, which is used to write a single
1139    file without invoking any GUI objects.
1140    '''
1141    # TODO: review exporters producing exceptions where .Writer can't be used where G2frame is None (see CIF)
1142    # TODO: review conflicting uses of .Writer with mode (SeqRef) & elsewhere
1143    # TODO: move this class to G2fil
1144    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1145        self.G2frame = G2frame
1146        self.formatName = formatName # short string naming file type
1147        self.extension = extension
1148        if longFormatName: # longer string naming file type
1149            self.longFormatName = longFormatName
1150        else:
1151            self.longFormatName = formatName
1152        self.OverallParms = {}
1153        self.Phases = {}
1154        self.Histograms = {}
1155        self.powderDict = {}
1156        self.sasdDict = {}
1157        self.refdDict = {}
1158        self.xtalDict = {}
1159        self.parmDict = {}
1160        self.sigDict = {}
1161        self.fp = None
1162        # updated in InitExport:
1163        self.currentExportType = None # type of export that has been requested
1164        # updated in ExportSelect (when used):
1165        self.phasenam = None # a list of selected phases
1166        self.histnam = None # a list of selected histograms
1167        self.filename = None # name of file to be written (single export) or template (multiple files)
1168        self.dirname = '' # name of directory where file(s) will be written
1169        self.fullpath = '' # name of file being written -- full path
1170       
1171        # items that should be defined in a subclass of this class
1172        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1173        # The following types are defined: 'project', "phase", "powder", "single"
1174        self.multiple = False # set as True if the class can export multiple phases or histograms
1175        # self.multiple is ignored for "project" exports
1176
1177    def InitExport(self,event):
1178        '''Determines the type of menu that called the Exporter and
1179        misc initialization.
1180        '''
1181        self.filename = None # name of file to be written (single export)
1182        self.dirname = '' # name of file to be written (multiple export)
1183        if event:
1184            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1185
1186    def MakePWDRfilename(self,hist):
1187        '''Make a filename root (no extension) from a PWDR histogram name
1188
1189        :param str hist: the histogram name in data tree (starts with "PWDR ")
1190        '''
1191        file0 = ''
1192        file1 = hist[5:]
1193        # replace repeated blanks
1194        while file1 != file0:
1195            file0 = file1
1196            file1 = file0.replace('  ',' ').strip()
1197        file0 = file1.replace('Azm= ','A')
1198        # if angle has unneeded decimal places on aziumuth, remove them
1199        if file0[-3:] == '.00': file0 = file0[:-3]
1200        file0 = file0.replace('.','_')
1201        file0 = file0.replace(' ','_')
1202        return file0
1203
1204    def ExportSelect(self,AskFile='ask'):
1205        '''Selects histograms or phases when needed. Sets a default file name when
1206        requested into self.filename; always sets a default directory in self.dirname.
1207
1208        :param bool AskFile: Determines how this routine processes getting a
1209          location to store the current export(s).
1210         
1211          * if AskFile is 'ask' (default option), get the name of the file to be written;
1212            self.filename and self.dirname are always set. In the case where
1213            multiple files must be generated, the export routine should do this
1214            based on self.filename as a template.
1215          * if AskFile is 'dir', get the name of the directory to be used;
1216            self.filename is not used, but self.dirname is always set. The export routine
1217            will always generate the file name.
1218          * if AskFile is 'single', get only the name of the directory to be used when
1219            multiple items will be written (as multiple files) are used
1220            *or* a complete file name is requested when a single file
1221            name is selected. self.dirname is always set and self.filename used
1222            only when a single file is selected. 
1223          * if AskFile is 'default', creates a name of the file to be used from
1224            the name of the project (.gpx) file. If the project has not been saved,
1225            then the name of file is requested.
1226            self.filename and self.dirname are always set. In the case where
1227            multiple file names must be generated, the export routine should do this
1228            based on self.filename.
1229          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1230            file. If the project has not been saved, then a directory is requested.
1231            self.filename is not used.
1232
1233        :returns: True in case of an error
1234        '''
1235       
1236        numselected = 1
1237        if self.currentExportType == 'phase':
1238            if len(self.Phases) == 0:
1239                self.G2frame.ErrorDialog(
1240                    'Empty project',
1241                    'Project does not contain any phases.')
1242                return True
1243            elif len(self.Phases) == 1:
1244                self.phasenam = list(self.Phases.keys())
1245            elif self.multiple: 
1246                choices = sorted(self.Phases.keys())
1247                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1248                if phasenum is None: return True
1249                self.phasenam = [choices[i] for i in phasenum]
1250                if not self.phasenam: return True
1251                numselected = len(self.phasenam)
1252            else:
1253                choices = sorted(self.Phases.keys())
1254                phasenum = G2G.ItemSelector(choices,self.G2frame)
1255                if phasenum is None: return True
1256                self.phasenam = [choices[phasenum]]
1257                numselected = len(self.phasenam)
1258        elif self.currentExportType == 'single':
1259            if len(self.xtalDict) == 0:
1260                self.G2frame.ErrorDialog(
1261                    'Empty project',
1262                    'Project does not contain any single crystal data.')
1263                return True
1264            elif len(self.xtalDict) == 1:
1265                self.histnam = self.xtalDict.values()
1266            elif self.multiple:
1267                choices = sorted(self.xtalDict.values())
1268                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1269                if not hnum: return True
1270                self.histnam = [choices[i] for i in hnum]
1271                numselected = len(self.histnam)
1272            else:
1273                choices = sorted(self.xtalDict.values())
1274                hnum = G2G.ItemSelector(choices,self.G2frame)
1275                if hnum is None: return True
1276                self.histnam = [choices[hnum]]
1277                numselected = len(self.histnam)
1278        elif self.currentExportType == 'powder':
1279            if len(self.powderDict) == 0:
1280                self.G2frame.ErrorDialog(
1281                    'Empty project',
1282                    'Project does not contain any powder data.')
1283                return True
1284            elif len(self.powderDict) == 1:
1285                self.histnam = list(self.powderDict.values())
1286            elif self.multiple:
1287                choices = sorted(self.powderDict.values())
1288                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1289                if not hnum: return True
1290                self.histnam = [choices[i] for i in hnum]
1291                numselected = len(self.histnam)
1292            else:
1293                choices = sorted(self.powderDict.values())
1294                hnum = G2G.ItemSelector(choices,self.G2frame)
1295                if hnum is None: return True
1296                self.histnam = [choices[hnum]]
1297                numselected = len(self.histnam)
1298        elif self.currentExportType == 'sasd':
1299            if len(self.sasdDict) == 0:
1300                self.G2frame.ErrorDialog(
1301                    'Empty project',
1302                    'Project does not contain any small angle data.')
1303                return True
1304            elif len(self.sasdDict) == 1:
1305                self.histnam = self.sasdDict.values()
1306            elif self.multiple:
1307                choices = sorted(self.sasdDict.values())
1308                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1309                if not hnum: return True
1310                self.histnam = [choices[i] for i in hnum]
1311                numselected = len(self.histnam)
1312            else:
1313                choices = sorted(self.sasdDict.values())
1314                hnum = G2G.ItemSelector(choices,self.G2frame)
1315                if hnum is None: return True
1316                self.histnam = [choices[hnum]]
1317                numselected = len(self.histnam)
1318        elif self.currentExportType == 'refd':
1319            if len(self.refdDict) == 0:
1320                self.G2frame.ErrorDialog(
1321                    'Empty project',
1322                    'Project does not contain any reflectivity data.')
1323                return True
1324            elif len(self.refdDict) == 1:
1325                self.histnam = self.refdDict.values()
1326            elif self.multiple:
1327                choices = sorted(self.refdDict.values())
1328                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1329                if not hnum: return True
1330                self.histnam = [choices[i] for i in hnum]
1331                numselected = len(self.histnam)
1332            else:
1333                choices = sorted(self.refdDict.values())
1334                hnum = G2G.ItemSelector(choices,self.G2frame)
1335                if hnum is None: return True
1336                self.histnam = [choices[hnum]]
1337                numselected = len(self.histnam)
1338        elif self.currentExportType == 'image':
1339            if len(self.Histograms) == 0:
1340                self.G2frame.ErrorDialog(
1341                    'Empty project',
1342                    'Project does not contain any images.')
1343                return True
1344            elif len(self.Histograms) == 1:
1345                self.histnam = list(self.Histograms.keys())
1346            else:
1347                choices = sorted(self.Histograms.keys())
1348                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1349                if self.multiple:
1350                    if not hnum: return True
1351                    self.histnam = [choices[i] for i in hnum]
1352                else:
1353                    if hnum is None: return True
1354                    self.histnam = [choices[hnum]]
1355                numselected = len(self.histnam)
1356        if self.currentExportType == 'map':
1357            # search for phases with maps
1358            mapPhases = []
1359            choices = []
1360            for phasenam in sorted(self.Phases):
1361                phasedict = self.Phases[phasenam] # pointer to current phase info           
1362                if len(phasedict['General']['Map'].get('rho',[])):
1363                    mapPhases.append(phasenam)
1364                    if phasedict['General']['Map'].get('Flip'):
1365                        choices.append('Charge flip map: '+str(phasenam))
1366                    elif phasedict['General']['Map'].get('MapType'):
1367                        choices.append(
1368                            str(phasedict['General']['Map'].get('MapType'))
1369                            + ' map: ' + str(phasenam))
1370                    else:
1371                        choices.append('unknown map: '+str(phasenam))
1372            # select a map if needed
1373            if len(mapPhases) == 0:
1374                self.G2frame.ErrorDialog(
1375                    'Empty project',
1376                    'Project does not contain any maps.')
1377                return True
1378            elif len(mapPhases) == 1:
1379                self.phasenam = mapPhases
1380            else: 
1381                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1382                if self.multiple:
1383                    if not phasenum: return True
1384                    self.phasenam = [mapPhases[i] for i in phasenum]
1385                else:
1386                    if phasenum is None: return True
1387                    self.phasenam = [mapPhases[phasenum]]
1388            numselected = len(self.phasenam)
1389
1390        # items selected, now set self.dirname and usually self.filename
1391        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
1392            AskFile == 'default' and not self.G2frame.GSASprojectfile
1393            ):
1394            filename = self.askSaveFile()
1395            if not filename: return True
1396            self.dirname,self.filename = os.path.split(filename)
1397        elif AskFile == 'dir' or AskFile == 'single' or (
1398            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
1399            ):
1400            self.dirname = self.askSaveDirectory()
1401            if not self.dirname: return True
1402        elif AskFile == 'default-dir' or AskFile == 'default':
1403            self.dirname,self.filename = os.path.split(
1404                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
1405                )
1406        else:
1407            raise Exception('This should not happen!')
1408
1409    def loadParmDict(self):
1410        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
1411        refined values to those from the last cycle and set the uncertainties for the
1412        refined parameters in another dict (self.sigDict).
1413
1414        Expands the parm & sig dicts to include values derived from constraints.
1415
1416        This could be made faster for sequential fits by reducing the histogram list to only
1417        the active histogram being exported.
1418        '''
1419        self.G2frame.CheckNotebook()
1420        self.parmDict = {}
1421        self.sigDict = {}
1422        rigidbodyDict = {}
1423        covDict = {}
1424        consDict = {}
1425        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1426        if self.G2frame.GPXtree.IsEmpty(): return # nothing to do
1427        item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1428        while item:
1429            name = self.G2frame.GPXtree.GetItemText(item)
1430            if name == 'Rigid bodies':
1431                 rigidbodyDict = self.G2frame.GPXtree.GetItemPyData(item)
1432            elif name == 'Covariance':
1433                 covDict = self.G2frame.GPXtree.GetItemPyData(item)
1434            elif name == 'Constraints':
1435                 consDict = self.G2frame.GPXtree.GetItemPyData(item)
1436            item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1437        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1438        self.parmDict.update(rbDict)
1439        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1440        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave =  G2stIO.GetPhaseData(
1441            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
1442        self.parmDict.update(phaseDict)
1443        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
1444            Phases,Histograms,Print=False,resetRefList=False)
1445        self.parmDict.update(hapDict)
1446        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
1447        self.parmDict.update(histDict)
1448        self.parmDict.update(zip(
1449            covDict.get('varyList',[]),
1450            covDict.get('variables',[])))
1451        self.sigDict = dict(zip(
1452            covDict.get('varyList',[]),
1453            covDict.get('sig',[])))
1454        # expand to include constraints: first compile a list of constraints
1455        constList = []
1456        for item in consDict:
1457            if item.startswith('_'): continue
1458            constList += consDict[item]
1459        # now process the constraints
1460        G2mv.InitVars()
1461        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
1462        varyList = covDict.get('varyListStart')
1463        if varyList is None and len(constDict) == 0:
1464            # no constraints can use varyList
1465            varyList = covDict.get('varyList')
1466        elif varyList is None:
1467            # old GPX file from before pre-constraint varyList is saved
1468            print (' *** Old refinement: Please use Calculate/Refine to redo  ***')
1469            raise Exception(' *** Export aborted ***')
1470        else:
1471            varyList = list(varyList)
1472        # add symmetry-generated constraints
1473        rigidbodyDict = self.G2frame.GPXtree.GetItemPyData(   
1474            G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Rigid bodies'))
1475        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1476        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1477        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData(
1478            Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
1479        msg = G2mv.EvaluateMultipliers(constDict,phaseDict)
1480        if msg:
1481            print('Unable to interpret multiplier(s): '+msg)
1482            raise Exception(' *** CIF creation aborted ***')
1483        try:
1484            G2mv.GenerateConstraints(varyList,constDict,fixedList,self.parmDict)
1485            #print(G2mv.VarRemapShow(varyList))
1486        except:
1487            # this really should not happen
1488            print (' *** ERROR - constraints are internally inconsistent ***')
1489            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
1490            print ('Errors'+errmsg)
1491            if warnmsg: print ('Warnings'+warnmsg)
1492            raise Exception(' *** CIF creation aborted ***')
1493        # add the constrained values to the parameter dictionary
1494        G2mv.Dict2Map(self.parmDict,varyList)
1495        # and add their uncertainties into the esd dictionary (sigDict)
1496        if covDict.get('covMatrix') is not None:
1497            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
1498
1499    def loadTree(self):
1500        '''Load the contents of the data tree into a set of dicts
1501        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
1502        & self.xtalDict)
1503       
1504        * The childrenless data tree items are overall parameters/controls for the
1505          entire project and are placed in self.OverallParms
1506        * Phase items are placed in self.Phases
1507        * Data items are placed in self.Histogram. The key for these data items
1508          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
1509        '''
1510        self.OverallParms = {}
1511        self.powderDict = {}
1512        self.sasdDict = {}
1513        self.refdDict = {}
1514        self.xtalDict = {}
1515        self.Phases = {}
1516        self.Histograms = {}
1517        self.SeqRefdata = None
1518        self.SeqRefhist = None
1519        if self.G2frame.GPXtree.IsEmpty(): return # nothing to do
1520        histType = None       
1521        if self.currentExportType == 'phase':
1522            # if exporting phases load them here
1523            sub = G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Phases')
1524            if not sub:
1525                print ('no phases found')
1526                return True
1527            item, cookie = self.G2frame.GPXtree.GetFirstChild(sub)
1528            while item:
1529                phaseName = self.G2frame.GPXtree.GetItemText(item)
1530                self.Phases[phaseName] =  self.G2frame.GPXtree.GetItemPyData(item)
1531                item, cookie = self.G2frame.GPXtree.GetNextChild(sub, cookie)
1532            return
1533        elif self.currentExportType == 'single':
1534            histType = 'HKLF'
1535        elif self.currentExportType == 'powder':
1536            histType = 'PWDR'
1537        elif self.currentExportType == 'image':
1538            histType = 'IMG'
1539        elif self.currentExportType == 'sasd':
1540            histType = 'SASD'
1541        elif self.currentExportType == 'refd':
1542            histType = 'REFD'
1543
1544        if histType: # Loading just one kind of tree entry
1545            item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1546            while item:
1547                name = self.G2frame.GPXtree.GetItemText(item)
1548                if name.startswith(histType):
1549                    if self.Histograms.get(name): # there is already an item with this name
1550                        print('Histogram name '+str(name)+' is repeated. Renaming')
1551                        if name[-1] == '9':
1552                            name = name[:-1] + '10'
1553                        elif name[-1] in '012345678':
1554                            name = name[:-1] + str(int(name[-1])+1)
1555                        else:                           
1556                            name += '-1'
1557                    self.Histograms[name] = {}
1558                    # the main info goes into Data, but the 0th
1559                    # element contains refinement results, carry
1560                    # that over too now.
1561                    self.Histograms[name]['Data'] = self.G2frame.GPXtree.GetItemPyData(item)[1]
1562                    self.Histograms[name][0] = self.G2frame.GPXtree.GetItemPyData(item)[0]
1563                    item2, cookie2 = self.G2frame.GPXtree.GetFirstChild(item)
1564                    while item2: 
1565                        child = self.G2frame.GPXtree.GetItemText(item2)
1566                        self.Histograms[name][child] = self.G2frame.GPXtree.GetItemPyData(item2)
1567                        item2, cookie2 = self.G2frame.GPXtree.GetNextChild(item, cookie2)
1568                item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1569            # index powder and single crystal histograms by number
1570            for hist in self.Histograms:
1571                if hist.startswith("PWDR"): 
1572                    d = self.powderDict
1573                elif hist.startswith("HKLF"): 
1574                    d = self.xtalDict
1575                elif hist.startswith("SASD"):
1576                    d = self.sasdDict
1577                elif hist.startswith("REFD"):
1578                    d = self.refdDict
1579                else:
1580                    return                   
1581                i = self.Histograms[hist].get('hId')
1582                if i is None and not d.keys():
1583                    i = 0
1584                elif i is None or i in d.keys():
1585                    i = max(d.keys())+1
1586                d[i] = hist
1587            return
1588        # else standard load: using all interlinked phases and histograms
1589        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1590        item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1591        while item:
1592            name = self.G2frame.GPXtree.GetItemText(item)
1593            item2, cookie2 = self.G2frame.GPXtree.GetFirstChild(item)
1594            if not item2: 
1595                self.OverallParms[name] = self.G2frame.GPXtree.GetItemPyData(item)
1596            item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1597        # index powder and single crystal histograms
1598        for hist in self.Histograms:
1599            i = self.Histograms[hist]['hId']
1600            if hist.startswith("PWDR"): 
1601                self.powderDict[i] = hist
1602            elif hist.startswith("HKLF"): 
1603                self.xtalDict[i] = hist
1604            elif hist.startswith("SASD"):
1605                self.sasdDict[i] = hist
1606            elif hist.startswith("REFD"):
1607                self.refdDict[i] = hist
1608
1609    def dumpTree(self,mode='type'):
1610        '''Print out information on the data tree dicts loaded in loadTree.
1611        Used for testing only.
1612        '''
1613        if self.SeqRefdata and self.SeqRefhist:
1614            print('Note that dumpTree does not show sequential results')
1615        print ('\nOverall')
1616        if mode == 'type':
1617            def Show(arg): return type(arg)
1618        else:
1619            def Show(arg): return arg
1620        for key in self.OverallParms:
1621            print ('  '+key+Show(self.OverallParms[key]))
1622        print ('Phases')
1623        for key1 in self.Phases:
1624            print ('    '+key1+Show(self.Phases[key1]))
1625        print ('Histogram')
1626        for key1 in self.Histograms:
1627            print ('    '+key1+Show(self.Histograms[key1]))
1628            for key2 in self.Histograms[key1]:
1629                print ('      '+key2+Show(self.Histograms[key1][key2]))
1630
1631    def defaultSaveFile(self):
1632        return os.path.abspath(
1633            os.path.splitext(self.G2frame.GSASprojectfile
1634                             )[0]+self.extension)
1635       
1636    def askSaveFile(self):
1637        '''Ask the user to supply a file name
1638
1639        :returns: a file name (str) or None if Cancel is pressed
1640
1641        '''
1642        #pth = G2G.GetExportPath(self.G2frame)
1643        if self.G2frame.GSASprojectfile:
1644            defnam = os.path.splitext(
1645                os.path.split(self.G2frame.GSASprojectfile)[1]
1646                )[0]+self.extension
1647        else:
1648            defnam = 'default' + self.extension
1649        return G2G.askSaveFile(self.G2frame,defnam,self.extension,self.longFormatName)
1650
1651    def askSaveDirectory(self):
1652        '''Ask the user to supply a directory name. Path name is used as the
1653        starting point for the next export path search.
1654
1655        :returns: a directory name (str) or None if Cancel is pressed
1656
1657        TODO: Can this be replaced with G2G.askSaveDirectory?
1658        '''
1659        pth = G2G.GetExportPath(self.G2frame)
1660        dlg = wx.DirDialog(
1661            self.G2frame, 'Input directory where file(s) will be written', pth,
1662            wx.DD_DEFAULT_STYLE)
1663        dlg.CenterOnParent()
1664        try:
1665            if dlg.ShowModal() == wx.ID_OK:
1666                filename = dlg.GetPath()
1667                self.G2frame.LastExportDir = filename
1668            else:
1669                filename = None
1670        finally:
1671            dlg.Destroy()
1672        return filename
1673
1674    # Tools for file writing.
1675    def OpenFile(self,fil=None,mode='w'):
1676        '''Open the output file
1677
1678        :param str fil: The name of the file to open. If None (default)
1679          the name defaults to self.dirname + self.filename.
1680          If an extension is supplied, it is not overridded,
1681          but if not, the default extension is used.
1682        :returns: the file object opened by the routine which is also
1683          saved as self.fp
1684        '''
1685        if mode == 'd': # debug mode
1686            self.fullpath = '(stdout)'
1687            self.fp = sys.stdout
1688            return
1689        if not fil:
1690            if not os.path.splitext(self.filename)[1]:
1691                self.filename += self.extension
1692            fil = os.path.join(self.dirname,self.filename)
1693        self.fullpath = os.path.abspath(fil)
1694        self.fp = open(self.fullpath,mode)
1695        return self.fp
1696
1697    def Write(self,line):
1698        '''write a line of output, attaching a line-end character
1699
1700        :param str line: the text to be written.
1701        '''
1702        if self.fp is None:
1703            raise Exception('Attempt to Write without use of OpenFile')
1704        self.fp.write(line+'\n')
1705       
1706    def CloseFile(self,fp=None):
1707        '''Close a file opened in OpenFile
1708
1709        :param file fp: the file object to be closed. If None (default)
1710          file object self.fp is closed.
1711        '''
1712        if self.fp is None:
1713            raise Exception('Attempt to CloseFile without use of OpenFile')
1714        if self.fp == sys.stdout: return # debug mode
1715        if fp is None:
1716            fp = self.fp
1717            self.fp = None
1718        if fp is not None: fp.close()
1719       
1720    def SetSeqRef(self,data,hist):
1721        '''Set the exporter to retrieve results from a sequential refinement
1722        rather than the main tree
1723        '''
1724        self.SeqRefdata = data
1725        self.SeqRefhist = hist
1726        data_name = data[hist]
1727        for i,val in zip(data_name['varyList'],data_name['sig']):
1728            self.sigDict[i] = val
1729            self.sigDict[striphist(i)] = val
1730        for i in data_name['parmDict']:
1731            self.parmDict[striphist(i)] = data_name['parmDict'][i]
1732            self.parmDict[i] = data_name['parmDict'][i]
1733            # zero out the dA[xyz] terms, they would only bring confusion
1734            key = i.split(':')
1735            if len(key) < 3: continue
1736            if key[2].startswith('dA'):
1737                self.parmDict[i] = 0.0
1738        for i,(val,sig) in data_name.get('depParmDict',{}).items():
1739            self.parmDict[i] = val
1740            self.sigDict[i] = sig
1741        #GSASIIpath.IPyBreak()
1742
1743    # Tools to pull information out of the data arrays
1744    def GetCell(self,phasenam):
1745        """Gets the unit cell parameters and their s.u.'s for a selected phase
1746
1747        :param str phasenam: the name for the selected phase
1748        :returns: `cellList,cellSig` where each is a 7 element list corresponding
1749          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
1750          cell values and `cellSig` has their uncertainties.
1751        """
1752        if self.SeqRefdata and self.SeqRefhist:
1753            return self.GetSeqCell(phasenam,self.SeqRefdata[self.SeqRefhist])
1754        phasedict = self.Phases[phasenam] # pointer to current phase info
1755        try:
1756            pfx = str(phasedict['pId'])+'::'
1757            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
1758            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
1759                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
1760            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
1761            return cellList,cellSig
1762        except KeyError:
1763            cell = phasedict['General']['Cell'][1:]
1764            return cell,7*[0]
1765           
1766    def GetSeqCell(self,phasenam,data_name):
1767        """Gets the unit cell parameters and their s.u.'s for a selected phase
1768        and histogram in a sequential fit
1769
1770        :param str phasenam: the name for the selected phase
1771        :param dict data_name: the sequential refinement parameters for the selected histogram
1772        :returns: `cellList,cellSig` where each is a 7 element list corresponding
1773          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
1774          cell values and `cellSig` has their uncertainties.
1775        """
1776        phasedict = self.Phases[phasenam]
1777        SGdata = phasedict['General']['SGData']
1778        pId = phasedict['pId']
1779        RecpCellTerms = G2lat.cell2A(phasedict['General']['Cell'][1:7])
1780        ESDlookup = {}
1781        Dlookup = {}
1782        varied = [striphist(i) for i in data_name['varyList']]
1783        for item,val in data_name['newCellDict'].items():
1784            if item in varied:
1785                ESDlookup[val[0]] = item
1786                Dlookup[item] = val[0]
1787        A = RecpCellTerms[:]
1788        for i in range(6):
1789            var = str(pId)+'::A'+str(i)
1790            if var in ESDlookup:
1791                A[i] = data_name['newCellDict'][ESDlookup[var]][1] # override with refined value
1792        cellDict = dict(zip([str(pId)+'::A'+str(i) for i in range(6)],A))
1793        zeroDict = {i:0.0 for i in cellDict}
1794        A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata,cellDict,zeroDict)
1795        covData = {
1796            'varyList': [Dlookup.get(striphist(v),v) for v in data_name['varyList']],
1797            'covMatrix': data_name['covMatrix']
1798            }
1799        return list(G2lat.A2cell(A)) + [G2lat.calc_V(A)], G2stIO.getCellEsd(str(pId)+'::',SGdata,A,covData)
1800               
1801    def GetAtoms(self,phasenam):
1802        """Gets the atoms associated with a phase. Can be used with standard
1803        or macromolecular phases
1804
1805        :param str phasenam: the name for the selected phase
1806        :returns: a list of items for eac atom where each item is a list containing:
1807          label, typ, mult, xyz, and td, where
1808
1809          * label and typ are the atom label and the scattering factor type (str)
1810          * mult is the site multiplicity (int)
1811          * xyz is contains a list with four pairs of numbers:
1812            x, y, z and fractional occupancy and
1813            their standard uncertainty (or a negative value)
1814          * td is contains a list with either one or six pairs of numbers:
1815            if one number it is U\ :sub:`iso` and with six numbers it is
1816            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
1817            paired with their standard uncertainty (or a negative value)
1818        """
1819        phasedict = self.Phases[phasenam] # pointer to current phase info           
1820        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
1821        cfrac = cx+3
1822        fpfx = str(phasedict['pId'])+'::Afrac:'       
1823        atomslist = []
1824        for i,at in enumerate(phasedict['Atoms']):
1825            if phasedict['General']['Type'] == 'macromolecular':
1826                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
1827            else:
1828                label = at[ct-1]
1829            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
1830            fsig = self.sigDict.get(fpfx+str(i),-0.009)
1831            mult = at[cs+1]
1832            typ = at[ct]
1833            xyz = []
1834            for j,v in enumerate(('x','y','z')):
1835                val = at[cx+j]
1836                pfx = str(phasedict['pId']) + '::A' + v + ':' + str(i)
1837                val = self.parmDict.get(pfx, val)
1838                dpfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
1839                sig = self.sigDict.get(dpfx,-0.000009)
1840                xyz.append((val,sig))
1841            xyz.append((fval,fsig))
1842            td = []
1843            if at[cia] == 'I':
1844                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
1845                val = self.parmDict.get(pfx,at[cia+1])
1846                sig = self.sigDict.get(pfx,-0.0009)
1847                td.append((val,sig))
1848            else:
1849                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
1850                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
1851                    val = self.parmDict.get(pfx,at[cia+2+i])
1852                    sig = self.sigDict.get(pfx,-0.0009)
1853                    td.append((val,sig))
1854            atomslist.append((label,typ,mult,xyz,td))
1855        return atomslist
1856######################################################################
1857def ExportPowderList(G2frame):
1858    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
1859    along with their descriptions (note that a extension may be repeated
1860    but descriptions are unique).
1861    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
1862   
1863    :param wx.Frame G2frame: the GSAS-II main data tree window
1864    '''
1865    extList = []
1866    extLabel = []
1867    for obj in G2frame.exporterlist:
1868        if 'powder' in obj.exporttype:
1869            try:
1870                obj.Writer
1871                extList.append(obj.extension)
1872                extLabel.append(obj.formatName)
1873            except AttributeError:
1874                pass
1875    return extList,extLabel
1876
1877def ExportPowder(G2frame,TreeName,fileroot,extension,hint=''):
1878    '''Writes a single powder histogram using the Export routines.
1879    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
1880
1881    :param wx.Frame G2frame: the GSAS-II main data tree window
1882    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
1883    :param str fileroot: name for file to be written, extension ignored
1884    :param str extension: extension for file to be written (start with '.'). Must
1885      match a powder export routine that has a Writer object.
1886    :param str hint: a string that must match the export's format
1887    '''
1888    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
1889    for obj in G2frame.exporterlist:
1890        if obj.extension == extension and 'powder' in obj.exporttype:
1891            if hint and hint not in obj.formatName: continue
1892            obj.currentExportType = 'powder'
1893            obj.InitExport(None)
1894            obj.loadTree() # load all histograms in tree into dicts
1895            if TreeName not in obj.Histograms:
1896                raise Exception('Histogram not found: '+str(TreeName))
1897            try:
1898                obj.Writer
1899            except AttributeError:
1900                continue
1901            try:
1902                obj.Writer(TreeName,filename)
1903                print('wrote file '+filename)
1904                return
1905            except Exception:
1906                print('Export Routine for '+extension+' failed.')
1907    else:
1908        print('No Export routine supports extension '+extension)
1909
1910def ExportSequential(G2frame,data,obj,exporttype):
1911    '''
1912    Used to export from every phase/dataset in a sequential refinement using
1913    a .Writer method for either projects or phases. Prompts to select histograms
1914    and for phase exports, which phase(s).
1915
1916    :param wx.Frame G2frame: the GSAS-II main data tree window
1917    :param dict data: the sequential refinement data object
1918    :param str exporttype: indicates the type of export ('project' or 'phase')
1919    '''
1920    if len(data['histNames']) == 0:
1921        G2G.G2MessageBox(G2frame,'There are no sequential histograms','Warning')
1922    obj.InitExport(None)
1923    obj.loadTree()
1924    obj.loadParmDict()
1925    if len(data['histNames']) == 1:
1926        histlist = data['histNames']
1927    else:
1928        dlg = G2G.G2MultiChoiceDialog(G2frame,'Select histograms to export from list',
1929                                 'Select histograms',data['histNames'])
1930        if dlg.ShowModal() == wx.ID_OK:
1931            histlist = [data['histNames'][l] for l in dlg.GetSelections()]
1932            dlg.Destroy()
1933        else:
1934            dlg.Destroy()
1935            return
1936    if exporttype == 'Phase':
1937        phaselist = list(obj.Phases.keys())
1938        if len(obj.Phases) == 0:
1939            G2G.G2MessageBox(G2frame,'There are no phases in sequential ref.','Warning')
1940            return
1941        elif len(obj.Phases) > 1:
1942            dlg = G2G.G2MultiChoiceDialog(G2frame,'Select phases to export from list',
1943                                    'Select phases', phaselist)
1944            if dlg.ShowModal() == wx.ID_OK:
1945                phaselist = [phaselist[l] for l in dlg.GetSelections()]
1946                dlg.Destroy()
1947            else:
1948                dlg.Destroy()
1949                return
1950        filename = obj.askSaveFile()
1951        if not filename: return True
1952        obj.dirname,obj.filename = os.path.split(filename)
1953        print('Writing output to file '+str(obj.filename)+"...")
1954        mode = 'w'
1955        for p in phaselist:
1956            for h in histlist:
1957                obj.SetSeqRef(data,h)
1958                #GSASIIpath.IPyBreak()
1959                obj.Writer(h,phasenam=p,mode=mode)
1960                mode = 'a'
1961        print('...done')
1962    elif exporttype == 'Project':  # note that the CIF exporter is not yet ready for this
1963        filename = obj.askSaveFile()
1964        if not filename: return True
1965        obj.dirname,obj.filename = os.path.split(filename)
1966        print('Writing output to file '+str(obj.filename)+"...")
1967        mode = 'w'
1968        for h in histlist:
1969            obj.SetSeqRef(data,h)
1970            obj.Writer(h,mode=mode)
1971            print('\t'+str(h)+' written')
1972            mode = 'a'
1973        print('...done')
1974    elif exporttype == 'Powder':
1975        filename = obj.askSaveFile()
1976        if not filename: return True
1977        obj.dirname,obj.filename = os.path.split(filename)
1978        print('Writing output to file '+str(obj.filename)+"...")
1979        mode = 'w'
1980        for h in histlist:
1981            obj.SetSeqRef(data,h)
1982            obj.Writer(h,mode=mode)
1983            print('\t'+str(h)+' written')
1984            mode = 'a'
1985        print('...done')
1986
1987def ReadDIFFaX(DIFFaXfile):
1988    print ('read '+DIFFaXfile)
1989    Layer = {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],'Width':[[10.,10.],[False,False]],
1990        'Layers':[],'Stacking':[],'Transitions':[],'Toler':0.01,'AtInfo':{}}
1991    df = open(DIFFaXfile,'r')
1992    lines = df.readlines()
1993    df.close()
1994    struct = False
1995    Struct = []
1996    stack = False
1997    Stack = []
1998    trans = False
1999    Trans = []
2000    for diff in lines:
2001        diff = diff[:-1].lower()
2002        if '!'  in diff:
2003            continue
2004        while '}' in diff: #strip comments
2005            iB = diff.index('{')
2006            iF = diff.index('}')+1
2007            if iB:
2008                diff = diff[:iB]
2009            else:
2010                diff = diff[iF:]
2011        if not diff:
2012            continue
2013        if diff.strip() == 'instrumental':
2014            continue
2015        if diff.strip() == 'structural':
2016            struct = True
2017            continue
2018        elif diff.strip() == 'stacking':
2019            struct = False
2020            stack = True
2021            continue
2022        elif diff.strip() == 'transitions':
2023            stack = False
2024            trans = True
2025            continue
2026        diff = diff.strip()
2027        if struct:
2028            if diff:
2029                Struct.append(diff)
2030        elif stack:
2031            if diff:
2032                Stack.append(diff)
2033        elif trans:
2034            if diff:
2035                Trans.append(diff)
2036   
2037#STRUCTURE records
2038    laueRec = Struct[1].split()
2039    Layer['Laue'] = laueRec[0]
2040    if Layer['Laue'] == 'unknown' and len(laueRec) > 1:
2041        Layer['Toler'] = float(laueRec[1])    #tolerance for 'unknown'?
2042    if Layer['Laue'] == '2/m(1)': Layer['Laue'] = '2/m(c)'
2043    if Layer['Laue'] == '2/m(2)': Layer['Laue'] = '2/m(ab)'
2044    cell = Struct[0].split()
2045    Layer['Cell'] = [False,float(cell[0]),float(cell[1]),float(cell[2]),90.,90.,float(cell[3]),1.0]
2046    nLayers = int(Struct[2])
2047    N = 3
2048    if 'layer' not in Struct[3]:
2049        N = 4
2050        if Struct[3] != 'infinite':
2051            width = Struct[3].split()
2052            Layer['Width'][0] = [float(width[0]),float(width[1])]
2053    for nL in range(nLayers):
2054        if '=' in Struct[N]:
2055            name = Struct[N].split('=')
2056            sameas = int(name[1])-1
2057            Layer['Layers'].append({'Name':name[0],'SameAs':Layer['Layers'][sameas]['Name'],'Symm':'None','Atoms':[]})
2058            N += 1
2059            continue
2060        Symm = 'None'
2061        if 'centro' in Struct[N+1]: Symm = '-1'
2062        Layer['Layers'].append({'Name':Struct[N],'SameAs':'','Symm':Symm,'Atoms':[]})
2063        N += 2
2064        while 'layer' not in Struct[N]:
2065            atom = Struct[N][4:].split()
2066            atomType = G2el.FixValence(Struct[N][:4].replace(' ','').strip().capitalize())
2067            if atomType not in Layer['AtInfo']:
2068                Layer['AtInfo'][atomType] = G2el.GetAtomInfo(atomType)
2069            atomName = '%s(%s)'%(atomType,atom[0])
2070            newVals = []
2071            for val in atom[1:6]:
2072                if '/' in val:
2073                    newVals.append(eval(val+'.'))
2074                else:
2075                    newVals.append(float(val))               
2076            atomRec = [atomName,atomType,newVals[0],newVals[1],newVals[2],newVals[4],newVals[3]/78.9568]
2077            Layer['Layers'][-1]['Atoms'].append(atomRec)
2078            N += 1
2079            if N > len(Struct)-1:
2080                break
2081#TRANSITIONS records
2082    transArray = []
2083    N = 0
2084    for i in range(nLayers):
2085        transArray.append([])
2086        for j in range(nLayers):
2087            vals = Trans[N].split()
2088            newVals = []
2089            for val in vals[:4]:
2090                if '/' in val:
2091                    newVals.append(eval(val+'.'))
2092                else:
2093                    newVals.append(float(val))
2094            transArray[-1].append(newVals+['',False])
2095            N += 1
2096    Layer['Transitions'] = transArray
2097#STACKING records
2098    Layer['Stacking'] = [Stack[0],'']
2099    if Stack[0] == 'recursive':
2100        Layer['Stacking'][1] = Stack[1]
2101    elif Stack[0] == 'explicit':
2102        if Stack[1] == 'random':
2103            Layer['Stacking'][1] = Stack[1]
2104        else:
2105            Layer['Stacking'][1] = 'list'
2106            Layer['Stacking'].append('')
2107            for stack in Stack[2:]:
2108                Layer['Stacking'][2] += ' '+stack
2109    return Layer
2110
2111def postURL(URL,postdict):
2112    '''Posts a set of values as from a web form. If access fails to an https
2113    site the access is retried with http.
2114
2115    :param str URL: the URL to post; typically something
2116       like 'http://www.../dir/page?'
2117    :param dict postdict: contains keywords and values, such
2118       as {'centrosymmetry': '0', 'crystalsystem': '0', ...}
2119    :returns: a string with the response from the web server or None
2120       if access fails.
2121    '''
2122    try:
2123        import requests # delay this until now, since rarely needed
2124    except:
2125        # this import seems to fail with the Anaconda pythonw on
2126        # macs; it should not!
2127        print('Warning: failed to import requests. Python config error')
2128        return None
2129       
2130    repeat = True
2131    while repeat:
2132        r = None
2133        repeat = False
2134        try:
2135            r = requests.get(URL,params=postdict)
2136            if r.status_code == 200:
2137                print('request OK')
2138                page = r.text
2139                return page # success
2140            else:
2141                print('request to {} failed. Reason={}'.format(URL,r.reason))
2142        except Exception as msg:     #ConnectionError?
2143            print('connection error - not on internet?')
2144            if GSASIIpath.GetConfigValue('debug'): print(msg)
2145        finally:
2146            if r: r.close()
2147        if URL.startswith('https:'):
2148            repeat = True
2149            URL = URL.replace('https:','http:')
2150    else:
2151        return None
2152
2153if __name__ == '__main__':
2154    import GSASIIdataGUI
2155    application = GSASIIdataGUI.GSASIImain(0)
2156    G2frame = application.main
2157    #app = wx.PySimpleApp()
2158    #G2frame = wx.Frame(None) # create a frame
2159    #frm.Show(True)
2160    #filename = '/tmp/notzip.zip'
2161    #filename = '/tmp/all.zip'
2162    #filename = '/tmp/11bmb_7652.zip'
2163   
2164    #selection=None, confirmoverwrite=True, parent=None
2165    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2166    #print ExtractFileFromZip(filename,multipleselect=True)
2167    #                         #confirmread=False, confirmoverwrite=False)
2168
2169    # choicelist=[ ('a','b','c'),
2170    #              ('test1','test2'),('no choice',)]
2171    # titles = [ 'a, b or c', 'tests', 'No option here']
2172    # dlg = MultipleChoicesDialog(
2173    #     choicelist,titles,
2174    #     parent=frm)
2175    # if dlg.ShowModal() == wx.ID_OK:
2176    #     print 'Got OK'
2177    imagefile = '/tmp/NDC5_00237_3.ge3'
2178    Comments, Data, Npix, Image = GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None)
2179
2180    print("\n\nResults loaded to Comments, Data, Npix and Image\n\n")
2181
2182    GSASIIpath.IPyBreak_base()
Note: See TracBrowser for help on using the repository browser.