source: trunk/GSASIIIO.py @ 2290

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

complex problem - fix summing of GE images; needed new item in Image Controls to save which reader was used. Change to OnImportGeneric? to keep self.formatName; GetImageData? put it in the call & check it with the available readers & GetImageZ to get it out of the Image Controls & apply it in GetImageData?.
Add a test on file size for GE files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 103.0 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2016-05-25 20:39:41 +0000 (Wed, 25 May 2016) $
4# $Author: vondreele $
5# $Revision: 2290 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 2290 2016-05-25 20:39:41Z 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: 2290 $")
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 += ' #'+str(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 += '(%03d)'%(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
1635    optional data items:
1636   
1637      * :attr:`repeat`: set to True if there are additional images to
1638        read in the file, False otherwise
1639      * :attr:`repeatcount`: set to the number of the image.
1640     
1641    Note that the above is initialized with :meth:`InitParameters`.
1642    (Also see :ref:`Writing a Import Routine<Import_Routines>`
1643    for an explanation on how to use import classes in general.)
1644    '''
1645    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1646        strictExtension=False,):
1647        ImportBaseclass.__init__(self,formatName,longFormatName,
1648            extensionlist,strictExtension)
1649        self.InitParameters()
1650       
1651    def ReInitialize(self):
1652        'Reinitialize the Reader to initial settings -- not used at present'
1653        ImportBaseclass.ReInitialize(self)
1654        self.InitParameters()
1655       
1656    def InitParameters(self):
1657        'initialize the instrument parameters structure'
1658        self.Comments = ['No comments']
1659        self.Data = {}
1660        self.Npix = 0
1661        self.Image = None
1662        self.repeat = False
1663        self.repeatcount = 1
1664
1665    def LoadImage(self,ParentFrame,imagefile,imagetag=None):
1666        '''Optionally, call this after reading in an image to load it into the tree.
1667        This saves time by preventing a reread of the same information.
1668        '''
1669        if ParentFrame:
1670            ParentFrame.ImageZ = self.Image   # store the image for plotting
1671            ParentFrame.oldImagefile = imagefile # save the name of the last image file read
1672            ParentFrame.oldImageTag = imagetag   # save the tag of the last image file read           
1673
1674######################################################################
1675class ExportBaseclass(object):
1676    '''Defines a base class for the exporting of GSAS-II results.
1677
1678    This class is subclassed in the various exports/G2export_*.py files. Those files
1679    are imported in :meth:`GSASII.GSASII._init_Exports` which defines the
1680    appropriate menu items for each one and the .Exporter method is called
1681    directly from the menu item.
1682
1683    Routines may also define a .Writer method, which is used to write a single
1684    file without invoking any GUI objects.
1685    '''
1686    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1687        self.G2frame = G2frame
1688        self.formatName = formatName # short string naming file type
1689        self.extension = extension
1690        if longFormatName: # longer string naming file type
1691            self.longFormatName = longFormatName
1692        else:
1693            self.longFormatName = formatName
1694        self.OverallParms = {}
1695        self.Phases = {}
1696        self.Histograms = {}
1697        self.powderDict = {}
1698        self.xtalDict = {}
1699        self.parmDict = {}
1700        self.sigDict = {}
1701        # updated in InitExport:
1702        self.currentExportType = None # type of export that has been requested
1703        # updated in ExportSelect (when used):
1704        self.phasenam = None # a list of selected phases
1705        self.histnam = None # a list of selected histograms
1706        self.filename = None # name of file to be written (single export) or template (multiple files)
1707        self.dirname = '' # name of directory where file(s) will be written
1708        self.fullpath = '' # name of file being written -- full path
1709       
1710        # items that should be defined in a subclass of this class
1711        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1712        # The following types are defined: 'project', "phase", "powder", "single"
1713        self.multiple = False # set as True if the class can export multiple phases or histograms
1714        # self.multiple is ignored for "project" exports
1715
1716    def InitExport(self,event):
1717        '''Determines the type of menu that called the Exporter and
1718        misc initialization.
1719        '''
1720        self.filename = None # name of file to be written (single export)
1721        self.dirname = '' # name of file to be written (multiple export)
1722        if event:
1723            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1724
1725    def MakePWDRfilename(self,hist):
1726        '''Make a filename root (no extension) from a PWDR histogram name
1727
1728        :param str hist: the histogram name in data tree (starts with "PWDR ")
1729        '''
1730        file0 = ''
1731        file1 = hist[5:]
1732        # replace repeated blanks
1733        while file1 != file0:
1734            file0 = file1
1735            file1 = file0.replace('  ',' ').strip()
1736        file0 = file1.replace('Azm= ','A')
1737        # if angle has unneeded decimal places on aziumuth, remove them
1738        if file0[-3:] == '.00': file0 = file0[:-3]
1739        file0 = file0.replace('.','_')
1740        file0 = file0.replace(' ','_')
1741        return file0
1742
1743    def ExportSelect(self,AskFile='ask'):
1744        '''Selects histograms or phases when needed. Sets a default file name when
1745        requested in self.filename; always sets a default directory in self.dirname.
1746
1747        :param bool AskFile: Determines how this routine processes getting a
1748          location to store the current export(s).
1749         
1750          * if AskFile is 'ask' (default option), get the name of the file to be written;
1751            self.filename and self.dirname are always set. In the case where
1752            multiple files must be generated, the export routine should do this
1753            based on self.filename as a template.
1754          * if AskFile is 'dir', get the name of the directory to be used;
1755            self.filename is not used, but self.dirname is always set. The export routine
1756            will always generate the file name.
1757          * if AskFile is 'single', get only the name of the directory to be used when
1758            multiple items will be written (as multiple files) are used
1759            *or* a complete file name is requested when a single file
1760            name is selected. self.dirname is always set and self.filename used
1761            only when a single file is selected.
1762          * if AskFile is 'default', creates a name of the file to be used from
1763            the name of the project (.gpx) file. If the project has not been saved,
1764            then the name of file is requested.
1765            self.filename and self.dirname are always set. In the case where
1766            multiple file names must be generated, the export routine should do this
1767            based on self.filename.
1768          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1769            file. If the project has not been saved, then a directory is requested.
1770            self.filename is not used.
1771
1772        :returns: True in case of an error
1773        '''
1774       
1775        numselected = 1
1776        if self.currentExportType == 'phase':
1777            if len(self.Phases) == 0:
1778                self.G2frame.ErrorDialog(
1779                    'Empty project',
1780                    'Project does not contain any phases.')
1781                return True
1782            elif len(self.Phases) == 1:
1783                self.phasenam = self.Phases.keys()
1784            elif self.multiple: 
1785                choices = sorted(self.Phases.keys())
1786                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1787                if phasenum is None: return True
1788                self.phasenam = [choices[i] for i in phasenum]
1789                if not self.phasenam: return True
1790                numselected = len(self.phasenam)
1791            else:
1792                choices = sorted(self.Phases.keys())
1793                phasenum = G2G.ItemSelector(choices,self.G2frame)
1794                if phasenum is None: return True
1795                self.phasenam = [choices[phasenum]]
1796                numselected = len(self.phasenam)
1797        elif self.currentExportType == 'single':
1798            if len(self.xtalDict) == 0:
1799                self.G2frame.ErrorDialog(
1800                    'Empty project',
1801                    'Project does not contain any single crystal data.')
1802                return True
1803            elif len(self.xtalDict) == 1:
1804                self.histnam = self.xtalDict.values()
1805            elif self.multiple:
1806                choices = sorted(self.xtalDict.values())
1807                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1808                if not hnum: return True
1809                self.histnam = [choices[i] for i in hnum]
1810                numselected = len(self.histnam)
1811            else:
1812                choices = sorted(self.xtalDict.values())
1813                hnum = G2G.ItemSelector(choices,self.G2frame)
1814                if hnum is None: return True
1815                self.histnam = [choices[hnum]]
1816                numselected = len(self.histnam)
1817        elif self.currentExportType == 'powder':
1818            if len(self.powderDict) == 0:
1819                self.G2frame.ErrorDialog(
1820                    'Empty project',
1821                    'Project does not contain any powder data.')
1822                return True
1823            elif len(self.powderDict) == 1:
1824                self.histnam = self.powderDict.values()
1825            elif self.multiple:
1826                choices = sorted(self.powderDict.values())
1827                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1828                if not hnum: return True
1829                self.histnam = [choices[i] for i in hnum]
1830                numselected = len(self.histnam)
1831            else:
1832                choices = sorted(self.powderDict.values())
1833                hnum = G2G.ItemSelector(choices,self.G2frame)
1834                if hnum is None: return True
1835                self.histnam = [choices[hnum]]
1836                numselected = len(self.histnam)
1837        elif self.currentExportType == 'image':
1838            if len(self.Histograms) == 0:
1839                self.G2frame.ErrorDialog(
1840                    'Empty project',
1841                    'Project does not contain any images.')
1842                return True
1843            elif len(self.Histograms) == 1:
1844                self.histnam = self.Histograms.keys()
1845            else:
1846                choices = sorted(self.Histograms.keys())
1847                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1848                if self.multiple:
1849                    if not hnum: return True
1850                    self.histnam = [choices[i] for i in hnum]
1851                else:
1852                    if hnum is None: return True
1853                    self.histnam = [choices[hnum]]
1854                numselected = len(self.histnam)
1855        if self.currentExportType == 'map':
1856            # search for phases with maps
1857            mapPhases = []
1858            choices = []
1859            for phasenam in sorted(self.Phases):
1860                phasedict = self.Phases[phasenam] # pointer to current phase info           
1861                if len(phasedict['General']['Map'].get('rho',[])):
1862                    mapPhases.append(phasenam)
1863                    if phasedict['General']['Map'].get('Flip'):
1864                        choices.append('Charge flip map: '+str(phasenam))
1865                    elif phasedict['General']['Map'].get('MapType'):
1866                        choices.append(
1867                            str(phasedict['General']['Map'].get('MapType'))
1868                            + ' map: ' + str(phasenam))
1869                    else:
1870                        choices.append('unknown map: '+str(phasenam))
1871            # select a map if needed
1872            if len(mapPhases) == 0:
1873                self.G2frame.ErrorDialog(
1874                    'Empty project',
1875                    'Project does not contain any maps.')
1876                return True
1877            elif len(mapPhases) == 1:
1878                self.phasenam = mapPhases
1879            else: 
1880                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1881                if self.multiple:
1882                    if not phasenum: return True
1883                    self.phasenam = [mapPhases[i] for i in phasenum]
1884                else:
1885                    if phasenum is None: return True
1886                    self.phasenam = [mapPhases[phasenum]]
1887            numselected = len(self.phasenam)
1888
1889        # items selected, now set self.dirname and usually self.filename
1890        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
1891            AskFile == 'default' and not self.G2frame.GSASprojectfile
1892            ):
1893            filename = self.askSaveFile()
1894            if not filename: return True
1895            self.dirname,self.filename = os.path.split(filename)
1896        elif AskFile == 'dir' or AskFile == 'single' or (
1897            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
1898            ):
1899            self.dirname = self.askSaveDirectory()
1900            if not self.dirname: return True
1901        elif AskFile == 'default-dir' or AskFile == 'default':
1902            self.dirname,self.filename = os.path.split(
1903                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
1904                )
1905        else:
1906            raise Exception('This should not happen!')
1907       
1908    def loadParmDict(self):
1909        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
1910        refined values to those from the last cycle and set the uncertainties for the
1911        refined parameters in another dict (self.sigDict).
1912
1913        Expands the parm & sig dicts to include values derived from constraints.
1914        '''
1915        self.parmDict = {}
1916        self.sigDict = {}
1917        rigidbodyDict = {}
1918        covDict = {}
1919        consDict = {}
1920        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1921        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
1922        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
1923        while item:
1924            name = self.G2frame.PatternTree.GetItemText(item)
1925            if name == 'Rigid bodies':
1926                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
1927            elif name == 'Covariance':
1928                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
1929            elif name == 'Constraints':
1930                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
1931            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
1932        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1933        self.parmDict.update(rbDict)
1934        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1935        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,maxSSwave =  G2stIO.GetPhaseData(
1936            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
1937        self.parmDict.update(phaseDict)
1938        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
1939            Phases,Histograms,Print=False,resetRefList=False)
1940        self.parmDict.update(hapDict)
1941        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
1942        self.parmDict.update(histDict)
1943        self.parmDict.update(zip(
1944            covDict.get('varyList',[]),
1945            covDict.get('variables',[])))
1946        self.sigDict = dict(zip(
1947            covDict.get('varyList',[]),
1948            covDict.get('sig',[])))
1949        # expand to include constraints: first compile a list of constraints
1950        constList = []
1951        for item in consDict:
1952            if item.startswith('_'): continue
1953            constList += consDict[item]
1954        # now process the constraints
1955        G2mv.InitVars()
1956        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
1957        varyList = covDict.get('varyListStart')
1958        if varyList is None and len(constDict) == 0:
1959            # no constraints can use varyList
1960            varyList = covDict.get('varyList')
1961        elif varyList is None:
1962            # old GPX file from before pre-constraint varyList is saved
1963            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
1964            raise Exception(' *** Export aborted ***')
1965        else:
1966            varyList = list(varyList)
1967        try:
1968            groups,parmlist = G2mv.GroupConstraints(constDict)
1969            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,self.parmDict)
1970        except:
1971            # this really should not happen
1972            print ' *** ERROR - constraints are internally inconsistent ***'
1973            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
1974            print 'Errors',errmsg
1975            if warnmsg: print 'Warnings',warnmsg
1976            raise Exception(' *** CIF creation aborted ***')
1977        # add the constrained values to the parameter dictionary
1978        G2mv.Dict2Map(self.parmDict,varyList)
1979        # and add their uncertainties into the esd dictionary (sigDict)
1980        if covDict.get('covMatrix') is not None:
1981            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
1982
1983    def loadTree(self):
1984        '''Load the contents of the data tree into a set of dicts
1985        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
1986        & self.xtalDict)
1987       
1988        * The childrenless data tree items are overall parameters/controls for the
1989          entire project and are placed in self.OverallParms
1990        * Phase items are placed in self.Phases
1991        * Data items are placed in self.Histogram. The key for these data items
1992          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
1993        '''
1994        self.OverallParms = {}
1995        self.powderDict = {}
1996        self.xtalDict = {}
1997        self.Phases = {}
1998        self.Histograms = {}
1999        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2000        histType = None       
2001        if self.currentExportType == 'phase':
2002            # if exporting phases load them here
2003            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
2004            if not sub:
2005                print 'no phases found'
2006                return True
2007            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
2008            while item:
2009                phaseName = self.G2frame.PatternTree.GetItemText(item)
2010                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
2011                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
2012            return
2013        elif self.currentExportType == 'single':
2014            histType = 'HKLF'
2015        elif self.currentExportType == 'powder':
2016            histType = 'PWDR'
2017        elif self.currentExportType == 'image':
2018            histType = 'IMG'
2019
2020        if histType: # Loading just one kind of tree entry
2021            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2022            while item:
2023                name = self.G2frame.PatternTree.GetItemText(item)
2024                if name.startswith(histType):
2025                    if self.Histograms.get(name): # there is already an item with this name
2026                        print('Histogram name '+str(name)+' is repeated. Renaming')
2027                        if name[-1] == '9':
2028                            name = name[:-1] + '10'
2029                        elif name[-1] in '012345678':
2030                            name = name[:-1] + str(int(name[-1])+1)
2031                        else:                           
2032                            name += '-1'
2033                    self.Histograms[name] = {}
2034                    # the main info goes into Data, but the 0th
2035                    # element contains refinement results, carry
2036                    # that over too now.
2037                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
2038                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
2039                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2040                    while item2: 
2041                        child = self.G2frame.PatternTree.GetItemText(item2)
2042                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
2043                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
2044                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2045            # index powder and single crystal histograms by number
2046            for hist in self.Histograms:
2047                if hist.startswith("PWDR"): 
2048                    d = self.powderDict
2049                elif hist.startswith("HKLF"): 
2050                    d = self.xtalDict
2051                else:
2052                    return                   
2053                i = self.Histograms[hist].get('hId')
2054                if i is None and not d.keys():
2055                    i = 0
2056                elif i is None or i in d.keys():
2057                    i = max(d.keys())+1
2058                d[i] = hist
2059            return
2060        # else standard load: using all interlinked phases and histograms
2061        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2062        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2063        while item:
2064            name = self.G2frame.PatternTree.GetItemText(item)
2065            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2066            if not item2: 
2067                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
2068            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2069        # index powder and single crystal histograms
2070        for hist in self.Histograms:
2071            i = self.Histograms[hist]['hId']
2072            if hist.startswith("PWDR"): 
2073                self.powderDict[i] = hist
2074            elif hist.startswith("HKLF"): 
2075                self.xtalDict[i] = hist
2076
2077    def dumpTree(self,mode='type'):
2078        '''Print out information on the data tree dicts loaded in loadTree
2079        '''
2080        print '\nOverall'
2081        if mode == 'type':
2082            def Show(arg): return type(arg)
2083        else:
2084            def Show(arg): return arg
2085        for key in self.OverallParms:
2086            print '  ',key,Show(self.OverallParms[key])
2087        print 'Phases'
2088        for key1 in self.Phases:
2089            print '    ',key1,Show(self.Phases[key1])
2090        print 'Histogram'
2091        for key1 in self.Histograms:
2092            print '    ',key1,Show(self.Histograms[key1])
2093            for key2 in self.Histograms[key1]:
2094                print '      ',key2,Show(self.Histograms[key1][key2])
2095
2096    def defaultSaveFile(self):
2097        return os.path.abspath(
2098            os.path.splitext(self.G2frame.GSASprojectfile
2099                             )[0]+self.extension)
2100       
2101    def askSaveFile(self):
2102        '''Ask the user to supply a file name
2103
2104        :returns: a file name (str) or None if Cancel is pressed
2105        '''
2106       
2107        pth = G2G.GetExportPath(self.G2frame)
2108        defnam = os.path.splitext(
2109            os.path.split(self.G2frame.GSASprojectfile)[1]
2110            )[0]+self.extension
2111        dlg = wx.FileDialog(
2112            self.G2frame, 'Input name for file to write', pth, defnam,
2113            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2114            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2115        dlg.CenterOnParent()
2116        try:
2117            if dlg.ShowModal() == wx.ID_OK:
2118                filename = dlg.GetPath()
2119                self.G2frame.LastExportDir = os.path.split(filename)[0]
2120                filename = os.path.splitext(filename)[0]+self.extension # make sure extension is correct
2121            else:
2122                filename = None
2123        finally:
2124            dlg.Destroy()
2125        return filename
2126
2127    def askSaveDirectory(self):
2128        '''Ask the user to supply a directory name. Path name is used as the
2129        starting point for the next export path search.
2130
2131        :returns: a directory name (str) or None if Cancel is pressed
2132        '''
2133        pth = G2G.GetExportPath(self.G2frame)
2134        dlg = wx.DirDialog(
2135            self.G2frame, 'Input directory where file(s) will be written', pth,
2136            wx.DD_DEFAULT_STYLE)
2137        dlg.CenterOnParent()
2138        try:
2139            if dlg.ShowModal() == wx.ID_OK:
2140                filename = dlg.GetPath()
2141                self.G2frame.LastExportDir = filename
2142            else:
2143                filename = None
2144        finally:
2145            dlg.Destroy()
2146        return filename
2147
2148    # Tools for file writing.
2149    def OpenFile(self,fil=None,mode='w'):
2150        '''Open the output file
2151
2152        :param str fil: The name of the file to open. If None (default)
2153          the name defaults to self.dirname + self.filename.
2154          If an extension is supplied, it is not overridded,
2155          but if not, the default extension is used.
2156        :returns: the file object opened by the routine which is also
2157          saved as self.fp
2158        '''
2159        if not fil:
2160            if not os.path.splitext(self.filename)[1]:
2161                self.filename += self.extension
2162            fil = os.path.join(self.dirname,self.filename)
2163        self.fullpath = os.path.abspath(fil)
2164        self.fp = open(self.fullpath,mode)
2165        return self.fp
2166
2167    def Write(self,line):
2168        '''write a line of output, attaching a line-end character
2169
2170        :param str line: the text to be written.
2171        '''
2172        self.fp.write(line+'\n')
2173    def CloseFile(self,fp=None):
2174        '''Close a file opened in OpenFile
2175
2176        :param file fp: the file object to be closed. If None (default)
2177          file object self.fp is closed.
2178        '''
2179        if fp is None:
2180            fp = self.fp
2181            self.fp = None
2182        fp.close()
2183    # Tools to pull information out of the data arrays
2184    def GetCell(self,phasenam):
2185        """Gets the unit cell parameters and their s.u.'s for a selected phase
2186
2187        :param str phasenam: the name for the selected phase
2188        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2189          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2190          cell values and `cellSig` has their uncertainties.
2191        """
2192        phasedict = self.Phases[phasenam] # pointer to current phase info
2193        try:
2194            pfx = str(phasedict['pId'])+'::'
2195            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2196            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
2197                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2198            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2199            return cellList,cellSig
2200        except KeyError:
2201            cell = phasedict['General']['Cell'][1:]
2202            return cell,7*[0]
2203   
2204    def GetAtoms(self,phasenam):
2205        """Gets the atoms associated with a phase. Can be used with standard
2206        or macromolecular phases
2207
2208        :param str phasenam: the name for the selected phase
2209        :returns: a list of items for eac atom where each item is a list containing:
2210          label, typ, mult, xyz, and td, where
2211
2212          * label and typ are the atom label and the scattering factor type (str)
2213          * mult is the site multiplicity (int)
2214          * xyz is contains a list with four pairs of numbers:
2215            x, y, z and fractional occupancy and
2216            their standard uncertainty (or a negative value)
2217          * td is contains a list with either one or six pairs of numbers:
2218            if one number it is U\ :sub:`iso` and with six numbers it is
2219            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2220            paired with their standard uncertainty (or a negative value)
2221        """
2222        phasedict = self.Phases[phasenam] # pointer to current phase info           
2223        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2224        cfrac = cx+3
2225        fpfx = str(phasedict['pId'])+'::Afrac:'       
2226        atomslist = []
2227        for i,at in enumerate(phasedict['Atoms']):
2228            if phasedict['General']['Type'] == 'macromolecular':
2229                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2230            else:
2231                label = at[ct-1]
2232            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2233            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2234            mult = at[cs+1]
2235            typ = at[ct]
2236            xyz = []
2237            for j,v in enumerate(('x','y','z')):
2238                val = at[cx+j]
2239                pfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2240                sig = self.sigDict.get(pfx,-0.000009)
2241                xyz.append((val,sig))
2242            xyz.append((fval,fsig))
2243            td = []
2244            if at[cia] == 'I':
2245                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2246                val = self.parmDict.get(pfx,at[cia+1])
2247                sig = self.sigDict.get(pfx,-0.0009)
2248                td.append((val,sig))
2249            else:
2250                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2251                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2252                    val = self.parmDict.get(pfx,at[cia+2+i])
2253                    sig = self.sigDict.get(pfx,-0.0009)
2254                    td.append((val,sig))
2255            atomslist.append((label,typ,mult,xyz,td))
2256        return atomslist
2257######################################################################
2258def ExportPowderList(G2frame):
2259    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
2260   
2261    :param wx.Frame G2frame: the GSAS-II main data tree window
2262    '''
2263    extList = []
2264    for obj in G2frame.exporterlist:
2265        if 'powder' in obj.exporttype:
2266            try:
2267                obj.Writer
2268                extList.append(obj.extension)
2269            except AttributeError:
2270                pass
2271    return extList
2272
2273def ExportPowder(G2frame,TreeName,fileroot,extension):
2274    '''Writes a single powder histogram using the Export routines
2275
2276    :param wx.Frame G2frame: the GSAS-II main data tree window
2277    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
2278    :param str fileroot: name for file to be written, extension ignored
2279    :param str extension: extension for file to be written (start with '.'). Must
2280      match a powder export routine that has a Writer object.
2281    '''
2282    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2283    for obj in G2frame.exporterlist:
2284        if obj.extension == extension and 'powder' in obj.exporttype:
2285            obj.currentExportType = 'powder'
2286            obj.InitExport(None)
2287            obj.loadTree() # load all histograms in tree into dicts
2288            if TreeName not in obj.Histograms:
2289                raise Exception('Histogram not found: '+str(TreeName))
2290            try:
2291                obj.Writer
2292            except AttributeError:
2293                continue
2294            try:
2295                obj.Writer(TreeName,filename)
2296                return
2297            except Exception,err:
2298                print('Export Routine for '+extension+' failed.')
2299                print err
2300    else:
2301        print('No Export routine supports extension '+extension)
2302       
2303def ReadDIFFaX(DIFFaXfile):
2304    print 'read ',DIFFaXfile
2305    Layer = {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],'Width':[[10.,10.],[False,False]],
2306        'Layers':[],'Stacking':[],'Transitions':[],'Toler':0.01,'AtInfo':{}}
2307    df = open(DIFFaXfile,'r')
2308    lines = df.readlines()
2309    df.close()
2310    struct = False
2311    Struct = []
2312    stack = False
2313    Stack = []
2314    trans = False
2315    Trans = []
2316    instr = False
2317    for diff in lines:
2318        diff = diff[:-1].lower()
2319        if '!'  in diff:
2320            continue
2321        while '}' in diff: #strip comments
2322            iB = diff.index('{')
2323            iF = diff.index('}')+1
2324            if iB:
2325                diff = diff[:iB]
2326            else:
2327                diff = diff[iF:]
2328        if not diff:
2329            continue
2330        if diff.strip() == 'instrumental':
2331            instr = True
2332            continue
2333        if diff.strip() == 'structural':
2334            instr = False
2335            struct = True
2336            continue
2337        elif diff.strip() == 'stacking':
2338            struct = False
2339            stack = True
2340            continue
2341        elif diff.strip() == 'transitions':
2342            stack = False
2343            trans = True
2344            continue
2345        diff = diff.strip()
2346        if struct:
2347            if diff:
2348                Struct.append(diff)
2349        elif stack:
2350            if diff:
2351                Stack.append(diff)
2352        elif trans:
2353            if diff:
2354                Trans.append(diff)
2355   
2356#STRUCTURE records
2357    laueRec = Struct[1].split()
2358    Layer['Laue'] = laueRec[0]
2359    if Layer['Laue'] == 'unknown' and len(laueRec) > 1:
2360        Layer['Toler'] = float(laueRec[1])    #tolerance for 'unknown'?
2361    if Layer['Laue'] == '2/m(1)': Layer['Laue'] = '2/m(c)'
2362    if Layer['Laue'] == '2/m(2)': Layer['Laue'] = '2/m(ab)'
2363    cell = Struct[0].split()
2364    Layer['Cell'] = [False,float(cell[0]),float(cell[1]),float(cell[2]),90.,90.,float(cell[3]),1.0]
2365    nLayers = int(Struct[2])
2366    N = 3
2367    if 'layer' not in Struct[3]:
2368        N = 4
2369        if Struct[3] != 'infinite':
2370            width = Struct[3].split()
2371            Layer['Width'][0] = [float(width[0]),float(width[1])]
2372    for nL in range(nLayers):
2373        if '=' in Struct[N]:
2374            name = Struct[N].split('=')
2375            sameas = int(name[1])-1
2376            Layer['Layers'].append({'Name':name[0],'SameAs':Layer['Layers'][sameas]['Name'],'Symm':'None','Atoms':[]})
2377            N += 1
2378            continue
2379        Symm = 'None'
2380        if 'centro' in Struct[N+1]: Symm = '-1'
2381        Layer['Layers'].append({'Name':Struct[N],'SameAs':'','Symm':Symm,'Atoms':[]})
2382        N += 2
2383        iatm = 0
2384        while 'layer' not in Struct[N]:
2385            atom = Struct[N][4:].split()
2386            atomType = G2el.FixValence(Struct[N][:4].replace(' ','').strip().capitalize())
2387            if atomType not in Layer['AtInfo']:
2388                Layer['AtInfo'][atomType] = G2el.GetAtomInfo(atomType)
2389            atomName = '%s(%s)'%(atomType,atom[0])
2390            newVals = []
2391            for val in atom[1:6]:
2392                if '/' in val:
2393                    newVals.append(eval(val+'.'))
2394                else:
2395                    newVals.append(float(val))               
2396            atomRec = [atomName,atomType,newVals[0],newVals[1],newVals[2],newVals[4],newVals[3]/78.9568]
2397            Layer['Layers'][-1]['Atoms'].append(atomRec)
2398            N += 1
2399            if N > len(Struct)-1:
2400                break
2401#TRANSITIONS records
2402    transArray = []
2403    N = 0
2404    for i in range(nLayers):
2405        transArray.append([])
2406        for j in range(nLayers):
2407            vals = Trans[N].split()
2408            newVals = []
2409            for val in vals[:4]:
2410                if '/' in val:
2411                    newVals.append(eval(val+'.'))
2412                else:
2413                    newVals.append(float(val))
2414            transArray[-1].append(newVals+['',False])
2415            N += 1
2416    Layer['Transitions'] = transArray
2417#STACKING records
2418    Layer['Stacking'] = [Stack[0],'']
2419    if Stack[0] == 'recursive':
2420        Layer['Stacking'][1] = Stack[1]
2421    elif Stack[0] == 'explicit':
2422        if Stack[1] == 'random':
2423            Layer['Stacking'][1] = Stack[1]
2424        else:
2425            Layer['Stacking'][1] = 'list'
2426            Layer['Stacking'].append('')
2427            for stack in Stack[2:]:
2428                Layer['Stacking'][2] += ' '+stack
2429    return Layer
2430
2431def ReadCIF(URLorFile):
2432    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2433    (from James Hester).
2434    The open routine gets confused with DOS names that begin with a letter and colon
2435    "C:\dir\" so this routine will try to open the passed name as a file and if that
2436    fails, try it as a URL
2437
2438    :param str URLorFile: string containing a URL or a file name. Code will try first
2439      to open it as a file and then as a URL.
2440
2441    :returns: a PyCifRW CIF object.
2442    '''
2443    import CifFile as cif # PyCifRW from James Hester
2444
2445    # alternate approach:
2446    #import urllib
2447    #ciffile = 'file:'+urllib.pathname2url(filename)
2448   
2449    try:
2450        fp = open(URLorFile,'r')
2451        cf = cif.ReadCif(fp)
2452        fp.close()
2453        return cf
2454    except IOError:
2455        return cif.ReadCif(URLorFile)
2456
2457if __name__ == '__main__':
2458    import GSASII
2459    application = GSASII.GSASIImain(0)
2460    G2frame = application.main
2461    #app = wx.PySimpleApp()
2462    #G2frame = wx.Frame(None) # create a frame
2463    #frm.Show(True)
2464    #filename = '/tmp/notzip.zip'
2465    #filename = '/tmp/all.zip'
2466    #filename = '/tmp/11bmb_7652.zip'
2467   
2468    #selection=None, confirmoverwrite=True, parent=None
2469    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2470    #print ExtractFileFromZip(filename,multipleselect=True)
2471    #                         #confirmread=False, confirmoverwrite=False)
2472
2473    # choicelist=[ ('a','b','c'),
2474    #              ('test1','test2'),('no choice',)]
2475    # titles = [ 'a, b or c', 'tests', 'No option here']
2476    # dlg = MultipleChoicesDialog(
2477    #     choicelist,titles,
2478    #     parent=frm)
2479    # if dlg.ShowModal() == wx.ID_OK:
2480    #     print 'Got OK'
2481    imagefile = '/tmp/NDC5_00237_3.ge3'
2482    Comments, Data, Npix, Image = GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None)
2483
2484    print("\n\nResults loaded to Comments, Data, Npix and Image\n\n")
2485
2486    GSASIIpath.IPyBreak_base()
Note: See TracBrowser for help on using the repository browser.