source: trunk/GSASIIIO.py @ 1355

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

initial 10.9/Canopy fixes

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