source: trunk/GSASIIIO.py @ 1207

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

add contrast calculator GUI (does nothing yet) to SASD
fix notebook editor - OK in Win & linux
small mod to MC/SA calcs & GUI

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