source: trunk/GSASIIIO.py @ 2532

Last change on this file since 2532 was 2532, checked in by vondreele, 5 years ago

clean up display of version info. etc. on console.

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