source: trunk/GSASIIIO.py @ 4881

Last change on this file since 4881 was 4802, checked in by vondreele, 4 years ago

upon integration add Dark, Background & Gain map info to Comments

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 93.6 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2021-02-06 16:39:39 +0000 (Sat, 06 Feb 2021) $
4# $Author: vondreele $
5# $Revision: 4802 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 4802 2021-02-06 16:39:39Z vondreele $
8########### SVN repository information ###################
9'''
10*GSASIIIO: Misc I/O routines*
11=============================
12
13Module with miscellaneous routines for input and output. Many
14are GUI routines to interact with user.
15
16Includes support for image reading.
17
18Also includes base class for data export routines (TODO: should move)
19
20TODO: This module needs some work to separate wx from non-wx routines. GUI
21routines should probably move to GSASIIctrlGUI.
22'''
23
24from __future__ import division, print_function
25
26# Allow this to be imported without wx present. Was needed for G2scriptable, but is
27# likely not needed anymore
28try:
29    import wx
30except ImportError:
31    # was needed by sphinx, but probably not anymore
32    class Placeholder(object):
33        def __init__(self):
34            self.Dialog = object
35    wx = Placeholder()
36import math
37import numpy as np
38import numpy.ma as ma
39
40import copy
41import platform
42if '2' in platform.python_version_tuple()[0]:
43    import cPickle
44else:
45    import pickle as cPickle
46import sys
47import re
48import random as ran
49import GSASIIpath
50GSASIIpath.SetVersionNumber("$Revision: 4802 $")
51try:
52    import GSASIIdataGUI as G2gd
53except ImportError:
54    pass
55import GSASIIobj as G2obj
56import GSASIIlattice as G2lat
57try:
58    import GSASIIpwdGUI as G2pdG
59    import GSASIIimgGUI as G2imG
60except ImportError:
61    pass
62import GSASIIElem as G2el
63import GSASIIstrIO as G2stIO
64import GSASIImapvars as G2mv
65import GSASIIfiles as G2fil
66try:
67    import GSASIIctrlGUI as G2G
68except ImportError:
69    pass
70import os
71import os.path as ospath
72
73DEBUG = False       #=True for various prints
74TRANSP = False      #=true to transpose images for testing
75if GSASIIpath.GetConfigValue('Transpose'): TRANSP = True
76npsind = lambda x: np.sin(x*np.pi/180.)
77
78def sfloat(S):
79    'Convert a string to float. An empty field or a unconvertable value is treated as zero'
80    if S.strip():
81        try:
82            return float(S)
83        except ValueError:
84            pass
85    return 0.0
86
87def sint(S):
88    'Convert a string to int. An empty field is treated as zero'
89    if S.strip():
90        return int(S)
91    else:
92        return 0
93
94def trim(val):
95    '''Simplify a string containing leading and trailing spaces
96    as well as newlines, tabs, repeated spaces etc. into a shorter and
97    more simple string, by replacing all ranges of whitespace
98    characters with a single space.
99
100    :param str val: the string to be simplified
101
102    :returns: the (usually) shortened version of the string
103    '''
104    return re.sub('\\s+', ' ', val).strip()
105
106def FileDlgFixExt(dlg,file):
107    'this is needed to fix a problem in linux wx.FileDialog'
108    ext = dlg.GetWildcard().split('|')[2*dlg.GetFilterIndex()+1].strip('*')
109    if ext not in file:
110        file += ext
111    return file
112       
113def GetPowderPeaks(fileName):
114    'Read powder peaks from a file'
115    sind = lambda x: math.sin(x*math.pi/180.)
116    asind = lambda x: 180.*math.asin(x)/math.pi
117    wave = 1.54052
118    File = open(fileName,'r')
119    Comments = []
120    peaks = []
121    S = File.readline()
122    while S:
123        if S[:1] == '#':
124            Comments.append(S[:-1])
125        else:
126            item = S.split()
127            if len(item) == 1:
128                peaks.append([float(item[0]),1.0])
129            elif len(item) > 1:
130                peaks.append([float(item[0]),float(item[0])])
131        S = File.readline()
132    File.close()
133    if Comments:
134       print ('Comments on file:')
135       for Comment in Comments: 
136            print (Comment)
137            if 'wavelength' in Comment:
138                wave = float(Comment.split('=')[1])
139    Peaks = []
140    if peaks[0][0] > peaks[-1][0]:          # d-spacings - assume CuKa
141        for peak in peaks:
142            dsp = peak[0]
143            sth = wave/(2.0*dsp)
144            if sth < 1.0:
145                tth = 2.0*asind(sth)
146            else:
147                break
148            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
149    else:                                   #2-thetas - assume Cuka (for now)
150        for peak in peaks:
151            tth = peak[0]
152            dsp = wave/(2.0*sind(tth/2.0))
153            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
154    limits = [1000.,0.]
155    for peak in Peaks:
156        limits[0] = min(limits[0],peak[0])
157        limits[1] = max(limits[1],peak[0])
158    limits[0] = max(1.,(int(limits[0]-1.)/5)*5.)
159    limits[1] = min(170.,(int(limits[1]+1.)/5)*5.)
160    return Comments,Peaks,limits,wave
161
162def GetCheckImageFile(G2frame,treeId):
163    '''Try to locate an image file if the project and image have been moved
164    together. If the image file cannot be found, request the location from
165    the user.
166
167    :param wx.Frame G2frame: main GSAS-II Frame and data object
168    :param wx.Id treeId: Id for the main tree item for the image
169    :returns: Npix,imagefile,imagetag with (Npix) number of pixels,
170       imagefile, if it exists, or the name of a file that does exist or False if the user presses Cancel
171       and (imagetag) an optional image number
172
173    '''
174    Npix,Imagefile,imagetag = G2frame.GPXtree.GetImageLoc(treeId)
175    if isinstance(Imagefile,list):
176        imagefile,imagetag = Imagefile
177    else:
178        imagefile = Imagefile
179    if not os.path.exists(imagefile):
180        print ('Image file '+imagefile+' not found')
181        fil = imagefile.replace('\\','/') # windows?!
182        # see if we can find a file with same name or in a similarly named sub-dir
183        pth,fil = os.path.split(fil)
184        prevpth = None
185        while pth and pth != prevpth:
186            prevpth = pth
187            if os.path.exists(os.path.join(G2frame.dirname,fil)):
188                print ('found image file '+os.path.join(G2frame.dirname,fil))
189                imagefile = os.path.join(G2frame.dirname,fil)
190                G2frame.GPXtree.UpdateImageLoc(treeId,imagefile)
191                return Npix,imagefile,imagetag
192            pth,enddir = os.path.split(pth)
193            fil = os.path.join(enddir,fil)
194        # not found as a subdirectory, drop common parts of path for last saved & image file names
195        #    if image was .../A/B/C/imgs/ima.ge
196        #      & GPX was  .../A/B/C/refs/fil.gpx but is now .../NEW/TEST/TEST1
197        #    will look for .../NEW/TEST/TEST1/imgs/ima.ge, .../NEW/TEST/imgs/ima.ge, .../NEW/imgs/ima.ge and so on
198        Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls'))
199        gpxPath = Controls.get('LastSavedAs','').replace('\\','/').split('/') # blank in older .GPX files
200        imgPath = imagefile.replace('\\','/').split('/')
201        for p1,p2 in zip(gpxPath,imgPath):
202            if p1 == p2:
203                gpxPath.pop(0),imgPath.pop(0)
204            else:
205                break
206        fil = os.path.join(*imgPath) # file with non-common prefix elements
207        prevpth = None
208        pth = os.path.abspath(G2frame.dirname)
209        while pth and pth != prevpth:
210            prevpth = pth
211            if os.path.exists(os.path.join(pth,fil)):
212                print ('found image file '+os.path.join(pth,fil))
213                imagefile = os.path.join(pth,fil)
214                G2frame.GPXtree.UpdateImageLoc(treeId,imagefile)
215                return Npix,imagefile,imagetag
216            pth,enddir = os.path.split(pth)
217        #GSASIIpath.IPyBreak()
218
219    if not os.path.exists(imagefile):
220        # note that this fails (at least on Mac) to get an image during the GUI initialization
221        prevnam = os.path.split(imagefile)[1]
222        prevext = os.path.splitext(imagefile)[1]
223        wildcard = 'Image format (*'+prevext+')|*'+prevext
224        dlg = wx.FileDialog(G2frame, 'Previous image file ('+prevnam+') not found; open here', '.', prevnam,
225                            wildcard,wx.FD_OPEN)
226        try:
227            dlg.SetFilename(''+ospath.split(imagefile)[1])
228            if dlg.ShowModal() == wx.ID_OK:
229                imagefile = dlg.GetPath()
230                G2frame.GPXtree.UpdateImageLoc(treeId,imagefile)
231            else:
232                imagefile = None # was False
233        finally:
234            dlg.Destroy()
235    return Npix,imagefile,imagetag
236
237def EditImageParms(parent,Data,Comments,Image,filename):
238    dlg = wx.Dialog(parent, wx.ID_ANY, 'Edit image parameters',
239                    style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
240    def onClose(event):
241        dlg.EndModal(wx.ID_OK)
242    mainsizer = wx.BoxSizer(wx.VERTICAL)
243    h,w = Image.size[:2]
244    mainsizer.Add(wx.StaticText(dlg,wx.ID_ANY,'File '+str(filename)+'\nImage size: '+str(h)+' x '+str(w)),
245        0,wx.ALIGN_LEFT|wx.ALL, 2)
246   
247    vsizer = wx.BoxSizer(wx.HORIZONTAL)
248    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Wavelength (\xC5) '),
249        0,wx.ALIGN_LEFT|wx.ALL, 2)
250    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'wavelength')
251    vsizer.Add(wdgt)
252    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
253
254    vsizer = wx.BoxSizer(wx.HORIZONTAL)
255    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Pixel size (\xb5m). Width '),
256        0,wx.ALIGN_LEFT|wx.ALL, 2)
257    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],0,size=(50,-1))
258    vsizer.Add(wdgt)
259    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Height '),wx.ALIGN_LEFT|wx.ALL, 2)
260    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],1,size=(50,-1))
261    vsizer.Add(wdgt)
262    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
263
264    vsizer = wx.BoxSizer(wx.HORIZONTAL)
265    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Sample to detector (mm) '),
266        0,wx.ALIGN_LEFT|wx.ALL, 2)
267    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'distance')
268    vsizer.Add(wdgt)
269    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
270
271    vsizer = wx.BoxSizer(wx.HORIZONTAL)
272    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Beam center (pixels). X = '),
273        0,wx.ALIGN_LEFT|wx.ALL, 2)
274    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],0,size=(75,-1))
275    vsizer.Add(wdgt)
276    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Y = '),wx.ALIGN_LEFT|wx.ALL, 2)
277    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],1,size=(75,-1))
278    vsizer.Add(wdgt)
279    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
280
281    vsizer = wx.BoxSizer(wx.HORIZONTAL)
282    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Comments '),
283        0,wx.ALIGN_LEFT|wx.ALL, 2)
284    wdgt = G2G.ValidatedTxtCtrl(dlg,Comments,0,size=(250,-1))
285    vsizer.Add(wdgt)
286    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
287
288    btnsizer = wx.StdDialogButtonSizer()
289    OKbtn = wx.Button(dlg, wx.ID_OK, 'Continue')
290    OKbtn.SetDefault()
291    OKbtn.Bind(wx.EVT_BUTTON,onClose)
292    btnsizer.AddButton(OKbtn) # not sure why this is needed
293    btnsizer.Realize()
294    mainsizer.Add(btnsizer, 1, wx.ALL|wx.EXPAND, 5)
295    dlg.SetSizer(mainsizer)
296    dlg.CenterOnParent()
297    dlg.ShowModal()
298   
299def LoadImage2Tree(imagefile,G2frame,Comments,Data,Npix,Image):
300    '''Load an image into the tree. Saves the location of the image, as well as the
301    ImageTag (where there is more than one image in the file), if defined.
302    '''
303    ImgNames = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
304    TreeLbl = 'IMG '+os.path.basename(imagefile)
305    ImageTag = Data.get('ImageTag')
306    if ImageTag:
307        TreeLbl += ' #'+'%04d'%(ImageTag)
308        imageInfo = (imagefile,ImageTag)
309    else:
310        imageInfo = imagefile
311    TreeName = G2obj.MakeUniqueLabel(TreeLbl,ImgNames)
312    Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text=TreeName)
313    G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Comments'),Comments)
314    Imax = np.amax(Image)
315    if G2frame.imageDefault:
316        Data = 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    Comments.append('Dark image = %s\n'%str(data['dark image']))
853    Comments.append('Background image = %s\n'%str(data['background image']))
854    Comments.append('Gain map = %s\n'%str(data['Gain map']))
855   
856    if 'PWDR' in name:
857        if 'target' in data:
858            names = ['Type','Lam1','Lam2','I(L2)/I(L1)','Zero','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth'] 
859            codes = [0 for i in range(14)]
860        else:
861            if data.get('IfPink',False):
862                names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','Z','alpha-0','alpha-1','beta-0','beta-1','Azimuth']
863                codes = [0 for i in range(15)]
864            else:
865                names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','Z','SH/L','Azimuth'] 
866                codes = [0 for i in range(12)]
867    elif 'SASD' in name:
868        names = ['Type','Lam','Zero','Azimuth'] 
869        codes = [0 for i in range(4)]
870        X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
871    Xminmax = [X[0],X[-1]]
872    Azms = np.zeros(data['outAzimuths'])
873    dazm = 0.
874    if data['outAzimuths'] > 1:
875        dazm = np.min(np.abs(np.diff(azms)))/2.
876    G2frame.IntgOutList = []
877    for i,azm in enumerate(azms[:-1]):
878        Aname = name+" Azm= %.2f"%((azm+dazm)%360.)
879        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
880        # if Overwrite delete any duplicate
881        if Overwrite and G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Aname):
882            print('Replacing '+Aname)
883            item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Aname)
884            G2frame.GPXtree.Delete(item)
885        else:
886            nOcc = 0
887            while item:
888                Name = G2frame.GPXtree.GetItemText(item)
889                if Aname in Name:
890                    nOcc += 1
891                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
892            if nOcc:
893                Aname += '(%d)'%(nOcc)
894        Sample = G2obj.SetDefaultSample()       #set as Debye-Scherrer
895        Sample['Gonio. radius'] = data['distance']
896        Sample['Omega'] = data['GonioAngles'][0]
897        Sample['Chi'] = data['GonioAngles'][1]
898        Sample['Phi'] = data['GonioAngles'][2]
899        Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
900        polariz = data['PolaVal'][0]
901        for item in Comments:
902            for key in ('Temperature','Pressure','Time','FreePrm1','FreePrm2','FreePrm3','Omega',
903                'Chi','Phi'):
904                if key.lower() in item.lower():
905                    try:
906                        Sample[key] = float(item.split('=')[1])
907                    except:
908                        pass
909            if 'label_prm' in item.lower():
910                for num in ('1','2','3'):
911                    if 'label_prm'+num in item.lower():
912                        Controls['FreePrm'+num] = item.split('=')[1].strip()
913        if 'PWDR' in Aname:
914            if 'target' in data:    #from lab x-ray 2D imaging data
915                wave1,wave2 = waves[data['target']]
916                parms = ['PXC',wave1,wave2,0.5,0.0,polariz,290.,-40.,30.,6.,-14.,0.0,0.0001,Azms[i]]
917            else:
918                if data.get('IfPink',False):
919                    parms = ['PXB',data['wavelength'],0.0,polariz,0.,8000.,-150.,-24.,0.,0.,0.,13.,-1300.,3.,-7.,Azms[i]]   #from Sect 35 LSS
920                else:
921                    parms = ['PXC',data['wavelength'],0.0,polariz,1.0,-0.10,0.4,0.30,1.0,0.0,0.0001,Azms[i]]
922        elif 'SASD' in Aname:
923            Sample['Trans'] = data['SampleAbs'][0]
924            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
925        Y = G2frame.Integrate[0][i]
926        Ymin = np.min(Y)
927        Ymax = np.max(Y)
928        W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
929        Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text=Aname)
930        G2frame.IntgOutList.append(Id)
931        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Comments'),Comments)                   
932        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Limits'),copy.deepcopy([tuple(Xminmax),Xminmax]))
933        if 'PWDR' in Aname:
934            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Background'),[['chebyschev-1',1,3,1.0,0.0,0.0],
935                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[],'background PWDR':['',1.0,False]}])
936        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
937        for item in inst[0]:
938            inst[0][item] = list(inst[0][item])
939        G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Instrument Parameters'),inst)
940        if 'PWDR' in Aname:
941            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Sample Parameters'),Sample)
942            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Peak List'),{'sigDict':{},'peaks':[]})
943            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Index Peak List'),[[],[]])
944            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Unit Cells List'),[])
945            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Reflection Lists'),{})
946        elif 'SASD' in Aname:             
947            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
948            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Sample Parameters'),Sample)
949            G2frame.GPXtree.SetItemPyData(G2frame.GPXtree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
950        valuesdict = {
951            'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxsize),'Offset':[0.0,0.0],'delOffset':0.02*Ymax,
952            'refOffset':-0.1*Ymax,'refDelt':0.1*Ymax,'Yminmax':[Ymin,Ymax]}
953        G2frame.GPXtree.SetItemPyData(Id,[valuesdict,
954            [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
955    return Id       #last powder pattern generated
956   
957def XYsave(G2frame,XY,labelX='X',labelY='Y',names=[]):
958    'Save XY table data'
959    pth = G2G.GetExportPath(G2frame)
960    dlg = wx.FileDialog(
961        G2frame, 'Enter csv filename for XY table', pth, '',
962        'XY table file (*.csv)|*.csv',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
963    try:
964        if dlg.ShowModal() == wx.ID_OK:
965            filename = dlg.GetPath()
966            filename = os.path.splitext(filename)[0]+'.csv'
967            File = open(filename,'w')
968        else:
969            filename = None
970    finally:
971        dlg.Destroy()
972    if not filename:
973        return
974    for i in range(len(XY)):
975        if len(names):
976            header = '%s,%s(%s)\n'%(labelX,labelY,names[i])
977        else:
978            header = '%s,%s(%d)\n'%(labelX,labelY,i)
979        File.write(header)
980        for x,y in XY[i].T:
981            File.write('%.3f,%.3f\n'%(x,y))   
982    File.close()
983    print (' XY data saved to: '+filename)                       
984   
985def PeakListSave(G2frame,file,peaks):
986    'Save powder peaks to a data file'
987    print ('save peak list to file: '+G2frame.peaklistfile)
988    if not peaks:
989        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
990        try:
991            dlg.ShowModal()
992        finally:
993            dlg.Destroy()
994        return
995    for peak in peaks:
996        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
997            (peak[0],peak[2],peak[4],peak[6]))
998    print ('peak list saved')
999             
1000def IndexPeakListSave(G2frame,peaks):
1001    'Save powder peaks from the indexing list'
1002    file = open(G2frame.peaklistfile,'wa')
1003    print ('save index peak list to file: '+G2frame.peaklistfile)
1004    wx.BeginBusyCursor()
1005    try:
1006        if not peaks:
1007            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1008            try:
1009                dlg.ShowModal()
1010            finally:
1011                dlg.Destroy()
1012            return
1013        for peak in peaks:
1014            file.write("%12.6f\n" % (peak[7]))
1015        file.close()
1016    finally:
1017        wx.EndBusyCursor()
1018    print ('index peak list saved')
1019               
1020def ExtractFileFromZip(filename, selection=None, confirmread=True,
1021                       confirmoverwrite=True, parent=None,
1022                       multipleselect=False):
1023    '''If the filename is a zip file, extract a file from that
1024    archive.
1025
1026    :param list Selection: used to predefine the name of the file
1027      to be extracted. Filename case and zip directory name are
1028      ignored in selection; the first matching file is used.
1029
1030    :param bool confirmread: if True asks the user to confirm before expanding
1031      the only file in a zip
1032
1033    :param bool confirmoverwrite: if True asks the user to confirm
1034      before overwriting if the extracted file already exists
1035
1036    :param bool multipleselect: if True allows more than one zip
1037      file to be extracted, a list of file(s) is returned.
1038      If only one file is present, do not ask which one, otherwise
1039      offer a list of choices (unless selection is used).
1040   
1041    :returns: the name of the file that has been created or a
1042      list of files (see multipleselect)
1043
1044    If the file is not a zipfile, return the name of the input file.
1045    If the zipfile is empty or no file has been selected, return None
1046    '''
1047    import zipfile # do this now, since we can save startup time by doing this only on need
1048    import shutil
1049    zloc = os.path.split(filename)[0]
1050    if not zipfile.is_zipfile(filename):
1051        #print("not zip")
1052        return filename
1053
1054    z = zipfile.ZipFile(filename,'r')
1055    zinfo = z.infolist()
1056
1057    if len(zinfo) == 0:
1058        #print('Zip has no files!')
1059        zlist = [-1]
1060    if selection:
1061        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1062        if selection.lower() in choices:
1063            zlist = [choices.index(selection.lower())]
1064        else:
1065            print('debug: file '+str(selection)+' was not found in '+str(filename))
1066            zlist = [-1]
1067    elif len(zinfo) == 1 and confirmread:
1068        result = wx.ID_NO
1069        dlg = wx.MessageDialog(
1070            parent,
1071            'Is file '+str(zinfo[0].filename)+
1072            ' what you want to extract from '+
1073            str(os.path.split(filename)[1])+'?',
1074            'Confirm file', 
1075            wx.YES_NO | wx.ICON_QUESTION)
1076        try:
1077            result = dlg.ShowModal()
1078        finally:
1079            dlg.Destroy()
1080        if result == wx.ID_NO:
1081            zlist = [-1]
1082        else:
1083            zlist = [0]
1084    elif len(zinfo) == 1:
1085        zlist = [0]
1086    elif multipleselect:
1087        # select one or more from a from list
1088        choices = [i.filename for i in zinfo]
1089        dlg = G2G.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1090            'Choose file(s)',choices)
1091        if dlg.ShowModal() == wx.ID_OK:
1092            zlist = dlg.GetSelections()
1093        else:
1094            zlist = []
1095        dlg.Destroy()
1096    else:
1097        # select one from a from list
1098        choices = [i.filename for i in zinfo]
1099        dlg = wx.SingleChoiceDialog(parent,
1100            'Select file to extract from zip file'+str(filename),'Choose file',
1101            choices,)
1102        if dlg.ShowModal() == wx.ID_OK:
1103            zlist = [dlg.GetSelection()]
1104        else:
1105            zlist = [-1]
1106        dlg.Destroy()
1107       
1108    outlist = []
1109    for zindex in zlist:
1110        if zindex >= 0:
1111            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1112            if os.path.exists(efil) and confirmoverwrite:
1113                result = wx.ID_NO
1114                dlg = wx.MessageDialog(parent,
1115                    'File '+str(efil)+' already exists. OK to overwrite it?',
1116                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1117                try:
1118                    result = dlg.ShowModal()
1119                finally:
1120                    dlg.Destroy()
1121                if result == wx.ID_NO:
1122                    zindex = -1
1123        if zindex >= 0:
1124            # extract the file to the current directory, regardless of it's original path
1125            #z.extract(zinfo[zindex],zloc)
1126            eloc,efil = os.path.split(zinfo[zindex].filename)
1127            outfile = os.path.join(zloc, efil)
1128            fpin = z.open(zinfo[zindex])
1129            fpout = open(outfile, "wb")
1130            shutil.copyfileobj(fpin, fpout)
1131            fpin.close()
1132            fpout.close()
1133            outlist.append(outfile)
1134    z.close()
1135    if multipleselect and len(outlist) >= 1:
1136        return outlist
1137    elif len(outlist) == 1:
1138        return outlist[0]
1139    else:
1140        return None
1141
1142def striphist(var,insChar=''):
1143    'strip a histogram number from a var name'
1144    sv = var.split(':')
1145    if len(sv) <= 1: return var
1146    if sv[1]:
1147        sv[1] = insChar
1148    return ':'.join(sv)
1149
1150######################################################################
1151# base classes for reading various types of data files
1152#   not used directly, only by subclassing
1153######################################################################
1154class ExportBaseclass(object):
1155    '''Defines a base class for the exporting of GSAS-II results.
1156
1157    This class is subclassed in the various exports/G2export_*.py files. Those files
1158    are imported in :meth:`GSASIIdataGUI.GSASII._init_Exports` which defines the
1159    appropriate menu items for each one and the .Exporter method is called
1160    directly from the menu item.
1161
1162    Routines may also define a .Writer method, which is used to write a single
1163    file without invoking any GUI objects.
1164    '''
1165    # TODO: review exporters producing exceptions where .Writer can't be used where G2frame is None (see CIF)
1166    # TODO: review conflicting uses of .Writer with mode (SeqRef) & elsewhere
1167    # TODO: move this class to G2fil
1168    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1169        self.G2frame = G2frame
1170        self.formatName = formatName # short string naming file type
1171        self.extension = extension
1172        if longFormatName: # longer string naming file type
1173            self.longFormatName = longFormatName
1174        else:
1175            self.longFormatName = formatName
1176        self.OverallParms = {}
1177        self.Phases = {}
1178        self.Histograms = {}
1179        self.powderDict = {}
1180        self.sasdDict = {}
1181        self.refdDict = {}
1182        self.xtalDict = {}
1183        self.parmDict = {}
1184        self.sigDict = {}
1185        self.fp = None
1186        # updated in InitExport:
1187        self.currentExportType = None # type of export that has been requested
1188        # updated in ExportSelect (when used):
1189        self.phasenam = None # a list of selected phases
1190        self.histnam = None # a list of selected histograms
1191        self.filename = None # name of file to be written (single export) or template (multiple files)
1192        self.dirname = '' # name of directory where file(s) will be written
1193        self.fullpath = '' # name of file being written -- full path
1194       
1195        # items that should be defined in a subclass of this class
1196        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1197        # The following types are defined: 'project', "phase", "powder", "single"
1198        self.multiple = False # set as True if the class can export multiple phases or histograms
1199        # self.multiple is ignored for "project" exports
1200
1201    def InitExport(self,event):
1202        '''Determines the type of menu that called the Exporter and
1203        misc initialization.
1204        '''
1205        self.filename = None # name of file to be written (single export)
1206        self.dirname = '' # name of file to be written (multiple export)
1207        if event:
1208            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1209
1210    def MakePWDRfilename(self,hist):
1211        '''Make a filename root (no extension) from a PWDR histogram name
1212
1213        :param str hist: the histogram name in data tree (starts with "PWDR ")
1214        '''
1215        file0 = ''
1216        file1 = hist[5:]
1217        # replace repeated blanks
1218        while file1 != file0:
1219            file0 = file1
1220            file1 = file0.replace('  ',' ').strip()
1221        file0 = file1.replace('Azm= ','A')
1222        # if angle has unneeded decimal places on aziumuth, remove them
1223        if file0[-3:] == '.00': file0 = file0[:-3]
1224        file0 = file0.replace('.','_')
1225        file0 = file0.replace(' ','_')
1226        return file0
1227
1228    def ExportSelect(self,AskFile='ask'):
1229        '''Selects histograms or phases when needed. Sets a default file name when
1230        requested into self.filename; always sets a default directory in self.dirname.
1231
1232        :param bool AskFile: Determines how this routine processes getting a
1233          location to store the current export(s).
1234         
1235          * if AskFile is 'ask' (default option), get the name of the file to be written;
1236            self.filename and self.dirname are always set. In the case where
1237            multiple files must be generated, the export routine should do this
1238            based on self.filename as a template.
1239          * if AskFile is 'dir', get the name of the directory to be used;
1240            self.filename is not used, but self.dirname is always set. The export routine
1241            will always generate the file name.
1242          * if AskFile is 'single', get only the name of the directory to be used when
1243            multiple items will be written (as multiple files) are used
1244            *or* a complete file name is requested when a single file
1245            name is selected. self.dirname is always set and self.filename used
1246            only when a single file is selected. 
1247          * if AskFile is 'default', creates a name of the file to be used from
1248            the name of the project (.gpx) file. If the project has not been saved,
1249            then the name of file is requested.
1250            self.filename and self.dirname are always set. In the case where
1251            multiple file names must be generated, the export routine should do this
1252            based on self.filename.
1253          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1254            file. If the project has not been saved, then a directory is requested.
1255            self.filename is not used.
1256
1257        :returns: True in case of an error
1258        '''
1259       
1260        numselected = 1
1261        if self.currentExportType == 'phase':
1262            if len(self.Phases) == 0:
1263                self.G2frame.ErrorDialog(
1264                    'Empty project',
1265                    'Project does not contain any phases.')
1266                return True
1267            elif len(self.Phases) == 1:
1268                self.phasenam = list(self.Phases.keys())
1269            elif self.multiple: 
1270                choices = sorted(self.Phases.keys())
1271                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1272                if phasenum is None: return True
1273                self.phasenam = [choices[i] for i in phasenum]
1274                if not self.phasenam: return True
1275                numselected = len(self.phasenam)
1276            else:
1277                choices = sorted(self.Phases.keys())
1278                phasenum = G2G.ItemSelector(choices,self.G2frame)
1279                if phasenum is None: return True
1280                self.phasenam = [choices[phasenum]]
1281                numselected = len(self.phasenam)
1282        elif self.currentExportType == 'single':
1283            if len(self.xtalDict) == 0:
1284                self.G2frame.ErrorDialog(
1285                    'Empty project',
1286                    'Project does not contain any single crystal data.')
1287                return True
1288            elif len(self.xtalDict) == 1:
1289                self.histnam = self.xtalDict.values()
1290            elif self.multiple:
1291                choices = sorted(self.xtalDict.values())
1292                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1293                if not hnum: return True
1294                self.histnam = [choices[i] for i in hnum]
1295                numselected = len(self.histnam)
1296            else:
1297                choices = sorted(self.xtalDict.values())
1298                hnum = G2G.ItemSelector(choices,self.G2frame)
1299                if hnum is None: return True
1300                self.histnam = [choices[hnum]]
1301                numselected = len(self.histnam)
1302        elif self.currentExportType == 'powder':
1303            if len(self.powderDict) == 0:
1304                self.G2frame.ErrorDialog(
1305                    'Empty project',
1306                    'Project does not contain any powder data.')
1307                return True
1308            elif len(self.powderDict) == 1:
1309                self.histnam = list(self.powderDict.values())
1310            elif self.multiple:
1311                choices = sorted(self.powderDict.values())
1312                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1313                if not hnum: return True
1314                self.histnam = [choices[i] for i in hnum]
1315                numselected = len(self.histnam)
1316            else:
1317                choices = sorted(self.powderDict.values())
1318                hnum = G2G.ItemSelector(choices,self.G2frame)
1319                if hnum is None: return True
1320                self.histnam = [choices[hnum]]
1321                numselected = len(self.histnam)
1322        elif self.currentExportType == 'sasd':
1323            if len(self.sasdDict) == 0:
1324                self.G2frame.ErrorDialog(
1325                    'Empty project',
1326                    'Project does not contain any small angle data.')
1327                return True
1328            elif len(self.sasdDict) == 1:
1329                self.histnam = self.sasdDict.values()
1330            elif self.multiple:
1331                choices = sorted(self.sasdDict.values())
1332                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1333                if not hnum: return True
1334                self.histnam = [choices[i] for i in hnum]
1335                numselected = len(self.histnam)
1336            else:
1337                choices = sorted(self.sasdDict.values())
1338                hnum = G2G.ItemSelector(choices,self.G2frame)
1339                if hnum is None: return True
1340                self.histnam = [choices[hnum]]
1341                numselected = len(self.histnam)
1342        elif self.currentExportType == 'refd':
1343            if len(self.refdDict) == 0:
1344                self.G2frame.ErrorDialog(
1345                    'Empty project',
1346                    'Project does not contain any reflectivity data.')
1347                return True
1348            elif len(self.refdDict) == 1:
1349                self.histnam = self.refdDict.values()
1350            elif self.multiple:
1351                choices = sorted(self.refdDict.values())
1352                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1353                if not hnum: return True
1354                self.histnam = [choices[i] for i in hnum]
1355                numselected = len(self.histnam)
1356            else:
1357                choices = sorted(self.refdDict.values())
1358                hnum = G2G.ItemSelector(choices,self.G2frame)
1359                if hnum is None: return True
1360                self.histnam = [choices[hnum]]
1361                numselected = len(self.histnam)
1362        elif self.currentExportType == 'image':
1363            if len(self.Histograms) == 0:
1364                self.G2frame.ErrorDialog(
1365                    'Empty project',
1366                    'Project does not contain any images.')
1367                return True
1368            elif len(self.Histograms) == 1:
1369                self.histnam = list(self.Histograms.keys())
1370            else:
1371                choices = sorted(self.Histograms.keys())
1372                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1373                if self.multiple:
1374                    if not hnum: return True
1375                    self.histnam = [choices[i] for i in hnum]
1376                else:
1377                    if hnum is None: return True
1378                    self.histnam = [choices[hnum]]
1379                numselected = len(self.histnam)
1380        if self.currentExportType == 'map':
1381            # search for phases with maps
1382            mapPhases = []
1383            choices = []
1384            for phasenam in sorted(self.Phases):
1385                phasedict = self.Phases[phasenam] # pointer to current phase info           
1386                if len(phasedict['General']['Map'].get('rho',[])):
1387                    mapPhases.append(phasenam)
1388                    if phasedict['General']['Map'].get('Flip'):
1389                        choices.append('Charge flip map: '+str(phasenam))
1390                    elif phasedict['General']['Map'].get('MapType'):
1391                        choices.append(
1392                            str(phasedict['General']['Map'].get('MapType'))
1393                            + ' map: ' + str(phasenam))
1394                    else:
1395                        choices.append('unknown map: '+str(phasenam))
1396            # select a map if needed
1397            if len(mapPhases) == 0:
1398                self.G2frame.ErrorDialog(
1399                    'Empty project',
1400                    'Project does not contain any maps.')
1401                return True
1402            elif len(mapPhases) == 1:
1403                self.phasenam = mapPhases
1404            else: 
1405                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1406                if self.multiple:
1407                    if not phasenum: return True
1408                    self.phasenam = [mapPhases[i] for i in phasenum]
1409                else:
1410                    if phasenum is None: return True
1411                    self.phasenam = [mapPhases[phasenum]]
1412            numselected = len(self.phasenam)
1413
1414        # items selected, now set self.dirname and usually self.filename
1415        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
1416            AskFile == 'default' and not self.G2frame.GSASprojectfile
1417            ):
1418            filename = self.askSaveFile()
1419            if not filename: return True
1420            self.dirname,self.filename = os.path.split(filename)
1421        elif AskFile == 'dir' or AskFile == 'single' or (
1422            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
1423            ):
1424            self.dirname = self.askSaveDirectory()
1425            if not self.dirname: return True
1426        elif AskFile == 'default-dir' or AskFile == 'default':
1427            self.dirname,self.filename = os.path.split(
1428                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
1429                )
1430        else:
1431            raise Exception('This should not happen!')
1432
1433    def loadParmDict(self):
1434        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
1435        refined values to those from the last cycle and set the uncertainties for the
1436        refined parameters in another dict (self.sigDict).
1437
1438        Expands the parm & sig dicts to include values derived from constraints.
1439
1440        This could be made faster for sequential fits by reducing the histogram list to only
1441        the active histogram being exported.
1442        '''
1443        self.G2frame.CheckNotebook()
1444        self.parmDict = {}
1445        self.sigDict = {}
1446        rigidbodyDict = {}
1447        covDict = {}
1448        consDict = {}
1449        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1450        if self.G2frame.GPXtree.IsEmpty(): return # nothing to do
1451        item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1452        while item:
1453            name = self.G2frame.GPXtree.GetItemText(item)
1454            if name == 'Rigid bodies':
1455                 rigidbodyDict = self.G2frame.GPXtree.GetItemPyData(item)
1456            elif name == 'Covariance':
1457                 covDict = self.G2frame.GPXtree.GetItemPyData(item)
1458            elif name == 'Constraints':
1459                 consDict = self.G2frame.GPXtree.GetItemPyData(item)
1460            item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1461        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1462        self.parmDict.update(rbDict)
1463        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1464        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave =  G2stIO.GetPhaseData(
1465            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
1466        self.parmDict.update(phaseDict)
1467        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
1468            Phases,Histograms,Print=False,resetRefList=False)
1469        self.parmDict.update(hapDict)
1470        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
1471        self.parmDict.update(histDict)
1472        self.parmDict.update(zip(
1473            covDict.get('varyList',[]),
1474            covDict.get('variables',[])))
1475        self.sigDict = dict(zip(
1476            covDict.get('varyList',[]),
1477            covDict.get('sig',[])))
1478        # expand to include constraints: first compile a list of constraints
1479        constList = []
1480        for item in consDict:
1481            if item.startswith('_'): continue
1482            constList += consDict[item]
1483        # now process the constraints
1484        G2mv.InitVars()
1485        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
1486        varyList = covDict.get('varyListStart')
1487        if varyList is None and len(constDict) == 0:
1488            # no constraints can use varyList
1489            varyList = covDict.get('varyList')
1490        elif varyList is None:
1491            # old GPX file from before pre-constraint varyList is saved
1492            print (' *** Old refinement: Please use Calculate/Refine to redo  ***')
1493            raise Exception(' *** Export aborted ***')
1494        else:
1495            varyList = list(varyList)
1496        # add symmetry-generated constraints
1497        rigidbodyDict = self.G2frame.GPXtree.GetItemPyData(   
1498            G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Rigid bodies'))
1499        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1500        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1501        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData(
1502            Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
1503        msg = G2mv.EvaluateMultipliers(constDict,phaseDict)
1504        if msg:
1505            print('Unable to interpret multiplier(s): '+msg)
1506            raise Exception(' *** CIF creation aborted ***')
1507        try:
1508            G2mv.GenerateConstraints(varyList,constDict,fixedList,self.parmDict)
1509            #print(G2mv.VarRemapShow(varyList))
1510        except:
1511            # this really should not happen
1512            print (' *** ERROR - constraints are internally inconsistent ***')
1513            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
1514            print ('Errors'+errmsg)
1515            if warnmsg: print ('Warnings'+warnmsg)
1516            raise Exception(' *** CIF creation aborted ***')
1517        # add the constrained values to the parameter dictionary
1518        G2mv.Dict2Map(self.parmDict,varyList)
1519        # and add their uncertainties into the esd dictionary (sigDict)
1520        if covDict.get('covMatrix') is not None:
1521            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
1522
1523    def loadTree(self):
1524        '''Load the contents of the data tree into a set of dicts
1525        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
1526        & self.xtalDict)
1527       
1528        * The childrenless data tree items are overall parameters/controls for the
1529          entire project and are placed in self.OverallParms
1530        * Phase items are placed in self.Phases
1531        * Data items are placed in self.Histogram. The key for these data items
1532          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
1533        '''
1534        self.OverallParms = {}
1535        self.powderDict = {}
1536        self.sasdDict = {}
1537        self.refdDict = {}
1538        self.xtalDict = {}
1539        self.Phases = {}
1540        self.Histograms = {}
1541        self.SeqRefdata = None
1542        self.SeqRefhist = None
1543        if self.G2frame.GPXtree.IsEmpty(): return # nothing to do
1544        histType = None       
1545        if self.currentExportType == 'phase':
1546            # if exporting phases load them here
1547            sub = G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Phases')
1548            if not sub:
1549                print ('no phases found')
1550                return True
1551            item, cookie = self.G2frame.GPXtree.GetFirstChild(sub)
1552            while item:
1553                phaseName = self.G2frame.GPXtree.GetItemText(item)
1554                self.Phases[phaseName] =  self.G2frame.GPXtree.GetItemPyData(item)
1555                item, cookie = self.G2frame.GPXtree.GetNextChild(sub, cookie)
1556            # Get rigid body info into self.OverallParms
1557            for key in ('Rigid bodies','Covariance'):
1558                item = G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,key)
1559                if item:
1560                    self.OverallParms[key] = self.G2frame.GPXtree.GetItemPyData(item)
1561                item, cookie = self.G2frame.GPXtree.GetNextChild(sub, cookie)
1562            return
1563        elif self.currentExportType == 'single':
1564            histType = 'HKLF'
1565        elif self.currentExportType == 'powder':
1566            histType = 'PWDR'
1567        elif self.currentExportType == 'image':
1568            histType = 'IMG'
1569        elif self.currentExportType == 'sasd':
1570            histType = 'SASD'
1571        elif self.currentExportType == 'refd':
1572            histType = 'REFD'
1573
1574        if histType: # Loading just one kind of tree entry
1575            item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1576            while item:
1577                name = self.G2frame.GPXtree.GetItemText(item)
1578                if name.startswith(histType):
1579                    if self.Histograms.get(name): # there is already an item with this name
1580                        print('Histogram name '+str(name)+' is repeated. Renaming')
1581                        if name[-1] == '9':
1582                            name = name[:-1] + '10'
1583                        elif name[-1] in '012345678':
1584                            name = name[:-1] + str(int(name[-1])+1)
1585                        else:                           
1586                            name += '-1'
1587                    self.Histograms[name] = {}
1588                    # the main info goes into Data, but the 0th
1589                    # element contains refinement results, carry
1590                    # that over too now.
1591                    self.Histograms[name]['Data'] = self.G2frame.GPXtree.GetItemPyData(item)[1]
1592                    self.Histograms[name][0] = self.G2frame.GPXtree.GetItemPyData(item)[0]
1593                    item2, cookie2 = self.G2frame.GPXtree.GetFirstChild(item)
1594                    while item2: 
1595                        child = self.G2frame.GPXtree.GetItemText(item2)
1596                        self.Histograms[name][child] = self.G2frame.GPXtree.GetItemPyData(item2)
1597                        item2, cookie2 = self.G2frame.GPXtree.GetNextChild(item, cookie2)
1598                item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1599            # index powder and single crystal histograms by number
1600            for hist in self.Histograms:
1601                if hist.startswith("PWDR"): 
1602                    d = self.powderDict
1603                elif hist.startswith("HKLF"): 
1604                    d = self.xtalDict
1605                elif hist.startswith("SASD"):
1606                    d = self.sasdDict
1607                elif hist.startswith("REFD"):
1608                    d = self.refdDict
1609                else:
1610                    return                   
1611                i = self.Histograms[hist].get('hId')
1612                if i is None and not d.keys():
1613                    i = 0
1614                elif i is None or i in d.keys():
1615                    i = max(d.keys())+1
1616                d[i] = hist
1617            return
1618        # else standard load: using all interlinked phases and histograms
1619        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1620        item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1621        while item:
1622            name = self.G2frame.GPXtree.GetItemText(item)
1623            item2, cookie2 = self.G2frame.GPXtree.GetFirstChild(item)
1624            if not item2: 
1625                self.OverallParms[name] = self.G2frame.GPXtree.GetItemPyData(item)
1626            item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1627        # index powder and single crystal histograms
1628        for hist in self.Histograms:
1629            i = self.Histograms[hist]['hId']
1630            if hist.startswith("PWDR"): 
1631                self.powderDict[i] = hist
1632            elif hist.startswith("HKLF"): 
1633                self.xtalDict[i] = hist
1634            elif hist.startswith("SASD"):
1635                self.sasdDict[i] = hist
1636            elif hist.startswith("REFD"):
1637                self.refdDict[i] = hist
1638
1639    def dumpTree(self,mode='type'):
1640        '''Print out information on the data tree dicts loaded in loadTree.
1641        Used for testing only.
1642        '''
1643        if self.SeqRefdata and self.SeqRefhist:
1644            print('Note that dumpTree does not show sequential results')
1645        print ('\nOverall')
1646        if mode == 'type':
1647            def Show(arg): return type(arg)
1648        else:
1649            def Show(arg): return arg
1650        for key in self.OverallParms:
1651            print ('  '+key+Show(self.OverallParms[key]))
1652        print ('Phases')
1653        for key1 in self.Phases:
1654            print ('    '+key1+Show(self.Phases[key1]))
1655        print ('Histogram')
1656        for key1 in self.Histograms:
1657            print ('    '+key1+Show(self.Histograms[key1]))
1658            for key2 in self.Histograms[key1]:
1659                print ('      '+key2+Show(self.Histograms[key1][key2]))
1660
1661    def defaultSaveFile(self):
1662        return os.path.abspath(
1663            os.path.splitext(self.G2frame.GSASprojectfile
1664                             )[0]+self.extension)
1665       
1666    def askSaveFile(self):
1667        '''Ask the user to supply a file name
1668
1669        :returns: a file name (str) or None if Cancel is pressed
1670
1671        '''
1672        #pth = G2G.GetExportPath(self.G2frame)
1673        if self.G2frame.GSASprojectfile:
1674            defnam = os.path.splitext(
1675                os.path.split(self.G2frame.GSASprojectfile)[1]
1676                )[0]+self.extension
1677        else:
1678            defnam = 'default' + self.extension
1679        return G2G.askSaveFile(self.G2frame,defnam,self.extension,self.longFormatName)
1680
1681    def askSaveDirectory(self):
1682        '''Ask the user to supply a directory name. Path name is used as the
1683        starting point for the next export path search.
1684
1685        :returns: a directory name (str) or None if Cancel is pressed
1686
1687        TODO: Can this be replaced with G2G.askSaveDirectory?
1688        '''
1689        pth = G2G.GetExportPath(self.G2frame)
1690        dlg = wx.DirDialog(
1691            self.G2frame, 'Input directory where file(s) will be written', pth,
1692            wx.DD_DEFAULT_STYLE)
1693        dlg.CenterOnParent()
1694        try:
1695            if dlg.ShowModal() == wx.ID_OK:
1696                filename = dlg.GetPath()
1697                self.G2frame.LastExportDir = filename
1698            else:
1699                filename = None
1700        finally:
1701            dlg.Destroy()
1702        return filename
1703
1704    # Tools for file writing.
1705    def OpenFile(self,fil=None,mode='w'):
1706        '''Open the output file
1707
1708        :param str fil: The name of the file to open. If None (default)
1709          the name defaults to self.dirname + self.filename.
1710          If an extension is supplied, it is not overridded,
1711          but if not, the default extension is used.
1712        :returns: the file object opened by the routine which is also
1713          saved as self.fp
1714        '''
1715        if mode == 'd': # debug mode
1716            self.fullpath = '(stdout)'
1717            self.fp = sys.stdout
1718            return
1719        if not fil:
1720            if not os.path.splitext(self.filename)[1]:
1721                self.filename += self.extension
1722            fil = os.path.join(self.dirname,self.filename)
1723        self.fullpath = os.path.abspath(fil)
1724        self.fp = open(self.fullpath,mode)
1725        return self.fp
1726
1727    def Write(self,line):
1728        '''write a line of output, attaching a line-end character
1729
1730        :param str line: the text to be written.
1731        '''
1732        if self.fp is None:
1733            raise Exception('Attempt to Write without use of OpenFile')
1734        self.fp.write(line+'\n')
1735       
1736    def CloseFile(self,fp=None):
1737        '''Close a file opened in OpenFile
1738
1739        :param file fp: the file object to be closed. If None (default)
1740          file object self.fp is closed.
1741        '''
1742        if self.fp is None:
1743            raise Exception('Attempt to CloseFile without use of OpenFile')
1744        if self.fp == sys.stdout: return # debug mode
1745        if fp is None:
1746            fp = self.fp
1747            self.fp = None
1748        if fp is not None: fp.close()
1749       
1750    def SetSeqRef(self,data,hist):
1751        '''Set the exporter to retrieve results from a sequential refinement
1752        rather than the main tree
1753        '''
1754        self.SeqRefdata = data
1755        self.SeqRefhist = hist
1756        data_name = data[hist]
1757        for i,val in zip(data_name['varyList'],data_name['sig']):
1758            self.sigDict[i] = val
1759            self.sigDict[striphist(i)] = val
1760        for i in data_name['parmDict']:
1761            self.parmDict[striphist(i)] = data_name['parmDict'][i]
1762            self.parmDict[i] = data_name['parmDict'][i]
1763            # zero out the dA[xyz] terms, they would only bring confusion
1764            key = i.split(':')
1765            if len(key) < 3: continue
1766            if key[2].startswith('dA'):
1767                self.parmDict[i] = 0.0
1768        for i,(val,sig) in data_name.get('depParmDict',{}).items():
1769            self.parmDict[i] = val
1770            self.sigDict[i] = sig
1771        #GSASIIpath.IPyBreak()
1772
1773    # Tools to pull information out of the data arrays
1774    def GetCell(self,phasenam):
1775        """Gets the unit cell parameters and their s.u.'s for a selected phase
1776
1777        :param str phasenam: the name for the selected phase
1778        :returns: `cellList,cellSig` where each is a 7 element list corresponding
1779          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
1780          cell values and `cellSig` has their uncertainties.
1781        """
1782        if self.SeqRefdata and self.SeqRefhist:
1783            return self.GetSeqCell(phasenam,self.SeqRefdata[self.SeqRefhist])
1784        phasedict = self.Phases[phasenam] # pointer to current phase info
1785        try:
1786            pfx = str(phasedict['pId'])+'::'
1787            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
1788            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
1789                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
1790            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
1791            return cellList,cellSig
1792        except KeyError:
1793            cell = phasedict['General']['Cell'][1:]
1794            return cell,7*[0]
1795           
1796    def GetSeqCell(self,phasenam,data_name):
1797        """Gets the unit cell parameters and their s.u.'s for a selected phase
1798        and histogram in a sequential fit
1799
1800        :param str phasenam: the name for the selected phase
1801        :param dict data_name: the sequential refinement parameters for the selected histogram
1802        :returns: `cellList,cellSig` where each is a 7 element list corresponding
1803          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
1804          cell values and `cellSig` has their uncertainties.
1805        """
1806        phasedict = self.Phases[phasenam]
1807        SGdata = phasedict['General']['SGData']
1808        pId = phasedict['pId']
1809        RecpCellTerms = G2lat.cell2A(phasedict['General']['Cell'][1:7])
1810        ESDlookup = {}
1811        Dlookup = {}
1812        varied = [striphist(i) for i in data_name['varyList']]
1813        for item,val in data_name['newCellDict'].items():
1814            if item in varied:
1815                ESDlookup[val[0]] = item
1816                Dlookup[item] = val[0]
1817        A = RecpCellTerms[:]
1818        for i in range(6):
1819            var = str(pId)+'::A'+str(i)
1820            if var in ESDlookup:
1821                A[i] = data_name['newCellDict'][ESDlookup[var]][1] # override with refined value
1822        cellDict = dict(zip([str(pId)+'::A'+str(i) for i in range(6)],A))
1823        zeroDict = {i:0.0 for i in cellDict}
1824        A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata,cellDict,zeroDict)
1825        covData = {
1826            'varyList': [Dlookup.get(striphist(v),v) for v in data_name['varyList']],
1827            'covMatrix': data_name['covMatrix']
1828            }
1829        return list(G2lat.A2cell(A)) + [G2lat.calc_V(A)], G2stIO.getCellEsd(str(pId)+'::',SGdata,A,covData)
1830               
1831    def GetAtoms(self,phasenam):
1832        """Gets the atoms associated with a phase. Can be used with standard
1833        or macromolecular phases
1834
1835        :param str phasenam: the name for the selected phase
1836        :returns: a list of items for eac atom where each item is a list containing:
1837          label, typ, mult, xyz, and td, where
1838
1839          * label and typ are the atom label and the scattering factor type (str)
1840          * mult is the site multiplicity (int)
1841          * xyz is contains a list with four pairs of numbers:
1842            x, y, z and fractional occupancy and
1843            their standard uncertainty (or a negative value)
1844          * td is contains a list with either one or six pairs of numbers:
1845            if one number it is U\\ :sub:`iso` and with six numbers it is
1846            U\\ :sub:`11`, U\\ :sub:`22`, U\\ :sub:`33`, U\\ :sub:`12`, U\\ :sub:`13` & U\\ :sub:`23`
1847            paired with their standard uncertainty (or a negative value)
1848        """
1849        phasedict = self.Phases[phasenam] # pointer to current phase info           
1850        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
1851        cfrac = cx+3
1852        fpfx = str(phasedict['pId'])+'::Afrac:'       
1853        atomslist = []
1854        for i,at in enumerate(phasedict['Atoms']):
1855            if phasedict['General']['Type'] == 'macromolecular':
1856                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
1857            else:
1858                label = at[ct-1]
1859            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
1860            fsig = self.sigDict.get(fpfx+str(i),-0.009)
1861            mult = at[cs+1]
1862            typ = at[ct]
1863            xyz = []
1864            for j,v in enumerate(('x','y','z')):
1865                val = at[cx+j]
1866                pfx = str(phasedict['pId']) + '::A' + v + ':' + str(i)
1867                val = self.parmDict.get(pfx, val)
1868                dpfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
1869                sig = self.sigDict.get(dpfx,-0.000009)
1870                xyz.append((val,sig))
1871            xyz.append((fval,fsig))
1872            td = []
1873            if at[cia] == 'I':
1874                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
1875                val = self.parmDict.get(pfx,at[cia+1])
1876                sig = self.sigDict.get(pfx,-0.0009)
1877                td.append((val,sig))
1878            else:
1879                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
1880                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
1881                    val = self.parmDict.get(pfx,at[cia+2+i])
1882                    sig = self.sigDict.get(pfx,-0.0009)
1883                    td.append((val,sig))
1884            atomslist.append((label,typ,mult,xyz,td))
1885        return atomslist
1886######################################################################
1887def ExportPowderList(G2frame):
1888    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
1889    along with their descriptions (note that a extension may be repeated
1890    but descriptions are unique).
1891    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
1892   
1893    :param wx.Frame G2frame: the GSAS-II main data tree window
1894    '''
1895    extList = []
1896    extLabel = []
1897    for obj in G2frame.exporterlist:
1898        if 'powder' in obj.exporttype:
1899            try:
1900                obj.Writer
1901                extList.append(obj.extension)
1902                extLabel.append(obj.formatName)
1903            except AttributeError:
1904                pass
1905    return extList,extLabel
1906
1907def ExportPowder(G2frame,TreeName,fileroot,extension,hint=''):
1908    '''Writes a single powder histogram using the Export routines.
1909    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
1910
1911    :param wx.Frame G2frame: the GSAS-II main data tree window
1912    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
1913    :param str fileroot: name for file to be written, extension ignored
1914    :param str extension: extension for file to be written (start with '.'). Must
1915      match a powder export routine that has a Writer object.
1916    :param str hint: a string that must match the export's format
1917    '''
1918    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
1919    for obj in G2frame.exporterlist:
1920        if obj.extension == extension and 'powder' in obj.exporttype:
1921            if hint and hint not in obj.formatName: continue
1922            obj.currentExportType = 'powder'
1923            obj.InitExport(None)
1924            obj.loadTree() # load all histograms in tree into dicts
1925            if TreeName not in obj.Histograms:
1926                raise Exception('Histogram not found: '+str(TreeName))
1927            try:
1928                obj.Writer
1929            except AttributeError:
1930                continue
1931            try:
1932                obj.Writer(TreeName,filename)
1933                print('wrote file '+filename)
1934                return
1935            except Exception:
1936                print('Export Routine for '+extension+' failed.')
1937    else:
1938        print('No Export routine supports extension '+extension)
1939
1940def ExportSequential(G2frame,data,obj,exporttype):
1941    '''
1942    Used to export from every phase/dataset in a sequential refinement using
1943    a .Writer method for either projects or phases. Prompts to select histograms
1944    and for phase exports, which phase(s).
1945
1946    :param wx.Frame G2frame: the GSAS-II main data tree window
1947    :param dict data: the sequential refinement data object
1948    :param str exporttype: indicates the type of export ('project' or 'phase')
1949    '''
1950    if len(data['histNames']) == 0:
1951        G2G.G2MessageBox(G2frame,'There are no sequential histograms','Warning')
1952    obj.InitExport(None)
1953    obj.loadTree()
1954    obj.loadParmDict()
1955    if len(data['histNames']) == 1:
1956        histlist = data['histNames']
1957    else:
1958        dlg = G2G.G2MultiChoiceDialog(G2frame,'Select histograms to export from list',
1959                                 'Select histograms',data['histNames'])
1960        if dlg.ShowModal() == wx.ID_OK:
1961            histlist = [data['histNames'][l] for l in dlg.GetSelections()]
1962            dlg.Destroy()
1963        else:
1964            dlg.Destroy()
1965            return
1966    if exporttype == 'Phase':
1967        phaselist = list(obj.Phases.keys())
1968        if len(obj.Phases) == 0:
1969            G2G.G2MessageBox(G2frame,'There are no phases in sequential ref.','Warning')
1970            return
1971        elif len(obj.Phases) > 1:
1972            dlg = G2G.G2MultiChoiceDialog(G2frame,'Select phases to export from list',
1973                                    'Select phases', phaselist)
1974            if dlg.ShowModal() == wx.ID_OK:
1975                phaselist = [phaselist[l] for l in dlg.GetSelections()]
1976                dlg.Destroy()
1977            else:
1978                dlg.Destroy()
1979                return
1980        filename = obj.askSaveFile()
1981        if not filename: return True
1982        obj.dirname,obj.filename = os.path.split(filename)
1983        print('Writing output to file '+str(obj.filename)+"...")
1984        mode = 'w'
1985        for p in phaselist:
1986            for h in histlist:
1987                obj.SetSeqRef(data,h)
1988                #GSASIIpath.IPyBreak()
1989                obj.Writer(h,phasenam=p,mode=mode)
1990                mode = 'a'
1991        print('...done')
1992    elif exporttype == 'Project':  # note that the CIF exporter is not yet ready for this
1993        filename = obj.askSaveFile()
1994        if not filename: return True
1995        obj.dirname,obj.filename = os.path.split(filename)
1996        print('Writing output to file '+str(obj.filename)+"...")
1997        mode = 'w'
1998        for h in histlist:
1999            obj.SetSeqRef(data,h)
2000            obj.Writer(h,mode=mode)
2001            print('\t'+str(h)+' written')
2002            mode = 'a'
2003        print('...done')
2004    elif exporttype == 'Powder':
2005        filename = obj.askSaveFile()
2006        if not filename: return True
2007        obj.dirname,obj.filename = os.path.split(filename)
2008        print('Writing output to file '+str(obj.filename)+"...")
2009        mode = 'w'
2010        for h in histlist:
2011            obj.SetSeqRef(data,h)
2012            obj.Writer(h,mode=mode)
2013            print('\t'+str(h)+' written')
2014            mode = 'a'
2015        print('...done')
2016
2017def ReadDIFFaX(DIFFaXfile):
2018    print ('read '+DIFFaXfile)
2019    Layer = {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],'Width':[[10.,10.],[False,False]],
2020        'Layers':[],'Stacking':[],'Transitions':[],'Toler':0.01,'AtInfo':{}}
2021    df = open(DIFFaXfile,'r')
2022    lines = df.readlines()
2023    df.close()
2024    struct = False
2025    Struct = []
2026    stack = False
2027    Stack = []
2028    trans = False
2029    Trans = []
2030    for diff in lines:
2031        diff = diff[:-1].lower()
2032        if '!'  in diff:
2033            continue
2034        while '}' in diff: #strip comments
2035            iB = diff.index('{')
2036            iF = diff.index('}')+1
2037            if iB:
2038                diff = diff[:iB]
2039            else:
2040                diff = diff[iF:]
2041        if not diff:
2042            continue
2043        if diff.strip() == 'instrumental':
2044            continue
2045        if diff.strip() == 'structural':
2046            struct = True
2047            continue
2048        elif diff.strip() == 'stacking':
2049            struct = False
2050            stack = True
2051            continue
2052        elif diff.strip() == 'transitions':
2053            stack = False
2054            trans = True
2055            continue
2056        diff = diff.strip()
2057        if struct:
2058            if diff:
2059                Struct.append(diff)
2060        elif stack:
2061            if diff:
2062                Stack.append(diff)
2063        elif trans:
2064            if diff:
2065                Trans.append(diff)
2066   
2067#STRUCTURE records
2068    laueRec = Struct[1].split()
2069    Layer['Laue'] = laueRec[0]
2070    if Layer['Laue'] == 'unknown' and len(laueRec) > 1:
2071        Layer['Toler'] = float(laueRec[1])    #tolerance for 'unknown'?
2072    if Layer['Laue'] == '2/m(1)': Layer['Laue'] = '2/m(c)'
2073    if Layer['Laue'] == '2/m(2)': Layer['Laue'] = '2/m(ab)'
2074    cell = Struct[0].split()
2075    Layer['Cell'] = [False,float(cell[0]),float(cell[1]),float(cell[2]),90.,90.,float(cell[3]),1.0]
2076    nLayers = int(Struct[2])
2077    N = 3
2078    if 'layer' not in Struct[3]:
2079        N = 4
2080        if Struct[3] != 'infinite':
2081            width = Struct[3].split()
2082            Layer['Width'][0] = [float(width[0]),float(width[1])]
2083    for nL in range(nLayers):
2084        if '=' in Struct[N]:
2085            name = Struct[N].split('=')
2086            sameas = int(name[1])-1
2087            Layer['Layers'].append({'Name':name[0],'SameAs':Layer['Layers'][sameas]['Name'],'Symm':'None','Atoms':[]})
2088            N += 1
2089            continue
2090        Symm = 'None'
2091        if 'centro' in Struct[N+1]: Symm = '-1'
2092        Layer['Layers'].append({'Name':Struct[N],'SameAs':'','Symm':Symm,'Atoms':[]})
2093        N += 2
2094        while 'layer' not in Struct[N]:
2095            atom = Struct[N][4:].split()
2096            atomType = G2el.FixValence(Struct[N][:4].replace(' ','').strip().capitalize())
2097            if atomType not in Layer['AtInfo']:
2098                Layer['AtInfo'][atomType] = G2el.GetAtomInfo(atomType)
2099            atomName = '%s(%s)'%(atomType,atom[0])
2100            newVals = []
2101            for val in atom[1:6]:
2102                if '/' in val:
2103                    newVals.append(eval(val+'.'))
2104                else:
2105                    newVals.append(float(val))               
2106            atomRec = [atomName,atomType,newVals[0],newVals[1],newVals[2],newVals[4],newVals[3]/78.9568]
2107            Layer['Layers'][-1]['Atoms'].append(atomRec)
2108            N += 1
2109            if N > len(Struct)-1:
2110                break
2111#TRANSITIONS records
2112    transArray = []
2113    N = 0
2114    for i in range(nLayers):
2115        transArray.append([])
2116        for j in range(nLayers):
2117            vals = Trans[N].split()
2118            newVals = []
2119            for val in vals[:4]:
2120                if '/' in val:
2121                    newVals.append(eval(val+'.'))
2122                else:
2123                    newVals.append(float(val))
2124            transArray[-1].append(newVals+['',False])
2125            N += 1
2126    Layer['Transitions'] = transArray
2127#STACKING records
2128    Layer['Stacking'] = [Stack[0],'']
2129    if Stack[0] == 'recursive':
2130        Layer['Stacking'][1] = Stack[1]
2131    elif Stack[0] == 'explicit':
2132        if Stack[1] == 'random':
2133            Layer['Stacking'][1] = Stack[1]
2134        else:
2135            Layer['Stacking'][1] = 'list'
2136            Layer['Stacking'].append('')
2137            for stack in Stack[2:]:
2138                Layer['Stacking'][2] += ' '+stack
2139    return Layer
2140
2141def postURL(URL,postdict):
2142    '''Posts a set of values as from a web form. If access fails to an https
2143    site the access is retried with http.
2144
2145    :param str URL: the URL to post; typically something
2146       like 'http://www.../dir/page?'
2147    :param dict postdict: contains keywords and values, such
2148       as {'centrosymmetry': '0', 'crystalsystem': '0', ...}
2149    :returns: a string with the response from the web server or None
2150       if access fails.
2151    '''
2152    try:
2153        import requests # delay this until now, since rarely needed
2154    except:
2155        # this import seems to fail with the Anaconda pythonw on
2156        # macs; it should not!
2157        print('Warning: failed to import requests. Python config error')
2158        return None
2159       
2160    repeat = True
2161    while repeat:
2162        r = None
2163        repeat = False
2164        try:
2165            r = requests.get(URL,params=postdict)
2166            if r.status_code == 200:
2167                print('request OK')
2168                page = r.text
2169                return page # success
2170            else:
2171                print('request to {} failed. Reason={}'.format(URL,r.reason))
2172        except Exception as msg:     #ConnectionError?
2173            print('connection error - not on internet?')
2174            if GSASIIpath.GetConfigValue('debug'): print(msg)
2175        finally:
2176            if r: r.close()
2177        if URL.startswith('https:'):
2178            repeat = True
2179            URL = URL.replace('https:','http:')
2180    else:
2181        return None
2182
2183if __name__ == '__main__':
2184    import GSASIIdataGUI
2185    application = GSASIIdataGUI.GSASIImain(0)
2186    G2frame = application.main
2187    #app = wx.PySimpleApp()
2188    #G2frame = wx.Frame(None) # create a frame
2189    #frm.Show(True)
2190    #filename = '/tmp/notzip.zip'
2191    #filename = '/tmp/all.zip'
2192    #filename = '/tmp/11bmb_7652.zip'
2193   
2194    #selection=None, confirmoverwrite=True, parent=None
2195    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2196    #print ExtractFileFromZip(filename,multipleselect=True)
2197    #                         #confirmread=False, confirmoverwrite=False)
2198
2199    # choicelist=[ ('a','b','c'),
2200    #              ('test1','test2'),('no choice',)]
2201    # titles = [ 'a, b or c', 'tests', 'No option here']
2202    # dlg = MultipleChoicesDialog(
2203    #     choicelist,titles,
2204    #     parent=frm)
2205    # if dlg.ShowModal() == wx.ID_OK:
2206    #     print 'Got OK'
2207    imagefile = '/tmp/NDC5_00237_3.ge3'
2208    Comments, Data, Npix, Image = GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None)
2209
2210    print("\n\nResults loaded to Comments, Data, Npix and Image\n\n")
2211
2212    GSASIIpath.IPyBreak_base()
Note: See TracBrowser for help on using the repository browser.