source: trunk/GSASIIIO.py @ 4918

Last change on this file since 4918 was 4918, checked in by toby, 5 months ago

add mechanism for posting a notice when G2 versions are updated

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