source: trunk/GSASIIIO.py @ 2341

Last change on this file since 2341 was 2341, checked in by toby, 7 years ago

improve powder imports: suppress printing of binary data; improve validation of old GSAS binary files

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