source: trunk/GSASIIIO.py @ 2227

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

avoid divide by zero effect if a zero image
refactor LoadImage2Tree
add new importer for summing GE images

  • 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-04-26 13:36:05 +0000 (Tue, 26 Apr 2016) $
4# $Author: vondreele $
5# $Revision: 2227 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 2227 2016-04-26 13:36:05Z 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: 2227 $")
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            if nOcc:
901                Aname += '(%d)'%(nOcc)
902        Sample = G2pdG.SetDefaultSample()
903        Sample['Gonio. radius'] = data['distance']
904        Sample['Omega'] = data['GonioAngles'][0]
905        Sample['Chi'] = data['GonioAngles'][1]
906        Sample['Phi'] = data['GonioAngles'][2]
907        Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
908        if 'PWDR' in Aname:
909            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!
910        elif 'SASD' in Aname:
911            Sample['Trans'] = data['SampleAbs'][0]
912            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
913        Y = G2frame.Integrate[0][i]
914        W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
915        Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=Aname)
916        G2frame.IntgOutList.append(Id)
917        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
918        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
919        if 'PWDR' in Aname:
920            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
921                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
922        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
923        for item in inst[0]:
924            inst[0][item] = list(inst[0][item])
925        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
926        if 'PWDR' in Aname:
927            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
928            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Peak List'),{'sigDict':{},'peaks':[]})
929            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
930            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Unit Cells List'),[])
931            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Reflection Lists'),{})
932        elif 'SASD' in Aname:             
933            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
934            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
935            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
936        valuesdict = {
937            'wtFactor':1.0,
938            'Dummy':False,
939            'ranId':ran.randint(0,sys.maxint),
940            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
941            'qPlot':False,'dPlot':False,'sqrtPlot':False
942            }
943        G2frame.PatternTree.SetItemPyData(
944            Id,[valuesdict,
945                [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
946    return Id       #last powder pattern generated
947           
948def PDFSave(G2frame,exports):
949    'Save a PDF G(r) and S(Q) in column formats'
950    for export in exports:
951        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
952        SQname = 'S(Q)'+export[4:]
953        GRname = 'G(R)'+export[4:]
954        sqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.sq')
955        grfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.gr')
956        sqId = G2gd.GetPatternTreeItemId(G2frame, PickId, SQname)
957        grId = G2gd.GetPatternTreeItemId(G2frame, PickId, GRname)
958        sqdata = np.array(G2frame.PatternTree.GetItemPyData(sqId)[1][:2]).T
959        grdata = np.array(G2frame.PatternTree.GetItemPyData(grId)[1][:2]).T
960        sqfile = open(sqfilename,'w')
961        grfile = open(grfilename,'w')
962        sqfile.write('#T S(Q) %s\n'%(export))
963        grfile.write('#T G(R) %s\n'%(export))
964        sqfile.write('#L Q     S(Q)\n')
965        grfile.write('#L R     G(R)\n')
966        for q,sq in sqdata:
967            sqfile.write("%15.6g %15.6g\n" % (q,sq))
968        sqfile.close()
969        for r,gr in grdata:
970            grfile.write("%15.6g %15.6g\n" % (r,gr))
971        grfile.close()
972   
973def PeakListSave(G2frame,file,peaks):
974    'Save powder peaks to a data file'
975    print 'save peak list to file: ',G2frame.peaklistfile
976    if not peaks:
977        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
978        try:
979            result = dlg.ShowModal()
980        finally:
981            dlg.Destroy()
982        return
983    for peak in peaks:
984        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
985            (peak[0],peak[2],peak[4],peak[6]))
986    print 'peak list saved'
987             
988def IndexPeakListSave(G2frame,peaks):
989    'Save powder peaks from the indexing list'
990    file = open(G2frame.peaklistfile,'wa')
991    print 'save index peak list to file: ',G2frame.peaklistfile
992    wx.BeginBusyCursor()
993    try:
994        if not peaks:
995            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
996            try:
997                result = dlg.ShowModal()
998            finally:
999                dlg.Destroy()
1000            return
1001        for peak in peaks:
1002            file.write("%12.6f\n" % (peak[7]))
1003        file.close()
1004    finally:
1005        wx.EndBusyCursor()
1006    print 'index peak list saved'
1007   
1008def SetNewPhase(Name='New Phase',SGData=None,cell=None,Super=None):
1009    '''Create a new phase dict with default values for various parameters
1010
1011    :param str Name: Name for new Phase
1012
1013    :param dict SGData: space group data from :func:`GSASIIspc:SpcGroup`;
1014      defaults to data for P 1
1015
1016    :param list cell: unit cell parameter list; defaults to
1017      [1.0,1.0,1.0,90.,90,90.,1.]
1018
1019    '''
1020    if SGData is None: SGData = G2spc.SpcGroup('P 1')[1]
1021    if cell is None: cell=[1.0,1.0,1.0,90.,90,90.,1.]
1022    phaseData = {
1023        'ranId':ran.randint(0,sys.maxint),
1024        'General':{
1025            'Name':Name,
1026            'Type':'nuclear',
1027            'AtomPtrs':[3,1,7,9],
1028            'SGData':SGData,
1029            'Cell':[False,]+cell,
1030            'Pawley dmin':1.0,
1031            'Data plot type':'None',
1032            'SH Texture':{
1033                'Order':0,
1034                'Model':'cylindrical',
1035                'Sample omega':[False,0.0],
1036                'Sample chi':[False,0.0],
1037                'Sample phi':[False,0.0],
1038                'SH Coeff':[False,{}],
1039                'SHShow':False,
1040                'PFhkl':[0,0,1],
1041                'PFxyz':[0,0,1],
1042                'PlotType':'Pole figure',
1043                'Penalty':[['',],0.1,False,1.0]}},
1044        'Atoms':[],
1045        'Drawing':{},
1046        'Histograms':{},
1047        'Pawley ref':[],
1048        'RBModels':{},
1049        }
1050    if Super and Super.get('Use',False):
1051        phaseData['General'].update({'Type':'modulated','Super':True,'SuperSg':Super['ssSymb']})
1052        phaseData['General']['SSGData'] = G2spc.SSpcGroup(SGData,Super['ssSymb'])
1053        phaseData['General']['SuperVec'] = [Super['ModVec'],False,Super['maxH']]
1054
1055    return phaseData
1056       
1057class MultipleChoicesDialog(wx.Dialog):
1058    '''A dialog that offers a series of choices, each with a
1059    title and a wx.Choice widget. Intended to be used Modally.
1060    typical input:
1061
1062        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1063        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1064       
1065    selections are placed in self.chosen when OK is pressed
1066    '''
1067    def __init__(self,choicelist,headinglist,
1068                 head='Select options',
1069                 title='Please select from options below',
1070                 parent=None):
1071        self.chosen = []
1072        wx.Dialog.__init__(
1073            self,parent,wx.ID_ANY,head, 
1074            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1075        panel = wx.Panel(self)
1076        mainSizer = wx.BoxSizer(wx.VERTICAL)
1077        mainSizer.Add((10,10),1)
1078        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1079        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1080        self.ChItems = []
1081        for choice,lbl in zip(choicelist,headinglist):
1082            mainSizer.Add((10,10),1)
1083            self.chosen.append(0)
1084            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1085            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1086            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1087            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1088
1089        OkBtn = wx.Button(panel,-1,"Ok")
1090        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1091        cancelBtn = wx.Button(panel,-1,"Cancel")
1092        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1093        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1094        btnSizer.Add((20,20),1)
1095        btnSizer.Add(OkBtn)
1096        btnSizer.Add((20,20),1)
1097        btnSizer.Add(cancelBtn)
1098        btnSizer.Add((20,20),1)
1099        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1100        panel.SetSizer(mainSizer)
1101        panel.Fit()
1102        self.Fit()
1103       
1104    def OnOk(self,event):
1105        parent = self.GetParent()
1106        if parent is not None: parent.Raise()
1107        # save the results from the choice widgets
1108        self.chosen = []
1109        for w in self.ChItems:
1110            self.chosen.append(w.GetSelection())
1111        self.EndModal(wx.ID_OK)             
1112           
1113    def OnCancel(self,event):
1114        parent = self.GetParent()
1115        if parent is not None: parent.Raise()
1116        self.chosen = []
1117        self.EndModal(wx.ID_CANCEL)             
1118           
1119def ExtractFileFromZip(filename, selection=None, confirmread=True,
1120                       confirmoverwrite=True, parent=None,
1121                       multipleselect=False):
1122    '''If the filename is a zip file, extract a file from that
1123    archive.
1124
1125    :param list Selection: used to predefine the name of the file
1126      to be extracted. Filename case and zip directory name are
1127      ignored in selection; the first matching file is used.
1128
1129    :param bool confirmread: if True asks the user to confirm before expanding
1130      the only file in a zip
1131
1132    :param bool confirmoverwrite: if True asks the user to confirm
1133      before overwriting if the extracted file already exists
1134
1135    :param bool multipleselect: if True allows more than one zip
1136      file to be extracted, a list of file(s) is returned.
1137      If only one file is present, do not ask which one, otherwise
1138      offer a list of choices (unless selection is used).
1139   
1140    :returns: the name of the file that has been created or a
1141      list of files (see multipleselect)
1142
1143    If the file is not a zipfile, return the name of the input file.
1144    If the zipfile is empty or no file has been selected, return None
1145    '''
1146    import zipfile # do this now, since we can save startup time by doing this only on need
1147    import shutil
1148    zloc = os.path.split(filename)[0]
1149    if not zipfile.is_zipfile(filename):
1150        #print("not zip")
1151        return filename
1152
1153    z = zipfile.ZipFile(filename,'r')
1154    zinfo = z.infolist()
1155
1156    if len(zinfo) == 0:
1157        #print('Zip has no files!')
1158        zlist = [-1]
1159    if selection:
1160        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1161        if selection.lower() in choices:
1162            zlist = [choices.index(selection.lower())]
1163        else:
1164            print('debug: file '+str(selection)+' was not found in '+str(filename))
1165            zlist = [-1]
1166    elif len(zinfo) == 1 and confirmread:
1167        result = wx.ID_NO
1168        dlg = wx.MessageDialog(
1169            parent,
1170            'Is file '+str(zinfo[0].filename)+
1171            ' what you want to extract from '+
1172            str(os.path.split(filename)[1])+'?',
1173            'Confirm file', 
1174            wx.YES_NO | wx.ICON_QUESTION)
1175        try:
1176            result = dlg.ShowModal()
1177        finally:
1178            dlg.Destroy()
1179        if result == wx.ID_NO:
1180            zlist = [-1]
1181        else:
1182            zlist = [0]
1183    elif len(zinfo) == 1:
1184        zlist = [0]
1185    elif multipleselect:
1186        # select one or more from a from list
1187        choices = [i.filename for i in zinfo]
1188        dlg = G2G.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1189            'Choose file(s)',choices)
1190        if dlg.ShowModal() == wx.ID_OK:
1191            zlist = dlg.GetSelections()
1192        else:
1193            zlist = []
1194        dlg.Destroy()
1195    else:
1196        # select one from a from list
1197        choices = [i.filename for i in zinfo]
1198        dlg = wx.SingleChoiceDialog(parent,
1199            'Select file to extract from zip file'+str(filename),'Choose file',
1200            choices,)
1201        if dlg.ShowModal() == wx.ID_OK:
1202            zlist = [dlg.GetSelection()]
1203        else:
1204            zlist = [-1]
1205        dlg.Destroy()
1206       
1207    outlist = []
1208    for zindex in zlist:
1209        if zindex >= 0:
1210            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1211            if os.path.exists(efil) and confirmoverwrite:
1212                result = wx.ID_NO
1213                dlg = wx.MessageDialog(parent,
1214                    'File '+str(efil)+' already exists. OK to overwrite it?',
1215                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1216                try:
1217                    result = dlg.ShowModal()
1218                finally:
1219                    dlg.Destroy()
1220                if result == wx.ID_NO:
1221                    zindex = -1
1222        if zindex >= 0:
1223            # extract the file to the current directory, regardless of it's original path
1224            #z.extract(zinfo[zindex],zloc)
1225            eloc,efil = os.path.split(zinfo[zindex].filename)
1226            outfile = os.path.join(zloc, efil)
1227            fpin = z.open(zinfo[zindex])
1228            fpout = file(outfile, "wb")
1229            shutil.copyfileobj(fpin, fpout)
1230            fpin.close()
1231            fpout.close()
1232            outlist.append(outfile)
1233    z.close()
1234    if multipleselect and len(outlist) >= 1:
1235        return outlist
1236    elif len(outlist) == 1:
1237        return outlist[0]
1238    else:
1239        return None
1240
1241######################################################################
1242# base classes for reading various types of data files
1243#   not used directly, only by subclassing
1244######################################################################
1245try:
1246    E,SGData = G2spc.SpcGroup('P 1') # data structure for default space group
1247except: # errors on doc build
1248    SGData = None
1249P1SGData = SGData
1250######################################################################
1251class ImportBaseclass(object):
1252    '''Defines a base class for the reading of input files (diffraction
1253    data, coordinates,...). See :ref:`Writing a Import Routine<Import_routines>`
1254    for an explanation on how to use a subclass of this class.
1255    '''
1256    class ImportException(Exception):
1257        '''Defines an Exception that is used when an import routine hits an expected error,
1258        usually in .Reader.
1259
1260        Good practice is that the Reader should define a value in self.errors that
1261        tells the user some information about what is wrong with their file.         
1262        '''
1263        pass
1264   
1265    UseReader = True  # in __init__ set value of self.UseReader to False to skip use of current importer
1266    def __init__(self,formatName,longFormatName=None,
1267                 extensionlist=[],strictExtension=False,):
1268        self.formatName = formatName # short string naming file type
1269        if longFormatName: # longer string naming file type
1270            self.longFormatName = longFormatName
1271        else:
1272            self.longFormatName = formatName
1273        # define extensions that are allowed for the file type
1274        # for windows, remove any extensions that are duplicate, as case is ignored
1275        if sys.platform == 'windows' and extensionlist:
1276            extensionlist = list(set([s.lower() for s in extensionlist]))
1277        self.extensionlist = extensionlist
1278        # If strictExtension is True, the file will not be read, unless
1279        # the extension matches one in the extensionlist
1280        self.strictExtension = strictExtension
1281        self.errors = ''
1282        self.warnings = ''
1283        # used for readers that will use multiple passes to read
1284        # more than one data block
1285        self.repeat = False
1286        self.selections = []
1287        self.repeatcount = 0
1288        self.readfilename = '?'
1289        #print 'created',self.__class__
1290
1291    def ReInitialize(self):
1292        'Reinitialize the Reader to initial settings'
1293        self.errors = ''
1294        self.warnings = ''
1295        self.repeat = False
1296        self.repeatcount = 0
1297        self.readfilename = '?'
1298
1299    def BlockSelector(self, ChoiceList, ParentFrame=None,title='Select a block',
1300        size=None, header='Block Selector',useCancel=True):
1301        ''' Provide a wx dialog to select a block if the file contains more
1302        than one set of data and one must be selected
1303        '''
1304        if useCancel:
1305            dlg = wx.SingleChoiceDialog(
1306                ParentFrame,title, header,ChoiceList)
1307        else:
1308            dlg = wx.SingleChoiceDialog(
1309                ParentFrame,title, header,ChoiceList,
1310                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1311        if size: dlg.SetSize(size)
1312        dlg.CenterOnParent()
1313        if dlg.ShowModal() == wx.ID_OK:
1314            sel = dlg.GetSelection()
1315            return sel
1316        else:
1317            return None
1318        dlg.Destroy()
1319
1320    def MultipleBlockSelector(self, ChoiceList, ParentFrame=None,
1321        title='Select a block',size=None, header='Block Selector'):
1322        '''Provide a wx dialog to select a block of data if the
1323        file contains more than one set of data and one must be
1324        selected.
1325
1326        :returns: a list of the selected blocks
1327        '''
1328        dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1329            wx.CHOICEDLG_STYLE)
1330        dlg.CenterOnScreen()
1331        if size: dlg.SetSize(size)
1332        if dlg.ShowModal() == wx.ID_OK:
1333            sel = dlg.GetSelections()
1334        else:
1335            return []
1336        dlg.Destroy()
1337        selected = []
1338        if len(ChoiceList) in sel:
1339            return range(len(ChoiceList))
1340        else:
1341            return sel
1342        return selected
1343
1344    def MultipleChoicesDialog(self, choicelist, headinglist, ParentFrame=None, **kwargs):
1345        '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1346        widget. Typical input:
1347       
1348           * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1349           
1350           * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1351           
1352        optional keyword parameters are: head (window title) and title
1353        returns a list of selected indicies for each choice (or None)
1354        '''
1355        result = None
1356        dlg = MultipleChoicesDialog(choicelist,headinglist,
1357            parent=ParentFrame, **kwargs)         
1358        dlg.CenterOnParent()
1359        if dlg.ShowModal() == wx.ID_OK:
1360            result = dlg.chosen
1361        dlg.Destroy()
1362        return result
1363
1364    def ShowBusy(self):
1365        wx.BeginBusyCursor()
1366#        wx.Yield() # make it happen now!
1367
1368    def DoneBusy(self):
1369        wx.EndBusyCursor()
1370        wx.Yield() # make it happen now!
1371       
1372#    def Reader(self, filename, filepointer, ParentFrame=None, **unused):
1373#        '''This method must be supplied in the child class to read the file.
1374#        if the read fails either return False or raise an Exception
1375#        preferably of type ImportException.
1376#        '''
1377#        #start reading
1378#        raise ImportException("Error occurred while...")
1379#        self.errors += "Hint for user on why the error occur
1380#        return False # if an error occurs
1381#        return True # if read OK
1382
1383    def ExtensionValidator(self, filename):
1384        '''This methods checks if the file has the correct extension
1385        Return False if this filename will not be supported by this reader
1386        Return True if the extension matches the list supplied by the reader
1387        Return None if the reader allows un-registered extensions
1388        '''
1389        if filename:
1390            ext = os.path.splitext(filename)[1]
1391            if sys.platform == 'windows': ext = ext.lower()
1392            if ext in self.extensionlist: return True
1393            if self.strictExtension: return False
1394        return None
1395
1396    def ContentsValidator(self, filepointer):
1397        '''This routine will attempt to determine if the file can be read
1398        with the current format.
1399        This will typically be overridden with a method that
1400        takes a quick scan of [some of]
1401        the file contents to do a "sanity" check if the file
1402        appears to match the selected format.
1403        Expected to be called via self.Validator()
1404        '''
1405        #filepointer.seek(0) # rewind the file pointer
1406        return True
1407
1408    def CIFValidator(self, filepointer):
1409        '''A :meth:`ContentsValidator` for use to validate CIF files.
1410        '''
1411        for i,l in enumerate(filepointer):
1412            if i >= 1000: return True
1413            '''Encountered only blank lines or comments in first 1000
1414            lines. This is unlikely, but assume it is CIF anyway, since we are
1415            even less likely to find a file with nothing but hashes and
1416            blank lines'''
1417            line = l.strip()
1418            if len(line) == 0: # ignore blank lines
1419                continue 
1420            elif line.startswith('#'): # ignore comments
1421                continue 
1422            elif line.startswith('data_'): # on the right track, accept this file
1423                return True
1424            else: # found something invalid
1425                self.errors = 'line '+str(i+1)+' contains unexpected data:\n'
1426                self.errors += '  '+str(l)
1427                self.errors += '  Note: a CIF should only have blank lines or comments before'
1428                self.errors += '        a data_ statement begins a block.'
1429                return False 
1430
1431######################################################################
1432class ImportPhase(ImportBaseclass):
1433    '''Defines a base class for the reading of files with coordinates
1434
1435    Objects constructed that subclass this (in import/G2phase_*.py etc.) will be used
1436    in :meth:`GSASII.GSASII.OnImportPhase`.
1437    See :ref:`Writing a Import Routine<Import_Routines>`
1438    for an explanation on how to use this class.
1439
1440    '''
1441    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1442        strictExtension=False,):
1443        # call parent __init__
1444        ImportBaseclass.__init__(self,formatName,longFormatName,
1445            extensionlist,strictExtension)
1446        self.Phase = None # a phase must be created with G2IO.SetNewPhase in the Reader
1447        self.Constraints = None
1448
1449    def PhaseSelector(self, ChoiceList, ParentFrame=None,
1450        title='Select a phase', size=None,header='Phase Selector'):
1451        ''' Provide a wx dialog to select a phase if the file contains more
1452        than one phase
1453        '''
1454        return self.BlockSelector(ChoiceList,ParentFrame,title,
1455            size,header)
1456
1457######################################################################
1458class ImportStructFactor(ImportBaseclass):
1459    '''Defines a base class for the reading of files with tables
1460    of structure factors.
1461
1462    Structure factors are read with a call to :meth:`GSASII.GSASII.OnImportSfact`
1463    which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`, which calls
1464    methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1465    :meth:`Reader`.
1466
1467    See :ref:`Writing a Import Routine<Import_Routines>`
1468    for an explanation on how to use import classes in general. The specifics
1469    for reading a structure factor histogram require that
1470    the ``Reader()`` routine in the import
1471    class need to do only a few things: It
1472    should load :attr:`RefDict` item ``'RefList'`` with the reflection list,
1473    and set :attr:`Parameters` with the instrument parameters
1474    (initialized with :meth:`InitParameters` and set with :meth:`UpdateParameters`).
1475    '''
1476    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1477        strictExtension=False,):
1478        ImportBaseclass.__init__(self,formatName,longFormatName,
1479            extensionlist,strictExtension)
1480
1481        # define contents of Structure Factor entry
1482        self.Parameters = []
1483        'self.Parameters is a list with two dicts for data parameter settings'
1484        self.InitParameters()
1485        self.RefDict = {'RefList':[],'FF':{},'Super':0}
1486        self.Banks = []             #for multi bank data (usually TOF)
1487        '''self.RefDict is a dict containing the reflection information, as read from the file.
1488        Item 'RefList' contains the reflection information. See the
1489        :ref:`Single Crystal Reflection Data Structure<XtalRefl_table>`
1490        for the contents of each row. Dict element 'FF'
1491        contains the form factor values for each element type; if this entry
1492        is left as initialized (an empty list) it will be initialized as needed later.
1493        '''
1494    def ReInitialize(self):
1495        'Reinitialize the Reader to initial settings'
1496        ImportBaseclass.ReInitialize(self)
1497        self.InitParameters()
1498        self.Banks = []             #for multi bank data (usually TOF)
1499        self.RefDict = {'RefList':[],'FF':{},'Super':0}
1500       
1501    def InitParameters(self):
1502        'initialize the instrument parameters structure'
1503        Lambda = 0.70926
1504        HistType = 'SXC'
1505        self.Parameters = [{'Type':[HistType,HistType], # create the structure
1506                            'Lam':[Lambda,Lambda]
1507                            }, {}]
1508        'Parameters is a list with two dicts for data parameter settings'
1509
1510    def UpdateParameters(self,Type=None,Wave=None):
1511        'Revise the instrument parameters'
1512        if Type is not None:
1513            self.Parameters[0]['Type'] = [Type,Type]
1514        if Wave is not None:
1515            self.Parameters[0]['Lam'] = [Wave,Wave]           
1516                       
1517######################################################################
1518class ImportPowderData(ImportBaseclass):
1519    '''Defines a base class for the reading of files with powder data.
1520
1521    Objects constructed that subclass this (in import/G2pwd_*.py etc.) will be used
1522    in :meth:`GSASII.GSASII.OnImportPowder`.
1523    See :ref:`Writing a Import Routine<Import_Routines>`
1524    for an explanation on how to use this class.
1525    '''
1526    def __init__(self,formatName,longFormatName=None,
1527        extensionlist=[],strictExtension=False,):
1528        ImportBaseclass.__init__(self,formatName,longFormatName,
1529            extensionlist,strictExtension)
1530        self.clockWd = None  # used in TOF
1531        self.ReInitialize()
1532       
1533    def ReInitialize(self):
1534        'Reinitialize the Reader to initial settings'
1535        ImportBaseclass.ReInitialize(self)
1536        self.powderentry = ['',None,None] #  (filename,Pos,Bank)
1537        self.powderdata = [] # Powder dataset
1538        '''A powder data set is a list with items [x,y,w,yc,yb,yd]:
1539                np.array(x), # x-axis values
1540                np.array(y), # powder pattern intensities
1541                np.array(w), # 1/sig(intensity)^2 values (weights)
1542                np.array(yc), # calc. intensities (zero)
1543                np.array(yb), # calc. background (zero)
1544                np.array(yd), # obs-calc profiles
1545        '''                           
1546        self.comments = []
1547        self.idstring = ''
1548        self.Sample = G2pdG.SetDefaultSample() # default sample parameters
1549        self.Controls = {}  # items to be placed in top-level Controls
1550        self.GSAS = None     # used in TOF
1551        self.repeat_instparm = True # Should a parm file be
1552        #                             used for multiple histograms?
1553        self.instparm = None # name hint from file of instparm to use
1554        self.instfile = '' # full path name to instrument parameter file
1555        self.instbank = '' # inst parm bank number
1556        self.instmsg = ''  # a label that gets printed to show
1557                           # where instrument parameters are from
1558        self.numbanks = 1
1559        self.instdict = {} # place items here that will be transferred to the instrument parameters
1560        self.pwdparms = {} # place parameters that are transferred directly to the tree
1561                           # here (typically from an existing GPX file)
1562######################################################################
1563class ImportSmallAngleData(ImportBaseclass):
1564    '''Defines a base class for the reading of files with small angle data.
1565    See :ref:`Writing a Import Routine<Import_Routines>`
1566    for an explanation on how to use this class.
1567    '''
1568    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1569        strictExtension=False,):
1570           
1571        ImportBaseclass.__init__(self,formatName,longFormatName,extensionlist,
1572            strictExtension)
1573        self.ReInitialize()
1574       
1575    def ReInitialize(self):
1576        'Reinitialize the Reader to initial settings'
1577        ImportBaseclass.ReInitialize(self)
1578        self.smallangleentry = ['',None,None] #  (filename,Pos,Bank)
1579        self.smallangledata = [] # SASD dataset
1580        '''A small angle data set is a list with items [x,y,w,yc,yd]:
1581                np.array(x), # x-axis values
1582                np.array(y), # powder pattern intensities
1583                np.array(w), # 1/sig(intensity)^2 values (weights)
1584                np.array(yc), # calc. intensities (zero)
1585                np.array(yd), # obs-calc profiles
1586                np.array(yb), # preset bkg
1587        '''                           
1588        self.comments = []
1589        self.idstring = ''
1590        self.Sample = G2pdG.SetDefaultSample()
1591        self.GSAS = None     # used in TOF
1592        self.clockWd = None  # used in TOF
1593        self.numbanks = 1
1594        self.instdict = {} # place items here that will be transferred to the instrument parameters
1595
1596######################################################################
1597class ImportImage(ImportBaseclass):
1598    '''Defines a base class for the reading of images
1599
1600    Images are read in only these places:
1601   
1602      * Initial reading is typically done from a menu item
1603        with a call to :meth:`GSASII.GSASII.OnImportImage`
1604        which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`. That calls
1605        methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1606        :meth:`Reader`. This returns a list of reader objects for each read image.
1607
1608      * Images are read alternatively in :func:`GSASIIIO.ReadImages`, which puts image info
1609        directly into the data tree.
1610
1611      * Images are reloaded with :func:`GSASIIIO.GetImageData`.
1612
1613    .. _Image_import_routines:
1614
1615    When reading an image, the ``Reader()`` routine in the ImportImage class
1616    should set:
1617   
1618      * :attr:`Comments`: a list of strings (str),
1619      * :attr:`Npix`: the number of pixels in the image (int),
1620      * :attr:`Image`: the actual image as a numpy array (np.array)
1621      * :attr:`Data`: a dict defining image parameters (dict). Within this dict the following
1622        data items are needed:
1623       
1624         * 'pixelSize': size of each pixel in microns (such as ``[200,200]``.
1625         * 'wavelength': wavelength in Angstoms.
1626         * 'distance': distance of detector from sample in cm.
1627         * 'center': uncalibrated center of beam on detector (such as ``[204.8,204.8]``.
1628         * 'size': size of image (such as ``[2048,2048]``).
1629         * 'ImageTag': image number or other keyword used to retrieve image from
1630           a multi-image data file (defaults to ``1`` if not specified).
1631
1632    optional data items:
1633   
1634      * :attr:`repeat`: set to True if there are additional images to
1635        read in the file, False otherwise
1636      * :attr:`repeatcount`: set to the number of the image.
1637     
1638    Note that the above is initialized with :meth:`InitParameters`.
1639    (Also see :ref:`Writing a Import Routine<Import_Routines>`
1640    for an explanation on how to use import classes in general.)
1641    '''
1642    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1643        strictExtension=False,):
1644        ImportBaseclass.__init__(self,formatName,longFormatName,
1645            extensionlist,strictExtension)
1646        self.InitParameters()
1647       
1648    def ReInitialize(self):
1649        'Reinitialize the Reader to initial settings -- not used at present'
1650        ImportBaseclass.ReInitialize(self)
1651        self.InitParameters()
1652       
1653    def InitParameters(self):
1654        'initialize the instrument parameters structure'
1655        self.Comments = ['No comments']
1656        self.Data = {}
1657        self.Npix = 0
1658        self.Image = None
1659        self.repeat = False
1660        self.repeatcount = 1
1661
1662    def LoadImage(self,ParentFrame,imagefile,imagetag=None):
1663        '''Optionally, call this after reading in an image to load it into the tree.
1664        This saves time by preventing a reread of the same information.
1665        '''
1666        if ParentFrame:
1667            ParentFrame.ImageZ = self.Image   # store the image for plotting
1668            ParentFrame.oldImagefile = imagefile # save the name of the last image file read
1669            ParentFrame.oldImageTag = imagetag   # save the tag of the last image file read           
1670
1671######################################################################
1672class ExportBaseclass(object):
1673    '''Defines a base class for the exporting of GSAS-II results.
1674
1675    This class is subclassed in the various exports/G2export_*.py files. Those files
1676    are imported in :meth:`GSASII.GSASII._init_Exports` which defines the
1677    appropriate menu items for each one and the .Exporter method is called
1678    directly from the menu item.
1679
1680    Routines may also define a .Writer method, which is used to write a single
1681    file without invoking any GUI objects.
1682    '''
1683    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1684        self.G2frame = G2frame
1685        self.formatName = formatName # short string naming file type
1686        self.extension = extension
1687        if longFormatName: # longer string naming file type
1688            self.longFormatName = longFormatName
1689        else:
1690            self.longFormatName = formatName
1691        self.OverallParms = {}
1692        self.Phases = {}
1693        self.Histograms = {}
1694        self.powderDict = {}
1695        self.xtalDict = {}
1696        self.parmDict = {}
1697        self.sigDict = {}
1698        # updated in InitExport:
1699        self.currentExportType = None # type of export that has been requested
1700        # updated in ExportSelect (when used):
1701        self.phasenam = None # a list of selected phases
1702        self.histnam = None # a list of selected histograms
1703        self.filename = None # name of file to be written (single export) or template (multiple files)
1704        self.dirname = '' # name of directory where file(s) will be written
1705        self.fullpath = '' # name of file being written -- full path
1706       
1707        # items that should be defined in a subclass of this class
1708        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1709        # The following types are defined: 'project', "phase", "powder", "single"
1710        self.multiple = False # set as True if the class can export multiple phases or histograms
1711        # self.multiple is ignored for "project" exports
1712
1713    def InitExport(self,event):
1714        '''Determines the type of menu that called the Exporter and
1715        misc initialization.
1716        '''
1717        self.filename = None # name of file to be written (single export)
1718        self.dirname = '' # name of file to be written (multiple export)
1719        if event:
1720            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1721
1722    def MakePWDRfilename(self,hist):
1723        '''Make a filename root (no extension) from a PWDR histogram name
1724
1725        :param str hist: the histogram name in data tree (starts with "PWDR ")
1726        '''
1727        file0 = ''
1728        file1 = hist[5:]
1729        # replace repeated blanks
1730        while file1 != file0:
1731            file0 = file1
1732            file1 = file0.replace('  ',' ').strip()
1733        file0 = file1.replace('Azm= ','A')
1734        # if angle has unneeded decimal places on aziumuth, remove them
1735        if file0[-3:] == '.00': file0 = file0[:-3]
1736        file0 = file0.replace('.','_')
1737        file0 = file0.replace(' ','_')
1738        return file0
1739
1740    def ExportSelect(self,AskFile='ask'):
1741        '''Selects histograms or phases when needed. Sets a default file name when
1742        requested in self.filename; always sets a default directory in self.dirname.
1743
1744        :param bool AskFile: Determines how this routine processes getting a
1745          location to store the current export(s).
1746         
1747          * if AskFile is 'ask' (default option), get the name of the file to be written;
1748            self.filename and self.dirname are always set. In the case where
1749            multiple files must be generated, the export routine should do this
1750            based on self.filename as a template.
1751          * if AskFile is 'dir', get the name of the directory to be used;
1752            self.filename is not used, but self.dirname is always set. The export routine
1753            will always generate the file name.
1754          * if AskFile is 'single', get only the name of the directory to be used when
1755            multiple items will be written (as multiple files) are used
1756            *or* a complete file name is requested when a single file
1757            name is selected. self.dirname is always set and self.filename used
1758            only when a single file is selected.
1759          * if AskFile is 'default', creates a name of the file to be used from
1760            the name of the project (.gpx) file. If the project has not been saved,
1761            then the name of file is requested.
1762            self.filename and self.dirname are always set. In the case where
1763            multiple file names must be generated, the export routine should do this
1764            based on self.filename.
1765          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1766            file. If the project has not been saved, then a directory is requested.
1767            self.filename is not used.
1768
1769        :returns: True in case of an error
1770        '''
1771       
1772        numselected = 1
1773        if self.currentExportType == 'phase':
1774            if len(self.Phases) == 0:
1775                self.G2frame.ErrorDialog(
1776                    'Empty project',
1777                    'Project does not contain any phases.')
1778                return True
1779            elif len(self.Phases) == 1:
1780                self.phasenam = self.Phases.keys()
1781            elif self.multiple: 
1782                choices = sorted(self.Phases.keys())
1783                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1784                if phasenum is None: return True
1785                self.phasenam = [choices[i] for i in phasenum]
1786                if not self.phasenam: return True
1787                numselected = len(self.phasenam)
1788            else:
1789                choices = sorted(self.Phases.keys())
1790                phasenum = G2G.ItemSelector(choices,self.G2frame)
1791                if phasenum is None: return True
1792                self.phasenam = [choices[phasenum]]
1793                numselected = len(self.phasenam)
1794        elif self.currentExportType == 'single':
1795            if len(self.xtalDict) == 0:
1796                self.G2frame.ErrorDialog(
1797                    'Empty project',
1798                    'Project does not contain any single crystal data.')
1799                return True
1800            elif len(self.xtalDict) == 1:
1801                self.histnam = self.xtalDict.values()
1802            elif self.multiple:
1803                choices = sorted(self.xtalDict.values())
1804                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1805                if not hnum: return True
1806                self.histnam = [choices[i] for i in hnum]
1807                numselected = len(self.histnam)
1808            else:
1809                choices = sorted(self.xtalDict.values())
1810                hnum = G2G.ItemSelector(choices,self.G2frame)
1811                if hnum is None: return True
1812                self.histnam = [choices[hnum]]
1813                numselected = len(self.histnam)
1814        elif self.currentExportType == 'powder':
1815            if len(self.powderDict) == 0:
1816                self.G2frame.ErrorDialog(
1817                    'Empty project',
1818                    'Project does not contain any powder data.')
1819                return True
1820            elif len(self.powderDict) == 1:
1821                self.histnam = self.powderDict.values()
1822            elif self.multiple:
1823                choices = sorted(self.powderDict.values())
1824                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1825                if not hnum: return True
1826                self.histnam = [choices[i] for i in hnum]
1827                numselected = len(self.histnam)
1828            else:
1829                choices = sorted(self.powderDict.values())
1830                hnum = G2G.ItemSelector(choices,self.G2frame)
1831                if hnum is None: return True
1832                self.histnam = [choices[hnum]]
1833                numselected = len(self.histnam)
1834        elif self.currentExportType == 'image':
1835            if len(self.Histograms) == 0:
1836                self.G2frame.ErrorDialog(
1837                    'Empty project',
1838                    'Project does not contain any images.')
1839                return True
1840            elif len(self.Histograms) == 1:
1841                self.histnam = self.Histograms.keys()
1842            else:
1843                choices = sorted(self.Histograms.keys())
1844                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1845                if self.multiple:
1846                    if not hnum: return True
1847                    self.histnam = [choices[i] for i in hnum]
1848                else:
1849                    if hnum is None: return True
1850                    self.histnam = [choices[hnum]]
1851                numselected = len(self.histnam)
1852        if self.currentExportType == 'map':
1853            # search for phases with maps
1854            mapPhases = []
1855            choices = []
1856            for phasenam in sorted(self.Phases):
1857                phasedict = self.Phases[phasenam] # pointer to current phase info           
1858                if len(phasedict['General']['Map'].get('rho',[])):
1859                    mapPhases.append(phasenam)
1860                    if phasedict['General']['Map'].get('Flip'):
1861                        choices.append('Charge flip map: '+str(phasenam))
1862                    elif phasedict['General']['Map'].get('MapType'):
1863                        choices.append(
1864                            str(phasedict['General']['Map'].get('MapType'))
1865                            + ' map: ' + str(phasenam))
1866                    else:
1867                        choices.append('unknown map: '+str(phasenam))
1868            # select a map if needed
1869            if len(mapPhases) == 0:
1870                self.G2frame.ErrorDialog(
1871                    'Empty project',
1872                    'Project does not contain any maps.')
1873                return True
1874            elif len(mapPhases) == 1:
1875                self.phasenam = mapPhases
1876            else: 
1877                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1878                if self.multiple:
1879                    if not phasenum: return True
1880                    self.phasenam = [mapPhases[i] for i in phasenum]
1881                else:
1882                    if phasenum is None: return True
1883                    self.phasenam = [mapPhases[phasenum]]
1884            numselected = len(self.phasenam)
1885
1886        # items selected, now set self.dirname and usually self.filename
1887        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
1888            AskFile == 'default' and not self.G2frame.GSASprojectfile
1889            ):
1890            filename = self.askSaveFile()
1891            if not filename: return True
1892            self.dirname,self.filename = os.path.split(filename)
1893        elif AskFile == 'dir' or AskFile == 'single' or (
1894            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
1895            ):
1896            self.dirname = self.askSaveDirectory()
1897            if not self.dirname: return True
1898        elif AskFile == 'default-dir' or AskFile == 'default':
1899            self.dirname,self.filename = os.path.split(
1900                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
1901                )
1902        else:
1903            raise Exception('This should not happen!')
1904       
1905    def loadParmDict(self):
1906        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
1907        refined values to those from the last cycle and set the uncertainties for the
1908        refined parameters in another dict (self.sigDict).
1909
1910        Expands the parm & sig dicts to include values derived from constraints.
1911        '''
1912        self.parmDict = {}
1913        self.sigDict = {}
1914        rigidbodyDict = {}
1915        covDict = {}
1916        consDict = {}
1917        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1918        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
1919        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
1920        while item:
1921            name = self.G2frame.PatternTree.GetItemText(item)
1922            if name == 'Rigid bodies':
1923                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
1924            elif name == 'Covariance':
1925                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
1926            elif name == 'Constraints':
1927                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
1928            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
1929        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1930        self.parmDict.update(rbDict)
1931        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1932        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,maxSSwave =  G2stIO.GetPhaseData(
1933            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
1934        self.parmDict.update(phaseDict)
1935        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
1936            Phases,Histograms,Print=False,resetRefList=False)
1937        self.parmDict.update(hapDict)
1938        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
1939        self.parmDict.update(histDict)
1940        self.parmDict.update(zip(
1941            covDict.get('varyList',[]),
1942            covDict.get('variables',[])))
1943        self.sigDict = dict(zip(
1944            covDict.get('varyList',[]),
1945            covDict.get('sig',[])))
1946        # expand to include constraints: first compile a list of constraints
1947        constList = []
1948        for item in consDict:
1949            if item.startswith('_'): continue
1950            constList += consDict[item]
1951        # now process the constraints
1952        G2mv.InitVars()
1953        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
1954        varyList = covDict.get('varyListStart')
1955        if varyList is None and len(constDict) == 0:
1956            # no constraints can use varyList
1957            varyList = covDict.get('varyList')
1958        elif varyList is None:
1959            # old GPX file from before pre-constraint varyList is saved
1960            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
1961            raise Exception(' *** Export aborted ***')
1962        else:
1963            varyList = list(varyList)
1964        try:
1965            groups,parmlist = G2mv.GroupConstraints(constDict)
1966            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,self.parmDict)
1967        except:
1968            # this really should not happen
1969            print ' *** ERROR - constraints are internally inconsistent ***'
1970            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
1971            print 'Errors',errmsg
1972            if warnmsg: print 'Warnings',warnmsg
1973            raise Exception(' *** CIF creation aborted ***')
1974        # add the constrained values to the parameter dictionary
1975        G2mv.Dict2Map(self.parmDict,varyList)
1976        # and add their uncertainties into the esd dictionary (sigDict)
1977        if covDict.get('covMatrix') is not None:
1978            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
1979
1980    def loadTree(self):
1981        '''Load the contents of the data tree into a set of dicts
1982        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
1983        & self.xtalDict)
1984       
1985        * The childrenless data tree items are overall parameters/controls for the
1986          entire project and are placed in self.OverallParms
1987        * Phase items are placed in self.Phases
1988        * Data items are placed in self.Histogram. The key for these data items
1989          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
1990        '''
1991        self.OverallParms = {}
1992        self.powderDict = {}
1993        self.xtalDict = {}
1994        self.Phases = {}
1995        self.Histograms = {}
1996        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
1997        histType = None       
1998        if self.currentExportType == 'phase':
1999            # if exporting phases load them here
2000            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
2001            if not sub:
2002                print 'no phases found'
2003                return True
2004            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
2005            while item:
2006                phaseName = self.G2frame.PatternTree.GetItemText(item)
2007                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
2008                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
2009            return
2010        elif self.currentExportType == 'single':
2011            histType = 'HKLF'
2012        elif self.currentExportType == 'powder':
2013            histType = 'PWDR'
2014        elif self.currentExportType == 'image':
2015            histType = 'IMG'
2016
2017        if histType: # Loading just one kind of tree entry
2018            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2019            while item:
2020                name = self.G2frame.PatternTree.GetItemText(item)
2021                if name.startswith(histType):
2022                    if self.Histograms.get(name): # there is already an item with this name
2023                        print('Histogram name '+str(name)+' is repeated. Renaming')
2024                        if name[-1] == '9':
2025                            name = name[:-1] + '10'
2026                        elif name[-1] in '012345678':
2027                            name = name[:-1] + str(int(name[-1])+1)
2028                        else:                           
2029                            name += '-1'
2030                    self.Histograms[name] = {}
2031                    # the main info goes into Data, but the 0th
2032                    # element contains refinement results, carry
2033                    # that over too now.
2034                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
2035                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
2036                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2037                    while item2: 
2038                        child = self.G2frame.PatternTree.GetItemText(item2)
2039                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
2040                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
2041                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2042            # index powder and single crystal histograms by number
2043            for hist in self.Histograms:
2044                if hist.startswith("PWDR"): 
2045                    d = self.powderDict
2046                elif hist.startswith("HKLF"): 
2047                    d = self.xtalDict
2048                else:
2049                    return                   
2050                i = self.Histograms[hist].get('hId')
2051                if i is None and not d.keys():
2052                    i = 0
2053                elif i is None or i in d.keys():
2054                    i = max(d.keys())+1
2055                d[i] = hist
2056            return
2057        # else standard load: using all interlinked phases and histograms
2058        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2059        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2060        while item:
2061            name = self.G2frame.PatternTree.GetItemText(item)
2062            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2063            if not item2: 
2064                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
2065            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2066        # index powder and single crystal histograms
2067        for hist in self.Histograms:
2068            i = self.Histograms[hist]['hId']
2069            if hist.startswith("PWDR"): 
2070                self.powderDict[i] = hist
2071            elif hist.startswith("HKLF"): 
2072                self.xtalDict[i] = hist
2073
2074    def dumpTree(self,mode='type'):
2075        '''Print out information on the data tree dicts loaded in loadTree
2076        '''
2077        print '\nOverall'
2078        if mode == 'type':
2079            def Show(arg): return type(arg)
2080        else:
2081            def Show(arg): return arg
2082        for key in self.OverallParms:
2083            print '  ',key,Show(self.OverallParms[key])
2084        print 'Phases'
2085        for key1 in self.Phases:
2086            print '    ',key1,Show(self.Phases[key1])
2087        print 'Histogram'
2088        for key1 in self.Histograms:
2089            print '    ',key1,Show(self.Histograms[key1])
2090            for key2 in self.Histograms[key1]:
2091                print '      ',key2,Show(self.Histograms[key1][key2])
2092
2093    def defaultSaveFile(self):
2094        return os.path.abspath(
2095            os.path.splitext(self.G2frame.GSASprojectfile
2096                             )[0]+self.extension)
2097       
2098    def askSaveFile(self):
2099        '''Ask the user to supply a file name
2100
2101        :returns: a file name (str) or None if Cancel is pressed
2102        '''
2103       
2104        pth = G2G.GetExportPath(self.G2frame)
2105        defnam = os.path.splitext(
2106            os.path.split(self.G2frame.GSASprojectfile)[1]
2107            )[0]+self.extension
2108        dlg = wx.FileDialog(
2109            self.G2frame, 'Input name for file to write', pth, defnam,
2110            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2111            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2112        dlg.CenterOnParent()
2113        try:
2114            if dlg.ShowModal() == wx.ID_OK:
2115                filename = dlg.GetPath()
2116                self.G2frame.LastExportDir = os.path.split(filename)[0]
2117                filename = os.path.splitext(filename)[0]+self.extension # make sure extension is correct
2118            else:
2119                filename = None
2120        finally:
2121            dlg.Destroy()
2122        return filename
2123
2124    def askSaveDirectory(self):
2125        '''Ask the user to supply a directory name. Path name is used as the
2126        starting point for the next export path search.
2127
2128        :returns: a directory name (str) or None if Cancel is pressed
2129        '''
2130        pth = G2G.GetExportPath(self.G2frame)
2131        dlg = wx.DirDialog(
2132            self.G2frame, 'Input directory where file(s) will be written', pth,
2133            wx.DD_DEFAULT_STYLE)
2134        dlg.CenterOnParent()
2135        try:
2136            if dlg.ShowModal() == wx.ID_OK:
2137                filename = dlg.GetPath()
2138                self.G2frame.LastExportDir = filename
2139            else:
2140                filename = None
2141        finally:
2142            dlg.Destroy()
2143        return filename
2144
2145    # Tools for file writing.
2146    def OpenFile(self,fil=None,mode='w'):
2147        '''Open the output file
2148
2149        :param str fil: The name of the file to open. If None (default)
2150          the name defaults to self.dirname + self.filename.
2151          If an extension is supplied, it is not overridded,
2152          but if not, the default extension is used.
2153        :returns: the file object opened by the routine which is also
2154          saved as self.fp
2155        '''
2156        if not fil:
2157            if not os.path.splitext(self.filename)[1]:
2158                self.filename += self.extension
2159            fil = os.path.join(self.dirname,self.filename)
2160        self.fullpath = os.path.abspath(fil)
2161        self.fp = open(self.fullpath,mode)
2162        return self.fp
2163
2164    def Write(self,line):
2165        '''write a line of output, attaching a line-end character
2166
2167        :param str line: the text to be written.
2168        '''
2169        self.fp.write(line+'\n')
2170    def CloseFile(self,fp=None):
2171        '''Close a file opened in OpenFile
2172
2173        :param file fp: the file object to be closed. If None (default)
2174          file object self.fp is closed.
2175        '''
2176        if fp is None:
2177            fp = self.fp
2178            self.fp = None
2179        fp.close()
2180    # Tools to pull information out of the data arrays
2181    def GetCell(self,phasenam):
2182        """Gets the unit cell parameters and their s.u.'s for a selected phase
2183
2184        :param str phasenam: the name for the selected phase
2185        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2186          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2187          cell values and `cellSig` has their uncertainties.
2188        """
2189        phasedict = self.Phases[phasenam] # pointer to current phase info
2190        try:
2191            pfx = str(phasedict['pId'])+'::'
2192            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2193            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
2194                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2195            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2196            return cellList,cellSig
2197        except KeyError:
2198            cell = phasedict['General']['Cell'][1:]
2199            return cell,7*[0]
2200   
2201    def GetAtoms(self,phasenam):
2202        """Gets the atoms associated with a phase. Can be used with standard
2203        or macromolecular phases
2204
2205        :param str phasenam: the name for the selected phase
2206        :returns: a list of items for eac atom where each item is a list containing:
2207          label, typ, mult, xyz, and td, where
2208
2209          * label and typ are the atom label and the scattering factor type (str)
2210          * mult is the site multiplicity (int)
2211          * xyz is contains a list with four pairs of numbers:
2212            x, y, z and fractional occupancy and
2213            their standard uncertainty (or a negative value)
2214          * td is contains a list with either one or six pairs of numbers:
2215            if one number it is U\ :sub:`iso` and with six numbers it is
2216            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2217            paired with their standard uncertainty (or a negative value)
2218        """
2219        phasedict = self.Phases[phasenam] # pointer to current phase info           
2220        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2221        cfrac = cx+3
2222        fpfx = str(phasedict['pId'])+'::Afrac:'       
2223        atomslist = []
2224        for i,at in enumerate(phasedict['Atoms']):
2225            if phasedict['General']['Type'] == 'macromolecular':
2226                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2227            else:
2228                label = at[ct-1]
2229            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2230            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2231            mult = at[cs+1]
2232            typ = at[ct]
2233            xyz = []
2234            for j,v in enumerate(('x','y','z')):
2235                val = at[cx+j]
2236                pfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2237                sig = self.sigDict.get(pfx,-0.000009)
2238                xyz.append((val,sig))
2239            xyz.append((fval,fsig))
2240            td = []
2241            if at[cia] == 'I':
2242                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2243                val = self.parmDict.get(pfx,at[cia+1])
2244                sig = self.sigDict.get(pfx,-0.0009)
2245                td.append((val,sig))
2246            else:
2247                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2248                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2249                    val = self.parmDict.get(pfx,at[cia+2+i])
2250                    sig = self.sigDict.get(pfx,-0.0009)
2251                    td.append((val,sig))
2252            atomslist.append((label,typ,mult,xyz,td))
2253        return atomslist
2254######################################################################
2255def ExportPowderList(G2frame):
2256    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
2257   
2258    :param wx.Frame G2frame: the GSAS-II main data tree window
2259    '''
2260    extList = []
2261    for obj in G2frame.exporterlist:
2262        if 'powder' in obj.exporttype:
2263            try:
2264                obj.Writer
2265                extList.append(obj.extension)
2266            except AttributeError:
2267                pass
2268    return extList
2269
2270def ExportPowder(G2frame,TreeName,fileroot,extension):
2271    '''Writes a single powder histogram using the Export routines
2272
2273    :param wx.Frame G2frame: the GSAS-II main data tree window
2274    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
2275    :param str fileroot: name for file to be written, extension ignored
2276    :param str extension: extension for file to be written (start with '.'). Must
2277      match a powder export routine that has a Writer object.
2278    '''
2279    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2280    for obj in G2frame.exporterlist:
2281        if obj.extension == extension and 'powder' in obj.exporttype:
2282            obj.currentExportType = 'powder'
2283            obj.InitExport(None)
2284            obj.loadTree() # load all histograms in tree into dicts
2285            if TreeName not in obj.Histograms:
2286                raise Exception('Histogram not found: '+str(TreeName))
2287            try:
2288                obj.Writer
2289            except AttributeError:
2290                continue
2291            try:
2292                obj.Writer(TreeName,filename)
2293                return
2294            except Exception,err:
2295                print('Export Routine for '+extension+' failed.')
2296                print err
2297    else:
2298        print('No Export routine supports extension '+extension)
2299       
2300def ReadDIFFaX(DIFFaXfile):
2301    print 'read ',DIFFaXfile
2302    Layer = {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],'Width':[[10.,10.],[False,False]],
2303        'Layers':[],'Stacking':[],'Transitions':[],'Toler':0.01,'AtInfo':{}}
2304    df = open(DIFFaXfile,'r')
2305    lines = df.readlines()
2306    df.close()
2307    struct = False
2308    Struct = []
2309    stack = False
2310    Stack = []
2311    trans = False
2312    Trans = []
2313    instr = False
2314    for diff in lines:
2315        diff = diff[:-1].lower()
2316        if '!'  in diff:
2317            continue
2318        while '}' in diff: #strip comments
2319            iB = diff.index('{')
2320            iF = diff.index('}')+1
2321            if iB:
2322                diff = diff[:iB]
2323            else:
2324                diff = diff[iF:]
2325        if not diff:
2326            continue
2327        if diff.strip() == 'instrumental':
2328            instr = True
2329            continue
2330        if diff.strip() == 'structural':
2331            instr = False
2332            struct = True
2333            continue
2334        elif diff.strip() == 'stacking':
2335            struct = False
2336            stack = True
2337            continue
2338        elif diff.strip() == 'transitions':
2339            stack = False
2340            trans = True
2341            continue
2342        diff = diff.strip()
2343        if struct:
2344            if diff:
2345                Struct.append(diff)
2346        elif stack:
2347            if diff:
2348                Stack.append(diff)
2349        elif trans:
2350            if diff:
2351                Trans.append(diff)
2352   
2353#STRUCTURE records
2354    laueRec = Struct[1].split()
2355    Layer['Laue'] = laueRec[0]
2356    if Layer['Laue'] == 'unknown' and len(laueRec) > 1:
2357        Layer['Toler'] = float(laueRec[1])    #tolerance for 'unknown'?
2358    if Layer['Laue'] == '2/m(1)': Layer['Laue'] = '2/m(c)'
2359    if Layer['Laue'] == '2/m(2)': Layer['Laue'] = '2/m(ab)'
2360    cell = Struct[0].split()
2361    Layer['Cell'] = [False,float(cell[0]),float(cell[1]),float(cell[2]),90.,90.,float(cell[3]),1.0]
2362    nLayers = int(Struct[2])
2363    N = 3
2364    if 'layer' not in Struct[3]:
2365        N = 4
2366        if Struct[3] != 'infinite':
2367            width = Struct[3].split()
2368            Layer['Width'][0] = [float(width[0]),float(width[1])]
2369    for nL in range(nLayers):
2370        if '=' in Struct[N]:
2371            name = Struct[N].split('=')
2372            sameas = int(name[1])-1
2373            Layer['Layers'].append({'Name':name[0],'SameAs':Layer['Layers'][sameas]['Name'],'Symm':'None','Atoms':[]})
2374            N += 1
2375            continue
2376        Symm = 'None'
2377        if 'centro' in Struct[N+1]: Symm = '-1'
2378        Layer['Layers'].append({'Name':Struct[N],'SameAs':'','Symm':Symm,'Atoms':[]})
2379        N += 2
2380        iatm = 0
2381        while 'layer' not in Struct[N]:
2382            atom = Struct[N][4:].split()
2383            atomType = G2el.FixValence(Struct[N][:4].replace(' ','').strip().capitalize())
2384            if atomType not in Layer['AtInfo']:
2385                Layer['AtInfo'][atomType] = G2el.GetAtomInfo(atomType)
2386            atomName = '%s(%s)'%(atomType,atom[0])
2387            newVals = []
2388            for val in atom[1:6]:
2389                if '/' in val:
2390                    newVals.append(eval(val+'.'))
2391                else:
2392                    newVals.append(float(val))               
2393            atomRec = [atomName,atomType,newVals[0],newVals[1],newVals[2],newVals[4],newVals[3]/78.9568]
2394            Layer['Layers'][-1]['Atoms'].append(atomRec)
2395            N += 1
2396            if N > len(Struct)-1:
2397                break
2398#TRANSITIONS records
2399    transArray = []
2400    N = 0
2401    for i in range(nLayers):
2402        transArray.append([])
2403        for j in range(nLayers):
2404            vals = Trans[N].split()
2405            newVals = []
2406            for val in vals[:4]:
2407                if '/' in val:
2408                    newVals.append(eval(val+'.'))
2409                else:
2410                    newVals.append(float(val))
2411            transArray[-1].append(newVals+['',False])
2412            N += 1
2413    Layer['Transitions'] = transArray
2414#STACKING records
2415    Layer['Stacking'] = [Stack[0],'']
2416    if Stack[0] == 'recursive':
2417        Layer['Stacking'][1] = Stack[1]
2418    elif Stack[0] == 'explicit':
2419        if Stack[1] == 'random':
2420            Layer['Stacking'][1] = Stack[1]
2421        else:
2422            Layer['Stacking'][1] = 'list'
2423            Layer['Stacking'].append('')
2424            for stack in Stack[2:]:
2425                Layer['Stacking'][2] += ' '+stack
2426    return Layer
2427
2428def ReadCIF(URLorFile):
2429    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2430    (from James Hester).
2431    The open routine gets confused with DOS names that begin with a letter and colon
2432    "C:\dir\" so this routine will try to open the passed name as a file and if that
2433    fails, try it as a URL
2434
2435    :param str URLorFile: string containing a URL or a file name. Code will try first
2436      to open it as a file and then as a URL.
2437
2438    :returns: a PyCifRW CIF object.
2439    '''
2440    import CifFile as cif # PyCifRW from James Hester
2441
2442    # alternate approach:
2443    #import urllib
2444    #ciffile = 'file:'+urllib.pathname2url(filename)
2445   
2446    try:
2447        fp = open(URLorFile,'r')
2448        cf = cif.ReadCif(fp)
2449        fp.close()
2450        return cf
2451    except IOError:
2452        return cif.ReadCif(URLorFile)
2453
2454if __name__ == '__main__':
2455    import GSASII
2456    application = GSASII.GSASIImain(0)
2457    G2frame = application.main
2458    #app = wx.PySimpleApp()
2459    #G2frame = wx.Frame(None) # create a frame
2460    #frm.Show(True)
2461    #filename = '/tmp/notzip.zip'
2462    #filename = '/tmp/all.zip'
2463    #filename = '/tmp/11bmb_7652.zip'
2464   
2465    #selection=None, confirmoverwrite=True, parent=None
2466    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2467    #print ExtractFileFromZip(filename,multipleselect=True)
2468    #                         #confirmread=False, confirmoverwrite=False)
2469
2470    # choicelist=[ ('a','b','c'),
2471    #              ('test1','test2'),('no choice',)]
2472    # titles = [ 'a, b or c', 'tests', 'No option here']
2473    # dlg = MultipleChoicesDialog(
2474    #     choicelist,titles,
2475    #     parent=frm)
2476    # if dlg.ShowModal() == wx.ID_OK:
2477    #     print 'Got OK'
2478    imagefile = '/tmp/NDC5_00237_3.ge3'
2479    Comments, Data, Npix, Image = GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None)
2480
2481    print("\n\nResults loaded to Comments, Data, Npix and Image\n\n")
2482
2483    GSASIIpath.IPyBreak_base()
Note: See TracBrowser for help on using the repository browser.