source: trunk/GSASIIIO.py @ 4411

Last change on this file since 4411 was 4411, checked in by toby, 4 years ago

bug in CIF export; misc cleanup

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