source: trunk/GSASIIIO.py @ 2163

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

plotting of stacking layers, some stacking GUI changes

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