source: trunk/GSASIIIO.py @ 2297

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

implement creation of summed file from multi-frame ge image files; creates a G2img file which is put on tree in place of multi frame file. Subsequent accesses get teg G2img file.
Insert skipPageChange = True to prevent plot tab change causing tree change from Strain plots & Image plots

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