source: trunk/GSASIIIO.py @ 2109

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

Andrey's enhancement: keep track of last GPX, import & export directories; optionally save the 1st two

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