source: trunk/GSASIIIO.py @ 2087

Last change on this file since 2087 was 2087, checked in by toby, 6 years ago

change error handling if h5py not installed; add new condition variable to importers (self.UseReader?); changes to docs to reflect this and consolidate description of package requirements

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