source: trunk/GSASIIIO.py @ 2065

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

Allow reading of multiple images from single file

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