source: trunk/GSASIIIO.py @ 1962

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

made PackageVersions? a list so it comes out in same order as the other list
slight reformatting so appearance is the same as well

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