source: trunk/GSASIIIO.py @ 2544

Last change on this file since 2544 was 2544, checked in by vondreele, 5 years ago

major cleanup of unused variables, etc.

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