source: trunk/GSASIIIO.py @ 1205

Last change on this file since 1205 was 1205, checked in by vondreele, 9 years ago

Add models to SASD data tree
Add error bar plotting to SASD data
Add I*Q4 option to log SASD data
got rid of the if, else blocks for all key driven toggles in G2plot

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