source: trunk/GSASIIIO.py @ 2068

Last change on this file since 2068 was 2068, checked in by toby, 8 years ago

cleanup image reader documetation

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