source: trunk/GSASIIIO.py @ 1204

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

SASD now in q only (not 2-theta); fix minor bugs

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