source: trunk/GSASIIIO.py @ 2436

Last change on this file since 2436 was 2436, checked in by vondreele, 7 years ago

implement decimal azimuth ranges (from integer)

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