source: trunk/GSASIIIO.py @ 2555

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

fix problems with missing image files; if user doesn't find them no plot is tried.

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