source: trunk/GSASIIIO.py @ 2223

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

make min Threshold = 0 for images on import; anything else is too confusing
further mods to OnCopyControls? - simplify & prevent image from having self as dark or background
& not allow setting of dark or background to current image

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