source: trunk/GSASIIIO.py @ 2459

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

fix import of edf SignedLong? image

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