source: trunk/GSASIIIO.py @ 1216

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

supplement GetTifData? with PIL

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