source: trunk/GSASIIIO.py @ 4410

Last change on this file since 4410 was 4410, checked in by toby, 18 months ago

do much more cleanup after phase delete; refactor some importer imports to avoid G2IO

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 93.6 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2020-04-24 16:06:35 +0000 (Fri, 24 Apr 2020) $
4# $Author: toby $
5# $Revision: 4410 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 4410 2020-04-24 16:06:35Z 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: 4410 $")
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
1196def striphist(var,insChar=''):
1197    'strip a histogram number from a var name'
1198    sv = var.split(':')
1199    if len(sv) <= 1: return var
1200    if sv[1]:
1201        sv[1] = insChar
1202    return ':'.join(sv)
1203
1204######################################################################
1205# base classes for reading various types of data files
1206#   not used directly, only by subclassing
1207######################################################################
1208class ExportBaseclass(object):
1209    '''Defines a base class for the exporting of GSAS-II results.
1210
1211    This class is subclassed in the various exports/G2export_*.py files. Those files
1212    are imported in :meth:`GSASIIdataGUI.GSASII._init_Exports` which defines the
1213    appropriate menu items for each one and the .Exporter method is called
1214    directly from the menu item.
1215
1216    Routines may also define a .Writer method, which is used to write a single
1217    file without invoking any GUI objects.
1218    '''
1219    # TODO: review exporters producing exceptions where .Writer can't be used where G2frame is None (see CIF)
1220    # TODO: review conflicting uses of .Writer with mode (SeqRef) & elsewhere
1221    # TODO: move this class to G2fil
1222    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1223        self.G2frame = G2frame
1224        self.formatName = formatName # short string naming file type
1225        self.extension = extension
1226        if longFormatName: # longer string naming file type
1227            self.longFormatName = longFormatName
1228        else:
1229            self.longFormatName = formatName
1230        self.OverallParms = {}
1231        self.Phases = {}
1232        self.Histograms = {}
1233        self.powderDict = {}
1234        self.sasdDict = {}
1235        self.xtalDict = {}
1236        self.parmDict = {}
1237        self.sigDict = {}
1238        # updated in InitExport:
1239        self.currentExportType = None # type of export that has been requested
1240        # updated in ExportSelect (when used):
1241        self.phasenam = None # a list of selected phases
1242        self.histnam = None # a list of selected histograms
1243        self.filename = None # name of file to be written (single export) or template (multiple files)
1244        self.dirname = '' # name of directory where file(s) will be written
1245        self.fullpath = '' # name of file being written -- full path
1246       
1247        # items that should be defined in a subclass of this class
1248        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1249        # The following types are defined: 'project', "phase", "powder", "single"
1250        self.multiple = False # set as True if the class can export multiple phases or histograms
1251        # self.multiple is ignored for "project" exports
1252
1253    def InitExport(self,event):
1254        '''Determines the type of menu that called the Exporter and
1255        misc initialization.
1256        '''
1257        self.filename = None # name of file to be written (single export)
1258        self.dirname = '' # name of file to be written (multiple export)
1259        if event:
1260            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1261
1262    def MakePWDRfilename(self,hist):
1263        '''Make a filename root (no extension) from a PWDR histogram name
1264
1265        :param str hist: the histogram name in data tree (starts with "PWDR ")
1266        '''
1267        file0 = ''
1268        file1 = hist[5:]
1269        # replace repeated blanks
1270        while file1 != file0:
1271            file0 = file1
1272            file1 = file0.replace('  ',' ').strip()
1273        file0 = file1.replace('Azm= ','A')
1274        # if angle has unneeded decimal places on aziumuth, remove them
1275        if file0[-3:] == '.00': file0 = file0[:-3]
1276        file0 = file0.replace('.','_')
1277        file0 = file0.replace(' ','_')
1278        return file0
1279
1280    def ExportSelect(self,AskFile='ask'):
1281        '''Selects histograms or phases when needed. Sets a default file name when
1282        requested into self.filename; always sets a default directory in self.dirname.
1283
1284        :param bool AskFile: Determines how this routine processes getting a
1285          location to store the current export(s).
1286         
1287          * if AskFile is 'ask' (default option), get the name of the file to be written;
1288            self.filename and self.dirname are always set. In the case where
1289            multiple files must be generated, the export routine should do this
1290            based on self.filename as a template.
1291          * if AskFile is 'dir', get the name of the directory to be used;
1292            self.filename is not used, but self.dirname is always set. The export routine
1293            will always generate the file name.
1294          * if AskFile is 'single', get only the name of the directory to be used when
1295            multiple items will be written (as multiple files) are used
1296            *or* a complete file name is requested when a single file
1297            name is selected. self.dirname is always set and self.filename used
1298            only when a single file is selected. 
1299          * if AskFile is 'default', creates a name of the file to be used from
1300            the name of the project (.gpx) file. If the project has not been saved,
1301            then the name of file is requested.
1302            self.filename and self.dirname are always set. In the case where
1303            multiple file names must be generated, the export routine should do this
1304            based on self.filename.
1305          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1306            file. If the project has not been saved, then a directory is requested.
1307            self.filename is not used.
1308
1309        :returns: True in case of an error
1310        '''
1311       
1312        numselected = 1
1313        if self.currentExportType == 'phase':
1314            if len(self.Phases) == 0:
1315                self.G2frame.ErrorDialog(
1316                    'Empty project',
1317                    'Project does not contain any phases.')
1318                return True
1319            elif len(self.Phases) == 1:
1320                self.phasenam = list(self.Phases.keys())
1321            elif self.multiple: 
1322                choices = sorted(self.Phases.keys())
1323                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1324                if phasenum is None: return True
1325                self.phasenam = [choices[i] for i in phasenum]
1326                if not self.phasenam: return True
1327                numselected = len(self.phasenam)
1328            else:
1329                choices = sorted(self.Phases.keys())
1330                phasenum = G2G.ItemSelector(choices,self.G2frame)
1331                if phasenum is None: return True
1332                self.phasenam = [choices[phasenum]]
1333                numselected = len(self.phasenam)
1334        elif self.currentExportType == 'single':
1335            if len(self.xtalDict) == 0:
1336                self.G2frame.ErrorDialog(
1337                    'Empty project',
1338                    'Project does not contain any single crystal data.')
1339                return True
1340            elif len(self.xtalDict) == 1:
1341                self.histnam = self.xtalDict.values()
1342            elif self.multiple:
1343                choices = sorted(self.xtalDict.values())
1344                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1345                if not hnum: return True
1346                self.histnam = [choices[i] for i in hnum]
1347                numselected = len(self.histnam)
1348            else:
1349                choices = sorted(self.xtalDict.values())
1350                hnum = G2G.ItemSelector(choices,self.G2frame)
1351                if hnum is None: return True
1352                self.histnam = [choices[hnum]]
1353                numselected = len(self.histnam)
1354        elif self.currentExportType == 'powder':
1355            if len(self.powderDict) == 0:
1356                self.G2frame.ErrorDialog(
1357                    'Empty project',
1358                    'Project does not contain any powder data.')
1359                return True
1360            elif len(self.powderDict) == 1:
1361                self.histnam = list(self.powderDict.values())
1362            elif self.multiple:
1363                choices = sorted(self.powderDict.values())
1364                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1365                if not hnum: return True
1366                self.histnam = [choices[i] for i in hnum]
1367                numselected = len(self.histnam)
1368            else:
1369                choices = sorted(self.powderDict.values())
1370                hnum = G2G.ItemSelector(choices,self.G2frame)
1371                if hnum is None: return True
1372                self.histnam = [choices[hnum]]
1373                numselected = len(self.histnam)
1374        elif self.currentExportType == 'sasd':
1375            if len(self.sasdDict) == 0:
1376                self.G2frame.ErrorDialog(
1377                    'Empty project',
1378                    'Project does not contain any small angle data.')
1379                return True
1380            elif len(self.sasdDict) == 1:
1381                self.histnam = self.sasdDict.values()
1382            elif self.multiple:
1383                choices = sorted(self.sasdDict.values())
1384                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1385                if not hnum: return True
1386                self.histnam = [choices[i] for i in hnum]
1387                numselected = len(self.histnam)
1388            else:
1389                choices = sorted(self.sasdDict.values())
1390                hnum = G2G.ItemSelector(choices,self.G2frame)
1391                if hnum is None: return True
1392                self.histnam = [choices[hnum]]
1393                numselected = len(self.histnam)
1394        elif self.currentExportType == 'image':
1395            if len(self.Histograms) == 0:
1396                self.G2frame.ErrorDialog(
1397                    'Empty project',
1398                    'Project does not contain any images.')
1399                return True
1400            elif len(self.Histograms) == 1:
1401                self.histnam = list(self.Histograms.keys())
1402            else:
1403                choices = sorted(self.Histograms.keys())
1404                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1405                if self.multiple:
1406                    if not hnum: return True
1407                    self.histnam = [choices[i] for i in hnum]
1408                else:
1409                    if hnum is None: return True
1410                    self.histnam = [choices[hnum]]
1411                numselected = len(self.histnam)
1412        if self.currentExportType == 'map':
1413            # search for phases with maps
1414            mapPhases = []
1415            choices = []
1416            for phasenam in sorted(self.Phases):
1417                phasedict = self.Phases[phasenam] # pointer to current phase info           
1418                if len(phasedict['General']['Map'].get('rho',[])):
1419                    mapPhases.append(phasenam)
1420                    if phasedict['General']['Map'].get('Flip'):
1421                        choices.append('Charge flip map: '+str(phasenam))
1422                    elif phasedict['General']['Map'].get('MapType'):
1423                        choices.append(
1424                            str(phasedict['General']['Map'].get('MapType'))
1425                            + ' map: ' + str(phasenam))
1426                    else:
1427                        choices.append('unknown map: '+str(phasenam))
1428            # select a map if needed
1429            if len(mapPhases) == 0:
1430                self.G2frame.ErrorDialog(
1431                    'Empty project',
1432                    'Project does not contain any maps.')
1433                return True
1434            elif len(mapPhases) == 1:
1435                self.phasenam = mapPhases
1436            else: 
1437                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1438                if self.multiple:
1439                    if not phasenum: return True
1440                    self.phasenam = [mapPhases[i] for i in phasenum]
1441                else:
1442                    if phasenum is None: return True
1443                    self.phasenam = [mapPhases[phasenum]]
1444            numselected = len(self.phasenam)
1445
1446        # items selected, now set self.dirname and usually self.filename
1447        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
1448            AskFile == 'default' and not self.G2frame.GSASprojectfile
1449            ):
1450            filename = self.askSaveFile()
1451            if not filename: return True
1452            self.dirname,self.filename = os.path.split(filename)
1453        elif AskFile == 'dir' or AskFile == 'single' or (
1454            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
1455            ):
1456            self.dirname = self.askSaveDirectory()
1457            if not self.dirname: return True
1458        elif AskFile == 'default-dir' or AskFile == 'default':
1459            self.dirname,self.filename = os.path.split(
1460                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
1461                )
1462        else:
1463            raise Exception('This should not happen!')
1464
1465    def loadParmDict(self):
1466        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
1467        refined values to those from the last cycle and set the uncertainties for the
1468        refined parameters in another dict (self.sigDict).
1469
1470        Expands the parm & sig dicts to include values derived from constraints.
1471
1472        This could be made faster for sequential fits by reducing the histogram list to only
1473        the active histogram being exported.
1474        '''
1475        self.G2frame.CheckNotebook()
1476        self.parmDict = {}
1477        self.sigDict = {}
1478        rigidbodyDict = {}
1479        covDict = {}
1480        consDict = {}
1481        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1482        if self.G2frame.GPXtree.IsEmpty(): return # nothing to do
1483        item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1484        while item:
1485            name = self.G2frame.GPXtree.GetItemText(item)
1486            if name == 'Rigid bodies':
1487                 rigidbodyDict = self.G2frame.GPXtree.GetItemPyData(item)
1488            elif name == 'Covariance':
1489                 covDict = self.G2frame.GPXtree.GetItemPyData(item)
1490            elif name == 'Constraints':
1491                 consDict = self.G2frame.GPXtree.GetItemPyData(item)
1492            item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1493        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1494        self.parmDict.update(rbDict)
1495        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1496        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave =  G2stIO.GetPhaseData(
1497            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
1498        self.parmDict.update(phaseDict)
1499        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
1500            Phases,Histograms,Print=False,resetRefList=False)
1501        self.parmDict.update(hapDict)
1502        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
1503        self.parmDict.update(histDict)
1504        self.parmDict.update(zip(
1505            covDict.get('varyList',[]),
1506            covDict.get('variables',[])))
1507        self.sigDict = dict(zip(
1508            covDict.get('varyList',[]),
1509            covDict.get('sig',[])))
1510        # expand to include constraints: first compile a list of constraints
1511        constList = []
1512        for item in consDict:
1513            if item.startswith('_'): continue
1514            constList += consDict[item]
1515        # now process the constraints
1516        G2mv.InitVars()
1517        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
1518        varyList = covDict.get('varyListStart')
1519        if varyList is None and len(constDict) == 0:
1520            # no constraints can use varyList
1521            varyList = covDict.get('varyList')
1522        elif varyList is None:
1523            # old GPX file from before pre-constraint varyList is saved
1524            print (' *** Old refinement: Please use Calculate/Refine to redo  ***')
1525            raise Exception(' *** Export aborted ***')
1526        else:
1527            varyList = list(varyList)
1528        # add symmetry-generated constraints
1529        rigidbodyDict = self.G2frame.GPXtree.GetItemPyData(   
1530            G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Rigid bodies'))
1531        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1532        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1533        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData(
1534            Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
1535        msg = G2mv.EvaluateMultipliers(constDict,phaseDict)
1536        if msg:
1537            print('Unable to interpret multiplier(s): '+msg)
1538            raise Exception(' *** CIF creation aborted ***')
1539        try:
1540            G2mv.GenerateConstraints(varyList,constDict,fixedList,self.parmDict)
1541            #print(G2mv.VarRemapShow(varyList))
1542        except:
1543            # this really should not happen
1544            print (' *** ERROR - constraints are internally inconsistent ***')
1545            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
1546            print ('Errors'+errmsg)
1547            if warnmsg: print ('Warnings'+warnmsg)
1548            raise Exception(' *** CIF creation aborted ***')
1549        # add the constrained values to the parameter dictionary
1550        G2mv.Dict2Map(self.parmDict,varyList)
1551        # and add their uncertainties into the esd dictionary (sigDict)
1552        if covDict.get('covMatrix') is not None:
1553            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
1554
1555    def loadTree(self):
1556        '''Load the contents of the data tree into a set of dicts
1557        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
1558        & self.xtalDict)
1559       
1560        * The childrenless data tree items are overall parameters/controls for the
1561          entire project and are placed in self.OverallParms
1562        * Phase items are placed in self.Phases
1563        * Data items are placed in self.Histogram. The key for these data items
1564          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
1565        '''
1566        self.OverallParms = {}
1567        self.powderDict = {}
1568        self.sasdDict = {}
1569        self.xtalDict = {}
1570        self.Phases = {}
1571        self.Histograms = {}
1572        self.SeqRefdata = None
1573        self.SeqRefhist = None
1574        if self.G2frame.GPXtree.IsEmpty(): return # nothing to do
1575        histType = None       
1576        if self.currentExportType == 'phase':
1577            # if exporting phases load them here
1578            sub = G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Phases')
1579            if not sub:
1580                print ('no phases found')
1581                return True
1582            item, cookie = self.G2frame.GPXtree.GetFirstChild(sub)
1583            while item:
1584                phaseName = self.G2frame.GPXtree.GetItemText(item)
1585                self.Phases[phaseName] =  self.G2frame.GPXtree.GetItemPyData(item)
1586                item, cookie = self.G2frame.GPXtree.GetNextChild(sub, cookie)
1587            return
1588        elif self.currentExportType == 'single':
1589            histType = 'HKLF'
1590        elif self.currentExportType == 'powder':
1591            histType = 'PWDR'
1592        elif self.currentExportType == 'image':
1593            histType = 'IMG'
1594        elif self.currentExportType == 'sasd':
1595            histType = 'SASD'
1596
1597        if histType: # Loading just one kind of tree entry
1598            item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1599            while item:
1600                name = self.G2frame.GPXtree.GetItemText(item)
1601                if name.startswith(histType):
1602                    if self.Histograms.get(name): # there is already an item with this name
1603                        print('Histogram name '+str(name)+' is repeated. Renaming')
1604                        if name[-1] == '9':
1605                            name = name[:-1] + '10'
1606                        elif name[-1] in '012345678':
1607                            name = name[:-1] + str(int(name[-1])+1)
1608                        else:                           
1609                            name += '-1'
1610                    self.Histograms[name] = {}
1611                    # the main info goes into Data, but the 0th
1612                    # element contains refinement results, carry
1613                    # that over too now.
1614                    self.Histograms[name]['Data'] = self.G2frame.GPXtree.GetItemPyData(item)[1]
1615                    self.Histograms[name][0] = self.G2frame.GPXtree.GetItemPyData(item)[0]
1616                    item2, cookie2 = self.G2frame.GPXtree.GetFirstChild(item)
1617                    while item2: 
1618                        child = self.G2frame.GPXtree.GetItemText(item2)
1619                        self.Histograms[name][child] = self.G2frame.GPXtree.GetItemPyData(item2)
1620                        item2, cookie2 = self.G2frame.GPXtree.GetNextChild(item, cookie2)
1621                item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1622            # index powder and single crystal histograms by number
1623            for hist in self.Histograms:
1624                if hist.startswith("PWDR"): 
1625                    d = self.powderDict
1626                elif hist.startswith("HKLF"): 
1627                    d = self.xtalDict
1628                elif hist.startswith("SASD"):
1629                    d = self.sasdDict
1630                else:
1631                    return                   
1632                i = self.Histograms[hist].get('hId')
1633                if i is None and not d.keys():
1634                    i = 0
1635                elif i is None or i in d.keys():
1636                    i = max(d.keys())+1
1637                d[i] = hist
1638            return
1639        # else standard load: using all interlinked phases and histograms
1640        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1641        item, cookie = self.G2frame.GPXtree.GetFirstChild(self.G2frame.root)
1642        while item:
1643            name = self.G2frame.GPXtree.GetItemText(item)
1644            item2, cookie2 = self.G2frame.GPXtree.GetFirstChild(item)
1645            if not item2: 
1646                self.OverallParms[name] = self.G2frame.GPXtree.GetItemPyData(item)
1647            item, cookie = self.G2frame.GPXtree.GetNextChild(self.G2frame.root, cookie)
1648        # index powder and single crystal histograms
1649        for hist in self.Histograms:
1650            i = self.Histograms[hist]['hId']
1651            if hist.startswith("PWDR"): 
1652                self.powderDict[i] = hist
1653            elif hist.startswith("HKLF"): 
1654                self.xtalDict[i] = hist
1655            elif hist.startswith("SASD"):
1656                self.sasdDict[i] = hist
1657
1658    def dumpTree(self,mode='type'):
1659        '''Print out information on the data tree dicts loaded in loadTree.
1660        Used for testing only.
1661        '''
1662        if self.SeqRefdata and self.SeqRefhist:
1663            print('Note that dumpTree does not show sequential results')
1664        print ('\nOverall')
1665        if mode == 'type':
1666            def Show(arg): return type(arg)
1667        else:
1668            def Show(arg): return arg
1669        for key in self.OverallParms:
1670            print ('  '+key+Show(self.OverallParms[key]))
1671        print ('Phases')
1672        for key1 in self.Phases:
1673            print ('    '+key1+Show(self.Phases[key1]))
1674        print ('Histogram')
1675        for key1 in self.Histograms:
1676            print ('    '+key1+Show(self.Histograms[key1]))
1677            for key2 in self.Histograms[key1]:
1678                print ('      '+key2+Show(self.Histograms[key1][key2]))
1679
1680    def defaultSaveFile(self):
1681        return os.path.abspath(
1682            os.path.splitext(self.G2frame.GSASprojectfile
1683                             )[0]+self.extension)
1684       
1685    def askSaveFile(self):
1686        '''Ask the user to supply a file name
1687
1688        :returns: a file name (str) or None if Cancel is pressed
1689
1690        '''
1691        pth = G2G.GetExportPath(self.G2frame)
1692        if self.G2frame.GSASprojectfile:
1693            defnam = os.path.splitext(
1694                os.path.split(self.G2frame.GSASprojectfile)[1]
1695                )[0]+self.extension
1696        else:
1697            defnam = 'default' + self.extension
1698        return G2G.askSaveFile(self.G2frame,defnam,self.extension,self.longFormatName)
1699
1700    def askSaveDirectory(self):
1701        '''Ask the user to supply a directory name. Path name is used as the
1702        starting point for the next export path search.
1703
1704        :returns: a directory name (str) or None if Cancel is pressed
1705
1706        TODO: Can this be replaced with G2G.askSaveDirectory?
1707        '''
1708        pth = G2G.GetExportPath(self.G2frame)
1709        dlg = wx.DirDialog(
1710            self.G2frame, 'Input directory where file(s) will be written', pth,
1711            wx.DD_DEFAULT_STYLE)
1712        dlg.CenterOnParent()
1713        try:
1714            if dlg.ShowModal() == wx.ID_OK:
1715                filename = dlg.GetPath()
1716                self.G2frame.LastExportDir = filename
1717            else:
1718                filename = None
1719        finally:
1720            dlg.Destroy()
1721        return filename
1722
1723    # Tools for file writing.
1724    def OpenFile(self,fil=None,mode='w'):
1725        '''Open the output file
1726
1727        :param str fil: The name of the file to open. If None (default)
1728          the name defaults to self.dirname + self.filename.
1729          If an extension is supplied, it is not overridded,
1730          but if not, the default extension is used.
1731        :returns: the file object opened by the routine which is also
1732          saved as self.fp
1733        '''
1734        if mode == 'd': # debug mode
1735            self.fullpath = '(stdout)'
1736            self.fp = sys.stdout
1737            return
1738        if not fil:
1739            if not os.path.splitext(self.filename)[1]:
1740                self.filename += self.extension
1741            fil = os.path.join(self.dirname,self.filename)
1742        self.fullpath = os.path.abspath(fil)
1743        self.fp = open(self.fullpath,mode)
1744        return self.fp
1745
1746    def Write(self,line):
1747        '''write a line of output, attaching a line-end character
1748
1749        :param str line: the text to be written.
1750        '''
1751        self.fp.write(line+'\n')
1752       
1753    def CloseFile(self,fp=None):
1754        '''Close a file opened in OpenFile
1755
1756        :param file fp: the file object to be closed. If None (default)
1757          file object self.fp is closed.
1758        '''
1759        if self.fp == sys.stdout: return # debug mode
1760        if fp is None:
1761            fp = self.fp
1762            self.fp = None
1763        if fp is not None: fp.close()
1764       
1765    def SetSeqRef(self,data,hist):
1766        '''Set the exporter to retrieve results from a sequential refinement
1767        rather than the main tree
1768        '''
1769        self.SeqRefdata = data
1770        self.SeqRefhist = hist
1771        data_name = data[hist]
1772        for i,val in zip(data_name['varyList'],data_name['sig']):
1773            self.sigDict[i] = val
1774            self.sigDict[striphist(i)] = val
1775        for i in data_name['parmDict']:
1776            self.parmDict[striphist(i)] = data_name['parmDict'][i]
1777            self.parmDict[i] = data_name['parmDict'][i]
1778            # zero out the dA[xyz] terms, they would only bring confusion
1779            key = i.split(':')
1780            if len(key) < 3: continue
1781            if key[2].startswith('dA'):
1782                self.parmDict[i] = 0.0
1783        for i,(val,sig) in data_name.get('depParmDict',{}).items():
1784            self.parmDict[i] = val
1785            self.sigDict[i] = sig
1786        #GSASIIpath.IPyBreak()
1787
1788    # Tools to pull information out of the data arrays
1789    def GetCell(self,phasenam):
1790        """Gets the unit cell parameters and their s.u.'s for a selected phase
1791
1792        :param str phasenam: the name for the selected phase
1793        :returns: `cellList,cellSig` where each is a 7 element list corresponding
1794          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
1795          cell values and `cellSig` has their uncertainties.
1796        """
1797        if self.SeqRefdata and self.SeqRefhist:
1798            return self.GetSeqCell(phasenam,self.SeqRefdata[self.SeqRefhist])
1799        phasedict = self.Phases[phasenam] # pointer to current phase info
1800        try:
1801            pfx = str(phasedict['pId'])+'::'
1802            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
1803            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
1804                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
1805            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
1806            return cellList,cellSig
1807        except KeyError:
1808            cell = phasedict['General']['Cell'][1:]
1809            return cell,7*[0]
1810           
1811    def GetSeqCell(self,phasenam,data_name):
1812        """Gets the unit cell parameters and their s.u.'s for a selected phase
1813        and histogram in a sequential fit
1814
1815        :param str phasenam: the name for the selected phase
1816        :param dict data_name: the sequential refinement parameters for the selected histogram
1817        :returns: `cellList,cellSig` where each is a 7 element list corresponding
1818          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
1819          cell values and `cellSig` has their uncertainties.
1820        """
1821        phasedict = self.Phases[phasenam]
1822        SGdata = phasedict['General']['SGData']
1823        pId = phasedict['pId']
1824        RecpCellTerms = G2lat.cell2A(phasedict['General']['Cell'][1:7])
1825        ESDlookup = {}
1826        Dlookup = {}
1827        varied = [striphist(i) for i in data_name['varyList']]
1828        for item,val in data_name['newCellDict'].items():
1829            if item in varied:
1830                ESDlookup[val[0]] = item
1831                Dlookup[item] = val[0]
1832        A = RecpCellTerms[:]
1833        for i in range(6):
1834            var = str(pId)+'::A'+str(i)
1835            if var in ESDlookup:
1836                A[i] = data_name['newCellDict'][ESDlookup[var]][1] # override with refined value
1837        cellDict = dict(zip([str(pId)+'::A'+str(i) for i in range(6)],A))
1838        zeroDict = {i:0.0 for i in cellDict}
1839        A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata,cellDict,zeroDict)
1840        covData = {
1841            'varyList': [Dlookup.get(striphist(v),v) for v in data_name['varyList']],
1842            'covMatrix': data_name['covMatrix']
1843            }
1844        return list(G2lat.A2cell(A)) + [G2lat.calc_V(A)], G2stIO.getCellEsd(str(pId)+'::',SGdata,A,covData)
1845               
1846    def GetAtoms(self,phasenam):
1847        """Gets the atoms associated with a phase. Can be used with standard
1848        or macromolecular phases
1849
1850        :param str phasenam: the name for the selected phase
1851        :returns: a list of items for eac atom where each item is a list containing:
1852          label, typ, mult, xyz, and td, where
1853
1854          * label and typ are the atom label and the scattering factor type (str)
1855          * mult is the site multiplicity (int)
1856          * xyz is contains a list with four pairs of numbers:
1857            x, y, z and fractional occupancy and
1858            their standard uncertainty (or a negative value)
1859          * td is contains a list with either one or six pairs of numbers:
1860            if one number it is U\ :sub:`iso` and with six numbers it is
1861            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
1862            paired with their standard uncertainty (or a negative value)
1863        """
1864        phasedict = self.Phases[phasenam] # pointer to current phase info           
1865        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
1866        cfrac = cx+3
1867        fpfx = str(phasedict['pId'])+'::Afrac:'       
1868        atomslist = []
1869        for i,at in enumerate(phasedict['Atoms']):
1870            if phasedict['General']['Type'] == 'macromolecular':
1871                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
1872            else:
1873                label = at[ct-1]
1874            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
1875            fsig = self.sigDict.get(fpfx+str(i),-0.009)
1876            mult = at[cs+1]
1877            typ = at[ct]
1878            xyz = []
1879            for j,v in enumerate(('x','y','z')):
1880                val = at[cx+j]
1881                pfx = str(phasedict['pId']) + '::A' + v + ':' + str(i)
1882                val = self.parmDict.get(pfx, val)
1883                dpfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
1884                sig = self.sigDict.get(dpfx,-0.000009)
1885                xyz.append((val,sig))
1886            xyz.append((fval,fsig))
1887            td = []
1888            if at[cia] == 'I':
1889                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
1890                val = self.parmDict.get(pfx,at[cia+1])
1891                sig = self.sigDict.get(pfx,-0.0009)
1892                td.append((val,sig))
1893            else:
1894                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
1895                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
1896                    val = self.parmDict.get(pfx,at[cia+2+i])
1897                    sig = self.sigDict.get(pfx,-0.0009)
1898                    td.append((val,sig))
1899            atomslist.append((label,typ,mult,xyz,td))
1900        return atomslist
1901######################################################################
1902def ExportPowderList(G2frame):
1903    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
1904    along with their descriptions (note that a extension may be repeated
1905    but descriptions are unique).
1906    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
1907   
1908    :param wx.Frame G2frame: the GSAS-II main data tree window
1909    '''
1910    extList = []
1911    extLabel = []
1912    for obj in G2frame.exporterlist:
1913        if 'powder' in obj.exporttype:
1914            try:
1915                obj.Writer
1916                extList.append(obj.extension)
1917                extLabel.append(obj.formatName)
1918            except AttributeError:
1919                pass
1920    return extList,extLabel
1921
1922def ExportPowder(G2frame,TreeName,fileroot,extension,hint=''):
1923    '''Writes a single powder histogram using the Export routines.
1924    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
1925
1926    :param wx.Frame G2frame: the GSAS-II main data tree window
1927    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
1928    :param str fileroot: name for file to be written, extension ignored
1929    :param str extension: extension for file to be written (start with '.'). Must
1930      match a powder export routine that has a Writer object.
1931    :param str hint: a string that must match the export's format
1932    '''
1933    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
1934    for obj in G2frame.exporterlist:
1935        if obj.extension == extension and 'powder' in obj.exporttype:
1936            if hint and hint not in obj.formatName: continue
1937            obj.currentExportType = 'powder'
1938            obj.InitExport(None)
1939            obj.loadTree() # load all histograms in tree into dicts
1940            if TreeName not in obj.Histograms:
1941                raise Exception('Histogram not found: '+str(TreeName))
1942            try:
1943                obj.Writer
1944            except AttributeError:
1945                continue
1946            try:
1947                obj.Writer(TreeName,filename)
1948                print('wrote file '+filename)
1949                return
1950            except Exception:
1951                print('Export Routine for '+extension+' failed.')
1952    else:
1953        print('No Export routine supports extension '+extension)
1954
1955def ExportSequential(G2frame,data,obj,exporttype):
1956    '''
1957    Used to export from every phase/dataset in a sequential refinement using
1958    a .Writer method for either projects or phases. Prompts to select histograms
1959    and for phase exports, which phase(s).
1960
1961    :param wx.Frame G2frame: the GSAS-II main data tree window
1962    :param dict data: the sequential refinement data object
1963    :param str exporttype: indicates the type of export ('project' or 'phase')
1964    '''
1965    if len(data['histNames']) == 0:
1966        G2G.G2MessageBox(G2frame,'There are no sequential histograms','Warning')
1967    obj.InitExport(None)
1968    obj.loadTree()
1969    obj.loadParmDict()
1970    if len(data['histNames']) == 1:
1971        histlist = data['histNames']
1972    else:
1973        dlg = G2G.G2MultiChoiceDialog(G2frame,'Select histograms to export from list',
1974                                 'Select histograms',data['histNames'])
1975        if dlg.ShowModal() == wx.ID_OK:
1976            histlist = [data['histNames'][l] for l in dlg.GetSelections()]
1977            dlg.Destroy()
1978        else:
1979            dlg.Destroy()
1980            return
1981    if exporttype == 'Phase':
1982        phaselist = list(obj.Phases.keys())
1983        if len(obj.Phases) == 0:
1984            G2G.G2MessageBox(G2frame,'There are no phases in sequential ref.','Warning')
1985            return
1986        elif len(obj.Phases) > 1:
1987            dlg = G2G.G2MultiChoiceDialog(G2frame,'Select phases to export from list',
1988                                    'Select phases', phaselist)
1989            if dlg.ShowModal() == wx.ID_OK:
1990                phaselist = [phaselist[l] for l in dlg.GetSelections()]
1991                dlg.Destroy()
1992            else:
1993                dlg.Destroy()
1994                return
1995        filename = obj.askSaveFile()
1996        if not filename: return True
1997        obj.dirname,obj.filename = os.path.split(filename)
1998        print('Writing output to file '+str(obj.filename)+"...")
1999        mode = 'w'
2000        for p in phaselist:
2001            for h in histlist:
2002                obj.SetSeqRef(data,h)
2003                #GSASIIpath.IPyBreak()
2004                obj.Writer(h,phasenam=p,mode=mode)
2005                mode = 'a'
2006        print('...done')
2007    elif exporttype == 'Project':  # note that the CIF exporter is not yet ready for this
2008        filename = obj.askSaveFile()
2009        if not filename: return True
2010        obj.dirname,obj.filename = os.path.split(filename)
2011        print('Writing output to file '+str(obj.filename)+"...")
2012        mode = 'w'
2013        for h in histlist:
2014            obj.SetSeqRef(data,h)
2015            obj.Writer(h,mode=mode)
2016            print('\t'+str(h)+' written')
2017            mode = 'a'
2018        print('...done')
2019    elif exporttype == 'Powder':
2020        filename = obj.askSaveFile()
2021        if not filename: return True
2022        obj.dirname,obj.filename = os.path.split(filename)
2023        print('Writing output to file '+str(obj.filename)+"...")
2024        mode = 'w'
2025        for h in histlist:
2026            obj.SetSeqRef(data,h)
2027            obj.Writer(h,mode=mode)
2028            print('\t'+str(h)+' written')
2029            mode = 'a'
2030        print('...done')
2031
2032def ReadDIFFaX(DIFFaXfile):
2033    print ('read '+DIFFaXfile)
2034    Layer = {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],'Width':[[10.,10.],[False,False]],
2035        'Layers':[],'Stacking':[],'Transitions':[],'Toler':0.01,'AtInfo':{}}
2036    df = open(DIFFaXfile,'r')
2037    lines = df.readlines()
2038    df.close()
2039    struct = False
2040    Struct = []
2041    stack = False
2042    Stack = []
2043    trans = False
2044    Trans = []
2045    for diff in lines:
2046        diff = diff[:-1].lower()
2047        if '!'  in diff:
2048            continue
2049        while '}' in diff: #strip comments
2050            iB = diff.index('{')
2051            iF = diff.index('}')+1
2052            if iB:
2053                diff = diff[:iB]
2054            else:
2055                diff = diff[iF:]
2056        if not diff:
2057            continue
2058        if diff.strip() == 'instrumental':
2059            continue
2060        if diff.strip() == 'structural':
2061            struct = True
2062            continue
2063        elif diff.strip() == 'stacking':
2064            struct = False
2065            stack = True
2066            continue
2067        elif diff.strip() == 'transitions':
2068            stack = False
2069            trans = True
2070            continue
2071        diff = diff.strip()
2072        if struct:
2073            if diff:
2074                Struct.append(diff)
2075        elif stack:
2076            if diff:
2077                Stack.append(diff)
2078        elif trans:
2079            if diff:
2080                Trans.append(diff)
2081   
2082#STRUCTURE records
2083    laueRec = Struct[1].split()
2084    Layer['Laue'] = laueRec[0]
2085    if Layer['Laue'] == 'unknown' and len(laueRec) > 1:
2086        Layer['Toler'] = float(laueRec[1])    #tolerance for 'unknown'?
2087    if Layer['Laue'] == '2/m(1)': Layer['Laue'] = '2/m(c)'
2088    if Layer['Laue'] == '2/m(2)': Layer['Laue'] = '2/m(ab)'
2089    cell = Struct[0].split()
2090    Layer['Cell'] = [False,float(cell[0]),float(cell[1]),float(cell[2]),90.,90.,float(cell[3]),1.0]
2091    nLayers = int(Struct[2])
2092    N = 3
2093    if 'layer' not in Struct[3]:
2094        N = 4
2095        if Struct[3] != 'infinite':
2096            width = Struct[3].split()
2097            Layer['Width'][0] = [float(width[0]),float(width[1])]
2098    for nL in range(nLayers):
2099        if '=' in Struct[N]:
2100            name = Struct[N].split('=')
2101            sameas = int(name[1])-1
2102            Layer['Layers'].append({'Name':name[0],'SameAs':Layer['Layers'][sameas]['Name'],'Symm':'None','Atoms':[]})
2103            N += 1
2104            continue
2105        Symm = 'None'
2106        if 'centro' in Struct[N+1]: Symm = '-1'
2107        Layer['Layers'].append({'Name':Struct[N],'SameAs':'','Symm':Symm,'Atoms':[]})
2108        N += 2
2109        while 'layer' not in Struct[N]:
2110            atom = Struct[N][4:].split()
2111            atomType = G2el.FixValence(Struct[N][:4].replace(' ','').strip().capitalize())
2112            if atomType not in Layer['AtInfo']:
2113                Layer['AtInfo'][atomType] = G2el.GetAtomInfo(atomType)
2114            atomName = '%s(%s)'%(atomType,atom[0])
2115            newVals = []
2116            for val in atom[1:6]:
2117                if '/' in val:
2118                    newVals.append(eval(val+'.'))
2119                else:
2120                    newVals.append(float(val))               
2121            atomRec = [atomName,atomType,newVals[0],newVals[1],newVals[2],newVals[4],newVals[3]/78.9568]
2122            Layer['Layers'][-1]['Atoms'].append(atomRec)
2123            N += 1
2124            if N > len(Struct)-1:
2125                break
2126#TRANSITIONS records
2127    transArray = []
2128    N = 0
2129    for i in range(nLayers):
2130        transArray.append([])
2131        for j in range(nLayers):
2132            vals = Trans[N].split()
2133            newVals = []
2134            for val in vals[:4]:
2135                if '/' in val:
2136                    newVals.append(eval(val+'.'))
2137                else:
2138                    newVals.append(float(val))
2139            transArray[-1].append(newVals+['',False])
2140            N += 1
2141    Layer['Transitions'] = transArray
2142#STACKING records
2143    Layer['Stacking'] = [Stack[0],'']
2144    if Stack[0] == 'recursive':
2145        Layer['Stacking'][1] = Stack[1]
2146    elif Stack[0] == 'explicit':
2147        if Stack[1] == 'random':
2148            Layer['Stacking'][1] = Stack[1]
2149        else:
2150            Layer['Stacking'][1] = 'list'
2151            Layer['Stacking'].append('')
2152            for stack in Stack[2:]:
2153                Layer['Stacking'][2] += ' '+stack
2154    return Layer
2155
2156def postURL(URL,postdict):
2157    '''Posts a set of values as from a web form. If access fails to an https
2158    site the access is retried with http.
2159
2160    :param str URL: the URL to post; typically something
2161       like 'http://www.../dir/page?'
2162    :param dict postdict: contains keywords and values, such
2163       as {'centrosymmetry': '0', 'crystalsystem': '0', ...}
2164    :returns: a string with the response from the web server or None
2165       if access fails.
2166    '''
2167    try:
2168        import requests # delay this until now, since rarely needed
2169    except:
2170        # this import seems to fail with the Anaconda pythonw on
2171        # macs; it should not!
2172        print('Warning: failed to import requests. Python config error')
2173        return None
2174       
2175    repeat = True
2176    while repeat:
2177        r = None
2178        repeat = False
2179        try:
2180            r = requests.get(URL,params=postdict)
2181            if r.status_code == 200:
2182                print('request OK')
2183                page = r.text
2184                return page # success
2185            else:
2186                print('request to {} failed. Reason={}'.format(URL,r.reason))
2187        except Exception as msg:     #ConnectionError?
2188            print('connection error - not on internet?')
2189            if GSASIIpath.GetConfigValue('debug'): print(msg)
2190        finally:
2191            if r: r.close()
2192        if URL.startswith('https:'):
2193            repeat = True
2194            URL = URL.replace('https:','http:')
2195    else:
2196        return None
2197
2198if __name__ == '__main__':
2199    import GSASIIdataGUI
2200    application = GSASIIdataGUI.GSASIImain(0)
2201    G2frame = application.main
2202    #app = wx.PySimpleApp()
2203    #G2frame = wx.Frame(None) # create a frame
2204    #frm.Show(True)
2205    #filename = '/tmp/notzip.zip'
2206    #filename = '/tmp/all.zip'
2207    #filename = '/tmp/11bmb_7652.zip'
2208   
2209    #selection=None, confirmoverwrite=True, parent=None
2210    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2211    #print ExtractFileFromZip(filename,multipleselect=True)
2212    #                         #confirmread=False, confirmoverwrite=False)
2213
2214    # choicelist=[ ('a','b','c'),
2215    #              ('test1','test2'),('no choice',)]
2216    # titles = [ 'a, b or c', 'tests', 'No option here']
2217    # dlg = MultipleChoicesDialog(
2218    #     choicelist,titles,
2219    #     parent=frm)
2220    # if dlg.ShowModal() == wx.ID_OK:
2221    #     print 'Got OK'
2222    imagefile = '/tmp/NDC5_00237_3.ge3'
2223    Comments, Data, Npix, Image = GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None)
2224
2225    print("\n\nResults loaded to Comments, Data, Npix and Image\n\n")
2226
2227    GSASIIpath.IPyBreak_base()
Note: See TracBrowser for help on using the repository browser.