source: trunk/GSASIIIO.py @ 4372

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

streamline Post access to web pages; try http before https

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