source: trunk/GSASIIIO.py @ 2214

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

missing import copy

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