source: trunk/GSASIIIO.py @ 4892

Last change on this file since 4892 was 4892, checked in by toby, 6 months ago

missed inage change; FPA: as per Cline, 5 line (4 w/mono) now default; rename tails width; tube-tails only w/o mono; max window height=850

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