source: trunk/GSASIIIO.py @ 2289

Last change on this file since 2289 was 2289, checked in by vondreele, 6 years ago

fixed interpretation of space group symbols with :1/2 at end indicating setting (in some cif files)
change naming of image integration PWDR names to have zero filled frame numbers (default= '000')
fix handling of constrained Uisos in Shelx ins/res files

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 102.8 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2016-05-25 14:29:51 +0000 (Wed, 25 May 2016) $
4# $Author: vondreele $
5# $Revision: 2289 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 2289 2016-05-25 14:29:51Z vondreele $
8########### SVN repository information ###################
9'''
10*GSASIIIO: Misc I/O routines*
11=============================
12
13Module with miscellaneous routines for input and output. Many
14are GUI routines to interact with user.
15
16Includes support for image reading.
17
18Also includes base classes for data import routines.
19
20'''
21"""GSASIIIO: functions for IO of data
22   Copyright: 2008, Robert B. Von Dreele (Argonne National Laboratory)
23"""
24import wx
25import math
26import numpy as np
27import copy
28import cPickle
29import sys
30import re
31import random as ran
32import GSASIIpath
33GSASIIpath.SetVersionNumber("$Revision: 2289 $")
34import GSASIIgrid as G2gd
35import GSASIIspc as G2spc
36import GSASIIobj as G2obj
37import GSASIIlattice as G2lat
38import GSASIIpwdGUI as G2pdG
39import GSASIIimgGUI as G2imG
40import GSASIIimage as G2img
41import GSASIIElem as G2el
42import GSASIIstrIO as G2stIO
43import GSASIImapvars as G2mv
44import GSASIIctrls as G2G
45import os
46import os.path as ospath
47
48DEBUG = False       #=True for various prints
49TRANSP = False      #=true to transpose images for testing
50if GSASIIpath.GetConfigValue('Transpose'): TRANSP = True
51npsind = lambda x: np.sin(x*np.pi/180.)
52
53def sfloat(S):
54    'Convert a string to float. An empty field or a unconvertable value is treated as zero'
55    if S.strip():
56        try:
57            return float(S)
58        except ValueError:
59            pass
60    return 0.0
61
62def sint(S):
63    'Convert a string to int. An empty field is treated as zero'
64    if S.strip():
65        return int(S)
66    else:
67        return 0
68
69def trim(val):
70    '''Simplify a string containing leading and trailing spaces
71    as well as newlines, tabs, repeated spaces etc. into a shorter and
72    more simple string, by replacing all ranges of whitespace
73    characters with a single space.
74
75    :param str val: the string to be simplified
76
77    :returns: the (usually) shortened version of the string
78    '''
79    return re.sub('\s+', ' ', val).strip()
80
81def makeInstDict(names,data,codes):
82    inst = dict(zip(names,zip(data,data,codes)))
83    for item in inst:
84        inst[item] = list(inst[item])
85    return inst
86
87def FileDlgFixExt(dlg,file):
88    'this is needed to fix a problem in linux wx.FileDialog'
89    ext = dlg.GetWildcard().split('|')[2*dlg.GetFilterIndex()+1].strip('*')
90    if ext not in file:
91        file += ext
92    return file
93       
94def GetPowderPeaks(fileName):
95    'Read powder peaks from a file'
96    sind = lambda x: math.sin(x*math.pi/180.)
97    asind = lambda x: 180.*math.asin(x)/math.pi
98    wave = 1.54052
99    File = open(fileName,'Ur')
100    Comments = []
101    peaks = []
102    S = File.readline()
103    while S:
104        if S[:1] == '#':
105            Comments.append(S[:-1])
106        else:
107            item = S.split()
108            if len(item) == 1:
109                peaks.append([float(item[0]),1.0])
110            elif len(item) > 1:
111                peaks.append([float(item[0]),float(item[0])])
112        S = File.readline()
113    File.close()
114    if Comments:
115       print 'Comments on file:'
116       for Comment in Comments: 
117            print Comment
118            if 'wavelength' in Comment:
119                wave = float(Comment.split('=')[1])
120    Peaks = []
121    if peaks[0][0] > peaks[-1][0]:          # d-spacings - assume CuKa
122        for peak in peaks:
123            dsp = peak[0]
124            sth = wave/(2.0*dsp)
125            if sth < 1.0:
126                tth = 2.0*asind(sth)
127            else:
128                break
129            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
130    else:                                   #2-thetas - assume Cuka (for now)
131        for peak in peaks:
132            tth = peak[0]
133            dsp = wave/(2.0*sind(tth/2.0))
134            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
135    limits = [1000.,0.]
136    for peak in Peaks:
137        limits[0] = min(limits[0],peak[0])
138        limits[1] = max(limits[1],peak[0])
139    limits[0] = max(1.,(int(limits[0]-1.)/5)*5.)
140    limits[1] = min(170.,(int(limits[1]+1.)/5)*5.)
141    return Comments,Peaks,limits,wave
142
143def CheckImageFile(G2frame,imagefile):
144    '''Try to locate an image file if the project and image have been moved
145    together. If the image file cannot be found, request the location from
146    the user.
147
148    :param wx.Frame G2frame: main GSAS-II Frame and data object
149    :param str imagefile: name of image file
150    :returns: imagefile, if it exists, or the name of a file
151      that does exist or False if the user presses Cancel
152
153    '''
154    if not os.path.exists(imagefile):
155        print 'Image file '+imagefile+' not found'
156        fil = imagefile.replace('\\','/') # windows?!
157        # see if we can find a file with same name or in a similarly named sub-dir
158        pth,fil = os.path.split(fil)
159        prevpth = None
160        while pth and pth != prevpth:
161            prevpth = pth
162            if os.path.exists(os.path.join(G2frame.dirname,fil)):
163                print 'found image file '+os.path.join(G2frame.dirname,fil)
164                return os.path.join(G2frame.dirname,fil)
165            pth,enddir = os.path.split(pth)
166            fil = os.path.join(enddir,fil)
167        # not found as a subdirectory, drop common parts of path for last saved & image file names
168        #    if image was .../A/B/C/imgs/ima.ge
169        #      & GPX was  .../A/B/C/refs/fil.gpx but is now .../NEW/TEST/TEST1
170        #    will look for .../NEW/TEST/TEST1/imgs/ima.ge, .../NEW/TEST/imgs/ima.ge, .../NEW/imgs/ima.ge and so on
171        Controls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
172        gpxPath = Controls.get('LastSavedAs','').replace('\\','/').split('/') # blank in older .GPX files
173        imgPath = imagefile.replace('\\','/').split('/')
174        for p1,p2 in zip(gpxPath,imgPath):
175            if p1 == p2:
176                gpxPath.pop(0),imgPath.pop(0)
177            else:
178                break
179        fil = os.path.join(*imgPath) # file with non-common prefix elements
180        prevpth = None
181        pth = os.path.abspath(G2frame.dirname)
182        while pth and pth != prevpth:
183            prevpth = pth
184            if os.path.exists(os.path.join(pth,fil)):
185                print 'found image file '+os.path.join(pth,fil)
186                return os.path.join(pth,fil)
187            pth,enddir = os.path.split(pth)
188        #GSASIIpath.IPyBreak()
189
190    if not os.path.exists(imagefile):
191        prevnam = os.path.split(imagefile)[1]
192        prevext = os.path.splitext(imagefile)[1]
193        wildcard = 'Image format (*'+prevext+')|*'+prevext
194        dlg = wx.FileDialog(G2frame, 'Previous image file ('+prevnam+') not found; open here', '.', prevnam,
195                            wildcard,wx.OPEN)
196        try:
197            dlg.SetFilename(''+ospath.split(imagefile)[1])
198            if dlg.ShowModal() == wx.ID_OK:
199                imagefile = dlg.GetPath()
200            else:
201                imagefile = False
202        finally:
203            dlg.Destroy()
204    return imagefile
205
206def EditImageParms(parent,Data,Comments,Image,filename):
207    dlg = wx.Dialog(parent, wx.ID_ANY, 'Edit image parameters',
208                    style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
209    def onClose(event):
210        dlg.EndModal(wx.ID_OK)
211    mainsizer = wx.BoxSizer(wx.VERTICAL)
212    h,w = Image.shape[:2]
213    mainsizer.Add(wx.StaticText(dlg,wx.ID_ANY,
214                                'File '+str(filename)+'\nImage size: '+str(h)+' x '+str(w)),
215                  0,wx.ALIGN_LEFT|wx.ALL, 2)
216   
217    vsizer = wx.BoxSizer(wx.HORIZONTAL)
218    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Wavelength (\xC5) '),
219               0,wx.ALIGN_LEFT|wx.ALL, 2)
220    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'wavelength')
221    vsizer.Add(wdgt)
222    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
223
224    vsizer = wx.BoxSizer(wx.HORIZONTAL)
225    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Pixel size (\xb5m). Width '),
226               0,wx.ALIGN_LEFT|wx.ALL, 2)
227    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],0,
228                                 size=(50,-1))
229    vsizer.Add(wdgt)
230    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Height '),
231               wx.ALIGN_LEFT|wx.ALL, 2)
232    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],1,
233                                 size=(50,-1))
234    vsizer.Add(wdgt)
235    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
236
237    vsizer = wx.BoxSizer(wx.HORIZONTAL)
238    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Sample to detector (mm) '),
239               0,wx.ALIGN_LEFT|wx.ALL, 2)
240    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'distance')
241    vsizer.Add(wdgt)
242    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
243
244    vsizer = wx.BoxSizer(wx.HORIZONTAL)
245    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Beam center (pixels). X = '),
246               0,wx.ALIGN_LEFT|wx.ALL, 2)
247    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],0,
248                                 size=(75,-1))
249    vsizer.Add(wdgt)
250    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Y = '),
251               wx.ALIGN_LEFT|wx.ALL, 2)
252    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],1,
253                                 size=(75,-1))
254    vsizer.Add(wdgt)
255    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
256
257    vsizer = wx.BoxSizer(wx.HORIZONTAL)
258    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Comments '),
259               0,wx.ALIGN_LEFT|wx.ALL, 2)
260    wdgt = G2G.ValidatedTxtCtrl(dlg,Comments,0,size=(250,-1))
261    vsizer.Add(wdgt)
262    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
263
264    btnsizer = wx.StdDialogButtonSizer()
265    OKbtn = wx.Button(dlg, wx.ID_OK, 'Continue')
266    OKbtn.SetDefault()
267    OKbtn.Bind(wx.EVT_BUTTON,onClose)
268    btnsizer.AddButton(OKbtn) # not sure why this is needed
269    btnsizer.Realize()
270    mainsizer.Add(btnsizer, 1, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 5)
271    dlg.SetSizer(mainsizer)
272    dlg.CenterOnParent()
273    dlg.ShowModal()
274   
275def LoadImage2Tree(imagefile,G2frame,Comments,Data,Npix,Image):
276    '''Load an image into the tree. Saves the location of the image, as well as the
277    ImageTag (where there is more than one image in the file), if defined.
278    '''
279    ImgNames = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
280    TreeLbl = 'IMG '+os.path.basename(imagefile)
281    ImageTag = Data.get('ImageTag')
282    if ImageTag:
283        TreeLbl += ' #'+str(ImageTag)
284        imageInfo = (imagefile,ImageTag)
285    else:
286        imageInfo = imagefile
287    TreeName = G2obj.MakeUniqueLabel(TreeLbl,ImgNames)
288    Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=TreeName)
289    G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)
290    Imax = np.amax(Image)
291    Imin = max(0.0,np.amin(Image))          #force positive
292    if G2frame.imageDefault:
293        Data = copy.copy(G2frame.imageDefault)
294        Data['showLines'] = True
295        Data['ring'] = []
296        Data['rings'] = []
297        Data['cutoff'] = 10
298        Data['pixLimit'] = 20
299        Data['edgemin'] = 100000000
300        Data['calibdmin'] = 0.5
301        Data['calibskip'] = 0
302        Data['ellipses'] = []
303        Data['calibrant'] = ''
304        Data['GonioAngles'] = [0.,0.,0.]
305        Data['DetDepthRef'] = False
306    else:
307        Data['type'] = 'PWDR'
308        Data['color'] = 'Paired'
309        Data['tilt'] = 0.0
310        Data['rotation'] = 0.0
311        Data['showLines'] = False
312        Data['ring'] = []
313        Data['rings'] = []
314        Data['cutoff'] = 10
315        Data['pixLimit'] = 20
316        Data['calibdmin'] = 0.5
317        Data['calibskip'] = 0
318        Data['edgemin'] = 100000000
319        Data['ellipses'] = []
320        Data['GonioAngles'] = [0.,0.,0.]
321        Data['DetDepth'] = 0.
322        Data['DetDepthRef'] = False
323        Data['calibrant'] = ''
324        Data['IOtth'] = [2.0,5.0]
325        Data['LRazimuth'] = [135,225]
326        Data['azmthOff'] = 0.0
327        Data['outChannels'] = 2500
328        Data['outAzimuths'] = 1
329        Data['centerAzm'] = False
330        Data['fullIntegrate'] = False
331        Data['setRings'] = False
332        Data['background image'] = ['',-1.0]                           
333        Data['dark image'] = ['',-1.0]
334        Data['Flat Bkg'] = 0.0
335    Data['setDefault'] = False
336    Data['range'] = [(0,Imax),[0,Imax]]
337    G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Image Controls'),Data)
338    Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(0,Imax),[0,Imax]]}
339    G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Masks'),Masks)
340    G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Stress/Strain'),
341        {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
342    G2frame.PatternTree.SetItemPyData(Id,[Npix,imageInfo])
343    G2frame.PickId = Id
344    G2frame.PickIdText = G2frame.GetTreeItemsList(G2frame.PickId)
345    G2frame.Image = Id
346
347def GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None):
348    '''Read a single image with an image importer.
349
350    :param wx.Frame G2frame: main GSAS-II Frame and data object.
351    :param str imagefile: name of image file
352    :param bool imageOnly: If True return only the image,
353      otherwise  (default) return more (see below)
354    :param int/str ImageTag: specifies a particular image to be read from a file.
355      First image is read if None (default).
356
357    :returns: an image as a numpy array or a list of four items:
358      Comments, Data, Npix and the Image, as selected by imageOnly
359
360    '''
361    # determine which formats are compatible with this file
362    primaryReaders = []
363    secondaryReaders = []
364    for rd in G2frame.ImportImageReaderlist:
365        flag = rd.ExtensionValidator(imagefile)
366        if flag is None: 
367            secondaryReaders.append(rd)
368        elif flag:
369            primaryReaders.append(rd)
370    if len(secondaryReaders) + len(primaryReaders) == 0:
371        print('Error: No matching format for file '+filename)
372        raise Exception('No image read')
373    fp = None
374    errorReport = ''
375    fp = open(imagefile,'Ur')
376    for rd in primaryReaders+secondaryReaders:
377        rd.ReInitialize() # purge anything from a previous read
378        fp.seek(0)  # rewind
379        rd.errors = "" # clear out any old errors
380        if not rd.ContentsValidator(fp): # rejected on cursory check
381            errorReport += "\n  "+rd.formatName + ' validator error'
382            if rd.errors: 
383                errorReport += ': '+rd.errors
384                continue
385        rdbuffer = {} # create temporary storage for file reader
386        if imageOnly:
387            ParentFrame = None # prevent GUI access on reread
388        else:
389            ParentFrame = G2frame
390        if GSASIIpath.GetConfigValue('debug'):
391            flag = rd.Reader(imagefile,fp,ParentFrame,blocknum=ImageTag)
392        else:
393            flag = False
394            try:
395                flag = rd.Reader(imagefile,fp,ParentFrame,blocknum=ImageTag)
396            except rd.ImportException as detail:
397                rd.errors += "\n  Read exception: "+str(detail)
398            except Exception as detail:
399                import traceback
400                rd.errors += "\n  Unhandled read exception: "+str(detail)
401                rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
402        if flag: # this read succeeded
403            if rd.Image is None:
404                raise Exception('No image read. Strange!')
405            if GSASIIpath.GetConfigValue('Transpose'):
406                print 'Transposing Image!'
407                rd.Image = rd.Image.T
408            #rd.readfilename = imagefile
409            if imageOnly:
410                return rd.Image
411            else:
412                return rd.Comments,rd.Data,rd.Npix,rd.Image
413    else:
414        print('Error reading file '+filename)
415        print('Error messages(s)\n'+errorReport)
416        raise Exception('No image read')   
417
418def ReadImages(G2frame,imagefile):
419    '''Read one or more images from a file and put them into the Tree
420    using image importers. Called only in :meth:`AutoIntFrame.OnTimerLoop`.
421
422    :param wx.Frame G2frame: main GSAS-II Frame and data object.
423    :param str imagefile: name of image file
424
425    :returns: a list of the id's of the IMG tree items created
426    '''
427    # determine which formats are compatible with this file
428    primaryReaders = []
429    secondaryReaders = []
430    for rd in G2frame.ImportImageReaderlist:
431        flag = rd.ExtensionValidator(imagefile)
432        if flag is None:
433            secondaryReaders.append(rd)
434        elif flag:
435            primaryReaders.append(rd)
436    if len(secondaryReaders) + len(primaryReaders) == 0:
437        print('Error: No matching format for file '+filename)
438        raise Exception('No image read')
439    errorReport = ''
440    fp = open(imagefile,'Ur')
441    rdbuffer = {} # create temporary storage for file reader
442    for rd in primaryReaders+secondaryReaders:
443        rd.ReInitialize() # purge anything from a previous read
444        fp.seek(0)  # rewind
445        rd.errors = "" # clear out any old errors
446        if not rd.ContentsValidator(fp): # rejected on cursory check
447            errorReport += "\n  "+rd.formatName + ' validator error'
448            if rd.errors: 
449                errorReport += ': '+rd.errors
450                continue
451        ParentFrame = G2frame
452        block = 0
453        repeat = True
454        CreatedIMGitems = []
455        while repeat: # loop if the reader asks for another pass on the file
456            block += 1
457            repeat = False
458            if GSASIIpath.GetConfigValue('debug'):
459                flag = rd.Reader(imagefile,fp,ParentFrame,blocknum=block,Buffer=rdbuffer)
460            else:
461                flag = False
462                try:
463                    flag = rd.Reader(imagefile,fp,ParentFrame,blocknum=block,Buffer=rdbuffer)
464                except rd.ImportException as detail:
465                    rd.errors += "\n  Read exception: "+str(detail)
466                except Exception as detail:
467                    import traceback
468                    rd.errors += "\n  Unhandled read exception: "+str(detail)
469                    rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
470            if flag: # this read succeeded
471                if rd.Image is None:
472                    raise Exception('No image read. Strange!')
473                if GSASIIpath.GetConfigValue('Transpose'):
474                    print 'Transposing Image!'
475                    rd.Image = rd.Image.T
476                rd.Data['ImageTag'] = rd.repeatcount
477                LoadImage2Tree(imagefile,G2frame,rd.Comments,rd.Data,rd.Npix,rd.Image)
478                repeat = rd.repeat
479            CreatedIMGitems.append(G2frame.Image)
480        if CreatedIMGitems: return CreatedIMGitems
481    else:
482        print('Error reading file '+filename)
483        print('Error messages(s)\n'+errorReport)
484        return []
485        #raise Exception('No image read')   
486
487def SaveMultipleImg(G2frame):
488    if not G2frame.PatternTree.GetCount():
489        print 'no images!'
490        return
491    choices = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
492    if len(choices) == 1:
493        names = choices
494    else:
495        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices)
496        dlg.SetSelections([])
497        names = []
498        if dlg.ShowModal() == wx.ID_OK:
499            names = [choices[sel] for sel in dlg.GetSelections()]
500        dlg.Destroy()
501    if not names: return
502    for name in names:
503        Id = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, name)
504        Npix,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(Id)
505        imroot = os.path.splitext(imagefile)[0]
506        if imagetag:
507            imroot += '_' + str(imagetag)
508        Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Image Controls'))
509        print('Writing '+imroot+'.imctrl')
510        File = open(imroot+'.imctrl','w')
511        keys = ['type','wavelength','calibrant','distance','center',
512                    'tilt','rotation','azmthOff','fullIntegrate','LRazimuth',
513                    'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
514                    'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg',
515                    'binType','SampleShape','PolaVal','SampleAbs','dark image','background image']
516        for key in keys:
517            if key not in Data: continue    #uncalibrated!
518            File.write(key+':'+str(Data[key])+'\n')
519        File.close()
520        mask = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Masks'))
521        G2imG.CleanupMasks(mask)
522        print('Writing '+imroot+'.immask')
523        File = open(imroot+'.immask','w')
524        for key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
525            File.write(key+':'+str(mask[key])+'\n')
526        File.close()
527       
528def PutG2Image(filename,Comments,Data,Npix,image):
529    'Write an image as a python pickle - might be better as an .edf file?'
530    File = open(filename,'wb')
531    cPickle.dump([Comments,Data,Npix,image],File,1)
532    File.close()
533    return
534   
535# should get moved to importer when ready to test
536def GetEdfData(filename,imageOnly=False):   
537    'Read European detector data edf file'
538    import struct as st
539    import array as ar
540    if not imageOnly:
541        print 'Read European detector data edf file: ',filename
542    File = open(filename,'rb')
543    fileSize = os.stat(filename).st_size
544    head = File.read(3072)
545    lines = head.split('\n')
546    sizexy = [0,0]
547    pixSize = [154,154]     #Pixium4700?
548    cent = [0,0]
549    wave = 1.54187  #default <CuKa>
550    dist = 1000.
551    head = ['European detector data',]
552    for line in lines:
553        line = line.replace(';',' ').strip()
554        fields = line.split()
555        if 'Dim_1' in line:
556            sizexy[0] = int(fields[2])
557        elif 'Dim_2' in line:
558            sizexy[1] = int(fields[2])
559        elif 'DataType' in line:
560            dType = fields[2]
561        elif 'wavelength' in line:
562            wave = float(fields[2])
563        elif 'Size' in line:
564            imSize = int(fields[2])
565#        elif 'DataType' in lines:
566#            dType = fields[2]
567        elif 'pixel_size_x' in line:
568            pixSize[0] = float(fields[2])
569        elif 'pixel_size_y' in line:
570            pixSize[1] = float(fields[2])
571        elif 'beam_center_x' in line:
572            cent[0] = float(fields[2])
573        elif 'beam_center_y' in line:
574            cent[1] = float(fields[2])
575        elif 'refined_distance' in line:
576            dist = float(fields[2])
577        if line:
578            head.append(line)
579        else:   #blank line at end of header
580            break 
581    File.seek(fileSize-imSize)
582    if dType == 'UnsignedShort':       
583        image = np.array(ar.array('H',File.read(imSize)),dtype=np.int32)
584    elif dType == 'UnsignedInt':
585        image = np.array(ar.array('L',File.read(imSize)),dtype=np.int32)
586    elif dType == 'UnsignedLong':
587        image = np.array(ar.array('L',File.read(imSize)),dtype=np.int32)
588    elif dType == 'SignedInteger':
589        image = np.array(ar.array('l',File.read(imSize)),dtype=np.int32)
590    image = np.reshape(image,(sizexy[1],sizexy[0]))
591    data = {'pixelSize':pixSize,'wavelength':wave,'distance':dist,'center':cent,'size':sizexy}
592    Npix = sizexy[0]*sizexy[1]
593    File.close()   
594    if imageOnly:
595        return image
596    else:
597        return head,data,Npix,image
598       
599# should get moved to importer when ready to test
600def GetRigaku(filename,imageOnly=False):
601    'Read Rigaku R-Axis IV image file'
602    import struct as st
603    import array as ar
604    if not imageOnly:
605        print 'Read Rigaku R-Axis IV file: ',filename   
606    File = open(filename,'rb')
607    fileSize = os.stat(filename).st_size
608    Npix = (fileSize-6000)/2
609    Head = File.read(6000)
610    head = ['Rigaku R-Axis IV detector data',]
611    image = np.array(ar.array('H',File.read(fileSize-6000)),dtype=np.int32)
612    print fileSize,image.shape
613    print head
614    if Npix == 9000000:
615        sizexy = [3000,3000]
616        pixSize = [100.,100.]       
617    elif Npix == 2250000:
618        sizexy = [1500,1500]
619        pixSize = [200.,200.]
620    else:
621        sizexy = [6000,6000]
622        pixSize = [50.,50.] 
623    image = np.reshape(image,(sizexy[1],sizexy[0]))       
624    data = {'pixelSize':pixSize,'wavelength':1.5428,'distance':250.0,'center':[150.,150.],'size':sizexy} 
625    File.close()   
626    if imageOnly:
627        return image
628    else:
629        return head,data,Npix,image
630   
631# should get moved to importer when ready to test       
632def GetImgData(filename,imageOnly=False):
633    'Read an ADSC image file'
634    import struct as st
635    import array as ar
636    if not imageOnly:
637        print 'Read ADSC img file: ',filename
638    File = open(filename,'rb')
639    head = File.read(511)
640    lines = head.split('\n')
641    head = []
642    center = [0,0]
643    for line in lines[1:-2]:
644        line = line.strip()[:-1]
645        if line:
646            if 'SIZE1' in line:
647                size = int(line.split('=')[1])
648                Npix = size*size
649            elif 'WAVELENGTH' in line:
650                wave = float(line.split('=')[1])
651            elif 'BIN' in line:
652                if line.split('=')[1] == '2x2':
653                    pixel=(102,102)
654                else:
655                    pixel = (51,51)
656            elif 'DISTANCE' in line:
657                distance = float(line.split('=')[1])
658            elif 'CENTER_X' in line:
659                center[0] = float(line.split('=')[1])
660            elif 'CENTER_Y' in line:
661                center[1] = float(line.split('=')[1])
662            head.append(line)
663    data = {'pixelSize':pixel,'wavelength':wave,'distance':distance,'center':center,'size':[size,size]}
664    image = []
665    row = 0
666    pos = 512
667    File.seek(pos)
668    image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
669    image = np.reshape(image,(sizexy[1],sizexy[0]))
670#    image = np.zeros(shape=(size,size),dtype=np.int32)   
671#    while row < size:
672#        File.seek(pos)
673#        line = ar.array('H',File.read(2*size))
674#        image[row] = np.asarray(line)
675#        row += 1
676#        pos += 2*size
677    File.close()
678    if imageOnly:
679        return image
680    else:
681        return lines[1:-2],data,Npix,image
682       
683# should get moved to importer when ready to test
684def GetMAR345Data(filename,imageOnly=False):
685    'Read a MAR-345 image plate image'
686    import array as ar
687    import struct as st
688    try:
689        import pack_f as pf
690    except:
691        msg = wx.MessageDialog(None, message="Unable to load the GSAS MAR image decompression, pack_f",
692                               caption="Import Error",
693                               style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
694        msg.ShowModal()
695        return None,None,None,None
696
697    if not imageOnly:
698        print 'Read Mar345 file: ',filename
699    File = open(filename,'rb')
700    head = File.read(4095)
701    numbers = st.unpack('<iiiiiiiiii',head[:40])
702    lines = head[128:].split('\n')
703    head = []
704    for line in lines:
705        line = line.strip()
706        if 'PIXEL' in line:
707            values = line.split()
708            pixel = (int(values[2]),int(values[4]))     #in microns
709        elif 'WAVELENGTH' in line:
710            wave = float(line.split()[1])
711        elif 'DISTANCE' in line:
712            distance = float(line.split()[1])           #in mm
713            if not distance:
714                distance = 500.
715        elif 'CENTER' in line:
716            values = line.split()
717            center = [float(values[2])/10.,float(values[4])/10.]    #make in mm from pixels
718        if line: 
719            head.append(line)
720    data = {'pixelSize':pixel,'wavelength':wave,'distance':distance,'center':center}
721    for line in head:
722        if 'FORMAT' in line[0:6]:
723            items = line.split()
724            sizex = int(items[1])
725            Npix = int(items[3])
726            sizey = int(Npix/sizex)
727    pos = 4096
728    data['size'] = [sizex,sizey]
729    File.seek(pos)
730    line = File.read(8)
731    while 'CCP4' not in line:       #get past overflow list for now
732        line = File.read(8)
733        pos += 8
734    pos += 37
735    File.seek(pos)
736    raw = File.read()
737    File.close()
738    image = np.zeros(shape=(sizex,sizey),dtype=np.int32)
739   
740    image = np.flipud(pf.pack_f(len(raw),raw,sizex,sizey,image).T)  #transpose to get it right way around & flip
741    if imageOnly:
742        return image
743    else:
744        return head,data,Npix,image
745       
746def ProjFileOpen(G2frame,showProvenance=True):
747    'Read a GSAS-II project file and load into the G2 data tree'
748    if not os.path.exists(G2frame.GSASprojectfile):
749        print ('\n*** Error attempt to open project file that does not exist:\n   '+
750               str(G2frame.GSASprojectfile))
751        return
752    LastSavedUsing = None
753    file = open(G2frame.GSASprojectfile,'rb')
754    if showProvenance: print 'loading from file: ',G2frame.GSASprojectfile
755    G2frame.SetTitle("GSAS-II data tree: "+
756                     os.path.split(G2frame.GSASprojectfile)[1])
757    wx.BeginBusyCursor()
758    try:
759        while True:
760            try:
761                data = cPickle.load(file)
762            except EOFError:
763                break
764            datum = data[0]
765           
766            Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=datum[0])
767            if 'PWDR' in datum[0]:               
768                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
769                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
770                G2frame.PatternTree.SetItemPyData(Id,datum[1][:3])  #temp. trim off junk (patch?)
771            elif datum[0].startswith('HKLF'): 
772                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
773                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
774                G2frame.PatternTree.SetItemPyData(Id,datum[1])
775            else:
776                G2frame.PatternTree.SetItemPyData(Id,datum[1])             
777                if datum[0] == 'Controls' and 'LastSavedUsing' in datum[1]:
778                    LastSavedUsing = datum[1]['LastSavedUsing']
779                if datum[0] == 'Controls' and 'PythonVersions' in datum[1] and GSASIIpath.GetConfigValue('debug') and showProvenance:
780                    print('Packages used to create .GPX file:')
781                    if 'dict' in str(type(datum[1]['PythonVersions'])):  #patch
782                        for p in sorted(datum[1]['PythonVersions'],key=lambda s: s.lower()):
783                            print("{:>14s}: {:s}".format(p[0],p[1]))
784                    else:
785                        for p in datum[1]['PythonVersions']:
786                            print("{:<12s} {:s}".format(p[0]+':',p[1]))
787            for datus in data[1:]:
788                sub = G2frame.PatternTree.AppendItem(Id,datus[0])
789#patch
790                if datus[0] == 'Instrument Parameters' and len(datus[1]) == 1:
791                    if 'PWDR' in datum[0]:
792                        datus[1] = [dict(zip(datus[1][3],zip(datus[1][0],datus[1][1],datus[1][2]))),{}]
793                    else:
794                        datus[1] = [dict(zip(datus[1][2],zip(datus[1][0],datus[1][1]))),{}]
795                    for item in datus[1][0]:               #zip makes tuples - now make lists!
796                        datus[1][0][item] = list(datus[1][0][item])
797#end patch
798                G2frame.PatternTree.SetItemPyData(sub,datus[1])
799            if 'IMG' in datum[0]:                   #retrieve image default flag & data if set
800                Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
801                if Data['setDefault']:
802                    G2frame.imageDefault = Data               
803        file.close()
804        if LastSavedUsing:
805            print('GPX load successful. Last saved with GSAS-II version '+LastSavedUsing)
806        else:
807            print('project load successful')
808        G2frame.NewPlot = True
809    except:
810        msg = wx.MessageDialog(G2frame,message="Error reading file "+
811            str(G2frame.GSASprojectfile)+". This is not a GSAS-II .gpx file",
812            caption="Load Error",style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
813        msg.ShowModal()
814    finally:
815        wx.EndBusyCursor()
816        G2frame.Status.SetStatusText('To reorder tree items, use mouse RB to drag/drop them')
817   
818def ProjFileSave(G2frame):
819    'Save a GSAS-II project file'
820    if not G2frame.PatternTree.IsEmpty():
821        file = open(G2frame.GSASprojectfile,'wb')
822        print 'save to file: ',G2frame.GSASprojectfile
823        # stick the file name into the tree and version info into tree so they are saved.
824        # (Controls should always be created at this point)
825        try:
826            Controls = G2frame.PatternTree.GetItemPyData(
827                G2gd.GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
828            Controls['LastSavedAs'] = os.path.abspath(G2frame.GSASprojectfile)
829            Controls['LastSavedUsing'] = str(GSASIIpath.GetVersionNumber())
830            Controls['PythonVersions'] = G2frame.PackageVersions
831        except:
832            pass
833        wx.BeginBusyCursor()
834        try:
835            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
836            while item:
837                data = []
838                name = G2frame.PatternTree.GetItemText(item)
839                data.append([name,G2frame.PatternTree.GetItemPyData(item)])
840                item2, cookie2 = G2frame.PatternTree.GetFirstChild(item)
841                while item2:
842                    name = G2frame.PatternTree.GetItemText(item2)
843                    data.append([name,G2frame.PatternTree.GetItemPyData(item2)])
844                    item2, cookie2 = G2frame.PatternTree.GetNextChild(item, cookie2)                           
845                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)                           
846                cPickle.dump(data,file,1)
847            file.close()
848            pth = os.path.split(os.path.abspath(G2frame.GSASprojectfile))[0]
849            if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
850            G2frame.LastGPXdir = pth
851        finally:
852            wx.EndBusyCursor()
853        print('project save successful')
854
855def SaveIntegration(G2frame,PickId,data,Overwrite=False):
856    'Save image integration results as powder pattern(s)'
857    azms = G2frame.Integrate[1]
858    X = G2frame.Integrate[2][:-1]
859    N = len(X)
860    Id = G2frame.PatternTree.GetItemParent(PickId)
861    name = G2frame.PatternTree.GetItemText(Id)
862    name = name.replace('IMG ',data['type']+' ')
863    Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Comments'))
864    if 'PWDR' in name:
865        names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
866        codes = [0 for i in range(11)]
867    elif 'SASD' in name:
868        names = ['Type','Lam','Zero','Azimuth'] 
869        codes = [0 for i in range(4)]
870        X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
871    Xminmax = [X[0],X[-1]]
872    LRazm = data['LRazimuth']
873    Azms = []
874    dazm = 0.
875    if data['fullIntegrate'] and data['outAzimuths'] == 1:
876        Azms = [45.0,]                              #a poor man's average?
877    else:
878        for i,azm in enumerate(azms[:-1]):
879            if azm > 360. and azms[i+1] > 360.:
880                Azms.append(G2img.meanAzm(azm%360.,azms[i+1]%360.))
881            else:   
882                Azms.append(G2img.meanAzm(azm,azms[i+1]))
883        dazm = np.min(np.abs(np.diff(azms)))/2.
884    G2frame.IntgOutList = []
885    for i,azm in enumerate(azms[:-1]):
886        Aname = name+" Azm= %.2f"%((azm+dazm)%360.)
887        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
888        # if Overwrite delete any duplicate
889        if Overwrite and G2gd.GetPatternTreeItemId(G2frame,G2frame.root,Aname):
890            print('Replacing '+Aname)
891            item = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,Aname)
892            G2frame.PatternTree.Delete(item)
893        else:
894            nOcc = 0
895            while item:
896                Name = G2frame.PatternTree.GetItemText(item)
897                if Aname in Name:
898                    nOcc += 1
899                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
900            Aname += '(%03d)'%(nOcc)
901        Sample = G2pdG.SetDefaultSample()
902        Sample['Gonio. radius'] = data['distance']
903        Sample['Omega'] = data['GonioAngles'][0]
904        Sample['Chi'] = data['GonioAngles'][1]
905        Sample['Phi'] = data['GonioAngles'][2]
906        Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
907        if 'PWDR' in Aname:
908            parms = ['PXC',data['wavelength'],0.0,0.99,1.0,-0.10,0.4,0.30,1.0,0.0001,Azms[i]]    #set polarization for synchrotron radiation!
909        elif 'SASD' in Aname:
910            Sample['Trans'] = data['SampleAbs'][0]
911            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
912        Y = G2frame.Integrate[0][i]
913        W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
914        Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=Aname)
915        G2frame.IntgOutList.append(Id)
916        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
917        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
918        if 'PWDR' in Aname:
919            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
920                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
921        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
922        for item in inst[0]:
923            inst[0][item] = list(inst[0][item])
924        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
925        if 'PWDR' in Aname:
926            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
927            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Peak List'),{'sigDict':{},'peaks':[]})
928            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
929            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Unit Cells List'),[])
930            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Reflection Lists'),{})
931        elif 'SASD' in Aname:             
932            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
933            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
934            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
935        valuesdict = {
936            'wtFactor':1.0,
937            'Dummy':False,
938            'ranId':ran.randint(0,sys.maxint),
939            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
940            'qPlot':False,'dPlot':False,'sqrtPlot':False
941            }
942        G2frame.PatternTree.SetItemPyData(
943            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 PDFSave(G2frame,exports):
948    'Save a PDF G(r) and S(Q) in column formats'
949    for export in exports:
950        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
951        SQname = 'S(Q)'+export[4:]
952        GRname = 'G(R)'+export[4:]
953        sqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.sq')
954        grfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.gr')
955        sqId = G2gd.GetPatternTreeItemId(G2frame, PickId, SQname)
956        grId = G2gd.GetPatternTreeItemId(G2frame, PickId, GRname)
957        sqdata = np.array(G2frame.PatternTree.GetItemPyData(sqId)[1][:2]).T
958        grdata = np.array(G2frame.PatternTree.GetItemPyData(grId)[1][:2]).T
959        sqfile = open(sqfilename,'w')
960        grfile = open(grfilename,'w')
961        sqfile.write('#T S(Q) %s\n'%(export))
962        grfile.write('#T G(R) %s\n'%(export))
963        sqfile.write('#L Q     S(Q)\n')
964        grfile.write('#L R     G(R)\n')
965        for q,sq in sqdata:
966            sqfile.write("%15.6g %15.6g\n" % (q,sq))
967        sqfile.close()
968        for r,gr in grdata:
969            grfile.write("%15.6g %15.6g\n" % (r,gr))
970        grfile.close()
971   
972def PeakListSave(G2frame,file,peaks):
973    'Save powder peaks to a data file'
974    print 'save peak list to file: ',G2frame.peaklistfile
975    if not peaks:
976        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
977        try:
978            result = dlg.ShowModal()
979        finally:
980            dlg.Destroy()
981        return
982    for peak in peaks:
983        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
984            (peak[0],peak[2],peak[4],peak[6]))
985    print 'peak list saved'
986             
987def IndexPeakListSave(G2frame,peaks):
988    'Save powder peaks from the indexing list'
989    file = open(G2frame.peaklistfile,'wa')
990    print 'save index peak list to file: ',G2frame.peaklistfile
991    wx.BeginBusyCursor()
992    try:
993        if not peaks:
994            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
995            try:
996                result = dlg.ShowModal()
997            finally:
998                dlg.Destroy()
999            return
1000        for peak in peaks:
1001            file.write("%12.6f\n" % (peak[7]))
1002        file.close()
1003    finally:
1004        wx.EndBusyCursor()
1005    print 'index peak list saved'
1006   
1007def SetNewPhase(Name='New Phase',SGData=None,cell=None,Super=None):
1008    '''Create a new phase dict with default values for various parameters
1009
1010    :param str Name: Name for new Phase
1011
1012    :param dict SGData: space group data from :func:`GSASIIspc:SpcGroup`;
1013      defaults to data for P 1
1014
1015    :param list cell: unit cell parameter list; defaults to
1016      [1.0,1.0,1.0,90.,90,90.,1.]
1017
1018    '''
1019    if SGData is None: SGData = G2spc.SpcGroup('P 1')[1]
1020    if cell is None: cell=[1.0,1.0,1.0,90.,90,90.,1.]
1021    phaseData = {
1022        'ranId':ran.randint(0,sys.maxint),
1023        'General':{
1024            'Name':Name,
1025            'Type':'nuclear',
1026            'AtomPtrs':[3,1,7,9],
1027            'SGData':SGData,
1028            'Cell':[False,]+cell,
1029            'Pawley dmin':1.0,
1030            'Data plot type':'None',
1031            'SH Texture':{
1032                'Order':0,
1033                'Model':'cylindrical',
1034                'Sample omega':[False,0.0],
1035                'Sample chi':[False,0.0],
1036                'Sample phi':[False,0.0],
1037                'SH Coeff':[False,{}],
1038                'SHShow':False,
1039                'PFhkl':[0,0,1],
1040                'PFxyz':[0,0,1],
1041                'PlotType':'Pole figure',
1042                'Penalty':[['',],0.1,False,1.0]}},
1043        'Atoms':[],
1044        'Drawing':{},
1045        'Histograms':{},
1046        'Pawley ref':[],
1047        'RBModels':{},
1048        }
1049    if Super and Super.get('Use',False):
1050        phaseData['General'].update({'Type':'modulated','Super':True,'SuperSg':Super['ssSymb']})
1051        phaseData['General']['SSGData'] = G2spc.SSpcGroup(SGData,Super['ssSymb'])
1052        phaseData['General']['SuperVec'] = [Super['ModVec'],False,Super['maxH']]
1053
1054    return phaseData
1055       
1056class MultipleChoicesDialog(wx.Dialog):
1057    '''A dialog that offers a series of choices, each with a
1058    title and a wx.Choice widget. Intended to be used Modally.
1059    typical input:
1060
1061        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1062        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1063       
1064    selections are placed in self.chosen when OK is pressed
1065    '''
1066    def __init__(self,choicelist,headinglist,
1067                 head='Select options',
1068                 title='Please select from options below',
1069                 parent=None):
1070        self.chosen = []
1071        wx.Dialog.__init__(
1072            self,parent,wx.ID_ANY,head, 
1073            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1074        panel = wx.Panel(self)
1075        mainSizer = wx.BoxSizer(wx.VERTICAL)
1076        mainSizer.Add((10,10),1)
1077        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1078        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1079        self.ChItems = []
1080        for choice,lbl in zip(choicelist,headinglist):
1081            mainSizer.Add((10,10),1)
1082            self.chosen.append(0)
1083            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1084            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1085            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1086            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1087
1088        OkBtn = wx.Button(panel,-1,"Ok")
1089        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1090        cancelBtn = wx.Button(panel,-1,"Cancel")
1091        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1092        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1093        btnSizer.Add((20,20),1)
1094        btnSizer.Add(OkBtn)
1095        btnSizer.Add((20,20),1)
1096        btnSizer.Add(cancelBtn)
1097        btnSizer.Add((20,20),1)
1098        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1099        panel.SetSizer(mainSizer)
1100        panel.Fit()
1101        self.Fit()
1102       
1103    def OnOk(self,event):
1104        parent = self.GetParent()
1105        if parent is not None: parent.Raise()
1106        # save the results from the choice widgets
1107        self.chosen = []
1108        for w in self.ChItems:
1109            self.chosen.append(w.GetSelection())
1110        self.EndModal(wx.ID_OK)             
1111           
1112    def OnCancel(self,event):
1113        parent = self.GetParent()
1114        if parent is not None: parent.Raise()
1115        self.chosen = []
1116        self.EndModal(wx.ID_CANCEL)             
1117           
1118def ExtractFileFromZip(filename, selection=None, confirmread=True,
1119                       confirmoverwrite=True, parent=None,
1120                       multipleselect=False):
1121    '''If the filename is a zip file, extract a file from that
1122    archive.
1123
1124    :param list Selection: used to predefine the name of the file
1125      to be extracted. Filename case and zip directory name are
1126      ignored in selection; the first matching file is used.
1127
1128    :param bool confirmread: if True asks the user to confirm before expanding
1129      the only file in a zip
1130
1131    :param bool confirmoverwrite: if True asks the user to confirm
1132      before overwriting if the extracted file already exists
1133
1134    :param bool multipleselect: if True allows more than one zip
1135      file to be extracted, a list of file(s) is returned.
1136      If only one file is present, do not ask which one, otherwise
1137      offer a list of choices (unless selection is used).
1138   
1139    :returns: the name of the file that has been created or a
1140      list of files (see multipleselect)
1141
1142    If the file is not a zipfile, return the name of the input file.
1143    If the zipfile is empty or no file has been selected, return None
1144    '''
1145    import zipfile # do this now, since we can save startup time by doing this only on need
1146    import shutil
1147    zloc = os.path.split(filename)[0]
1148    if not zipfile.is_zipfile(filename):
1149        #print("not zip")
1150        return filename
1151
1152    z = zipfile.ZipFile(filename,'r')
1153    zinfo = z.infolist()
1154
1155    if len(zinfo) == 0:
1156        #print('Zip has no files!')
1157        zlist = [-1]
1158    if selection:
1159        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1160        if selection.lower() in choices:
1161            zlist = [choices.index(selection.lower())]
1162        else:
1163            print('debug: file '+str(selection)+' was not found in '+str(filename))
1164            zlist = [-1]
1165    elif len(zinfo) == 1 and confirmread:
1166        result = wx.ID_NO
1167        dlg = wx.MessageDialog(
1168            parent,
1169            'Is file '+str(zinfo[0].filename)+
1170            ' what you want to extract from '+
1171            str(os.path.split(filename)[1])+'?',
1172            'Confirm file', 
1173            wx.YES_NO | wx.ICON_QUESTION)
1174        try:
1175            result = dlg.ShowModal()
1176        finally:
1177            dlg.Destroy()
1178        if result == wx.ID_NO:
1179            zlist = [-1]
1180        else:
1181            zlist = [0]
1182    elif len(zinfo) == 1:
1183        zlist = [0]
1184    elif multipleselect:
1185        # select one or more from a from list
1186        choices = [i.filename for i in zinfo]
1187        dlg = G2G.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1188            'Choose file(s)',choices)
1189        if dlg.ShowModal() == wx.ID_OK:
1190            zlist = dlg.GetSelections()
1191        else:
1192            zlist = []
1193        dlg.Destroy()
1194    else:
1195        # select one from a from list
1196        choices = [i.filename for i in zinfo]
1197        dlg = wx.SingleChoiceDialog(parent,
1198            'Select file to extract from zip file'+str(filename),'Choose file',
1199            choices,)
1200        if dlg.ShowModal() == wx.ID_OK:
1201            zlist = [dlg.GetSelection()]
1202        else:
1203            zlist = [-1]
1204        dlg.Destroy()
1205       
1206    outlist = []
1207    for zindex in zlist:
1208        if zindex >= 0:
1209            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1210            if os.path.exists(efil) and confirmoverwrite:
1211                result = wx.ID_NO
1212                dlg = wx.MessageDialog(parent,
1213                    'File '+str(efil)+' already exists. OK to overwrite it?',
1214                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1215                try:
1216                    result = dlg.ShowModal()
1217                finally:
1218                    dlg.Destroy()
1219                if result == wx.ID_NO:
1220                    zindex = -1
1221        if zindex >= 0:
1222            # extract the file to the current directory, regardless of it's original path
1223            #z.extract(zinfo[zindex],zloc)
1224            eloc,efil = os.path.split(zinfo[zindex].filename)
1225            outfile = os.path.join(zloc, efil)
1226            fpin = z.open(zinfo[zindex])
1227            fpout = file(outfile, "wb")
1228            shutil.copyfileobj(fpin, fpout)
1229            fpin.close()
1230            fpout.close()
1231            outlist.append(outfile)
1232    z.close()
1233    if multipleselect and len(outlist) >= 1:
1234        return outlist
1235    elif len(outlist) == 1:
1236        return outlist[0]
1237    else:
1238        return None
1239
1240######################################################################
1241# base classes for reading various types of data files
1242#   not used directly, only by subclassing
1243######################################################################
1244try:
1245    E,SGData = G2spc.SpcGroup('P 1') # data structure for default space group
1246except: # errors on doc build
1247    SGData = None
1248P1SGData = SGData
1249######################################################################
1250class ImportBaseclass(object):
1251    '''Defines a base class for the reading of input files (diffraction
1252    data, coordinates,...). See :ref:`Writing a Import Routine<Import_routines>`
1253    for an explanation on how to use a subclass of this class.
1254    '''
1255    class ImportException(Exception):
1256        '''Defines an Exception that is used when an import routine hits an expected error,
1257        usually in .Reader.
1258
1259        Good practice is that the Reader should define a value in self.errors that
1260        tells the user some information about what is wrong with their file.         
1261        '''
1262        pass
1263   
1264    UseReader = True  # in __init__ set value of self.UseReader to False to skip use of current importer
1265    def __init__(self,formatName,longFormatName=None,
1266                 extensionlist=[],strictExtension=False,):
1267        self.formatName = formatName # short string naming file type
1268        if longFormatName: # longer string naming file type
1269            self.longFormatName = longFormatName
1270        else:
1271            self.longFormatName = formatName
1272        # define extensions that are allowed for the file type
1273        # for windows, remove any extensions that are duplicate, as case is ignored
1274        if sys.platform == 'windows' and extensionlist:
1275            extensionlist = list(set([s.lower() for s in extensionlist]))
1276        self.extensionlist = extensionlist
1277        # If strictExtension is True, the file will not be read, unless
1278        # the extension matches one in the extensionlist
1279        self.strictExtension = strictExtension
1280        self.errors = ''
1281        self.warnings = ''
1282        # used for readers that will use multiple passes to read
1283        # more than one data block
1284        self.repeat = False
1285        self.selections = []
1286        self.repeatcount = 0
1287        self.readfilename = '?'
1288        #print 'created',self.__class__
1289
1290    def ReInitialize(self):
1291        'Reinitialize the Reader to initial settings'
1292        self.errors = ''
1293        self.warnings = ''
1294        self.repeat = False
1295        self.repeatcount = 0
1296        self.readfilename = '?'
1297
1298    def BlockSelector(self, ChoiceList, ParentFrame=None,title='Select a block',
1299        size=None, header='Block Selector',useCancel=True):
1300        ''' Provide a wx dialog to select a block if the file contains more
1301        than one set of data and one must be selected
1302        '''
1303        if useCancel:
1304            dlg = wx.SingleChoiceDialog(
1305                ParentFrame,title, header,ChoiceList)
1306        else:
1307            dlg = wx.SingleChoiceDialog(
1308                ParentFrame,title, header,ChoiceList,
1309                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1310        if size: dlg.SetSize(size)
1311        dlg.CenterOnParent()
1312        if dlg.ShowModal() == wx.ID_OK:
1313            sel = dlg.GetSelection()
1314            return sel
1315        else:
1316            return None
1317        dlg.Destroy()
1318
1319    def MultipleBlockSelector(self, ChoiceList, ParentFrame=None,
1320        title='Select a block',size=None, header='Block Selector'):
1321        '''Provide a wx dialog to select a block of data if the
1322        file contains more than one set of data and one must be
1323        selected.
1324
1325        :returns: a list of the selected blocks
1326        '''
1327        dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1328            wx.CHOICEDLG_STYLE)
1329        dlg.CenterOnScreen()
1330        if size: dlg.SetSize(size)
1331        if dlg.ShowModal() == wx.ID_OK:
1332            sel = dlg.GetSelections()
1333        else:
1334            return []
1335        dlg.Destroy()
1336        selected = []
1337        if len(ChoiceList) in sel:
1338            return range(len(ChoiceList))
1339        else:
1340            return sel
1341        return selected
1342
1343    def MultipleChoicesDialog(self, choicelist, headinglist, ParentFrame=None, **kwargs):
1344        '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1345        widget. Typical input:
1346       
1347           * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1348           
1349           * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1350           
1351        optional keyword parameters are: head (window title) and title
1352        returns a list of selected indicies for each choice (or None)
1353        '''
1354        result = None
1355        dlg = MultipleChoicesDialog(choicelist,headinglist,
1356            parent=ParentFrame, **kwargs)         
1357        dlg.CenterOnParent()
1358        if dlg.ShowModal() == wx.ID_OK:
1359            result = dlg.chosen
1360        dlg.Destroy()
1361        return result
1362
1363    def ShowBusy(self):
1364        wx.BeginBusyCursor()
1365#        wx.Yield() # make it happen now!
1366
1367    def DoneBusy(self):
1368        wx.EndBusyCursor()
1369        wx.Yield() # make it happen now!
1370       
1371#    def Reader(self, filename, filepointer, ParentFrame=None, **unused):
1372#        '''This method must be supplied in the child class to read the file.
1373#        if the read fails either return False or raise an Exception
1374#        preferably of type ImportException.
1375#        '''
1376#        #start reading
1377#        raise ImportException("Error occurred while...")
1378#        self.errors += "Hint for user on why the error occur
1379#        return False # if an error occurs
1380#        return True # if read OK
1381
1382    def ExtensionValidator(self, filename):
1383        '''This methods checks if the file has the correct extension
1384        Return False if this filename will not be supported by this reader
1385        Return True if the extension matches the list supplied by the reader
1386        Return None if the reader allows un-registered extensions
1387        '''
1388        if filename:
1389            ext = os.path.splitext(filename)[1]
1390            if sys.platform == 'windows': ext = ext.lower()
1391            if ext in self.extensionlist: return True
1392            if self.strictExtension: return False
1393        return None
1394
1395    def ContentsValidator(self, filepointer):
1396        '''This routine will attempt to determine if the file can be read
1397        with the current format.
1398        This will typically be overridden with a method that
1399        takes a quick scan of [some of]
1400        the file contents to do a "sanity" check if the file
1401        appears to match the selected format.
1402        Expected to be called via self.Validator()
1403        '''
1404        #filepointer.seek(0) # rewind the file pointer
1405        return True
1406
1407    def CIFValidator(self, filepointer):
1408        '''A :meth:`ContentsValidator` for use to validate CIF files.
1409        '''
1410        for i,l in enumerate(filepointer):
1411            if i >= 1000: return True
1412            '''Encountered only blank lines or comments in first 1000
1413            lines. This is unlikely, but assume it is CIF anyway, since we are
1414            even less likely to find a file with nothing but hashes and
1415            blank lines'''
1416            line = l.strip()
1417            if len(line) == 0: # ignore blank lines
1418                continue 
1419            elif line.startswith('#'): # ignore comments
1420                continue 
1421            elif line.startswith('data_'): # on the right track, accept this file
1422                return True
1423            else: # found something invalid
1424                self.errors = 'line '+str(i+1)+' contains unexpected data:\n'
1425                self.errors += '  '+str(l)
1426                self.errors += '  Note: a CIF should only have blank lines or comments before'
1427                self.errors += '        a data_ statement begins a block.'
1428                return False 
1429
1430######################################################################
1431class ImportPhase(ImportBaseclass):
1432    '''Defines a base class for the reading of files with coordinates
1433
1434    Objects constructed that subclass this (in import/G2phase_*.py etc.) will be used
1435    in :meth:`GSASII.GSASII.OnImportPhase`.
1436    See :ref:`Writing a Import Routine<Import_Routines>`
1437    for an explanation on how to use this class.
1438
1439    '''
1440    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1441        strictExtension=False,):
1442        # call parent __init__
1443        ImportBaseclass.__init__(self,formatName,longFormatName,
1444            extensionlist,strictExtension)
1445        self.Phase = None # a phase must be created with G2IO.SetNewPhase in the Reader
1446        self.Constraints = None
1447
1448    def PhaseSelector(self, ChoiceList, ParentFrame=None,
1449        title='Select a phase', size=None,header='Phase Selector'):
1450        ''' Provide a wx dialog to select a phase if the file contains more
1451        than one phase
1452        '''
1453        return self.BlockSelector(ChoiceList,ParentFrame,title,
1454            size,header)
1455
1456######################################################################
1457class ImportStructFactor(ImportBaseclass):
1458    '''Defines a base class for the reading of files with tables
1459    of structure factors.
1460
1461    Structure factors are read with a call to :meth:`GSASII.GSASII.OnImportSfact`
1462    which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`, which calls
1463    methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1464    :meth:`Reader`.
1465
1466    See :ref:`Writing a Import Routine<Import_Routines>`
1467    for an explanation on how to use import classes in general. The specifics
1468    for reading a structure factor histogram require that
1469    the ``Reader()`` routine in the import
1470    class need to do only a few things: It
1471    should load :attr:`RefDict` item ``'RefList'`` with the reflection list,
1472    and set :attr:`Parameters` with the instrument parameters
1473    (initialized with :meth:`InitParameters` and set with :meth:`UpdateParameters`).
1474    '''
1475    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1476        strictExtension=False,):
1477        ImportBaseclass.__init__(self,formatName,longFormatName,
1478            extensionlist,strictExtension)
1479
1480        # define contents of Structure Factor entry
1481        self.Parameters = []
1482        'self.Parameters is a list with two dicts for data parameter settings'
1483        self.InitParameters()
1484        self.RefDict = {'RefList':[],'FF':{},'Super':0}
1485        self.Banks = []             #for multi bank data (usually TOF)
1486        '''self.RefDict is a dict containing the reflection information, as read from the file.
1487        Item 'RefList' contains the reflection information. See the
1488        :ref:`Single Crystal Reflection Data Structure<XtalRefl_table>`
1489        for the contents of each row. Dict element 'FF'
1490        contains the form factor values for each element type; if this entry
1491        is left as initialized (an empty list) it will be initialized as needed later.
1492        '''
1493    def ReInitialize(self):
1494        'Reinitialize the Reader to initial settings'
1495        ImportBaseclass.ReInitialize(self)
1496        self.InitParameters()
1497        self.Banks = []             #for multi bank data (usually TOF)
1498        self.RefDict = {'RefList':[],'FF':{},'Super':0}
1499       
1500    def InitParameters(self):
1501        'initialize the instrument parameters structure'
1502        Lambda = 0.70926
1503        HistType = 'SXC'
1504        self.Parameters = [{'Type':[HistType,HistType], # create the structure
1505                            'Lam':[Lambda,Lambda]
1506                            }, {}]
1507        'Parameters is a list with two dicts for data parameter settings'
1508
1509    def UpdateParameters(self,Type=None,Wave=None):
1510        'Revise the instrument parameters'
1511        if Type is not None:
1512            self.Parameters[0]['Type'] = [Type,Type]
1513        if Wave is not None:
1514            self.Parameters[0]['Lam'] = [Wave,Wave]           
1515                       
1516######################################################################
1517class ImportPowderData(ImportBaseclass):
1518    '''Defines a base class for the reading of files with powder data.
1519
1520    Objects constructed that subclass this (in import/G2pwd_*.py etc.) will be used
1521    in :meth:`GSASII.GSASII.OnImportPowder`.
1522    See :ref:`Writing a Import Routine<Import_Routines>`
1523    for an explanation on how to use this class.
1524    '''
1525    def __init__(self,formatName,longFormatName=None,
1526        extensionlist=[],strictExtension=False,):
1527        ImportBaseclass.__init__(self,formatName,longFormatName,
1528            extensionlist,strictExtension)
1529        self.clockWd = None  # used in TOF
1530        self.ReInitialize()
1531       
1532    def ReInitialize(self):
1533        'Reinitialize the Reader to initial settings'
1534        ImportBaseclass.ReInitialize(self)
1535        self.powderentry = ['',None,None] #  (filename,Pos,Bank)
1536        self.powderdata = [] # Powder dataset
1537        '''A powder data set is a list with items [x,y,w,yc,yb,yd]:
1538                np.array(x), # x-axis values
1539                np.array(y), # powder pattern intensities
1540                np.array(w), # 1/sig(intensity)^2 values (weights)
1541                np.array(yc), # calc. intensities (zero)
1542                np.array(yb), # calc. background (zero)
1543                np.array(yd), # obs-calc profiles
1544        '''                           
1545        self.comments = []
1546        self.idstring = ''
1547        self.Sample = G2pdG.SetDefaultSample() # default sample parameters
1548        self.Controls = {}  # items to be placed in top-level Controls
1549        self.GSAS = None     # used in TOF
1550        self.repeat_instparm = True # Should a parm file be
1551        #                             used for multiple histograms?
1552        self.instparm = None # name hint from file of instparm to use
1553        self.instfile = '' # full path name to instrument parameter file
1554        self.instbank = '' # inst parm bank number
1555        self.instmsg = ''  # a label that gets printed to show
1556                           # where instrument parameters are from
1557        self.numbanks = 1
1558        self.instdict = {} # place items here that will be transferred to the instrument parameters
1559        self.pwdparms = {} # place parameters that are transferred directly to the tree
1560                           # here (typically from an existing GPX file)
1561######################################################################
1562class ImportSmallAngleData(ImportBaseclass):
1563    '''Defines a base class for the reading of files with small angle data.
1564    See :ref:`Writing a Import Routine<Import_Routines>`
1565    for an explanation on how to use this class.
1566    '''
1567    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1568        strictExtension=False,):
1569           
1570        ImportBaseclass.__init__(self,formatName,longFormatName,extensionlist,
1571            strictExtension)
1572        self.ReInitialize()
1573       
1574    def ReInitialize(self):
1575        'Reinitialize the Reader to initial settings'
1576        ImportBaseclass.ReInitialize(self)
1577        self.smallangleentry = ['',None,None] #  (filename,Pos,Bank)
1578        self.smallangledata = [] # SASD dataset
1579        '''A small angle data set is a list with items [x,y,w,yc,yd]:
1580                np.array(x), # x-axis values
1581                np.array(y), # powder pattern intensities
1582                np.array(w), # 1/sig(intensity)^2 values (weights)
1583                np.array(yc), # calc. intensities (zero)
1584                np.array(yd), # obs-calc profiles
1585                np.array(yb), # preset bkg
1586        '''                           
1587        self.comments = []
1588        self.idstring = ''
1589        self.Sample = G2pdG.SetDefaultSample()
1590        self.GSAS = None     # used in TOF
1591        self.clockWd = None  # used in TOF
1592        self.numbanks = 1
1593        self.instdict = {} # place items here that will be transferred to the instrument parameters
1594
1595######################################################################
1596class ImportImage(ImportBaseclass):
1597    '''Defines a base class for the reading of images
1598
1599    Images are read in only these places:
1600   
1601      * Initial reading is typically done from a menu item
1602        with a call to :meth:`GSASII.GSASII.OnImportImage`
1603        which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`. That calls
1604        methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1605        :meth:`Reader`. This returns a list of reader objects for each read image.
1606
1607      * Images are read alternatively in :func:`GSASIIIO.ReadImages`, which puts image info
1608        directly into the data tree.
1609
1610      * Images are reloaded with :func:`GSASIIIO.GetImageData`.
1611
1612    .. _Image_import_routines:
1613
1614    When reading an image, the ``Reader()`` routine in the ImportImage class
1615    should set:
1616   
1617      * :attr:`Comments`: a list of strings (str),
1618      * :attr:`Npix`: the number of pixels in the image (int),
1619      * :attr:`Image`: the actual image as a numpy array (np.array)
1620      * :attr:`Data`: a dict defining image parameters (dict). Within this dict the following
1621        data items are needed:
1622       
1623         * 'pixelSize': size of each pixel in microns (such as ``[200,200]``.
1624         * 'wavelength': wavelength in Angstoms.
1625         * 'distance': distance of detector from sample in cm.
1626         * 'center': uncalibrated center of beam on detector (such as ``[204.8,204.8]``.
1627         * 'size': size of image (such as ``[2048,2048]``).
1628         * 'ImageTag': image number or other keyword used to retrieve image from
1629           a multi-image data file (defaults to ``1`` if not specified).
1630
1631    optional data items:
1632   
1633      * :attr:`repeat`: set to True if there are additional images to
1634        read in the file, False otherwise
1635      * :attr:`repeatcount`: set to the number of the image.
1636     
1637    Note that the above is initialized with :meth:`InitParameters`.
1638    (Also see :ref:`Writing a Import Routine<Import_Routines>`
1639    for an explanation on how to use import classes in general.)
1640    '''
1641    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1642        strictExtension=False,):
1643        ImportBaseclass.__init__(self,formatName,longFormatName,
1644            extensionlist,strictExtension)
1645        self.InitParameters()
1646       
1647    def ReInitialize(self):
1648        'Reinitialize the Reader to initial settings -- not used at present'
1649        ImportBaseclass.ReInitialize(self)
1650        self.InitParameters()
1651       
1652    def InitParameters(self):
1653        'initialize the instrument parameters structure'
1654        self.Comments = ['No comments']
1655        self.Data = {}
1656        self.Npix = 0
1657        self.Image = None
1658        self.repeat = False
1659        self.repeatcount = 1
1660
1661    def LoadImage(self,ParentFrame,imagefile,imagetag=None):
1662        '''Optionally, call this after reading in an image to load it into the tree.
1663        This saves time by preventing a reread of the same information.
1664        '''
1665        if ParentFrame:
1666            ParentFrame.ImageZ = self.Image   # store the image for plotting
1667            ParentFrame.oldImagefile = imagefile # save the name of the last image file read
1668            ParentFrame.oldImageTag = imagetag   # save the tag of the last image file read           
1669
1670######################################################################
1671class ExportBaseclass(object):
1672    '''Defines a base class for the exporting of GSAS-II results.
1673
1674    This class is subclassed in the various exports/G2export_*.py files. Those files
1675    are imported in :meth:`GSASII.GSASII._init_Exports` which defines the
1676    appropriate menu items for each one and the .Exporter method is called
1677    directly from the menu item.
1678
1679    Routines may also define a .Writer method, which is used to write a single
1680    file without invoking any GUI objects.
1681    '''
1682    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1683        self.G2frame = G2frame
1684        self.formatName = formatName # short string naming file type
1685        self.extension = extension
1686        if longFormatName: # longer string naming file type
1687            self.longFormatName = longFormatName
1688        else:
1689            self.longFormatName = formatName
1690        self.OverallParms = {}
1691        self.Phases = {}
1692        self.Histograms = {}
1693        self.powderDict = {}
1694        self.xtalDict = {}
1695        self.parmDict = {}
1696        self.sigDict = {}
1697        # updated in InitExport:
1698        self.currentExportType = None # type of export that has been requested
1699        # updated in ExportSelect (when used):
1700        self.phasenam = None # a list of selected phases
1701        self.histnam = None # a list of selected histograms
1702        self.filename = None # name of file to be written (single export) or template (multiple files)
1703        self.dirname = '' # name of directory where file(s) will be written
1704        self.fullpath = '' # name of file being written -- full path
1705       
1706        # items that should be defined in a subclass of this class
1707        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1708        # The following types are defined: 'project', "phase", "powder", "single"
1709        self.multiple = False # set as True if the class can export multiple phases or histograms
1710        # self.multiple is ignored for "project" exports
1711
1712    def InitExport(self,event):
1713        '''Determines the type of menu that called the Exporter and
1714        misc initialization.
1715        '''
1716        self.filename = None # name of file to be written (single export)
1717        self.dirname = '' # name of file to be written (multiple export)
1718        if event:
1719            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1720
1721    def MakePWDRfilename(self,hist):
1722        '''Make a filename root (no extension) from a PWDR histogram name
1723
1724        :param str hist: the histogram name in data tree (starts with "PWDR ")
1725        '''
1726        file0 = ''
1727        file1 = hist[5:]
1728        # replace repeated blanks
1729        while file1 != file0:
1730            file0 = file1
1731            file1 = file0.replace('  ',' ').strip()
1732        file0 = file1.replace('Azm= ','A')
1733        # if angle has unneeded decimal places on aziumuth, remove them
1734        if file0[-3:] == '.00': file0 = file0[:-3]
1735        file0 = file0.replace('.','_')
1736        file0 = file0.replace(' ','_')
1737        return file0
1738
1739    def ExportSelect(self,AskFile='ask'):
1740        '''Selects histograms or phases when needed. Sets a default file name when
1741        requested in self.filename; always sets a default directory in self.dirname.
1742
1743        :param bool AskFile: Determines how this routine processes getting a
1744          location to store the current export(s).
1745         
1746          * if AskFile is 'ask' (default option), get the name of the file to be written;
1747            self.filename and self.dirname are always set. In the case where
1748            multiple files must be generated, the export routine should do this
1749            based on self.filename as a template.
1750          * if AskFile is 'dir', get the name of the directory to be used;
1751            self.filename is not used, but self.dirname is always set. The export routine
1752            will always generate the file name.
1753          * if AskFile is 'single', get only the name of the directory to be used when
1754            multiple items will be written (as multiple files) are used
1755            *or* a complete file name is requested when a single file
1756            name is selected. self.dirname is always set and self.filename used
1757            only when a single file is selected.
1758          * if AskFile is 'default', creates a name of the file to be used from
1759            the name of the project (.gpx) file. If the project has not been saved,
1760            then the name of file is requested.
1761            self.filename and self.dirname are always set. In the case where
1762            multiple file names must be generated, the export routine should do this
1763            based on self.filename.
1764          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1765            file. If the project has not been saved, then a directory is requested.
1766            self.filename is not used.
1767
1768        :returns: True in case of an error
1769        '''
1770       
1771        numselected = 1
1772        if self.currentExportType == 'phase':
1773            if len(self.Phases) == 0:
1774                self.G2frame.ErrorDialog(
1775                    'Empty project',
1776                    'Project does not contain any phases.')
1777                return True
1778            elif len(self.Phases) == 1:
1779                self.phasenam = self.Phases.keys()
1780            elif self.multiple: 
1781                choices = sorted(self.Phases.keys())
1782                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1783                if phasenum is None: return True
1784                self.phasenam = [choices[i] for i in phasenum]
1785                if not self.phasenam: return True
1786                numselected = len(self.phasenam)
1787            else:
1788                choices = sorted(self.Phases.keys())
1789                phasenum = G2G.ItemSelector(choices,self.G2frame)
1790                if phasenum is None: return True
1791                self.phasenam = [choices[phasenum]]
1792                numselected = len(self.phasenam)
1793        elif self.currentExportType == 'single':
1794            if len(self.xtalDict) == 0:
1795                self.G2frame.ErrorDialog(
1796                    'Empty project',
1797                    'Project does not contain any single crystal data.')
1798                return True
1799            elif len(self.xtalDict) == 1:
1800                self.histnam = self.xtalDict.values()
1801            elif self.multiple:
1802                choices = sorted(self.xtalDict.values())
1803                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1804                if not hnum: return True
1805                self.histnam = [choices[i] for i in hnum]
1806                numselected = len(self.histnam)
1807            else:
1808                choices = sorted(self.xtalDict.values())
1809                hnum = G2G.ItemSelector(choices,self.G2frame)
1810                if hnum is None: return True
1811                self.histnam = [choices[hnum]]
1812                numselected = len(self.histnam)
1813        elif self.currentExportType == 'powder':
1814            if len(self.powderDict) == 0:
1815                self.G2frame.ErrorDialog(
1816                    'Empty project',
1817                    'Project does not contain any powder data.')
1818                return True
1819            elif len(self.powderDict) == 1:
1820                self.histnam = self.powderDict.values()
1821            elif self.multiple:
1822                choices = sorted(self.powderDict.values())
1823                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1824                if not hnum: return True
1825                self.histnam = [choices[i] for i in hnum]
1826                numselected = len(self.histnam)
1827            else:
1828                choices = sorted(self.powderDict.values())
1829                hnum = G2G.ItemSelector(choices,self.G2frame)
1830                if hnum is None: return True
1831                self.histnam = [choices[hnum]]
1832                numselected = len(self.histnam)
1833        elif self.currentExportType == 'image':
1834            if len(self.Histograms) == 0:
1835                self.G2frame.ErrorDialog(
1836                    'Empty project',
1837                    'Project does not contain any images.')
1838                return True
1839            elif len(self.Histograms) == 1:
1840                self.histnam = self.Histograms.keys()
1841            else:
1842                choices = sorted(self.Histograms.keys())
1843                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1844                if self.multiple:
1845                    if not hnum: return True
1846                    self.histnam = [choices[i] for i in hnum]
1847                else:
1848                    if hnum is None: return True
1849                    self.histnam = [choices[hnum]]
1850                numselected = len(self.histnam)
1851        if self.currentExportType == 'map':
1852            # search for phases with maps
1853            mapPhases = []
1854            choices = []
1855            for phasenam in sorted(self.Phases):
1856                phasedict = self.Phases[phasenam] # pointer to current phase info           
1857                if len(phasedict['General']['Map'].get('rho',[])):
1858                    mapPhases.append(phasenam)
1859                    if phasedict['General']['Map'].get('Flip'):
1860                        choices.append('Charge flip map: '+str(phasenam))
1861                    elif phasedict['General']['Map'].get('MapType'):
1862                        choices.append(
1863                            str(phasedict['General']['Map'].get('MapType'))
1864                            + ' map: ' + str(phasenam))
1865                    else:
1866                        choices.append('unknown map: '+str(phasenam))
1867            # select a map if needed
1868            if len(mapPhases) == 0:
1869                self.G2frame.ErrorDialog(
1870                    'Empty project',
1871                    'Project does not contain any maps.')
1872                return True
1873            elif len(mapPhases) == 1:
1874                self.phasenam = mapPhases
1875            else: 
1876                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1877                if self.multiple:
1878                    if not phasenum: return True
1879                    self.phasenam = [mapPhases[i] for i in phasenum]
1880                else:
1881                    if phasenum is None: return True
1882                    self.phasenam = [mapPhases[phasenum]]
1883            numselected = len(self.phasenam)
1884
1885        # items selected, now set self.dirname and usually self.filename
1886        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
1887            AskFile == 'default' and not self.G2frame.GSASprojectfile
1888            ):
1889            filename = self.askSaveFile()
1890            if not filename: return True
1891            self.dirname,self.filename = os.path.split(filename)
1892        elif AskFile == 'dir' or AskFile == 'single' or (
1893            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
1894            ):
1895            self.dirname = self.askSaveDirectory()
1896            if not self.dirname: return True
1897        elif AskFile == 'default-dir' or AskFile == 'default':
1898            self.dirname,self.filename = os.path.split(
1899                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
1900                )
1901        else:
1902            raise Exception('This should not happen!')
1903       
1904    def loadParmDict(self):
1905        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
1906        refined values to those from the last cycle and set the uncertainties for the
1907        refined parameters in another dict (self.sigDict).
1908
1909        Expands the parm & sig dicts to include values derived from constraints.
1910        '''
1911        self.parmDict = {}
1912        self.sigDict = {}
1913        rigidbodyDict = {}
1914        covDict = {}
1915        consDict = {}
1916        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1917        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
1918        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
1919        while item:
1920            name = self.G2frame.PatternTree.GetItemText(item)
1921            if name == 'Rigid bodies':
1922                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
1923            elif name == 'Covariance':
1924                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
1925            elif name == 'Constraints':
1926                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
1927            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
1928        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1929        self.parmDict.update(rbDict)
1930        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1931        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,maxSSwave =  G2stIO.GetPhaseData(
1932            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
1933        self.parmDict.update(phaseDict)
1934        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
1935            Phases,Histograms,Print=False,resetRefList=False)
1936        self.parmDict.update(hapDict)
1937        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
1938        self.parmDict.update(histDict)
1939        self.parmDict.update(zip(
1940            covDict.get('varyList',[]),
1941            covDict.get('variables',[])))
1942        self.sigDict = dict(zip(
1943            covDict.get('varyList',[]),
1944            covDict.get('sig',[])))
1945        # expand to include constraints: first compile a list of constraints
1946        constList = []
1947        for item in consDict:
1948            if item.startswith('_'): continue
1949            constList += consDict[item]
1950        # now process the constraints
1951        G2mv.InitVars()
1952        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
1953        varyList = covDict.get('varyListStart')
1954        if varyList is None and len(constDict) == 0:
1955            # no constraints can use varyList
1956            varyList = covDict.get('varyList')
1957        elif varyList is None:
1958            # old GPX file from before pre-constraint varyList is saved
1959            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
1960            raise Exception(' *** Export aborted ***')
1961        else:
1962            varyList = list(varyList)
1963        try:
1964            groups,parmlist = G2mv.GroupConstraints(constDict)
1965            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,self.parmDict)
1966        except:
1967            # this really should not happen
1968            print ' *** ERROR - constraints are internally inconsistent ***'
1969            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
1970            print 'Errors',errmsg
1971            if warnmsg: print 'Warnings',warnmsg
1972            raise Exception(' *** CIF creation aborted ***')
1973        # add the constrained values to the parameter dictionary
1974        G2mv.Dict2Map(self.parmDict,varyList)
1975        # and add their uncertainties into the esd dictionary (sigDict)
1976        if covDict.get('covMatrix') is not None:
1977            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
1978
1979    def loadTree(self):
1980        '''Load the contents of the data tree into a set of dicts
1981        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
1982        & self.xtalDict)
1983       
1984        * The childrenless data tree items are overall parameters/controls for the
1985          entire project and are placed in self.OverallParms
1986        * Phase items are placed in self.Phases
1987        * Data items are placed in self.Histogram. The key for these data items
1988          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
1989        '''
1990        self.OverallParms = {}
1991        self.powderDict = {}
1992        self.xtalDict = {}
1993        self.Phases = {}
1994        self.Histograms = {}
1995        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
1996        histType = None       
1997        if self.currentExportType == 'phase':
1998            # if exporting phases load them here
1999            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
2000            if not sub:
2001                print 'no phases found'
2002                return True
2003            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
2004            while item:
2005                phaseName = self.G2frame.PatternTree.GetItemText(item)
2006                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
2007                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
2008            return
2009        elif self.currentExportType == 'single':
2010            histType = 'HKLF'
2011        elif self.currentExportType == 'powder':
2012            histType = 'PWDR'
2013        elif self.currentExportType == 'image':
2014            histType = 'IMG'
2015
2016        if histType: # Loading just one kind of tree entry
2017            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2018            while item:
2019                name = self.G2frame.PatternTree.GetItemText(item)
2020                if name.startswith(histType):
2021                    if self.Histograms.get(name): # there is already an item with this name
2022                        print('Histogram name '+str(name)+' is repeated. Renaming')
2023                        if name[-1] == '9':
2024                            name = name[:-1] + '10'
2025                        elif name[-1] in '012345678':
2026                            name = name[:-1] + str(int(name[-1])+1)
2027                        else:                           
2028                            name += '-1'
2029                    self.Histograms[name] = {}
2030                    # the main info goes into Data, but the 0th
2031                    # element contains refinement results, carry
2032                    # that over too now.
2033                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
2034                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
2035                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2036                    while item2: 
2037                        child = self.G2frame.PatternTree.GetItemText(item2)
2038                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
2039                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
2040                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2041            # index powder and single crystal histograms by number
2042            for hist in self.Histograms:
2043                if hist.startswith("PWDR"): 
2044                    d = self.powderDict
2045                elif hist.startswith("HKLF"): 
2046                    d = self.xtalDict
2047                else:
2048                    return                   
2049                i = self.Histograms[hist].get('hId')
2050                if i is None and not d.keys():
2051                    i = 0
2052                elif i is None or i in d.keys():
2053                    i = max(d.keys())+1
2054                d[i] = hist
2055            return
2056        # else standard load: using all interlinked phases and histograms
2057        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2058        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2059        while item:
2060            name = self.G2frame.PatternTree.GetItemText(item)
2061            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2062            if not item2: 
2063                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
2064            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2065        # index powder and single crystal histograms
2066        for hist in self.Histograms:
2067            i = self.Histograms[hist]['hId']
2068            if hist.startswith("PWDR"): 
2069                self.powderDict[i] = hist
2070            elif hist.startswith("HKLF"): 
2071                self.xtalDict[i] = hist
2072
2073    def dumpTree(self,mode='type'):
2074        '''Print out information on the data tree dicts loaded in loadTree
2075        '''
2076        print '\nOverall'
2077        if mode == 'type':
2078            def Show(arg): return type(arg)
2079        else:
2080            def Show(arg): return arg
2081        for key in self.OverallParms:
2082            print '  ',key,Show(self.OverallParms[key])
2083        print 'Phases'
2084        for key1 in self.Phases:
2085            print '    ',key1,Show(self.Phases[key1])
2086        print 'Histogram'
2087        for key1 in self.Histograms:
2088            print '    ',key1,Show(self.Histograms[key1])
2089            for key2 in self.Histograms[key1]:
2090                print '      ',key2,Show(self.Histograms[key1][key2])
2091
2092    def defaultSaveFile(self):
2093        return os.path.abspath(
2094            os.path.splitext(self.G2frame.GSASprojectfile
2095                             )[0]+self.extension)
2096       
2097    def askSaveFile(self):
2098        '''Ask the user to supply a file name
2099
2100        :returns: a file name (str) or None if Cancel is pressed
2101        '''
2102       
2103        pth = G2G.GetExportPath(self.G2frame)
2104        defnam = os.path.splitext(
2105            os.path.split(self.G2frame.GSASprojectfile)[1]
2106            )[0]+self.extension
2107        dlg = wx.FileDialog(
2108            self.G2frame, 'Input name for file to write', pth, defnam,
2109            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2110            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2111        dlg.CenterOnParent()
2112        try:
2113            if dlg.ShowModal() == wx.ID_OK:
2114                filename = dlg.GetPath()
2115                self.G2frame.LastExportDir = os.path.split(filename)[0]
2116                filename = os.path.splitext(filename)[0]+self.extension # make sure extension is correct
2117            else:
2118                filename = None
2119        finally:
2120            dlg.Destroy()
2121        return filename
2122
2123    def askSaveDirectory(self):
2124        '''Ask the user to supply a directory name. Path name is used as the
2125        starting point for the next export path search.
2126
2127        :returns: a directory name (str) or None if Cancel is pressed
2128        '''
2129        pth = G2G.GetExportPath(self.G2frame)
2130        dlg = wx.DirDialog(
2131            self.G2frame, 'Input directory where file(s) will be written', pth,
2132            wx.DD_DEFAULT_STYLE)
2133        dlg.CenterOnParent()
2134        try:
2135            if dlg.ShowModal() == wx.ID_OK:
2136                filename = dlg.GetPath()
2137                self.G2frame.LastExportDir = filename
2138            else:
2139                filename = None
2140        finally:
2141            dlg.Destroy()
2142        return filename
2143
2144    # Tools for file writing.
2145    def OpenFile(self,fil=None,mode='w'):
2146        '''Open the output file
2147
2148        :param str fil: The name of the file to open. If None (default)
2149          the name defaults to self.dirname + self.filename.
2150          If an extension is supplied, it is not overridded,
2151          but if not, the default extension is used.
2152        :returns: the file object opened by the routine which is also
2153          saved as self.fp
2154        '''
2155        if not fil:
2156            if not os.path.splitext(self.filename)[1]:
2157                self.filename += self.extension
2158            fil = os.path.join(self.dirname,self.filename)
2159        self.fullpath = os.path.abspath(fil)
2160        self.fp = open(self.fullpath,mode)
2161        return self.fp
2162
2163    def Write(self,line):
2164        '''write a line of output, attaching a line-end character
2165
2166        :param str line: the text to be written.
2167        '''
2168        self.fp.write(line+'\n')
2169    def CloseFile(self,fp=None):
2170        '''Close a file opened in OpenFile
2171
2172        :param file fp: the file object to be closed. If None (default)
2173          file object self.fp is closed.
2174        '''
2175        if fp is None:
2176            fp = self.fp
2177            self.fp = None
2178        fp.close()
2179    # Tools to pull information out of the data arrays
2180    def GetCell(self,phasenam):
2181        """Gets the unit cell parameters and their s.u.'s for a selected phase
2182
2183        :param str phasenam: the name for the selected phase
2184        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2185          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2186          cell values and `cellSig` has their uncertainties.
2187        """
2188        phasedict = self.Phases[phasenam] # pointer to current phase info
2189        try:
2190            pfx = str(phasedict['pId'])+'::'
2191            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2192            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
2193                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2194            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2195            return cellList,cellSig
2196        except KeyError:
2197            cell = phasedict['General']['Cell'][1:]
2198            return cell,7*[0]
2199   
2200    def GetAtoms(self,phasenam):
2201        """Gets the atoms associated with a phase. Can be used with standard
2202        or macromolecular phases
2203
2204        :param str phasenam: the name for the selected phase
2205        :returns: a list of items for eac atom where each item is a list containing:
2206          label, typ, mult, xyz, and td, where
2207
2208          * label and typ are the atom label and the scattering factor type (str)
2209          * mult is the site multiplicity (int)
2210          * xyz is contains a list with four pairs of numbers:
2211            x, y, z and fractional occupancy and
2212            their standard uncertainty (or a negative value)
2213          * td is contains a list with either one or six pairs of numbers:
2214            if one number it is U\ :sub:`iso` and with six numbers it is
2215            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2216            paired with their standard uncertainty (or a negative value)
2217        """
2218        phasedict = self.Phases[phasenam] # pointer to current phase info           
2219        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2220        cfrac = cx+3
2221        fpfx = str(phasedict['pId'])+'::Afrac:'       
2222        atomslist = []
2223        for i,at in enumerate(phasedict['Atoms']):
2224            if phasedict['General']['Type'] == 'macromolecular':
2225                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2226            else:
2227                label = at[ct-1]
2228            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2229            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2230            mult = at[cs+1]
2231            typ = at[ct]
2232            xyz = []
2233            for j,v in enumerate(('x','y','z')):
2234                val = at[cx+j]
2235                pfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2236                sig = self.sigDict.get(pfx,-0.000009)
2237                xyz.append((val,sig))
2238            xyz.append((fval,fsig))
2239            td = []
2240            if at[cia] == 'I':
2241                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2242                val = self.parmDict.get(pfx,at[cia+1])
2243                sig = self.sigDict.get(pfx,-0.0009)
2244                td.append((val,sig))
2245            else:
2246                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2247                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2248                    val = self.parmDict.get(pfx,at[cia+2+i])
2249                    sig = self.sigDict.get(pfx,-0.0009)
2250                    td.append((val,sig))
2251            atomslist.append((label,typ,mult,xyz,td))
2252        return atomslist
2253######################################################################
2254def ExportPowderList(G2frame):
2255    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
2256   
2257    :param wx.Frame G2frame: the GSAS-II main data tree window
2258    '''
2259    extList = []
2260    for obj in G2frame.exporterlist:
2261        if 'powder' in obj.exporttype:
2262            try:
2263                obj.Writer
2264                extList.append(obj.extension)
2265            except AttributeError:
2266                pass
2267    return extList
2268
2269def ExportPowder(G2frame,TreeName,fileroot,extension):
2270    '''Writes a single powder histogram using the Export routines
2271
2272    :param wx.Frame G2frame: the GSAS-II main data tree window
2273    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
2274    :param str fileroot: name for file to be written, extension ignored
2275    :param str extension: extension for file to be written (start with '.'). Must
2276      match a powder export routine that has a Writer object.
2277    '''
2278    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2279    for obj in G2frame.exporterlist:
2280        if obj.extension == extension and 'powder' in obj.exporttype:
2281            obj.currentExportType = 'powder'
2282            obj.InitExport(None)
2283            obj.loadTree() # load all histograms in tree into dicts
2284            if TreeName not in obj.Histograms:
2285                raise Exception('Histogram not found: '+str(TreeName))
2286            try:
2287                obj.Writer
2288            except AttributeError:
2289                continue
2290            try:
2291                obj.Writer(TreeName,filename)
2292                return
2293            except Exception,err:
2294                print('Export Routine for '+extension+' failed.')
2295                print err
2296    else:
2297        print('No Export routine supports extension '+extension)
2298       
2299def ReadDIFFaX(DIFFaXfile):
2300    print 'read ',DIFFaXfile
2301    Layer = {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],'Width':[[10.,10.],[False,False]],
2302        'Layers':[],'Stacking':[],'Transitions':[],'Toler':0.01,'AtInfo':{}}
2303    df = open(DIFFaXfile,'r')
2304    lines = df.readlines()
2305    df.close()
2306    struct = False
2307    Struct = []
2308    stack = False
2309    Stack = []
2310    trans = False
2311    Trans = []
2312    instr = False
2313    for diff in lines:
2314        diff = diff[:-1].lower()
2315        if '!'  in diff:
2316            continue
2317        while '}' in diff: #strip comments
2318            iB = diff.index('{')
2319            iF = diff.index('}')+1
2320            if iB:
2321                diff = diff[:iB]
2322            else:
2323                diff = diff[iF:]
2324        if not diff:
2325            continue
2326        if diff.strip() == 'instrumental':
2327            instr = True
2328            continue
2329        if diff.strip() == 'structural':
2330            instr = False
2331            struct = True
2332            continue
2333        elif diff.strip() == 'stacking':
2334            struct = False
2335            stack = True
2336            continue
2337        elif diff.strip() == 'transitions':
2338            stack = False
2339            trans = True
2340            continue
2341        diff = diff.strip()
2342        if struct:
2343            if diff:
2344                Struct.append(diff)
2345        elif stack:
2346            if diff:
2347                Stack.append(diff)
2348        elif trans:
2349            if diff:
2350                Trans.append(diff)
2351   
2352#STRUCTURE records
2353    laueRec = Struct[1].split()
2354    Layer['Laue'] = laueRec[0]
2355    if Layer['Laue'] == 'unknown' and len(laueRec) > 1:
2356        Layer['Toler'] = float(laueRec[1])    #tolerance for 'unknown'?
2357    if Layer['Laue'] == '2/m(1)': Layer['Laue'] = '2/m(c)'
2358    if Layer['Laue'] == '2/m(2)': Layer['Laue'] = '2/m(ab)'
2359    cell = Struct[0].split()
2360    Layer['Cell'] = [False,float(cell[0]),float(cell[1]),float(cell[2]),90.,90.,float(cell[3]),1.0]
2361    nLayers = int(Struct[2])
2362    N = 3
2363    if 'layer' not in Struct[3]:
2364        N = 4
2365        if Struct[3] != 'infinite':
2366            width = Struct[3].split()
2367            Layer['Width'][0] = [float(width[0]),float(width[1])]
2368    for nL in range(nLayers):
2369        if '=' in Struct[N]:
2370            name = Struct[N].split('=')
2371            sameas = int(name[1])-1
2372            Layer['Layers'].append({'Name':name[0],'SameAs':Layer['Layers'][sameas]['Name'],'Symm':'None','Atoms':[]})
2373            N += 1
2374            continue
2375        Symm = 'None'
2376        if 'centro' in Struct[N+1]: Symm = '-1'
2377        Layer['Layers'].append({'Name':Struct[N],'SameAs':'','Symm':Symm,'Atoms':[]})
2378        N += 2
2379        iatm = 0
2380        while 'layer' not in Struct[N]:
2381            atom = Struct[N][4:].split()
2382            atomType = G2el.FixValence(Struct[N][:4].replace(' ','').strip().capitalize())
2383            if atomType not in Layer['AtInfo']:
2384                Layer['AtInfo'][atomType] = G2el.GetAtomInfo(atomType)
2385            atomName = '%s(%s)'%(atomType,atom[0])
2386            newVals = []
2387            for val in atom[1:6]:
2388                if '/' in val:
2389                    newVals.append(eval(val+'.'))
2390                else:
2391                    newVals.append(float(val))               
2392            atomRec = [atomName,atomType,newVals[0],newVals[1],newVals[2],newVals[4],newVals[3]/78.9568]
2393            Layer['Layers'][-1]['Atoms'].append(atomRec)
2394            N += 1
2395            if N > len(Struct)-1:
2396                break
2397#TRANSITIONS records
2398    transArray = []
2399    N = 0
2400    for i in range(nLayers):
2401        transArray.append([])
2402        for j in range(nLayers):
2403            vals = Trans[N].split()
2404            newVals = []
2405            for val in vals[:4]:
2406                if '/' in val:
2407                    newVals.append(eval(val+'.'))
2408                else:
2409                    newVals.append(float(val))
2410            transArray[-1].append(newVals+['',False])
2411            N += 1
2412    Layer['Transitions'] = transArray
2413#STACKING records
2414    Layer['Stacking'] = [Stack[0],'']
2415    if Stack[0] == 'recursive':
2416        Layer['Stacking'][1] = Stack[1]
2417    elif Stack[0] == 'explicit':
2418        if Stack[1] == 'random':
2419            Layer['Stacking'][1] = Stack[1]
2420        else:
2421            Layer['Stacking'][1] = 'list'
2422            Layer['Stacking'].append('')
2423            for stack in Stack[2:]:
2424                Layer['Stacking'][2] += ' '+stack
2425    return Layer
2426
2427def ReadCIF(URLorFile):
2428    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2429    (from James Hester).
2430    The open routine gets confused with DOS names that begin with a letter and colon
2431    "C:\dir\" so this routine will try to open the passed name as a file and if that
2432    fails, try it as a URL
2433
2434    :param str URLorFile: string containing a URL or a file name. Code will try first
2435      to open it as a file and then as a URL.
2436
2437    :returns: a PyCifRW CIF object.
2438    '''
2439    import CifFile as cif # PyCifRW from James Hester
2440
2441    # alternate approach:
2442    #import urllib
2443    #ciffile = 'file:'+urllib.pathname2url(filename)
2444   
2445    try:
2446        fp = open(URLorFile,'r')
2447        cf = cif.ReadCif(fp)
2448        fp.close()
2449        return cf
2450    except IOError:
2451        return cif.ReadCif(URLorFile)
2452
2453if __name__ == '__main__':
2454    import GSASII
2455    application = GSASII.GSASIImain(0)
2456    G2frame = application.main
2457    #app = wx.PySimpleApp()
2458    #G2frame = wx.Frame(None) # create a frame
2459    #frm.Show(True)
2460    #filename = '/tmp/notzip.zip'
2461    #filename = '/tmp/all.zip'
2462    #filename = '/tmp/11bmb_7652.zip'
2463   
2464    #selection=None, confirmoverwrite=True, parent=None
2465    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2466    #print ExtractFileFromZip(filename,multipleselect=True)
2467    #                         #confirmread=False, confirmoverwrite=False)
2468
2469    # choicelist=[ ('a','b','c'),
2470    #              ('test1','test2'),('no choice',)]
2471    # titles = [ 'a, b or c', 'tests', 'No option here']
2472    # dlg = MultipleChoicesDialog(
2473    #     choicelist,titles,
2474    #     parent=frm)
2475    # if dlg.ShowModal() == wx.ID_OK:
2476    #     print 'Got OK'
2477    imagefile = '/tmp/NDC5_00237_3.ge3'
2478    Comments, Data, Npix, Image = GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None)
2479
2480    print("\n\nResults loaded to Comments, Data, Npix and Image\n\n")
2481
2482    GSASIIpath.IPyBreak_base()
Note: See TracBrowser for help on using the repository browser.