source: trunk/GSASIIIO.py @ 1168

Last change on this file since 1168 was 1168, checked in by toby, 8 years ago

update imports to provide error messages

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