source: trunk/GSASIIIO.py @ 1634

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

Add input for CHESS MedOptics? D1 CCD detector tiff files

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