source: trunk/GSASIIIO.py @ 2152

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

all PWDR exporters will make file name from histogram name
allow read of multibank data
alert user to duplicate histograms (by name)
rename data will not change Bank or Azm part of histogram name
fix L&R plotting commands for TOF data

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