source: trunk/GSASIIIO.py @ 1960

Last change on this file since 1960 was 1960, checked in by toby, 6 years ago

add current version number to Controls dict on save; display on read

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