source: trunk/GSASIIIO.py @ 1275

Last change on this file since 1275 was 1275, checked in by vondreele, 8 years ago

fixed bug in background peak refinement & multiple histograms
forced name change with duplicate image integration names;
name now has '(n)' n=1,2,3,... appended to duplicates.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 101.4 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2014-04-11 19:03:10 +0000 (Fri, 11 Apr 2014) $
4# $Author: vondreele $
5# $Revision: 1275 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 1275 2014-04-11 19:03:10Z vondreele $
8########### SVN repository information ###################
9'''
10*GSASIIIO: Misc I/O routines*
11=============================
12
13Module with miscellaneous routines for input and output. Many
14are GUI routines to interact with user.
15
16Includes support for image reading.
17
18Also includes base classes for data import routines.
19
20'''
21"""GSASIIIO: functions for IO of data
22   Copyright: 2008, Robert B. Von Dreele (Argonne National Laboratory)
23"""
24import wx
25import math
26import numpy as np
27import cPickle
28import sys
29import re
30import random as ran
31import GSASIIpath
32GSASIIpath.SetVersionNumber("$Revision: 1275 $")
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    file = open(G2frame.GSASprojectfile,'rb')
868    print 'load from file: ',G2frame.GSASprojectfile
869    G2frame.SetTitle("GSAS-II data tree: "+
870                     os.path.split(G2frame.GSASprojectfile)[1])
871    wx.BeginBusyCursor()
872    try:
873        while True:
874            try:
875                data = cPickle.load(file)
876            except EOFError:
877                break
878            datum = data[0]
879           
880            Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=datum[0])
881            if 'PWDR' in datum[0]:               
882                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
883                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
884                G2frame.PatternTree.SetItemPyData(Id,datum[1][:3])  #temp. trim off junk (patch?)
885            elif datum[0].startswith('HKLF'): 
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])
889            else:
890                G2frame.PatternTree.SetItemPyData(Id,datum[1])
891            for datus in data[1:]:
892                sub = G2frame.PatternTree.AppendItem(Id,datus[0])
893#patch
894                if datus[0] == 'Instrument Parameters' and len(datus[1]) == 1:
895                    if 'PWDR' in datum[0]:
896                        datus[1] = [dict(zip(datus[1][3],zip(datus[1][0],datus[1][1],datus[1][2]))),{}]
897                    else:
898                        datus[1] = [dict(zip(datus[1][2],zip(datus[1][0],datus[1][1]))),{}]
899                    for item in datus[1][0]:               #zip makes tuples - now make lists!
900                        datus[1][0][item] = list(datus[1][0][item])
901#end patch
902                G2frame.PatternTree.SetItemPyData(sub,datus[1])
903            if 'IMG' in datum[0]:                   #retrieve image default flag & data if set
904                Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
905                if Data['setDefault']:
906                    G2frame.imageDefault = Data               
907        file.close()
908        print('project load successful')
909        G2frame.NewPlot = True
910    except:
911        msg = wx.MessageDialog(G2frame,message="Error reading file "+
912            str(G2frame.GSASprojectfile)+". This is not a GSAS-II .gpx file",
913            caption="Load Error",style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
914        msg.ShowModal()
915    finally:
916        wx.EndBusyCursor()
917   
918def ProjFileSave(G2frame):
919    'Save a GSAS-II project file'
920    if not G2frame.PatternTree.IsEmpty():
921        file = open(G2frame.GSASprojectfile,'wb')
922        print 'save to file: ',G2frame.GSASprojectfile
923        wx.BeginBusyCursor()
924        try:
925            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
926            while item:
927                data = []
928                name = G2frame.PatternTree.GetItemText(item)
929                data.append([name,G2frame.PatternTree.GetItemPyData(item)])
930                item2, cookie2 = G2frame.PatternTree.GetFirstChild(item)
931                while item2:
932                    name = G2frame.PatternTree.GetItemText(item2)
933                    data.append([name,G2frame.PatternTree.GetItemPyData(item2)])
934                    item2, cookie2 = G2frame.PatternTree.GetNextChild(item, cookie2)                           
935                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)                           
936                cPickle.dump(data,file,1)
937            file.close()
938        finally:
939            wx.EndBusyCursor()
940        print('project save successful')
941
942def SaveIntegration(G2frame,PickId,data):
943    'Save image integration results as powder pattern(s)'
944    azms = G2frame.Integrate[1]
945    X = G2frame.Integrate[2][:-1]
946    N = len(X)
947    Id = G2frame.PatternTree.GetItemParent(PickId)
948    name = G2frame.PatternTree.GetItemText(Id)
949    name = name.replace('IMG ',data['type']+' ')
950    Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Comments'))
951    if 'PWDR' in name:
952        names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
953        codes = [0 for i in range(11)]
954    elif 'SASD' in name:
955        names = ['Type','Lam','Zero','Azimuth'] 
956        codes = [0 for i in range(4)]
957        X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
958    Xminmax = [X[0],X[-1]]
959    LRazm = data['LRazimuth']
960    Azms = []
961    if data['fullIntegrate'] and data['outAzimuths'] == 1:
962        Azms = [45.0,]                              #a poor man's average?
963    else:
964        for i,azm in enumerate(azms[:-1]):
965            Azms.append((azms[i+1]+azm)/2.)
966    for i,azm in enumerate(azms[:-1]):
967        name += " Azm= %.2f"%(Azms[i])
968        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
969        nOcc = 0
970        while item:
971            Name = G2frame.PatternTree.GetItemText(item)
972            if name in Name:
973                nOcc += 1
974            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
975        if nOcc:
976            name += '(%d)'%(nOcc)
977        Sample = G2pdG.SetDefaultSample()
978        Sample['Gonio. radius'] = data['distance']
979        Sample['Omega'] = data['GonioAngles'][0]
980        Sample['Chi'] = data['GonioAngles'][1]
981        Sample['Phi'] = data['GonioAngles'][2]
982        if 'PWDR' in name:
983            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!
984        elif 'SASD' in name:
985            Sample['Trans'] = data['SampleAbs'][0]
986            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
987        Y = G2frame.Integrate[0][i]
988        W = 1./Y                    #probably not true
989        Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=name)
990        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
991        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
992        if 'PWDR' in name:
993            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
994                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
995        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
996        for item in inst[0]:
997            inst[0][item] = list(inst[0][item])
998        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
999        if 'PWDR' in name:
1000            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1001            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Peak List'),[])
1002            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Index Peak List'),[])
1003            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Unit Cells List'),[])
1004            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Reflection Lists'),{})
1005        elif 'SASD' in name:             
1006            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1007            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1008            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1009        valuesdict = {
1010            'wtFactor':1.0,
1011            'Dummy':False,
1012            'ranId':ran.randint(0,sys.maxint),
1013            }
1014        G2frame.PatternTree.SetItemPyData(
1015            Id,[valuesdict,
1016                [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
1017    G2frame.PatternTree.SelectItem(Id)
1018    G2frame.PatternTree.Expand(Id)
1019    G2frame.PatternId = Id
1020           
1021# def powderFxyeSave(G2frame,exports,powderfile):
1022#     'Save a powder histogram as a GSAS FXYE file'
1023#     head,tail = ospath.split(powderfile)
1024#     name,ext = tail.split('.')
1025#     for i,export in enumerate(exports):
1026#         filename = ospath.join(head,name+'-%03d.'%(i)+ext)
1027#         prmname = filename.strip(ext)+'prm'
1028#         prm = open(prmname,'w')      #old style GSAS parm file
1029#         PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1030#         Inst = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame, \
1031#             PickId, 'Instrument Parameters'))[0]
1032#         prm.write( '            123456789012345678901234567890123456789012345678901234567890        '+'\n')
1033#         prm.write( 'INS   BANK      1                                                               '+'\n')
1034#         prm.write(('INS   HTYPE   %sR                                                              '+'\n')%(Inst['Type'][0]))
1035#         if 'Lam1' in Inst:              #Ka1 & Ka2
1036#             prm.write(('INS  1 ICONS%10.7f%10.7f    0.0000               0.990    0     0.500   '+'\n')%(Inst['Lam1'][0],Inst['Lam2'][0]))
1037#         elif 'Lam' in Inst:             #single wavelength
1038#             prm.write(('INS  1 ICONS%10.7f%10.7f    0.0000               0.990    0     0.500   '+'\n')%(Inst['Lam'][1],0.0))
1039#         prm.write( 'INS  1 IRAD     0                                                               '+'\n')
1040#         prm.write( 'INS  1I HEAD                                                                    '+'\n')
1041#         prm.write( 'INS  1I ITYP    0    0.0000  180.0000         1                                 '+'\n')
1042#         prm.write(('INS  1DETAZM%10.3f                                                          '+'\n')%(Inst['Azimuth'][0]))
1043#         prm.write( 'INS  1PRCF1     3    8   0.00100                                                '+'\n')
1044#         prm.write(('INS  1PRCF11     %15.6g%15.6g%15.6g%15.6g   '+'\n')%(Inst['U'][1],Inst['V'][1],Inst['W'][1],0.0))
1045#         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.))
1046#         prm.close()
1047#         file = open(filename,'w')
1048#         print 'save powder pattern to file: ',filename
1049#         x,y,w,yc,yb,yd = G2frame.PatternTree.GetItemPyData(PickId)[1]
1050#         file.write(powderfile+'\n')
1051#         file.write('Instrument parameter file:'+ospath.split(prmname)[1]+'\n')
1052#         file.write('BANK 1 %d %d CONS %.2f %.2f 0 0 FXYE\n'%(len(x),len(x),\
1053#             100.*x[0],100.*(x[1]-x[0])))
1054#         s = list(np.sqrt(1./np.array(w)))       
1055#         XYW = zip(x,y,s)
1056#         for X,Y,S in XYW:
1057#             file.write("%15.6g %15.6g %15.6g\n" % (100.*X,Y,max(S,1.0)))
1058#         file.close()
1059#         print 'powder pattern file '+filename+' written'
1060       
1061# def powderXyeSave(G2frame,exports,powderfile):
1062#     'Save a powder histogram as a Topas XYE file'
1063#     head,tail = ospath.split(powderfile)
1064#     name,ext = tail.split('.')
1065#     for i,export in enumerate(exports):
1066#         filename = ospath.join(head,name+'-%03d.'%(i)+ext)
1067#         PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1068#         file = open(filename,'w')
1069#         file.write('#%s\n'%(export))
1070#         print 'save powder pattern to file: ',filename
1071#         x,y,w,yc,yb,yd = G2frame.PatternTree.GetItemPyData(PickId)[1]
1072#         s = list(np.sqrt(1./np.array(w)))       
1073#         XYW = zip(x,y,s)
1074#         for X,Y,W in XYW:
1075#             file.write("%15.6g %15.6g %15.6g\n" % (X,Y,W))
1076#         file.close()
1077#         print 'powder pattern file '+filename+' written'
1078       
1079def PDFSave(G2frame,exports):
1080    'Save a PDF G(r) and S(Q) in column formats'
1081    for export in exports:
1082        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1083        SQname = 'S(Q)'+export[4:]
1084        GRname = 'G(R)'+export[4:]
1085        sqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.sq')
1086        grfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.gr')
1087        sqId = G2gd.GetPatternTreeItemId(G2frame, PickId, SQname)
1088        grId = G2gd.GetPatternTreeItemId(G2frame, PickId, GRname)
1089        sqdata = np.array(G2frame.PatternTree.GetItemPyData(sqId)[1][:2]).T
1090        grdata = np.array(G2frame.PatternTree.GetItemPyData(grId)[1][:2]).T
1091        sqfile = open(sqfilename,'w')
1092        grfile = open(grfilename,'w')
1093        sqfile.write('#T S(Q) %s\n'%(export))
1094        grfile.write('#T G(R) %s\n'%(export))
1095        sqfile.write('#L Q     S(Q)\n')
1096        grfile.write('#L R     G(R)\n')
1097        for q,sq in sqdata:
1098            sqfile.write("%15.6g %15.6g\n" % (q,sq))
1099        sqfile.close()
1100        for r,gr in grdata:
1101            grfile.write("%15.6g %15.6g\n" % (r,gr))
1102        grfile.close()
1103   
1104def PeakListSave(G2frame,file,peaks):
1105    'Save powder peaks to a data file'
1106    print 'save peak list to file: ',G2frame.peaklistfile
1107    if not peaks:
1108        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1109        try:
1110            result = dlg.ShowModal()
1111        finally:
1112            dlg.Destroy()
1113        return
1114    for peak in peaks:
1115        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
1116            (peak[0],peak[2],peak[4],peak[6]))
1117    print 'peak list saved'
1118             
1119def IndexPeakListSave(G2frame,peaks):
1120    'Save powder peaks from the indexing list'
1121    file = open(G2frame.peaklistfile,'wa')
1122    print 'save index peak list to file: ',G2frame.peaklistfile
1123    wx.BeginBusyCursor()
1124    try:
1125        if not peaks:
1126            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1127            try:
1128                result = dlg.ShowModal()
1129            finally:
1130                dlg.Destroy()
1131            return
1132        for peak in peaks:
1133            file.write("%12.6f\n" % (peak[7]))
1134        file.close()
1135    finally:
1136        wx.EndBusyCursor()
1137    print 'index peak list saved'
1138   
1139def SetNewPhase(Name='New Phase',SGData=None,cell=None):
1140    '''Create a new phase dict with default values for various parameters
1141
1142    :param str Name: Name for new Phase
1143
1144    :param dict SGData: space group data from :func:`GSASIIspc:SpcGroup`;
1145      defaults to data for P 1
1146
1147    :param list cell: unit cell parameter list; defaults to
1148      [1.0,1.0,1.0,90.,90,90.,1.]
1149
1150    '''
1151    if SGData is None: SGData = G2spc.SpcGroup('P 1')[1]
1152    if cell is None: cell=[1.0,1.0,1.0,90.,90,90.,1.]
1153    phaseData = {
1154        'ranId':ran.randint(0,sys.maxint),
1155        'General':{
1156            'Name':Name,
1157            'Type':'nuclear',
1158            'AtomPtrs':[3,1,7,9],
1159            'SGData':SGData,
1160            'Cell':[False,]+cell,
1161            'Pawley dmin':1.0,
1162            'Data plot type':'None',
1163            'SH Texture':{
1164                'Order':0,
1165                'Model':'cylindrical',
1166                'Sample omega':[False,0.0],
1167                'Sample chi':[False,0.0],
1168                'Sample phi':[False,0.0],
1169                'SH Coeff':[False,{}],
1170                'SHShow':False,
1171                'PFhkl':[0,0,1],
1172                'PFxyz':[0,0,1],
1173                'PlotType':'Pole figure'}},
1174        'Atoms':[],
1175        'Drawing':{},
1176        'Histograms':{},
1177        'Pawley ref':[],
1178        'RBModels':{},
1179        }
1180    return phaseData
1181       
1182class MultipleChoicesDialog(wx.Dialog):
1183    '''A dialog that offers a series of choices, each with a
1184    title and a wx.Choice widget. Intended to be used Modally.
1185    typical input:
1186
1187        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1188        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1189       
1190    selections are placed in self.chosen when OK is pressed
1191    '''
1192    def __init__(self,choicelist,headinglist,
1193                 head='Select options',
1194                 title='Please select from options below',
1195                 parent=None):
1196        self.chosen = []
1197        wx.Dialog.__init__(
1198            self,parent,wx.ID_ANY,head, 
1199            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1200        panel = wx.Panel(self)
1201        mainSizer = wx.BoxSizer(wx.VERTICAL)
1202        mainSizer.Add((10,10),1)
1203        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1204        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1205        self.ChItems = []
1206        for choice,lbl in zip(choicelist,headinglist):
1207            mainSizer.Add((10,10),1)
1208            self.chosen.append(0)
1209            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1210            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1211            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1212            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1213
1214        OkBtn = wx.Button(panel,-1,"Ok")
1215        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1216        cancelBtn = wx.Button(panel,-1,"Cancel")
1217        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1218        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1219        btnSizer.Add((20,20),1)
1220        btnSizer.Add(OkBtn)
1221        btnSizer.Add((20,20),1)
1222        btnSizer.Add(cancelBtn)
1223        btnSizer.Add((20,20),1)
1224        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1225        panel.SetSizer(mainSizer)
1226        panel.Fit()
1227        self.Fit()
1228       
1229    def OnOk(self,event):
1230        parent = self.GetParent()
1231        if parent is not None: parent.Raise()
1232        # save the results from the choice widgets
1233        self.chosen = []
1234        for w in self.ChItems:
1235            self.chosen.append(w.GetSelection())
1236        self.EndModal(wx.ID_OK)             
1237           
1238    def OnCancel(self,event):
1239        parent = self.GetParent()
1240        if parent is not None: parent.Raise()
1241        self.chosen = []
1242        self.EndModal(wx.ID_CANCEL)             
1243           
1244def ExtractFileFromZip(filename, selection=None, confirmread=True,
1245                       confirmoverwrite=True, parent=None,
1246                       multipleselect=False):
1247    '''If the filename is a zip file, extract a file from that
1248    archive.
1249
1250    :param list Selection: used to predefine the name of the file
1251      to be extracted. Filename case and zip directory name are
1252      ignored in selection; the first matching file is used.
1253
1254    :param bool confirmread: if True asks the user to confirm before expanding
1255      the only file in a zip
1256
1257    :param bool confirmoverwrite: if True asks the user to confirm
1258      before overwriting if the extracted file already exists
1259
1260    :param bool multipleselect: if True allows more than one zip
1261      file to be extracted, a list of file(s) is returned.
1262      If only one file is present, do not ask which one, otherwise
1263      offer a list of choices (unless selection is used).
1264   
1265    :returns: the name of the file that has been created or a
1266      list of files (see multipleselect)
1267
1268    If the file is not a zipfile, return the name of the input file.
1269    If the zipfile is empty or no file has been selected, return None
1270    '''
1271    import zipfile # do this now, since we can save startup time by doing this only on need
1272    import shutil
1273    zloc = os.path.split(filename)[0]
1274    if not zipfile.is_zipfile(filename):
1275        #print("not zip")
1276        return filename
1277
1278    z = zipfile.ZipFile(filename,'r')
1279    zinfo = z.infolist()
1280
1281    if len(zinfo) == 0:
1282        #print('Zip has no files!')
1283        zlist = [-1]
1284    if selection:
1285        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1286        if selection.lower() in choices:
1287            zlist = [choices.index(selection.lower())]
1288        else:
1289            print('debug: file '+str(selection)+' was not found in '+str(filename))
1290            zlist = [-1]
1291    elif len(zinfo) == 1 and confirmread:
1292        result = wx.ID_NO
1293        dlg = wx.MessageDialog(
1294            parent,
1295            'Is file '+str(zinfo[0].filename)+
1296            ' what you want to extract from '+
1297            str(os.path.split(filename)[1])+'?',
1298            'Confirm file', 
1299            wx.YES_NO | wx.ICON_QUESTION)
1300        try:
1301            result = dlg.ShowModal()
1302        finally:
1303            dlg.Destroy()
1304        if result == wx.ID_NO:
1305            zlist = [-1]
1306        else:
1307            zlist = [0]
1308    elif len(zinfo) == 1:
1309        zlist = [0]
1310    elif multipleselect:
1311        # select one or more from a from list
1312        choices = [i.filename for i in zinfo]
1313        dlg = wx.MultiChoiceDialog(parent,'Select file(s) to extract from zip file'+str(filename),
1314            'Choose file(s)',choices,wx.CHOICEDLG_STYLE,)
1315        if dlg.ShowModal() == wx.ID_OK:
1316            zlist = dlg.GetSelections()
1317        else:
1318            zlist = []
1319        dlg.Destroy()
1320    else:
1321        # select one from a from list
1322        choices = [i.filename for i in zinfo]
1323        dlg = wx.SingleChoiceDialog(parent,
1324            'Select file to extract from zip file'+str(filename),'Choose file',
1325            choices,)
1326        if dlg.ShowModal() == wx.ID_OK:
1327            zlist = [dlg.GetSelection()]
1328        else:
1329            zlist = [-1]
1330        dlg.Destroy()
1331       
1332    outlist = []
1333    for zindex in zlist:
1334        if zindex >= 0:
1335            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1336            if os.path.exists(efil) and confirmoverwrite:
1337                result = wx.ID_NO
1338                dlg = wx.MessageDialog(parent,
1339                    'File '+str(efil)+' already exists. OK to overwrite it?',
1340                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1341                try:
1342                    result = dlg.ShowModal()
1343                finally:
1344                    dlg.Destroy()
1345                if result == wx.ID_NO:
1346                    zindex = -1
1347        if zindex >= 0:
1348            # extract the file to the current directory, regardless of it's original path
1349            #z.extract(zinfo[zindex],zloc)
1350            eloc,efil = os.path.split(zinfo[zindex].filename)
1351            outfile = os.path.join(zloc, efil)
1352            fpin = z.open(zinfo[zindex])
1353            fpout = file(outfile, "wb")
1354            shutil.copyfileobj(fpin, fpout)
1355            fpin.close()
1356            fpout.close()
1357            outlist.append(outfile)
1358    z.close()
1359    if multipleselect and len(outlist) >= 1:
1360        return outlist
1361    elif len(outlist) == 1:
1362        return outlist[0]
1363    else:
1364        return None
1365
1366######################################################################
1367# base classes for reading various types of data files
1368#   not used directly, only by subclassing
1369######################################################################
1370E,SGData = G2spc.SpcGroup('P 1') # data structure for default space group
1371P1SGData = SGData
1372class ImportBaseclass(object):
1373    '''Defines a base class for the reading of input files (diffraction
1374    data, coordinates,...). See :ref:`Writing a Import Routine<Import_routines>`
1375    for an explanation on how to use a subclass of this class.
1376    '''
1377    class ImportException(Exception):
1378        '''Defines an Exception that is used when an import routine hits an expected error,
1379        usually in .Reader.
1380
1381        Good practice is that the Reader should define a value in self.errors that
1382        tells the user some information about what is wrong with their file.         
1383        '''
1384        pass
1385
1386    def __init__(self,
1387                 formatName,
1388                 longFormatName=None,
1389                 extensionlist=[],
1390                 strictExtension=False,
1391                 ):
1392        self.formatName = formatName # short string naming file type
1393        if longFormatName: # longer string naming file type
1394            self.longFormatName = longFormatName
1395        else:
1396            self.longFormatName = formatName
1397        # define extensions that are allowed for the file type
1398        # for windows, remove any extensions that are duplicate, as case is ignored
1399        if sys.platform == 'windows' and extensionlist:
1400            extensionlist = list(set([s.lower() for s in extensionlist]))
1401        self.extensionlist = extensionlist
1402        # If strictExtension is True, the file will not be read, unless
1403        # the extension matches one in the extensionlist
1404        self.strictExtension = strictExtension
1405        self.errors = ''
1406        self.warnings = ''
1407        # used for readers that will use multiple passes to read
1408        # more than one data block
1409        self.repeat = False
1410        self.repeatcount = 0
1411        #print 'created',self.__class__
1412
1413    def ReInitialize(self):
1414        'Reinitialize the Reader to initial settings'
1415        self.errors = ''
1416        self.warnings = ''
1417        self.repeat = False
1418        self.repeatcount = 0
1419
1420    def BlockSelector(self, ChoiceList, ParentFrame=None,
1421                      title='Select a block',
1422                      size=None, header='Block Selector',
1423                      useCancel=True):
1424        ''' Provide a wx dialog to select a block if the file contains more
1425        than one set of data and one must be selected
1426        '''
1427        if useCancel:
1428            dlg = wx.SingleChoiceDialog(
1429                ParentFrame,title, header,ChoiceList)
1430        else:
1431            dlg = wx.SingleChoiceDialog(
1432                ParentFrame,title, header,ChoiceList,
1433                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1434        if size: dlg.SetSize(size)
1435        dlg.CenterOnParent()
1436        if dlg.ShowModal() == wx.ID_OK:
1437            sel = dlg.GetSelection()
1438            return sel
1439        else:
1440            return None
1441        dlg.Destroy()
1442
1443    def MultipleBlockSelector(self, ChoiceList, ParentFrame=None,
1444        title='Select a block',size=None, header='Block Selector'):
1445        '''Provide a wx dialog to select a block of data if the
1446        file contains more than one set of data and one must be
1447        selected.
1448
1449        :returns: a list of the selected blocks
1450        '''
1451        dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1452            wx.CHOICEDLG_STYLE)
1453        dlg.CenterOnParent()
1454        if size: dlg.SetSize(size)
1455        if dlg.ShowModal() == wx.ID_OK:
1456            sel = dlg.GetSelections()
1457        else:
1458            return []
1459        dlg.Destroy()
1460        selected = []
1461        if len(ChoiceList) in sel:
1462            return range(len(ChoiceList))
1463        else:
1464            return sel
1465        return selected
1466
1467    def MultipleChoicesDialog(self, choicelist, headinglist, ParentFrame=None, **kwargs):
1468        '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1469        widget. Typical input:
1470       
1471           * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1472           
1473           * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1474           
1475        optional keyword parameters are: head (window title) and title
1476        returns a list of selected indicies for each choice (or None)
1477        '''
1478        result = None
1479        dlg = MultipleChoicesDialog(choicelist,headinglist,
1480            parent=ParentFrame, **kwargs)         
1481        dlg.CenterOnParent()
1482        if dlg.ShowModal() == wx.ID_OK:
1483            result = dlg.chosen
1484        dlg.Destroy()
1485        return result
1486
1487    def ShowBusy(self):
1488        wx.BeginBusyCursor()
1489        wx.Yield() # make it happen now!
1490
1491    def DoneBusy(self):
1492        wx.EndBusyCursor()
1493        wx.Yield() # make it happen now!
1494       
1495#    def Reader(self, filename, filepointer, ParentFrame=None, **unused):
1496#        '''This method must be supplied in the child class to read the file.
1497#        if the read fails either return False or raise an Exception
1498#        preferably of type ImportException.
1499#        '''
1500#        #start reading
1501#        raise ImportException("Error occurred while...")
1502#        self.errors += "Hint for user on why the error occur
1503#        return False # if an error occurs
1504#        return True # if read OK
1505
1506    def ExtensionValidator(self, filename):
1507        '''This methods checks if the file has the correct extension
1508        Return False if this filename will not be supported by this reader
1509        Return True if the extension matches the list supplied by the reader
1510        Return None if the reader allows un-registered extensions
1511        '''
1512        if filename:
1513            ext = os.path.splitext(filename)[1]
1514            if sys.platform == 'windows': ext = ext.lower()
1515            if ext in self.extensionlist: return True
1516            if self.strictExtension: return False
1517        return None
1518
1519    def ContentsValidator(self, filepointer):
1520        '''This routine will attempt to determine if the file can be read
1521        with the current format.
1522        This will typically be overridden with a method that
1523        takes a quick scan of [some of]
1524        the file contents to do a "sanity" check if the file
1525        appears to match the selected format.
1526        Expected to be called via self.Validator()
1527        '''
1528        #filepointer.seek(0) # rewind the file pointer
1529        return True
1530
1531    def CIFValidator(self, filepointer):
1532        '''A :meth:`ContentsValidator` for use to validate CIF files.
1533        '''
1534        for i,l in enumerate(filepointer):
1535            if i >= 1000: return True
1536            '''Encountered only blank lines or comments in first 1000
1537            lines. This is unlikely, but assume it is CIF anyway, since we are
1538            even less likely to find a file with nothing but hashes and
1539            blank lines'''
1540            line = l.strip()
1541            if len(line) == 0: # ignore blank lines
1542                continue 
1543            elif line.startswith('#'): # ignore comments
1544                continue 
1545            elif line.startswith('data_'): # on the right track, accept this file
1546                return True
1547            else: # found something invalid
1548                self.errors = 'line '+str(i+1)+' contains unexpected data:\n'
1549                self.errors += '  '+str(l)
1550                self.errors += '  Note: a CIF should only have blank lines or comments before'
1551                self.errors += '        a data_ statement begins a block.'
1552                return False 
1553
1554class ImportPhase(ImportBaseclass):
1555    '''Defines a base class for the reading of files with coordinates
1556
1557    Objects constructed that subclass this (in import/G2phase_*.py etc.) will be used
1558    in :meth:`GSASII.GSASII.OnImportPhase`.
1559    See :ref:`Writing a Import Routine<Import_Routines>`
1560    for an explanation on how to use this class.
1561
1562    '''
1563    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1564        strictExtension=False,):
1565        # call parent __init__
1566        ImportBaseclass.__init__(self,formatName,longFormatName,
1567            extensionlist,strictExtension)
1568        self.Phase = None # a phase must be created with G2IO.SetNewPhase in the Reader
1569        self.Constraints = None
1570
1571    def PhaseSelector(self, ChoiceList, ParentFrame=None,
1572        title='Select a phase', size=None,header='Phase Selector'):
1573        ''' Provide a wx dialog to select a phase if the file contains more
1574        than one phase
1575        '''
1576        return self.BlockSelector(ChoiceList,ParentFrame,title,
1577            size,header)
1578
1579class ImportStructFactor(ImportBaseclass):
1580    '''Defines a base class for the reading of files with tables
1581    of structure factors.
1582
1583    Structure factors are read with a call to :meth:`GSASII.GSASII.OnImportSfact`
1584    which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`, which calls
1585    methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1586    :meth:`Reader`.
1587
1588    See :ref:`Writing a Import Routine<Import_Routines>`
1589    for an explanation on how to use import classes in general. The specifics
1590    for reading a structure factor histogram require that
1591    the ``Reader()`` routine in the import
1592    class need to do only a few things: It
1593    should load :attr:`RefDict` item ``'RefList'`` with the reflection list,
1594    and set :attr:`Parameters` with the instrument parameters
1595    (initialized with :meth:`InitParameters` and set with :meth:`UpdateParameters`).
1596    Also, set :attr:`Controls`,
1597    which specifies how the histogram is plotted
1598    (initialized with :meth:`InitControls` and set with :meth:`UpdateControls`).
1599    '''
1600    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1601        strictExtension=False,):
1602        ImportBaseclass.__init__(self,formatName,longFormatName,
1603            extensionlist,strictExtension)
1604
1605        # define contents of Structure Factor entry
1606        self.Parameters = []
1607        'self.Parameters is a list with two dicts for data parameter settings'
1608        self.InitParameters()
1609        self.Controls = {}
1610        'self.Controls is a dict with plotting controls'
1611        self.InitControls() # initialize the above
1612        self.RefDict = {'RefList':[],'FF':[]}
1613        '''self.RefDict is a dict containing the reflection information, as read from the file.
1614        Item 'RefList' contains the reflection information. See the
1615        :ref:`Single Crystal Reflection Data Structure<XtalRefl_table>`
1616        for the contents of each row. Dict element 'FF'
1617        contains the form factor values for each element type; if this entry
1618        is left as initialized (an empty list) it will be initialized as needed later.
1619        '''
1620    def ReInitialize(self):
1621        'Reinitialize the Reader to initial settings'
1622        ImportBaseclass.ReInitialize(self)
1623        self.InitParameters()
1624        self.InitControls()
1625        self.RefDict = {'RefList':[],'FF':[]}
1626
1627       
1628    def InitControls(self):
1629        'initialize the controls structure'
1630        self.Controls = { # dictionary with plotting controls
1631            'Type' : 'Fosq',
1632            'ifFc' : False,    #
1633            'HKLmax' : [None,None,None],
1634            'HKLmin' : [None,None,None],
1635            'FoMax' : None,   # maximum observed structure factor as Fo
1636            'Zone' : '001',
1637            'Layer' : 0,
1638            'Scale' : 1.0,
1639            'log-lin' : 'lin',
1640            }
1641
1642    def InitParameters(self):
1643        'initialize the instrument parameters structure'
1644        Lambda = 0.70926
1645        HistType = 'SXC'
1646        self.Parameters = [{'Type':[HistType,HistType], # create the structure
1647                            'Lam':[Lambda,Lambda]
1648                            }, {}]
1649        'Parameters is a list with two dicts for data parameter settings'
1650
1651    def UpdateParameters(self,Type=None,Wave=None):
1652        'Revise the instrument parameters'
1653        if Type is not None:
1654            self.Parameters[0]['Type'] = [Type,Type]
1655        if Wave is not None:
1656            self.Parameters[0]['Lam'] = [Wave,Wave]
1657           
1658    def UpdateControls(self,Type='Fosq',FcalcPresent=False):
1659        '''Scan through the reflections to update the Controls dictionary
1660        '''
1661        self.Controls['Type'] = Type
1662        self.Controls['ifFc'] = FcalcPresent
1663        HKLmax = [None,None,None]
1664        HKLmin = [None,None,None]
1665        Fo2max = None
1666        for refl in self.RefDict['RefList']:
1667            HKL = refl[:3]
1668            if Fo2max is None:
1669                Fo2max = refl[8]
1670            else:
1671                Fo2max = max(Fo2max,refl[8])
1672            for i,hkl in enumerate(HKL):
1673                if HKLmax[i] is None:
1674                    HKLmax[i] = hkl
1675                    HKLmin[i] = hkl
1676                else:
1677                    HKLmax[i] = max(HKLmax[i],hkl)
1678                    HKLmin[i] = min(HKLmin[i],hkl)
1679        self.Controls['HKLmax'] = HKLmax
1680        self.Controls['HKLmin'] = HKLmin
1681        if Type ==  'Fosq':
1682            self.Controls['FoMax'] = np.sqrt(Fo2max)
1683        elif Type ==  'Fo':
1684            self.Controls['FoMax'] = Fo2max
1685        else:
1686            print "Unsupported Struct Fact type in ImportStructFactor.UpdateControls"
1687            raise Exception,"Unsupported Struct Fact type in ImportStructFactor.UpdateControls"
1688
1689######################################################################
1690class ImportPowderData(ImportBaseclass):
1691    '''Defines a base class for the reading of files with powder data.
1692
1693    Objects constructed that subclass this (in import/G2pwd_*.py etc.) will be used
1694    in :meth:`GSASII.GSASII.OnImportPowder`.
1695    See :ref:`Writing a Import Routine<Import_Routines>`
1696    for an explanation on how to use this class.
1697    '''
1698    # define some default instrument parameter files
1699    # just like GSAS, sigh
1700    defaultIparm_lbl = []
1701    defaultIparms = []
1702    defaultIparm_lbl.append('CuKa lab data')
1703    defaultIparms.append({
1704        'INS   HTYPE ':'PXC ',
1705        'INS  1 ICONS':'  1.540500  1.544300       0.0         0       0.7    0       0.5   ',
1706        'INS  1PRCF1 ':'    3    8      0.01                                                ',
1707        'INS  1PRCF11':'   2.000000E+00  -2.000000E+00   5.000000E+00   0.000000E+00        ',
1708        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.150000E-01   0.150000E-01        ',
1709        })
1710    defaultIparm_lbl.append('0.6A synch')
1711    defaultIparms.append({
1712        'INS   HTYPE ':'PXC ',
1713        'INS  1 ICONS':'  0.600000  0.000000       0.0         0      0.99    0       0.5   ',
1714        'INS  1PRCF1 ':'    3    8      0.01                                                ',
1715        'INS  1PRCF11':'   1.000000E+00  -1.000000E+00   0.300000E+00   0.000000E+00        ',
1716        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.100000E-01   0.100000E-01        ',
1717        })
1718    defaultIparm_lbl.append('1.5A CW neutron data')
1719    defaultIparms.append({
1720        'INS   HTYPE ':'PNC',
1721        'INS  1 ICONS':'   1.54020   0.00000   0.04000         0',
1722        'INS  1PRCF1 ':'    3    8     0.005',
1723        'INS  1PRCF11':'   0.239700E+03  -0.298200E+03   0.180800E+03   0.000000E+00',
1724        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.400000E-01   0.300000E-01',
1725        })
1726    defaultIparm_lbl.append('10m TOF backscattering bank')
1727    defaultIparms.append({
1728        'INS   HTYPE ':'PNT',
1729        'INS  1 ICONS':'   5000.00      0.00      0.00',
1730        'INS  1BNKPAR':'    1.0000   150.000',       
1731        'INS  1PRCF1 ':'    1    8   0.01000',
1732        'INS  1PRCF11':'   0.000000E+00   5.000000E+00   3.000000E-02   1.000000E-03',
1733        'INS  1PRCF12':'   0.000000E+00   4.000000E+01   0.000000E+00   0.000000E+00',       
1734        })
1735    defaultIparm_lbl.append('10m TOF 90deg bank')
1736    defaultIparms.append({
1737        'INS   HTYPE ':'PNT',
1738        'INS  1 ICONS':'   3500.00      0.00      0.00',
1739        'INS  1BNKPAR':'    1.0000    90.000',       
1740        'INS  1PRCF1 ':'    1    8   0.01000',
1741        'INS  1PRCF11':'   0.000000E+00   5.000000E+00   3.000000E-02   4.000000E-03',
1742        'INS  1PRCF12':'   0.000000E+00   8.000000E+01   0.000000E+00   0.000000E+00',       
1743        })
1744    defaultIparm_lbl.append('63m POWGEN 90deg bank')
1745    defaultIparms.append({
1746        'INS   HTYPE ':'PNT',
1747        'INS  1 ICONS':'  22585.80      0.00      0.00',
1748        'INS  1BNKPAR':'    1.0000    90.000',       
1749        'INS  1PRCF1 ':'    1    8   0.01000',
1750        'INS  1PRCF11':'   0.000000E+00   1.000000E+00   3.000000E-02   4.000000E-03',
1751        'INS  1PRCF12':'   0.000000E+00   8.000000E+01   0.000000E+00   0.000000E+00',       
1752        })
1753    def __init__(self,
1754                 formatName,
1755                 longFormatName=None,
1756                 extensionlist=[],
1757                 strictExtension=False,
1758                 ):
1759        ImportBaseclass.__init__(self,formatName,
1760                                            longFormatName,
1761                                            extensionlist,
1762                                            strictExtension)
1763        self.clockWd = None  # used in TOF
1764        self.ReInitialize()
1765       
1766    def ReInitialize(self):
1767        'Reinitialize the Reader to initial settings'
1768        ImportBaseclass.ReInitialize(self)
1769        self.powderentry = ['',None,None] #  (filename,Pos,Bank)
1770        self.powderdata = [] # Powder dataset
1771        '''A powder data set is a list with items [x,y,w,yc,yb,yd]:
1772                np.array(x), # x-axis values
1773                np.array(y), # powder pattern intensities
1774                np.array(w), # 1/sig(intensity)^2 values (weights)
1775                np.array(yc), # calc. intensities (zero)
1776                np.array(yb), # calc. background (zero)
1777                np.array(yd), # obs-calc profiles
1778        '''                           
1779        self.comments = []
1780        self.idstring = ''
1781        self.Sample = G2pdG.SetDefaultSample()
1782        self.GSAS = None     # used in TOF
1783        self.repeat_instparm = True # Should a parm file be
1784        #                             used for multiple histograms?
1785        self.instparm = None # name hint from file of instparm to use
1786        self.instfile = '' # full path name to instrument parameter file
1787        self.instbank = '' # inst parm bank number
1788        self.instmsg = ''  # a label that gets printed to show
1789                           # where instrument parameters are from
1790        self.numbanks = 1
1791        self.instdict = {} # place items here that will be transferred to the instrument parameters
1792######################################################################
1793class ImportSmallAngleData(ImportBaseclass):
1794    '''Defines a base class for the reading of files with small angle data.
1795    See :ref:`Writing a Import Routine<Import_Routines>`
1796    for an explanation on how to use this class.
1797    '''
1798    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1799        strictExtension=False,):
1800           
1801        ImportBaseclass.__init__(self,formatName,longFormatName,extensionlist,
1802            strictExtension)
1803        self.ReInitialize()
1804       
1805    def ReInitialize(self):
1806        'Reinitialize the Reader to initial settings'
1807        ImportBaseclass.ReInitialize(self)
1808        self.smallangleentry = ['',None,None] #  (filename,Pos,Bank)
1809        self.smallangledata = [] # SASD dataset
1810        '''A small angle data set is a list with items [x,y,w,yc,yd]:
1811                np.array(x), # x-axis values
1812                np.array(y), # powder pattern intensities
1813                np.array(w), # 1/sig(intensity)^2 values (weights)
1814                np.array(yc), # calc. intensities (zero)
1815                np.array(yd), # obs-calc profiles
1816        '''                           
1817        self.comments = []
1818        self.idstring = ''
1819        self.Sample = G2pdG.SetDefaultSample()
1820        self.GSAS = None     # used in TOF
1821        self.clockWd = None  # used in TOF
1822        self.numbanks = 1
1823        self.instdict = {} # place items here that will be transferred to the instrument parameters
1824######################################################################
1825class ExportBaseclass(object):
1826    '''Defines a base class for the exporting of GSAS-II results.
1827
1828    This class is subclassed in the various exports/G2export_*.py files. Those files
1829    are imported in :meth:`GSASII.GSASII._init_Exports` which defines the
1830    appropriate menu items for each one and the .Exporter method is called
1831    directly from the menu item.
1832   
1833    '''
1834    def __init__(self,
1835                 G2frame,
1836                 formatName,
1837                 extension,
1838                 longFormatName=None,
1839                 ):
1840        self.G2frame = G2frame
1841        self.formatName = formatName # short string naming file type
1842        self.extension = extension
1843        if longFormatName: # longer string naming file type
1844            self.longFormatName = longFormatName
1845        else:
1846            self.longFormatName = formatName
1847        self.OverallParms = {}
1848        self.Phases = {}
1849        self.Histograms = {}
1850        self.powderDict = {}
1851        self.xtalDict = {}
1852        self.parmDict = {}
1853        self.sigDict = {}
1854        # updated in InitExport:
1855        self.currentExportType = None # type of export that has been requested
1856        # updated in ExportSelect (when used):
1857        self.phasenam = None # a list of selected phases
1858        self.histnam = None # a list of selected histograms
1859        self.filename = None # name of file to be written (single export) or template (multiple files)
1860        self.dirname = '' # name of directory where file(s) will be written
1861        self.fullpath = '' # name of file being written -- full path
1862       
1863        # items that should be defined in a subclass of this class
1864        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1865        # The following types are defined: 'project', "phase", "powder", "single"
1866        self.multiple = False # set as True if the class can export multiple phases or histograms
1867        # self.multiple is ignored for "project" exports
1868
1869    def InitExport(self,event):
1870        '''Determines the type of menu that called the Exporter and
1871        misc initialization.
1872        '''
1873        self.filename = None # name of file to be written (single export)
1874        self.dirname = '' # name of file to be written (multiple export)
1875        if event:
1876            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1877
1878    def MakePWDRfilename(self,hist):
1879        '''Make a filename root (no extension) from a PWDR histogram name
1880
1881        :param str hist: the histogram name in data tree (starts with "PWDR ")
1882        '''
1883        file0 = ''
1884        file1 = hist[5:]
1885        # replace repeated blanks
1886        while file1 != file0:
1887            file0 = file1
1888            file1 = file0.replace('  ',' ').strip()
1889        file0 = file1.replace('Azm= ','A')
1890        # if angle has unneeded decimal places on aziumuth, remove them
1891        if file0[-3:] == '.00': file0 = file0[:-3]
1892        file0 = file0.replace('.','_')
1893        file0 = file0.replace(' ','_')
1894        return file0
1895
1896    def ExportSelect(self,AskFile='ask'):
1897        '''Selects histograms or phases when needed. Sets a default file name when
1898        requested in self.filename; always sets a default directory in self.dirname.
1899
1900        :param bool AskFile: Determines how this routine processes getting a
1901          location to store the current export(s).
1902          * if AskFile is 'ask' (default option), get the name of the file to be written;
1903            self.filename and self.dirname are always set. In the case where
1904            multiple files must be generated, the export routine should do this
1905            based on self.filename as a template.
1906          * if AskFile is 'dir', get the name of the directory to be used;
1907            self.filename is not used, but self.dirname is always set. The export routine
1908            will always generate the file name.
1909          * if AskFile is 'single', get only the name of the directory to be used when
1910            multiple items will be written (as multiple files) are used
1911            *or* a complete file name is requested when a single file
1912            name is selected. self.dirname is always set and self.filename used
1913            only when a single file is selected.
1914          * if AskFile is 'default', creates a name of the file to be used from
1915            the name of the project (.gpx) file. If the project has not been saved,
1916            then the name of file is requested.
1917            self.filename and self.dirname are always set. In the case where
1918            multiple file names must be generated, the export routine should do this
1919            based on self.filename.
1920          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1921            file. If the project has not been saved, then a directory is requested.
1922            self.filename is not used.
1923
1924        :returns: True in case of an error
1925        '''
1926       
1927        numselected = 1
1928        if self.currentExportType == 'phase':
1929            if len(self.Phases) == 0:
1930                self.G2frame.ErrorDialog(
1931                    'Empty project',
1932                    'Project does not contain any phases.')
1933                return True
1934            elif len(self.Phases) == 1:
1935                self.phasenam = self.Phases.keys()
1936            elif self.multiple: 
1937                choices = sorted(self.Phases.keys())
1938                phasenum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1939                if phasenum is None: return True
1940                self.phasenam = [choices[i] for i in phasenum]
1941                if not self.phasenam: return True
1942                numselected = len(self.phasenam)
1943            else:
1944                choices = sorted(self.Phases.keys())
1945                phasenum = G2gd.ItemSelector(choices,self.G2frame)
1946                if phasenum is None: return True
1947                self.phasenam = [choices[phasenum]]
1948                numselected = len(self.phasenam)
1949        elif self.currentExportType == 'single':
1950            if len(self.xtalDict) == 0:
1951                self.G2frame.ErrorDialog(
1952                    'Empty project',
1953                    'Project does not contain any single crystal data.')
1954                return True
1955            elif len(self.xtalDict) == 1:
1956                self.histnam = self.xtalDict.values()
1957            elif self.multiple:
1958                choices = sorted(self.xtalDict.values())
1959                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1960                if not hnum: return True
1961                self.histnam = [choices[i] for i in hnum]
1962                numselected = len(self.histnam)
1963            else:
1964                choices = sorted(self.xtalDict.values())
1965                hnum = G2gd.ItemSelector(choices,self.G2frame)
1966                if hnum is None: return True
1967                self.histnam = [choices[hnum]]
1968                numselected = len(self.histnam)
1969        elif self.currentExportType == 'powder':
1970            if len(self.powderDict) == 0:
1971                self.G2frame.ErrorDialog(
1972                    'Empty project',
1973                    'Project does not contain any powder data.')
1974                return True
1975            elif len(self.powderDict) == 1:
1976                self.histnam = self.powderDict.values()
1977            elif self.multiple:
1978                choices = sorted(self.powderDict.values())
1979                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1980                if not hnum: return True
1981                self.histnam = [choices[i] for i in hnum]
1982                numselected = len(self.histnam)
1983            else:
1984                choices = sorted(self.powderDict.values())
1985                hnum = G2gd.ItemSelector(choices,self.G2frame)
1986                if hnum is None: return True
1987                self.histnam = [choices[hnum]]
1988                numselected = len(self.histnam)
1989        elif self.currentExportType == 'image':
1990            if len(self.Histograms) == 0:
1991                self.G2frame.ErrorDialog(
1992                    'Empty project',
1993                    'Project does not contain any images.')
1994                return True
1995            elif len(self.Histograms) == 1:
1996                self.histnam = self.Histograms.keys()
1997            else:
1998                choices = sorted(self.Histograms.keys())
1999                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=self.multiple)
2000                if self.multiple:
2001                    if not hnum: return True
2002                    self.histnam = [choices[i] for i in hnum]
2003                else:
2004                    if hnum is None: return True
2005                    self.histnam = [choices[hnum]]
2006                numselected = len(self.histnam)
2007        if self.currentExportType == 'map':
2008            # search for phases with maps
2009            mapPhases = []
2010            choices = []
2011            for phasenam in sorted(self.Phases):
2012                phasedict = self.Phases[phasenam] # pointer to current phase info           
2013                if len(phasedict['General']['Map'].get('rho',[])):
2014                    mapPhases.append(phasenam)
2015                    if phasedict['General']['Map'].get('Flip'):
2016                        choices.append('Charge flip map: '+str(phasenam))
2017                    elif phasedict['General']['Map'].get('MapType'):
2018                        choices.append(
2019                            str(phasedict['General']['Map'].get('MapType'))
2020                            + ' map: ' + str(phasenam))
2021                    else:
2022                        choices.append('unknown map: '+str(phasenam))
2023            # select a map if needed
2024            if len(mapPhases) == 0:
2025                self.G2frame.ErrorDialog(
2026                    'Empty project',
2027                    'Project does not contain any maps.')
2028                return True
2029            elif len(mapPhases) == 1:
2030                self.phasenam = mapPhases
2031            else: 
2032                phasenum = G2gd.ItemSelector(choices,self.G2frame,multiple=self.multiple)
2033                if self.multiple:
2034                    if not phasenum: return True
2035                    self.phasenam = [mapPhases[i] for i in phasenum]
2036                else:
2037                    if phasenum is None: return True
2038                    self.phasenam = [mapPhases[phasenum]]
2039            numselected = len(self.phasenam)
2040
2041        # items selected, now set self.dirname and usually self.filename
2042        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
2043            AskFile == 'default' and not self.G2frame.GSASprojectfile
2044            ):
2045            filename = self.askSaveFile()
2046            if not filename: return True
2047            self.dirname,self.filename = os.path.split(filename)
2048        elif AskFile == 'dir' or AskFile == 'single' or (
2049            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
2050            ):
2051            self.dirname = self.askSaveDirectory()
2052            if not self.dirname: return True
2053        elif AskFile == 'default-dir' or AskFile == 'default':
2054            self.dirname,self.filename = os.path.split(
2055                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
2056                )
2057        else:
2058            raise Exception('This should not happen!')
2059       
2060    def loadParmDict(self):
2061        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
2062        refined values to those from the last cycle and set the uncertainties for the
2063        refined parameters in another dict (self.sigDict).
2064
2065        Expands the parm & sig dicts to include values derived from constraints.
2066        '''
2067        self.parmDict = {}
2068        self.sigDict = {}
2069        rigidbodyDict = {}
2070        covDict = {}
2071        consDict = {}
2072        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2073        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2074        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2075        while item:
2076            name = self.G2frame.PatternTree.GetItemText(item)
2077            if name == 'Rigid bodies':
2078                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
2079            elif name == 'Covariance':
2080                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
2081            elif name == 'Constraints':
2082                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
2083            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2084        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2085        self.parmDict.update(rbDict)
2086        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2087        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables =  G2stIO.GetPhaseData(
2088            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
2089        self.parmDict.update(phaseDict)
2090        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
2091            Phases,Histograms,Print=False,resetRefList=False)
2092        self.parmDict.update(hapDict)
2093        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
2094        self.parmDict.update(histDict)
2095        self.parmDict.update(zip(
2096            covDict.get('varyList',[]),
2097            covDict.get('variables',[])))
2098        self.sigDict = dict(zip(
2099            covDict.get('varyList',[]),
2100            covDict.get('sig',[])))
2101        # expand to include constraints: first compile a list of constraints
2102        constList = []
2103        for item in consDict:
2104            if item.startswith('_'): continue
2105            constList += consDict[item]
2106        # now process the constraints
2107        G2mv.InitVars()
2108        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
2109        varyList = covDict.get('varyListStart')
2110        if varyList is None and len(constDict) == 0:
2111            # no constraints can use varyList
2112            varyList = covDict.get('varyList')
2113        elif varyList is None:
2114            # old GPX file from before pre-constraint varyList is saved
2115            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
2116            raise Exception(' *** Export aborted ***')
2117        else:
2118            varyList = list(varyList)
2119        try:
2120            groups,parmlist = G2mv.GroupConstraints(constDict)
2121            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,self.parmDict)
2122        except:
2123            # this really should not happen
2124            print ' *** ERROR - constraints are internally inconsistent ***'
2125            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
2126            print 'Errors',errmsg
2127            if warnmsg: print 'Warnings',warnmsg
2128            raise Exception(' *** CIF creation aborted ***')
2129        # add the constrained values to the parameter dictionary
2130        G2mv.Dict2Map(self.parmDict,varyList)
2131        # and add their uncertainties into the esd dictionary (sigDict)
2132        if covDict.get('covMatrix') is not None:
2133            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
2134
2135    def loadTree(self):
2136        '''Load the contents of the data tree into a set of dicts
2137        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
2138        & self.xtalDict)
2139       
2140        * The childrenless data tree items are overall parameters/controls for the
2141          entire project and are placed in self.OverallParms
2142        * Phase items are placed in self.Phases
2143        * Data items are placed in self.Histogram. The key for these data items
2144          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
2145        '''
2146        self.OverallParms = {}
2147        self.powderDict = {}
2148        self.xtalDict = {}
2149        self.Phases = {}
2150        self.Histograms = {}
2151        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2152        histType = None       
2153        if self.currentExportType == 'phase':
2154            # if exporting phases load them here
2155            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
2156            if not sub:
2157                print 'no phases found'
2158                return True
2159            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
2160            while item:
2161                phaseName = self.G2frame.PatternTree.GetItemText(item)
2162                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
2163                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
2164            return
2165        elif self.currentExportType == 'single':
2166            histType = 'HKLF'
2167        elif self.currentExportType == 'powder':
2168            histType = 'PWDR'
2169        elif self.currentExportType == 'image':
2170            histType = 'IMG'
2171
2172        if histType: # Loading just one kind of tree entry
2173            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2174            while item:
2175                name = self.G2frame.PatternTree.GetItemText(item)
2176                if name.startswith(histType):
2177                    if self.Histograms.get(name): # there is already an item with this name
2178                        print('Histogram name '+str(name)+' is repeated. Renaming')
2179                        if name[-1] == '9':
2180                            name = name[:-1] + '10'
2181                        elif name[-1] in '012345678':
2182                            name = name[:-1] + str(int(name[-1])+1)
2183                        else:                           
2184                            name += '-1'
2185                    self.Histograms[name] = {}
2186                    # the main info goes into Data, but the 0th
2187                    # element contains refinement results, carry
2188                    # that over too now.
2189                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
2190                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
2191                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2192                    while item2: 
2193                        child = self.G2frame.PatternTree.GetItemText(item2)
2194                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
2195                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
2196                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2197            # index powder and single crystal histograms by number
2198            for hist in self.Histograms:
2199                if hist.startswith("PWDR"): 
2200                    d = self.powderDict
2201                elif hist.startswith("HKLF"): 
2202                    d = self.xtalDict
2203                else:
2204                    return                   
2205                i = self.Histograms[hist].get('hId')
2206                if i is None and not d.keys():
2207                    i = 0
2208                elif i is None or i in d.keys():
2209                    i = max(d.keys())+1
2210                d[i] = hist
2211            return
2212        # else standard load: using all interlinked phases and histograms
2213        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2214        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2215        while item:
2216            name = self.G2frame.PatternTree.GetItemText(item)
2217            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2218            if not item2: 
2219                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
2220            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2221        # index powder and single crystal histograms
2222        for hist in self.Histograms:
2223            i = self.Histograms[hist]['hId']
2224            if hist.startswith("PWDR"): 
2225                self.powderDict[i] = hist
2226            elif hist.startswith("HKLF"): 
2227                self.xtalDict[i] = hist
2228
2229    def dumpTree(self,mode='type'):
2230        '''Print out information on the data tree dicts loaded in loadTree
2231        '''
2232        print '\nOverall'
2233        if mode == 'type':
2234            def Show(arg): return type(arg)
2235        else:
2236            def Show(arg): return arg
2237        for key in self.OverallParms:
2238            print '  ',key,Show(self.OverallParms[key])
2239        print 'Phases'
2240        for key1 in self.Phases:
2241            print '    ',key1,Show(self.Phases[key1])
2242        print 'Histogram'
2243        for key1 in self.Histograms:
2244            print '    ',key1,Show(self.Histograms[key1])
2245            for key2 in self.Histograms[key1]:
2246                print '      ',key2,Show(self.Histograms[key1][key2])
2247
2248    def defaultSaveFile(self):
2249        return os.path.abspath(
2250            os.path.splitext(self.G2frame.GSASprojectfile
2251                             )[0]+self.extension)
2252       
2253    def askSaveFile(self):
2254        '''Ask the user to supply a file name
2255
2256        :returns: a file name (str) or None if Cancel is pressed
2257        '''
2258        defnam = os.path.splitext(
2259            os.path.split(self.G2frame.GSASprojectfile)[1]
2260            )[0]+self.extension
2261        dlg = wx.FileDialog(
2262            self.G2frame, 'Input name for file to write', '.', defnam,
2263            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2264            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2265        dlg.CenterOnParent()
2266        try:
2267            if dlg.ShowModal() == wx.ID_OK:
2268                filename = dlg.GetPath()
2269                # make sure extension is correct
2270                filename = os.path.splitext(filename)[0]+self.extension
2271            else:
2272                filename = None
2273        finally:
2274            dlg.Destroy()
2275        return filename
2276
2277    def askSaveDirectory(self):
2278        '''Ask the user to supply a directory name. Path name is used as the
2279        starting point for the next export path search.
2280
2281        :returns: a directory name (str) or None if Cancel is pressed
2282        '''
2283        if self.G2frame.exportDir:
2284            startdir = self.G2frame.exportDir
2285        elif self.G2frame.GSASprojectfile:
2286            startdir = os.path.split(self.G2frame.GSASprojectfile)[0]
2287        elif self.G2frame.dirname:
2288            startdir = self.G2frame.dirname
2289        else:
2290            startdir = ''
2291        dlg = wx.DirDialog(
2292            self.G2frame, 'Input directory where file(s) will be written', startdir,
2293            wx.DD_DEFAULT_STYLE)
2294        dlg.CenterOnParent()
2295        try:
2296            if dlg.ShowModal() == wx.ID_OK:
2297                filename = dlg.GetPath()
2298                self.G2frame.exportDir = filename
2299            else:
2300                filename = None
2301        finally:
2302            dlg.Destroy()
2303        return filename
2304
2305    # Tools for file writing.
2306    def OpenFile(self,fil=None,mode='w'):
2307        '''Open the output file
2308
2309        :param str fil: The name of the file to open. If None (default)
2310          the name defaults to self.dirname + self.filename.
2311          If an extension is supplied, it is not overridded,
2312          but if not, the default extension is used.
2313        :returns: the file object opened by the routine which is also
2314          saved as self.fp
2315        '''
2316        if not fil:
2317            if not os.path.splitext(self.filename)[1]:
2318                self.filename += self.extension
2319            fil = os.path.join(self.dirname,self.filename)
2320        self.fullpath = fil
2321        self.fp = open(fil,mode)
2322        return self.fp
2323
2324    def Write(self,line):
2325        '''write a line of output, attaching a line-end character
2326
2327        :param str line: the text to be written.
2328        '''
2329        self.fp.write(line+'\n')
2330    def CloseFile(self,fp=None):
2331        '''Close a file opened in OpenFile
2332
2333        :param file fp: the file object to be closed. If None (default)
2334          file object self.fp is closed.
2335        '''
2336        if fp is None:
2337            fp = self.fp
2338            self.fp = None
2339        fp.close()
2340    # Tools to pull information out of the data arrays
2341    def GetCell(self,phasenam):
2342        """Gets the unit cell parameters and their s.u.'s for a selected phase
2343
2344        :param str phasenam: the name for the selected phase
2345        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2346          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2347          cell values and `cellSig` has their uncertainties.
2348        """
2349        phasedict = self.Phases[phasenam] # pointer to current phase info
2350        try:
2351            pfx = str(phasedict['pId'])+'::'
2352            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2353            cellSig = G2stIO.getCellEsd(pfx,
2354                                        phasedict['General']['SGData'],A,
2355                                        self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2356            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2357            return cellList,cellSig
2358        except KeyError:
2359            cell = phasedict['General']['Cell'][1:]
2360            return cell,7*[0]
2361   
2362    def GetAtoms(self,phasenam):
2363        """Gets the atoms associated with a phase. Can be used with standard
2364        or macromolecular phases
2365
2366        :param str phasenam: the name for the selected phase
2367        :returns: a list of items for eac atom where each item is a list containing:
2368          label, typ, mult, xyz, and td, where
2369
2370          * label and typ are the atom label and the scattering factor type (str)
2371          * mult is the site multiplicity (int)
2372          * xyz is contains a list with four pairs of numbers:
2373            x, y, z and fractional occupancy and
2374            their standard uncertainty (or a negative value)
2375          * td is contains a list with either one or six pairs of numbers:
2376            if one number it is U\ :sub:`iso` and with six numbers it is
2377            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2378            paired with their standard uncertainty (or a negative value)
2379        """
2380        phasedict = self.Phases[phasenam] # pointer to current phase info           
2381        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2382        cfrac = cx+3
2383        fpfx = str(phasedict['pId'])+'::Afrac:'       
2384        atomslist = []
2385        for i,at in enumerate(phasedict['Atoms']):
2386            if phasedict['General']['Type'] == 'macromolecular':
2387                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2388            else:
2389                label = at[ct-1]
2390            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2391            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2392            mult = at[cs+1]
2393            typ = at[ct]
2394            xyz = []
2395            for j,v in enumerate(('x','y','z')):
2396                val = at[cx+j]
2397                pfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2398                sig = self.sigDict.get(pfx,-0.000009)
2399                xyz.append((val,sig))
2400            xyz.append((fval,fsig))
2401            td = []
2402            if at[cia] == 'I':
2403                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2404                val = self.parmDict.get(pfx,at[cia+1])
2405                sig = self.sigDict.get(pfx,-0.0009)
2406                td.append((val,sig))
2407            else:
2408                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2409                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2410                    val = self.parmDict.get(pfx,at[cia+2+i])
2411                    sig = self.sigDict.get(pfx,-0.0009)
2412                    td.append((val,sig))
2413            atomslist.append((label,typ,mult,xyz,td))
2414        return atomslist
2415######################################################################
2416
2417def ReadCIF(URLorFile):
2418    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2419    (from James Hester).
2420    The open routine gets confused with DOS names that begin with a letter and colon
2421    "C:\dir\" so this routine will try to open the passed name as a file and if that
2422    fails, try it as a URL
2423
2424    :param str URLorFile: string containing a URL or a file name. Code will try first
2425      to open it as a file and then as a URL.
2426
2427    :returns: a PyCifRW CIF object.
2428    '''
2429    import CifFile as cif # PyCifRW from James Hester
2430
2431    # alternate approach:
2432    #import urllib
2433    #ciffile = 'file:'+urllib.pathname2url(filename)
2434   
2435    try:
2436        fp = open(URLorFile,'r')
2437        cf = cif.ReadCif(fp)
2438        fp.close()
2439        return cf
2440    except IOError:
2441        return cif.ReadCif(URLorFile)
2442
2443if __name__ == '__main__':
2444    app = wx.PySimpleApp()
2445    frm = wx.Frame(None) # create a frame
2446    frm.Show(True)
2447    filename = '/tmp/notzip.zip'
2448    filename = '/tmp/all.zip'
2449    #filename = '/tmp/11bmb_7652.zip'
2450   
2451    #selection=None, confirmoverwrite=True, parent=None
2452    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2453    print ExtractFileFromZip(filename,multipleselect=True)
2454                             #confirmread=False, confirmoverwrite=False)
2455
2456    # choicelist=[ ('a','b','c'),
2457    #              ('test1','test2'),('no choice',)]
2458    # titles = [ 'a, b or c', 'tests', 'No option here']
2459    # dlg = MultipleChoicesDialog(
2460    #     choicelist,titles,
2461    #     parent=frm)
2462    # if dlg.ShowModal() == wx.ID_OK:
2463    #     print 'Got OK'
Note: See TracBrowser for help on using the repository browser.