source: trunk/GSASIIIO.py @ 2093

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

autoint: show IMGs to be integrated; replace PWDR when auto-integrated

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