source: trunk/GSASIIIO.py @ 1172

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

ask to link phases to histograms after an import of either; rethink import initialization

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 90.6 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2013-12-17 22:12:54 +0000 (Tue, 17 Dec 2013) $
4# $Author: toby $
5# $Revision: 1172 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 1172 2013-12-17 22:12:54Z 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: 1172 $")
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 ReInitialize(self):
1297        'Reinitialize the Reader to initital settings'
1298        self.errors = ''
1299        self.warnings = ''
1300        self.repeat = False
1301        self.repeatcount = 0
1302
1303    def BlockSelector(self, ChoiceList, ParentFrame=None,
1304                      title='Select a block',
1305                      size=None, header='Block Selector',
1306                      useCancel=True):
1307        ''' Provide a wx dialog to select a block if the file contains more
1308        than one set of data and one must be selected
1309        '''
1310        if useCancel:
1311            dlg = wx.SingleChoiceDialog(
1312                ParentFrame,title, header,ChoiceList)
1313        else:
1314            dlg = wx.SingleChoiceDialog(
1315                ParentFrame,title, header,ChoiceList,
1316                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1317        if size: dlg.SetSize(size)
1318        dlg.CenterOnParent()
1319        if dlg.ShowModal() == wx.ID_OK:
1320            sel = dlg.GetSelection()
1321            return sel
1322        else:
1323            return None
1324        dlg.Destroy()
1325
1326    def MultipleBlockSelector(self, ChoiceList, ParentFrame=None,
1327        title='Select a block',size=None, header='Block Selector'):
1328        '''Provide a wx dialog to select a block of data if the
1329        file contains more than one set of data and one must be
1330        selected.
1331
1332        :returns: a list of the selected blocks
1333        '''
1334        dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1335            wx.CHOICEDLG_STYLE)
1336        dlg.CenterOnParent()
1337        if size: dlg.SetSize(size)
1338        if dlg.ShowModal() == wx.ID_OK:
1339            sel = dlg.GetSelections()
1340        else:
1341            return []
1342        dlg.Destroy()
1343        selected = []
1344        if len(ChoiceList) in sel:
1345            return range(len(ChoiceList))
1346        else:
1347            return sel
1348        return selected
1349
1350    def MultipleChoicesDialog(self, choicelist, headinglist, ParentFrame=None, **kwargs):
1351        '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1352        widget. Typical input:
1353       
1354           * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1355           
1356           * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1357           
1358        optional keyword parameters are: head (window title) and title
1359        returns a list of selected indicies for each choice (or None)
1360        '''
1361        result = None
1362        dlg = MultipleChoicesDialog(choicelist,headinglist,
1363            parent=ParentFrame, **kwargs)         
1364        dlg.CenterOnParent()
1365        if dlg.ShowModal() == wx.ID_OK:
1366            result = dlg.chosen
1367        dlg.Destroy()
1368        return result
1369
1370    def ShowBusy(self):
1371        wx.BeginBusyCursor()
1372        wx.Yield() # make it happen now!
1373
1374    def DoneBusy(self):
1375        wx.EndBusyCursor()
1376        wx.Yield() # make it happen now!
1377       
1378#    def Reader(self, filename, filepointer, ParentFrame=None, **unused):
1379#        '''This method must be supplied in the child class to read the file.
1380#        if the read fails either return False or raise an Exception
1381#        preferably of type ImportException.
1382#        '''
1383#        #start reading
1384#        raise ImportException("Error occurred while...")
1385#        self.errors += "Hint for user on why the error occur
1386#        return False # if an error occurs
1387#        return True # if read OK
1388
1389    def ExtensionValidator(self, filename):
1390        '''This methods checks if the file has the correct extension
1391        Return False if this filename will not be supported by this reader
1392        Return True if the extension matches the list supplied by the reader
1393        Return None if the reader allows un-registered extensions
1394        '''
1395        if filename:
1396            ext = os.path.splitext(filename)[1]
1397            if sys.platform == 'windows': ext = ext.lower()
1398            if ext in self.extensionlist: return True
1399            if self.strictExtension: return False
1400        return None
1401
1402    def ContentsValidator(self, filepointer):
1403        '''This routine will attempt to determine if the file can be read
1404        with the current format.
1405        This will typically be overridden with a method that
1406        takes a quick scan of [some of]
1407        the file contents to do a "sanity" check if the file
1408        appears to match the selected format.
1409        Expected to be called via self.Validator()
1410        '''
1411        #filepointer.seek(0) # rewind the file pointer
1412        return True
1413
1414    def CIFValidator(self, filepointer):
1415        '''A :meth:`ContentsValidator` for use to validate CIF files.
1416        '''
1417        for i,l in enumerate(filepointer):
1418            if i >= 1000: return True
1419            '''Encountered only blank lines or comments in first 1000
1420            lines. This is unlikely, but assume it is CIF anyway, since we are
1421            even less likely to find a file with nothing but hashes and
1422            blank lines'''
1423            line = l.strip()
1424            if len(line) == 0: # ignore blank lines
1425                continue 
1426            elif line.startswith('#'): # ignore comments
1427                continue 
1428            elif line.startswith('data_'): # on the right track, accept this file
1429                return True
1430            else: # found something invalid
1431                self.errors = 'line '+str(i+1)+' contains unexpected data:\n'
1432                self.errors += '  '+str(l)
1433                self.errors += '  Note: a CIF should only have blank lines or comments before'
1434                self.errors += '        a data_ statement begins a block.'
1435                return False 
1436
1437class ImportPhase(ImportBaseclass):
1438    '''Defines a base class for the reading of files with coordinates
1439
1440    Objects constructed that subclass this (in import/G2phase_*.py) will be used
1441    in :meth:`GSASII.GSASII.OnImportPhase`.
1442    See :ref:`Writing a Import Routine<Import_Routines>`
1443    for an explanation on how to use this class.
1444
1445    '''
1446    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1447        strictExtension=False,):
1448        # call parent __init__
1449        ImportBaseclass.__init__(self,formatName,longFormatName,
1450            extensionlist,strictExtension)
1451        self.Phase = None # a phase must be created with G2IO.SetNewPhase in the Reader
1452        self.Constraints = None
1453
1454    def PhaseSelector(self, ChoiceList, ParentFrame=None,
1455        title='Select a phase', size=None,header='Phase Selector'):
1456        ''' Provide a wx dialog to select a phase if the file contains more
1457        than one phase
1458        '''
1459        return self.BlockSelector(ChoiceList,ParentFrame,title,
1460            size,header)
1461
1462class ImportStructFactor(ImportBaseclass):
1463    '''Defines a base class for the reading of files with tables
1464    of structure factors.
1465
1466    Structure factors are read with a call to :meth:`GSASII.GSASII.OnImportSfact`
1467    which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`, which calls
1468    methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1469    :meth:`Reader`.
1470
1471    See :ref:`Writing a Import Routine<Import_Routines>`
1472    for an explanation on how to use import classes in general. The specifics
1473    for reading a structure factor histogram require that
1474    the ``Reader()`` routine in the import
1475    class need to do only a few things: It
1476    should load :attr:`RefDict` item ``'RefList'`` with the reflection list,
1477    and set :attr:`Parameters` with the instrument parameters
1478    (initialized with :meth:`InitParameters` and set with :meth:`UpdateParameters`).
1479    Also, set :attr:`Controls`,
1480    which specifies how the histogram is plotted
1481    (initialized with :meth:`InitControls` and set with :meth:`UpdateControls`).
1482    '''
1483    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1484        strictExtension=False,):
1485        ImportBaseclass.__init__(self,formatName,longFormatName,
1486            extensionlist,strictExtension)
1487
1488        # define contents of Structure Factor entry
1489        self.Parameters = []
1490        'self.Parameters is a list with two dicts for data parameter settings'
1491        self.InitParameters()
1492        self.Controls = {}
1493        'self.Controls is a dict with plotting controls'
1494        self.InitControls() # initialize the above
1495        self.RefDict = {'RefList':[],'FF':[]}
1496        '''self.RefDict is a dict containing the reflection information, as read from the file.
1497        Item 'RefList' contains the reflection information. See the
1498        :ref:`Single Crystal Reflection Data Structure<XtalRefl_table>`
1499        for the contents of each row. Dict element 'FF'
1500        contains the form factor values for each element type; if this entry
1501        is left as initialized (an empty list) it will be initialized as needed later.
1502        '''
1503    def ReInitialize(self):
1504        'Reinitialize the Reader to initital settings'
1505        ImportBaseclass.ReInitialize(self)
1506        self.InitParameters()
1507        self.InitControls()
1508        self.RefDict = {'RefList':[],'FF':[]}
1509
1510       
1511    def InitControls(self):
1512        'initialize the controls structure'
1513        self.Controls = { # dictionary with plotting controls
1514            'Type' : 'Fosq',
1515            'ifFc' : False,    #
1516            'HKLmax' : [None,None,None],
1517            'HKLmin' : [None,None,None],
1518            'FoMax' : None,   # maximum observed structure factor as Fo
1519            'Zone' : '001',
1520            'Layer' : 0,
1521            'Scale' : 1.0,
1522            'log-lin' : 'lin',
1523            }
1524
1525    def InitParameters(self):
1526        'initialize the instrument parameters structure'
1527        Lambda = 0.70926
1528        HistType = 'SXC'
1529        self.Parameters = [{'Type':[HistType,HistType], # create the structure
1530                            'Lam':[Lambda,Lambda]
1531                            }, {}]
1532        'Parameters is a list with two dicts for data parameter settings'
1533
1534    def UpdateParameters(self,Type=None,Wave=None):
1535        'Revise the instrument parameters'
1536        if Type is not None:
1537            self.Parameters[0]['Type'] = [Type,Type]
1538        if Wave is not None:
1539            self.Parameters[0]['Lam'] = [Wave,Wave]
1540           
1541    def UpdateControls(self,Type='Fosq',FcalcPresent=False):
1542        '''Scan through the reflections to update the Controls dictionary
1543        '''
1544        self.Controls['Type'] = Type
1545        self.Controls['ifFc'] = FcalcPresent
1546        HKLmax = [None,None,None]
1547        HKLmin = [None,None,None]
1548        Fo2max = None
1549        for refl in self.RefDict['RefList']:
1550            HKL = refl[:3]
1551            if Fo2max is None:
1552                Fo2max = refl[8]
1553            else:
1554                Fo2max = max(Fo2max,refl[8])
1555            for i,hkl in enumerate(HKL):
1556                if HKLmax[i] is None:
1557                    HKLmax[i] = hkl
1558                    HKLmin[i] = hkl
1559                else:
1560                    HKLmax[i] = max(HKLmax[i],hkl)
1561                    HKLmin[i] = min(HKLmin[i],hkl)
1562        self.Controls['HKLmax'] = HKLmax
1563        self.Controls['HKLmin'] = HKLmin
1564        if Type ==  'Fosq':
1565            self.Controls['FoMax'] = np.sqrt(Fo2max)
1566        elif Type ==  'Fo':
1567            self.Controls['FoMax'] = Fo2max
1568        else:
1569            print "Unsupported Struct Fact type in ImportStructFactor.UpdateControls"
1570            raise Exception,"Unsupported Struct Fact type in ImportStructFactor.UpdateControls"
1571
1572######################################################################
1573class ImportPowderData(ImportBaseclass):
1574    '''Defines a base class for the reading of files with powder data.
1575    See :ref:`Writing a Import Routine<Import_Routines>`
1576    for an explanation on how to use this class.
1577    '''
1578    # define some default instrument parameter files
1579    # just like GSAS, sigh
1580    defaultIparm_lbl = []
1581    defaultIparms = []
1582    defaultIparm_lbl.append('CuKa lab data')
1583    defaultIparms.append({
1584        'INS   HTYPE ':'PXC ',
1585        'INS  1 ICONS':'  1.540500  1.544300       0.0         0       0.7    0       0.5   ',
1586        'INS  1PRCF1 ':'    3    8      0.01                                                ',
1587        'INS  1PRCF11':'   2.000000E+00  -2.000000E+00   5.000000E+00   0.000000E+00        ',
1588        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.150000E-01   0.150000E-01        ',
1589        })
1590    defaultIparm_lbl.append('0.6A synch')
1591    defaultIparms.append({
1592        'INS   HTYPE ':'PXC ',
1593        'INS  1 ICONS':'  0.600000  0.000000       0.0         0      0.99    0       0.5   ',
1594        'INS  1PRCF1 ':'    3    8      0.01                                                ',
1595        'INS  1PRCF11':'   1.000000E+00  -1.000000E+00   0.300000E+00   0.000000E+00        ',
1596        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.100000E-01   0.100000E-01        ',
1597        })
1598    defaultIparm_lbl.append('1.5A CW neutron data')
1599    defaultIparms.append({
1600        'INS   HTYPE ':'PNC',
1601        'INS  1 ICONS':'   1.54020   0.00000   0.04000         0',
1602        'INS  1PRCF1 ':'    3    8     0.005',
1603        'INS  1PRCF11':'   0.239700E+03  -0.298200E+03   0.180800E+03   0.000000E+00',
1604        'INS  1PRCF12':'   0.000000E+00   0.000000E+00   0.400000E-01   0.300000E-01',
1605        })
1606    defaultIparm_lbl.append('10m TOF backscattering bank')
1607    defaultIparms.append({
1608        'INS   HTYPE ':'PNT',
1609        'INS  1 ICONS':'   5000.00      0.00      0.00',
1610        'INS  1BNKPAR':'    1.0000   150.000',       
1611        'INS  1PRCF1 ':'    1    8   0.01000',
1612        'INS  1PRCF11':'   0.000000E+00   5.000000E+00   3.000000E-02   1.000000E-03',
1613        'INS  1PRCF12':'   0.000000E+00   4.000000E+01   0.000000E+00   0.000000E+00',       
1614        })
1615    defaultIparm_lbl.append('10m TOF 90deg bank')
1616    defaultIparms.append({
1617        'INS   HTYPE ':'PNT',
1618        'INS  1 ICONS':'   3500.00      0.00      0.00',
1619        'INS  1BNKPAR':'    1.0000    90.000',       
1620        'INS  1PRCF1 ':'    1    8   0.01000',
1621        'INS  1PRCF11':'   0.000000E+00   5.000000E+00   3.000000E-02   4.000000E-03',
1622        'INS  1PRCF12':'   0.000000E+00   8.000000E+01   0.000000E+00   0.000000E+00',       
1623        })
1624    defaultIparm_lbl.append('63m POWGEN 90deg bank')
1625    defaultIparms.append({
1626        'INS   HTYPE ':'PNT',
1627        'INS  1 ICONS':'  22585.80      0.00      0.00',
1628        'INS  1BNKPAR':'    1.0000    90.000',       
1629        'INS  1PRCF1 ':'    1    8   0.01000',
1630        'INS  1PRCF11':'   0.000000E+00   1.000000E+00   3.000000E-02   4.000000E-03',
1631        'INS  1PRCF12':'   0.000000E+00   8.000000E+01   0.000000E+00   0.000000E+00',       
1632        })
1633    def __init__(self,
1634                 formatName,
1635                 longFormatName=None,
1636                 extensionlist=[],
1637                 strictExtension=False,
1638                 ):
1639        ImportBaseclass.__init__(self,formatName,
1640                                            longFormatName,
1641                                            extensionlist,
1642                                            strictExtension)
1643        self.powderentry = ['',None,None] #  (filename,Pos,Bank)
1644        self.powderdata = [] # Powder dataset
1645        '''A powder data set is a list with items [x,y,w,yc,yb,yd]:
1646                np.array(x), # x-axis values
1647                np.array(y), # powder pattern intensities
1648                np.array(w), # 1/sig(intensity)^2 values (weights)
1649                np.array(yc), # calc. intensities (zero)
1650                np.array(yb), # calc. background (zero)
1651                np.array(yd), # obs-calc profiles
1652        '''                           
1653        self.comments = []
1654        self.idstring = ''
1655        self.Sample = G2pdG.SetDefaultSample()
1656        self.GSAS = None     # used in TOF
1657        self.clockWd = None  # used in TOF
1658        self.repeat_instparm = True # Should a parm file be
1659        #                             used for multiple histograms?
1660        self.instparm = None # name hint
1661        self.instfile = '' # full path name to instrument parameter file
1662        self.instbank = '' # inst parm bank number
1663        self.instmsg = ''  # a label that gets printed to show
1664                           # where instrument parameters are from
1665        self.numbanks = 1
1666        self.instdict = {} # place items here that will be transferred to the instrument parameters
1667######################################################################
1668class ExportBaseclass(object):
1669    '''Defines a base class for the exporting of GSAS-II results
1670    '''
1671    def __init__(self,
1672                 G2frame,
1673                 formatName,
1674                 extension,
1675                 longFormatName=None,
1676                 ):
1677        self.G2frame = G2frame
1678        self.formatName = formatName # short string naming file type
1679        self.extension = extension
1680        if longFormatName: # longer string naming file type
1681            self.longFormatName = longFormatName
1682        else:
1683            self.longFormatName = formatName
1684        self.OverallParms = {}
1685        self.Phases = {}
1686        self.Histograms = {}
1687        self.powderDict = {}
1688        self.xtalDict = {}
1689        self.parmDict = {}
1690        self.sigDict = {}
1691        # updated in InitExport:
1692        self.currentExportType = None # type of export that has been requested
1693        # updated in ExportSelect (when used):
1694        self.phasenam = None # a list of selected phases
1695        self.histnam = None # a list of selected histograms
1696        self.filename = None # name of file to be written
1697       
1698        # items that should be defined in a subclass of this class
1699        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1700        # The following types are defined: 'project', "phase", "powder", "single"
1701        self.multiple = False # set as True if the class can export multiple phases or histograms
1702        # self.multiple is ignored for "project" exports
1703
1704    def InitExport(self,event):
1705        '''Determines the type of menu that called the Exporter.
1706        '''
1707        if event:
1708            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1709
1710    def ExportSelect(self,AskFile=True):
1711        '''Selects histograms or phases when needed. Sets a default file name.
1712
1713        :param bool AskFile: if AskFile is True (default) get the name of the file
1714          in a dialog
1715        :returns: True in case of an error
1716        '''
1717       
1718        if self.currentExportType == 'phase':
1719            if len(self.Phases) == 0:
1720                self.G2frame.ErrorDialog(
1721                    'Empty project',
1722                    'Project does not contain any phases.')
1723                return True
1724            elif len(self.Phases) == 1:
1725                self.phasenam = self.Phases.keys()
1726            elif self.multiple: 
1727                choices = sorted(self.Phases.keys())
1728                phasenum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1729                if phasenum is None: return True
1730                self.phasenam = [choices[i] for i in phasenum]
1731                if not self.phasenam: return True
1732            else:
1733                choices = sorted(self.Phases.keys())
1734                phasenum = G2gd.ItemSelector(choices,self.G2frame)
1735                if phasenum is None: return True
1736                self.phasenam = [choices[phasenum]]
1737        elif self.currentExportType == 'single':
1738            if len(self.xtalDict) == 0:
1739                self.G2frame.ErrorDialog(
1740                    'Empty project',
1741                    'Project does not contain any single crystal data.')
1742                return True
1743            elif len(self.xtalDict) == 1:
1744                self.histnam = self.xtalDict.values()
1745            elif self.multiple:
1746                choices = sorted(self.xtalDict.values())
1747                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1748                if not hnum: return True
1749                self.histnam = [choices[i] for i in hnum]
1750            else:
1751                choices = sorted(self.xtalDict.values())
1752                hnum = G2gd.ItemSelector(choices,self.G2frame)
1753                if hnum is None: return True
1754                self.histnam = [choices[hnum]]
1755        elif self.currentExportType == 'powder':
1756            if len(self.powderDict) == 0:
1757                self.G2frame.ErrorDialog(
1758                    'Empty project',
1759                    'Project does not contain any powder data.')
1760                return True
1761            elif len(self.powderDict) == 1:
1762                self.histnam = self.powderDict.values()
1763            elif self.multiple:
1764                choices = sorted(self.powderDict.values())
1765                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=True)
1766                if not hnum: return True
1767                self.histnam = [choices[i] for i in hnum]
1768            else:
1769                choices = sorted(self.powderDict.values())
1770                hnum = G2gd.ItemSelector(choices,self.G2frame)
1771                if hnum is None: return True
1772                self.histnam = [choices[hnum]]
1773        elif self.currentExportType == 'image':
1774            if len(self.Histograms) == 0:
1775                self.G2frame.ErrorDialog(
1776                    'Empty project',
1777                    'Project does not contain any images.')
1778                return True
1779            elif len(self.Histograms) == 1:
1780                self.histnam = self.Histograms.keys()
1781            else:
1782                choices = sorted(self.Histograms.keys())
1783                hnum = G2gd.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1784                if self.multiple:
1785                    if not hnum: return True
1786                    self.histnam = [choices[i] for i in hnum]
1787                else:
1788                    if hnum is None: return True
1789                    self.histnam = [choices[hnum]]
1790        if self.currentExportType == 'map':
1791            # search for phases with maps
1792            mapPhases = []
1793            choices = []
1794            for phasenam in sorted(self.Phases):
1795                phasedict = self.Phases[phasenam] # pointer to current phase info           
1796                if len(phasedict['General']['Map'].get('rho',[])):
1797                    mapPhases.append(phasenam)
1798                    if phasedict['General']['Map'].get('Flip'):
1799                        choices.append('Charge flip map: '+str(phasenam))
1800                    elif phasedict['General']['Map'].get('MapType'):
1801                        choices.append(
1802                            str(phasedict['General']['Map'].get('MapType'))
1803                            + ' map: ' + str(phasenam))
1804                    else:
1805                        choices.append('unknown map: '+str(phasenam))
1806            # select a map if needed
1807            if len(mapPhases) == 0:
1808                self.G2frame.ErrorDialog(
1809                    'Empty project',
1810                    'Project does not contain any maps.')
1811                return True
1812            elif len(mapPhases) == 1:
1813                self.phasenam = mapPhases
1814            else: 
1815                phasenum = G2gd.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1816                if self.multiple:
1817                    if not phasenum: return True
1818                    self.phasenam = [mapPhases[i] for i in phasenum]
1819                else:
1820                    if phasenum is None: return True
1821                    self.phasenam = [mapPhases[phasenum]]
1822
1823        if AskFile:
1824            self.filename = self.askSaveFile()
1825        else:
1826            self.filename = self.defaultSaveFile()
1827        if not self.filename: return True
1828       
1829    # def SetupExport(self,event,AskFile=True):
1830    #     '''Determines the type of menu that called the Exporter. Selects histograms
1831    #     or phases when needed. Better to replace with individual calls to
1832    #     self.InitExport() and self.ExportSelect() so that the former can be called prior
1833    #     to self.LoadTree()
1834
1835    #     :param bool AskFile: if AskFile is True (default) get the name of the file
1836    #       in a dialog
1837    #     :returns: True in case of an error
1838    #     '''
1839    #     self.ExportInit(event)
1840    #     return self.ExportSelect(AskFile)
1841                   
1842    def loadParmDict(self):
1843        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
1844        refined values to those from the last cycle and set the uncertainties for the
1845        refined parameters in another dict (self.sigDict).
1846
1847        Expands the parm & sig dicts to include values derived from constraints.
1848        '''
1849        self.parmDict = {}
1850        self.sigDict = {}
1851        rigidbodyDict = {}
1852        covDict = {}
1853        consDict = {}
1854        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1855        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
1856        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
1857        while item:
1858            name = self.G2frame.PatternTree.GetItemText(item)
1859            if name == 'Rigid bodies':
1860                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
1861            elif name == 'Covariance':
1862                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
1863            elif name == 'Constraints':
1864                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
1865            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
1866        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1867        self.parmDict.update(rbDict)
1868        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1869        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables =  G2stIO.GetPhaseData(
1870            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
1871        self.parmDict.update(phaseDict)
1872        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
1873            Phases,Histograms,Print=False,resetRefList=False)
1874        self.parmDict.update(hapDict)
1875        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
1876        self.parmDict.update(histDict)
1877        self.parmDict.update(zip(
1878            covDict.get('varyList',[]),
1879            covDict.get('variables',[])))
1880        self.sigDict = dict(zip(
1881            covDict.get('varyList',[]),
1882            covDict.get('sig',[])))
1883        # expand to include constraints: first compile a list of constraints
1884        constList = []
1885        for item in consDict:
1886            if item.startswith('_'): continue
1887            constList += consDict[item]
1888        # now process the constraints
1889        G2mv.InitVars()
1890        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
1891        varyList = covDict.get('varyListStart')
1892        if varyList is None and len(constDict) == 0:
1893            # no constraints can use varyList
1894            varyList = covDict.get('varyList')
1895        elif varyList is None:
1896            # old GPX file from before pre-constraint varyList is saved
1897            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
1898            raise Exception(' *** Export aborted ***')
1899        else:
1900            varyList = list(varyList)
1901        try:
1902            groups,parmlist = G2mv.GroupConstraints(constDict)
1903            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList)
1904        except:
1905            # this really should not happen
1906            print ' *** ERROR - constraints are internally inconsistent ***'
1907            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
1908            print 'Errors',errmsg
1909            if warnmsg: print 'Warnings',warnmsg
1910            raise Exception(' *** CIF creation aborted ***')
1911        # add the constrained values to the parameter dictionary
1912        G2mv.Dict2Map(self.parmDict,varyList)
1913        # and add their uncertainties into the esd dictionary (sigDict)
1914        if covDict.get('covMatrix') is not None:
1915            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
1916
1917    def loadTree(self):
1918        '''Load the contents of the data tree into a set of dicts
1919        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
1920        & self.xtalDict)
1921       
1922        * The childrenless data tree items are overall parameters/controls for the
1923          entire project and are placed in self.OverallParms
1924        * Phase items are placed in self.Phases
1925        * Data items are placed in self.Histogram. The key for these data items
1926          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
1927        '''
1928        self.OverallParms = {}
1929        self.powderDict = {}
1930        self.xtalDict = {}
1931        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
1932        histType = None       
1933        if self.currentExportType == 'phase':
1934            # if exporting phases load them here
1935            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
1936            if not sub:
1937                print 'no phases found'
1938                return True
1939            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
1940            while item:
1941                phaseName = self.G2frame.PatternTree.GetItemText(item)
1942                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
1943                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
1944            return
1945        elif self.currentExportType == 'single':
1946            histType = 'HKLF'
1947        elif self.currentExportType == 'powder':
1948            histType = 'PWDR'
1949        elif self.currentExportType == 'image':
1950            histType = 'IMG'
1951
1952        if histType: # Loading just one kind of tree entry
1953            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
1954            while item:
1955                name = self.G2frame.PatternTree.GetItemText(item)
1956                if name.startswith(histType):
1957                    if self.Histograms.get(name): # there is already an item with this name
1958                        if name[-1] == '9':
1959                            name = name[:-1] + '10'
1960                        elif name[-1] in '012345678':
1961                            name = name[:-1] + str(int(name[:-1])+1)
1962                        else:                           
1963                            name += '-1'
1964                    self.Histograms[name] = {}
1965                    # the main info goes into Data, but the 0th
1966                    # element contains refinement results, carry
1967                    # that over too now.
1968                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
1969                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
1970                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
1971                    while item2: 
1972                        child = self.G2frame.PatternTree.GetItemText(item2)
1973                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
1974                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
1975                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
1976            # index powder and single crystal histograms by number
1977            for hist in self.Histograms:
1978                if hist.startswith("PWDR"): 
1979                    d = self.powderDict
1980                elif hist.startswith("HKLF"): 
1981                    d = self.xtalDict
1982                else:
1983                    return                   
1984                i = self.Histograms[hist].get('hId')
1985                if i is None and not d.keys():
1986                    i = 0
1987                elif i is None or i in d.keys():
1988                    i = max(d.keys())+1
1989                d[i] = hist
1990            return
1991        # else standard load: using all interlinked phases and histograms
1992        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1993        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
1994        while item:
1995            name = self.G2frame.PatternTree.GetItemText(item)
1996            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
1997            if not item2: 
1998                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
1999            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2000        # index powder and single crystal histograms
2001        for hist in self.Histograms:
2002            i = self.Histograms[hist]['hId']
2003            if hist.startswith("PWDR"): 
2004                self.powderDict[i] = hist
2005            elif hist.startswith("HKLF"): 
2006                self.xtalDict[i] = hist
2007
2008    def dumpTree(self,mode='type'):
2009        '''Print out information on the data tree dicts loaded in loadTree
2010        '''
2011        print '\nOverall'
2012        if mode == 'type':
2013            def Show(arg): return type(arg)
2014        else:
2015            def Show(arg): return arg
2016        for key in self.OverallParms:
2017            print '  ',key,Show(self.OverallParms[key])
2018        print 'Phases'
2019        for key1 in self.Phases:
2020            print '    ',key1,Show(self.Phases[key1])
2021        print 'Histogram'
2022        for key1 in self.Histograms:
2023            print '    ',key1,Show(self.Histograms[key1])
2024            for key2 in self.Histograms[key1]:
2025                print '      ',key2,Show(self.Histograms[key1][key2])
2026
2027    def defaultSaveFile(self):
2028        return os.path.abspath(
2029            os.path.splitext(self.G2frame.GSASprojectfile
2030                             )[0]+self.extension)
2031       
2032    def askSaveFile(self):
2033        '''Ask the user to supply a file name
2034
2035        :returns: a file name (str)
2036        '''
2037        defnam = os.path.splitext(
2038            os.path.split(self.G2frame.GSASprojectfile)[1]
2039            )[0]+self.extension
2040        dlg = wx.FileDialog(
2041            self.G2frame, 'Input name for file to write', '.', defnam,
2042            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2043            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2044        dlg.CenterOnParent()
2045        try:
2046            if dlg.ShowModal() == wx.ID_OK:
2047                filename = dlg.GetPath()
2048                # make sure extension is correct
2049                filename = os.path.splitext(filename)[0]+self.extension
2050            else:
2051                filename = None
2052        finally:
2053            dlg.Destroy()
2054        return filename
2055
2056    # Tools for file writing.
2057    def OpenFile(self,fil=None):
2058        '''Open the output file
2059
2060        :param str fil: The name of the file to open. If None (default)
2061          the name defaults to self.filename.
2062        :returns: the file object opened by the routine which is also
2063          saved as self.fp
2064        '''
2065        if not fil:
2066            fil = self.filename
2067        self.fp = open(fil,'w')
2068        return self.fp
2069    def Write(self,line):
2070        '''write a line of output, attaching a line-end character
2071
2072        :param str line: the text to be written.
2073        '''
2074        self.fp.write(line+'\n')
2075    def CloseFile(self,fp=None):
2076        '''Close a file opened in OpenFile
2077
2078        :param file fp: the file object to be closed. If None (default)
2079          file object self.fp is closed.
2080        '''
2081        if fp is None:
2082            fp = self.fp
2083            self.fp = None
2084        fp.close()
2085    # Tools to pull information out of the data arrays
2086    def GetCell(self,phasenam):
2087        """Gets the unit cell parameters and their s.u.'s for a selected phase
2088
2089        :param str phasenam: the name for the selected phase
2090        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2091          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2092          cell values and `cellSig` has their uncertainties.
2093        """
2094        phasedict = self.Phases[phasenam] # pointer to current phase info
2095        try:
2096            pfx = str(phasedict['pId'])+'::'
2097            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2098            cellSig = G2stIO.getCellEsd(pfx,
2099                                        phasedict['General']['SGData'],A,
2100                                        self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2101            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2102            return cellList,cellSig
2103        except KeyError:
2104            cell = phasedict['General']['Cell'][1:]
2105            return cell,7*[0]
2106   
2107    def GetAtoms(self,phasenam):
2108        """Gets the atoms associated with a phase. Can be used with standard
2109        or macromolecular phases
2110
2111        :param str phasenam: the name for the selected phase
2112        :returns: a list of items for eac atom where each item is a list containing:
2113          label, typ, mult, xyz, and td, where
2114
2115          * label and typ are the atom label and the scattering factor type (str)
2116          * mult is the site multiplicity (int)
2117          * xyz is contains a list with four pairs of numbers:
2118            x, y, z and fractional occupancy and
2119            their standard uncertainty (or a negative value)
2120          * td is contains a list with either one or six pairs of numbers:
2121            if one number it is U\ :sub:`iso` and with six numbers it is
2122            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2123            paired with their standard uncertainty (or a negative value)
2124        """
2125        phasedict = self.Phases[phasenam] # pointer to current phase info           
2126        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2127        cfrac = cx+3
2128        fpfx = str(phasedict['pId'])+'::Afrac:'       
2129        atomslist = []
2130        for i,at in enumerate(phasedict['Atoms']):
2131            if phasedict['General']['Type'] == 'macromolecular':
2132                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2133            else:
2134                label = at[ct-1]
2135            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2136            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2137            mult = at[cs+1]
2138            typ = at[ct]
2139            xyz = []
2140            for j,v in enumerate(('x','y','z')):
2141                val = at[cx+j]
2142                pfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2143                sig = self.sigDict.get(pfx,-0.000009)
2144                xyz.append((val,sig))
2145            xyz.append((fval,fsig))
2146            td = []
2147            if at[cia] == 'I':
2148                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2149                val = self.parmDict.get(pfx,at[cia+1])
2150                sig = self.sigDict.get(pfx,-0.0009)
2151                td.append((val,sig))
2152            else:
2153                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2154                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2155                    val = self.parmDict.get(pfx,at[cia+2+i])
2156                    sig = self.sigDict.get(pfx,-0.0009)
2157                    td.append((val,sig))
2158            atomslist.append((label,typ,mult,xyz,td))
2159        return atomslist
2160######################################################################
2161
2162def ReadCIF(URLorFile):
2163    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2164    (from James Hester).
2165    The open routine gets confused with DOS names that begin with a letter and colon
2166    "C:\dir\" so this routine will try to open the passed name as a file and if that
2167    fails, try it as a URL
2168
2169    :param str URLorFile: string containing a URL or a file name. Code will try first
2170      to open it as a file and then as a URL.
2171
2172    :returns: a PyCifRW CIF object.
2173    '''
2174    import CifFile as cif # PyCifRW from James Hester
2175
2176    # alternate approach:
2177    #import urllib
2178    #ciffile = 'file:'+urllib.pathname2url(filename)
2179   
2180    try:
2181        fp = open(URLorFile,'r')
2182        cf = cif.ReadCif(fp)
2183        fp.close()
2184        return cf
2185    except IOError:
2186        return cif.ReadCif(URLorFile)
2187
2188if __name__ == '__main__':
2189    app = wx.PySimpleApp()
2190    frm = wx.Frame(None) # create a frame
2191    frm.Show(True)
2192    filename = '/tmp/notzip.zip'
2193    filename = '/tmp/all.zip'
2194    #filename = '/tmp/11bmb_7652.zip'
2195   
2196    #selection=None, confirmoverwrite=True, parent=None
2197    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2198    print ExtractFileFromZip(filename,multipleselect=True)
2199                             #confirmread=False, confirmoverwrite=False)
2200
2201    # choicelist=[ ('a','b','c'),
2202    #              ('test1','test2'),('no choice',)]
2203    # titles = [ 'a, b or c', 'tests', 'No option here']
2204    # dlg = MultipleChoicesDialog(
2205    #     choicelist,titles,
2206    #     parent=frm)
2207    # if dlg.ShowModal() == wx.ID_OK:
2208    #     print 'Got OK'
Note: See TracBrowser for help on using the repository browser.