source: trunk/GSASIIIO.py @ 1949

Last change on this file since 1949 was 1949, checked in by vondreele, 6 years ago

add image read for Rigaku R-Axis IV files

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