source: trunk/GSASIIIO.py @ 4492

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

cleanup here & there

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