source: trunk/GSASIIIO.py @ 1208

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

complete contrast calculator for small angle data.
implement a Load substances from a file

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