source: trunk/GSASIIIO.py @ 4661

Last change on this file since 4661 was 4661, checked in by toby, 11 months ago

update CIF export: add rigid body, fix bug in single xtal ref tbl; picker bug on single xtal plot

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