source: trunk/GSASIIIO.py @ 2460

Last change on this file since 2460 was 2460, checked in by vondreele, 6 years ago

further fixes to edf importer

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