source: trunk/GSASIIIO.py @ 2298

Last change on this file since 2298 was 2298, checked in by vondreele, 7 years ago

Add stubs for BondDialog? & AngleDialog? for adding these to Seq results table
Add menu items for above
fix a bug in showing seq results
revert the occurrence counter for image integrations
capture bad pids in OnPageChanged?

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 103.2 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2016-05-27 19:48:22 +0000 (Fri, 27 May 2016) $
4# $Author: vondreele $
5# $Revision: 2298 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 2298 2016-05-27 19:48:22Z vondreele $
8########### SVN repository information ###################
9'''
10*GSASIIIO: Misc I/O routines*
11=============================
12
13Module with miscellaneous routines for input and output. Many
14are GUI routines to interact with user.
15
16Includes support for image reading.
17
18Also includes base classes for data import routines.
19
20'''
21"""GSASIIIO: functions for IO of data
22   Copyright: 2008, Robert B. Von Dreele (Argonne National Laboratory)
23"""
24import wx
25import math
26import numpy as np
27import copy
28import cPickle
29import sys
30import re
31import random as ran
32import GSASIIpath
33GSASIIpath.SetVersionNumber("$Revision: 2298 $")
34import GSASIIgrid as G2gd
35import GSASIIspc as G2spc
36import GSASIIobj as G2obj
37import GSASIIlattice as G2lat
38import GSASIIpwdGUI as G2pdG
39import GSASIIimgGUI as G2imG
40import GSASIIimage as G2img
41import GSASIIElem as G2el
42import GSASIIstrIO as G2stIO
43import GSASIImapvars as G2mv
44import GSASIIctrls as G2G
45import os
46import os.path as ospath
47
48DEBUG = False       #=True for various prints
49TRANSP = False      #=true to transpose images for testing
50if GSASIIpath.GetConfigValue('Transpose'): TRANSP = True
51npsind = lambda x: np.sin(x*np.pi/180.)
52
53def sfloat(S):
54    'Convert a string to float. An empty field or a unconvertable value is treated as zero'
55    if S.strip():
56        try:
57            return float(S)
58        except ValueError:
59            pass
60    return 0.0
61
62def sint(S):
63    'Convert a string to int. An empty field is treated as zero'
64    if S.strip():
65        return int(S)
66    else:
67        return 0
68
69def trim(val):
70    '''Simplify a string containing leading and trailing spaces
71    as well as newlines, tabs, repeated spaces etc. into a shorter and
72    more simple string, by replacing all ranges of whitespace
73    characters with a single space.
74
75    :param str val: the string to be simplified
76
77    :returns: the (usually) shortened version of the string
78    '''
79    return re.sub('\s+', ' ', val).strip()
80
81def makeInstDict(names,data,codes):
82    inst = dict(zip(names,zip(data,data,codes)))
83    for item in inst:
84        inst[item] = list(inst[item])
85    return inst
86
87def FileDlgFixExt(dlg,file):
88    'this is needed to fix a problem in linux wx.FileDialog'
89    ext = dlg.GetWildcard().split('|')[2*dlg.GetFilterIndex()+1].strip('*')
90    if ext not in file:
91        file += ext
92    return file
93       
94def GetPowderPeaks(fileName):
95    'Read powder peaks from a file'
96    sind = lambda x: math.sin(x*math.pi/180.)
97    asind = lambda x: 180.*math.asin(x)/math.pi
98    wave = 1.54052
99    File = open(fileName,'Ur')
100    Comments = []
101    peaks = []
102    S = File.readline()
103    while S:
104        if S[:1] == '#':
105            Comments.append(S[:-1])
106        else:
107            item = S.split()
108            if len(item) == 1:
109                peaks.append([float(item[0]),1.0])
110            elif len(item) > 1:
111                peaks.append([float(item[0]),float(item[0])])
112        S = File.readline()
113    File.close()
114    if Comments:
115       print 'Comments on file:'
116       for Comment in Comments: 
117            print Comment
118            if 'wavelength' in Comment:
119                wave = float(Comment.split('=')[1])
120    Peaks = []
121    if peaks[0][0] > peaks[-1][0]:          # d-spacings - assume CuKa
122        for peak in peaks:
123            dsp = peak[0]
124            sth = wave/(2.0*dsp)
125            if sth < 1.0:
126                tth = 2.0*asind(sth)
127            else:
128                break
129            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
130    else:                                   #2-thetas - assume Cuka (for now)
131        for peak in peaks:
132            tth = peak[0]
133            dsp = wave/(2.0*sind(tth/2.0))
134            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
135    limits = [1000.,0.]
136    for peak in Peaks:
137        limits[0] = min(limits[0],peak[0])
138        limits[1] = max(limits[1],peak[0])
139    limits[0] = max(1.,(int(limits[0]-1.)/5)*5.)
140    limits[1] = min(170.,(int(limits[1]+1.)/5)*5.)
141    return Comments,Peaks,limits,wave
142
143def CheckImageFile(G2frame,imagefile):
144    '''Try to locate an image file if the project and image have been moved
145    together. If the image file cannot be found, request the location from
146    the user.
147
148    :param wx.Frame G2frame: main GSAS-II Frame and data object
149    :param str imagefile: name of image file
150    :returns: imagefile, if it exists, or the name of a file
151      that does exist or False if the user presses Cancel
152
153    '''
154    if not os.path.exists(imagefile):
155        print 'Image file '+imagefile+' not found'
156        fil = imagefile.replace('\\','/') # windows?!
157        # see if we can find a file with same name or in a similarly named sub-dir
158        pth,fil = os.path.split(fil)
159        prevpth = None
160        while pth and pth != prevpth:
161            prevpth = pth
162            if os.path.exists(os.path.join(G2frame.dirname,fil)):
163                print 'found image file '+os.path.join(G2frame.dirname,fil)
164                return os.path.join(G2frame.dirname,fil)
165            pth,enddir = os.path.split(pth)
166            fil = os.path.join(enddir,fil)
167        # not found as a subdirectory, drop common parts of path for last saved & image file names
168        #    if image was .../A/B/C/imgs/ima.ge
169        #      & GPX was  .../A/B/C/refs/fil.gpx but is now .../NEW/TEST/TEST1
170        #    will look for .../NEW/TEST/TEST1/imgs/ima.ge, .../NEW/TEST/imgs/ima.ge, .../NEW/imgs/ima.ge and so on
171        Controls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
172        gpxPath = Controls.get('LastSavedAs','').replace('\\','/').split('/') # blank in older .GPX files
173        imgPath = imagefile.replace('\\','/').split('/')
174        for p1,p2 in zip(gpxPath,imgPath):
175            if p1 == p2:
176                gpxPath.pop(0),imgPath.pop(0)
177            else:
178                break
179        fil = os.path.join(*imgPath) # file with non-common prefix elements
180        prevpth = None
181        pth = os.path.abspath(G2frame.dirname)
182        while pth and pth != prevpth:
183            prevpth = pth
184            if os.path.exists(os.path.join(pth,fil)):
185                print 'found image file '+os.path.join(pth,fil)
186                return os.path.join(pth,fil)
187            pth,enddir = os.path.split(pth)
188        #GSASIIpath.IPyBreak()
189
190    if not os.path.exists(imagefile):
191        prevnam = os.path.split(imagefile)[1]
192        prevext = os.path.splitext(imagefile)[1]
193        wildcard = 'Image format (*'+prevext+')|*'+prevext
194        dlg = wx.FileDialog(G2frame, 'Previous image file ('+prevnam+') not found; open here', '.', prevnam,
195                            wildcard,wx.OPEN)
196        try:
197            dlg.SetFilename(''+ospath.split(imagefile)[1])
198            if dlg.ShowModal() == wx.ID_OK:
199                imagefile = dlg.GetPath()
200            else:
201                imagefile = False
202        finally:
203            dlg.Destroy()
204    return imagefile
205
206def EditImageParms(parent,Data,Comments,Image,filename):
207    dlg = wx.Dialog(parent, wx.ID_ANY, 'Edit image parameters',
208                    style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
209    def onClose(event):
210        dlg.EndModal(wx.ID_OK)
211    mainsizer = wx.BoxSizer(wx.VERTICAL)
212    h,w = Image.shape[:2]
213    mainsizer.Add(wx.StaticText(dlg,wx.ID_ANY,
214                                'File '+str(filename)+'\nImage size: '+str(h)+' x '+str(w)),
215                  0,wx.ALIGN_LEFT|wx.ALL, 2)
216   
217    vsizer = wx.BoxSizer(wx.HORIZONTAL)
218    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Wavelength (\xC5) '),
219               0,wx.ALIGN_LEFT|wx.ALL, 2)
220    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'wavelength')
221    vsizer.Add(wdgt)
222    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
223
224    vsizer = wx.BoxSizer(wx.HORIZONTAL)
225    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Pixel size (\xb5m). Width '),
226               0,wx.ALIGN_LEFT|wx.ALL, 2)
227    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],0,
228                                 size=(50,-1))
229    vsizer.Add(wdgt)
230    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Height '),
231               wx.ALIGN_LEFT|wx.ALL, 2)
232    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],1,
233                                 size=(50,-1))
234    vsizer.Add(wdgt)
235    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
236
237    vsizer = wx.BoxSizer(wx.HORIZONTAL)
238    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Sample to detector (mm) '),
239               0,wx.ALIGN_LEFT|wx.ALL, 2)
240    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'distance')
241    vsizer.Add(wdgt)
242    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
243
244    vsizer = wx.BoxSizer(wx.HORIZONTAL)
245    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Beam center (pixels). X = '),
246               0,wx.ALIGN_LEFT|wx.ALL, 2)
247    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],0,
248                                 size=(75,-1))
249    vsizer.Add(wdgt)
250    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Y = '),
251               wx.ALIGN_LEFT|wx.ALL, 2)
252    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],1,
253                                 size=(75,-1))
254    vsizer.Add(wdgt)
255    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
256
257    vsizer = wx.BoxSizer(wx.HORIZONTAL)
258    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Comments '),
259               0,wx.ALIGN_LEFT|wx.ALL, 2)
260    wdgt = G2G.ValidatedTxtCtrl(dlg,Comments,0,size=(250,-1))
261    vsizer.Add(wdgt)
262    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
263
264    btnsizer = wx.StdDialogButtonSizer()
265    OKbtn = wx.Button(dlg, wx.ID_OK, 'Continue')
266    OKbtn.SetDefault()
267    OKbtn.Bind(wx.EVT_BUTTON,onClose)
268    btnsizer.AddButton(OKbtn) # not sure why this is needed
269    btnsizer.Realize()
270    mainsizer.Add(btnsizer, 1, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 5)
271    dlg.SetSizer(mainsizer)
272    dlg.CenterOnParent()
273    dlg.ShowModal()
274   
275def LoadImage2Tree(imagefile,G2frame,Comments,Data,Npix,Image):
276    '''Load an image into the tree. Saves the location of the image, as well as the
277    ImageTag (where there is more than one image in the file), if defined.
278    '''
279    ImgNames = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
280    TreeLbl = 'IMG '+os.path.basename(imagefile)
281    ImageTag = Data.get('ImageTag')
282    if ImageTag:
283        TreeLbl += ' #'+'%04d'%(ImageTag)
284        imageInfo = (imagefile,ImageTag)
285    else:
286        imageInfo = imagefile
287    TreeName = G2obj.MakeUniqueLabel(TreeLbl,ImgNames)
288    Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=TreeName)
289    G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)
290    Imax = np.amax(Image)
291    Imin = max(0.0,np.amin(Image))          #force positive
292    if G2frame.imageDefault:
293        Data = copy.copy(G2frame.imageDefault)
294        Data['showLines'] = True
295        Data['ring'] = []
296        Data['rings'] = []
297        Data['cutoff'] = 10
298        Data['pixLimit'] = 20
299        Data['edgemin'] = 100000000
300        Data['calibdmin'] = 0.5
301        Data['calibskip'] = 0
302        Data['ellipses'] = []
303        Data['calibrant'] = ''
304        Data['GonioAngles'] = [0.,0.,0.]
305        Data['DetDepthRef'] = False
306    else:
307        Data['type'] = 'PWDR'
308        Data['color'] = 'Paired'
309        Data['tilt'] = 0.0
310        Data['rotation'] = 0.0
311        Data['showLines'] = False
312        Data['ring'] = []
313        Data['rings'] = []
314        Data['cutoff'] = 10
315        Data['pixLimit'] = 20
316        Data['calibdmin'] = 0.5
317        Data['calibskip'] = 0
318        Data['edgemin'] = 100000000
319        Data['ellipses'] = []
320        Data['GonioAngles'] = [0.,0.,0.]
321        Data['DetDepth'] = 0.
322        Data['DetDepthRef'] = False
323        Data['calibrant'] = ''
324        Data['IOtth'] = [2.0,5.0]
325        Data['LRazimuth'] = [135,225]
326        Data['azmthOff'] = 0.0
327        Data['outChannels'] = 2500
328        Data['outAzimuths'] = 1
329        Data['centerAzm'] = False
330        Data['fullIntegrate'] = False
331        Data['setRings'] = False
332        Data['background image'] = ['',-1.0]                           
333        Data['dark image'] = ['',-1.0]
334        Data['Flat Bkg'] = 0.0
335    Data['setDefault'] = False
336    Data['range'] = [(0,Imax),[0,Imax]]
337    G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Image Controls'),Data)
338    Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(0,Imax),[0,Imax]]}
339    G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Masks'),Masks)
340    G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Stress/Strain'),
341        {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
342    G2frame.PatternTree.SetItemPyData(Id,[Npix,imageInfo])
343    G2frame.PickId = Id
344    G2frame.PickIdText = G2frame.GetTreeItemsList(G2frame.PickId)
345    G2frame.Image = Id
346
347def GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None,FormatName=''):
348    '''Read a single image with an image importer.
349
350    :param wx.Frame G2frame: main GSAS-II Frame and data object.
351    :param str imagefile: name of image file
352    :param bool imageOnly: If True return only the image,
353      otherwise  (default) return more (see below)
354    :param int/str ImageTag: specifies a particular image to be read from a file.
355      First image is read if None (default).
356    :param str formatName: the image reader formatName
357
358    :returns: an image as a numpy array or a list of four items:
359      Comments, Data, Npix and the Image, as selected by imageOnly
360
361    '''
362    # determine which formats are compatible with this file
363    primaryReaders = []
364    secondaryReaders = []
365    for rd in G2frame.ImportImageReaderlist:
366        flag = rd.ExtensionValidator(imagefile)
367        if flag is None: 
368            secondaryReaders.append(rd)
369        elif flag:
370            if not FormatName:
371                primaryReaders.append(rd)
372            elif FormatName == rd.formatName:
373                primaryReaders.append(rd)
374    if len(secondaryReaders) + len(primaryReaders) == 0:
375        print('Error: No matching format for file '+filename)
376        raise Exception('No image read')
377    fp = None
378    errorReport = ''
379    fp = open(imagefile,'Ur')
380    for rd in primaryReaders+secondaryReaders:
381        rd.ReInitialize() # purge anything from a previous read
382        fp.seek(0)  # rewind
383        rd.errors = "" # clear out any old errors
384        if not rd.ContentsValidator(fp): # rejected on cursory check
385            errorReport += "\n  "+rd.formatName + ' validator error'
386            if rd.errors: 
387                errorReport += ': '+rd.errors
388                continue
389        rdbuffer = {} # create temporary storage for file reader
390        if imageOnly:
391            ParentFrame = None # prevent GUI access on reread
392        else:
393            ParentFrame = G2frame
394        if GSASIIpath.GetConfigValue('debug'):
395            flag = rd.Reader(imagefile,fp,ParentFrame,blocknum=ImageTag)
396        else:
397            flag = False
398            try:
399                flag = rd.Reader(imagefile,fp,ParentFrame,blocknum=ImageTag)
400            except rd.ImportException as detail:
401                rd.errors += "\n  Read exception: "+str(detail)
402            except Exception as detail:
403                import traceback
404                rd.errors += "\n  Unhandled read exception: "+str(detail)
405                rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
406        if flag: # this read succeeded
407            if rd.Image is None:
408                raise Exception('No image read. Strange!')
409            if GSASIIpath.GetConfigValue('Transpose'):
410                print 'Transposing Image!'
411                rd.Image = rd.Image.T
412            #rd.readfilename = imagefile
413            if imageOnly:
414                return rd.Image
415            else:
416                return rd.Comments,rd.Data,rd.Npix,rd.Image
417    else:
418        print('Error reading file '+filename)
419        print('Error messages(s)\n'+errorReport)
420        raise Exception('No image read')   
421
422def ReadImages(G2frame,imagefile):
423    '''Read one or more images from a file and put them into the Tree
424    using image importers. Called only in :meth:`AutoIntFrame.OnTimerLoop`.
425
426    :param wx.Frame G2frame: main GSAS-II Frame and data object.
427    :param str imagefile: name of image file
428
429    :returns: a list of the id's of the IMG tree items created
430    '''
431    # determine which formats are compatible with this file
432    primaryReaders = []
433    secondaryReaders = []
434    for rd in G2frame.ImportImageReaderlist:
435        flag = rd.ExtensionValidator(imagefile)
436        if flag is None:
437            secondaryReaders.append(rd)
438        elif flag:
439            primaryReaders.append(rd)
440    if len(secondaryReaders) + len(primaryReaders) == 0:
441        print('Error: No matching format for file '+filename)
442        raise Exception('No image read')
443    errorReport = ''
444    fp = open(imagefile,'Ur')
445    rdbuffer = {} # create temporary storage for file reader
446    for rd in primaryReaders+secondaryReaders:
447        rd.ReInitialize() # purge anything from a previous read
448        fp.seek(0)  # rewind
449        rd.errors = "" # clear out any old errors
450        if not rd.ContentsValidator(fp): # rejected on cursory check
451            errorReport += "\n  "+rd.formatName + ' validator error'
452            if rd.errors: 
453                errorReport += ': '+rd.errors
454                continue
455        ParentFrame = G2frame
456        block = 0
457        repeat = True
458        CreatedIMGitems = []
459        while repeat: # loop if the reader asks for another pass on the file
460            block += 1
461            repeat = False
462            if GSASIIpath.GetConfigValue('debug'):
463                flag = rd.Reader(imagefile,fp,ParentFrame,blocknum=block,Buffer=rdbuffer)
464            else:
465                flag = False
466                try:
467                    flag = rd.Reader(imagefile,fp,ParentFrame,blocknum=block,Buffer=rdbuffer)
468                except rd.ImportException as detail:
469                    rd.errors += "\n  Read exception: "+str(detail)
470                except Exception as detail:
471                    import traceback
472                    rd.errors += "\n  Unhandled read exception: "+str(detail)
473                    rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
474            if flag: # this read succeeded
475                if rd.Image is None:
476                    raise Exception('No image read. Strange!')
477                if GSASIIpath.GetConfigValue('Transpose'):
478                    print 'Transposing Image!'
479                    rd.Image = rd.Image.T
480                rd.Data['ImageTag'] = rd.repeatcount
481                LoadImage2Tree(imagefile,G2frame,rd.Comments,rd.Data,rd.Npix,rd.Image)
482                repeat = rd.repeat
483            CreatedIMGitems.append(G2frame.Image)
484        if CreatedIMGitems: return CreatedIMGitems
485    else:
486        print('Error reading file '+filename)
487        print('Error messages(s)\n'+errorReport)
488        return []
489        #raise Exception('No image read')   
490
491def SaveMultipleImg(G2frame):
492    if not G2frame.PatternTree.GetCount():
493        print 'no images!'
494        return
495    choices = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
496    if len(choices) == 1:
497        names = choices
498    else:
499        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices)
500        dlg.SetSelections([])
501        names = []
502        if dlg.ShowModal() == wx.ID_OK:
503            names = [choices[sel] for sel in dlg.GetSelections()]
504        dlg.Destroy()
505    if not names: return
506    for name in names:
507        Id = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, name)
508        Npix,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(Id)
509        imroot = os.path.splitext(imagefile)[0]
510        if imagetag:
511            imroot += '_' + str(imagetag)
512        Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Image Controls'))
513        print('Writing '+imroot+'.imctrl')
514        File = open(imroot+'.imctrl','w')
515        keys = ['type','wavelength','calibrant','distance','center',
516                    'tilt','rotation','azmthOff','fullIntegrate','LRazimuth',
517                    'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
518                    'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg',
519                    'binType','SampleShape','PolaVal','SampleAbs','dark image','background image']
520        for key in keys:
521            if key not in Data: continue    #uncalibrated!
522            File.write(key+':'+str(Data[key])+'\n')
523        File.close()
524        mask = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Masks'))
525        G2imG.CleanupMasks(mask)
526        print('Writing '+imroot+'.immask')
527        File = open(imroot+'.immask','w')
528        for key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
529            File.write(key+':'+str(mask[key])+'\n')
530        File.close()
531       
532def PutG2Image(filename,Comments,Data,Npix,image):
533    'Write an image as a python pickle - might be better as an .edf file?'
534    File = open(filename,'wb')
535    cPickle.dump([Comments,Data,Npix,image],File,1)
536    File.close()
537    return
538   
539# should get moved to importer when ready to test
540def GetEdfData(filename,imageOnly=False):   
541    'Read European detector data edf file'
542    import struct as st
543    import array as ar
544    if not imageOnly:
545        print 'Read European detector data edf file: ',filename
546    File = open(filename,'rb')
547    fileSize = os.stat(filename).st_size
548    head = File.read(3072)
549    lines = head.split('\n')
550    sizexy = [0,0]
551    pixSize = [154,154]     #Pixium4700?
552    cent = [0,0]
553    wave = 1.54187  #default <CuKa>
554    dist = 1000.
555    head = ['European detector data',]
556    for line in lines:
557        line = line.replace(';',' ').strip()
558        fields = line.split()
559        if 'Dim_1' in line:
560            sizexy[0] = int(fields[2])
561        elif 'Dim_2' in line:
562            sizexy[1] = int(fields[2])
563        elif 'DataType' in line:
564            dType = fields[2]
565        elif 'wavelength' in line:
566            wave = float(fields[2])
567        elif 'Size' in line:
568            imSize = int(fields[2])
569#        elif 'DataType' in lines:
570#            dType = fields[2]
571        elif 'pixel_size_x' in line:
572            pixSize[0] = float(fields[2])
573        elif 'pixel_size_y' in line:
574            pixSize[1] = float(fields[2])
575        elif 'beam_center_x' in line:
576            cent[0] = float(fields[2])
577        elif 'beam_center_y' in line:
578            cent[1] = float(fields[2])
579        elif 'refined_distance' in line:
580            dist = float(fields[2])
581        if line:
582            head.append(line)
583        else:   #blank line at end of header
584            break 
585    File.seek(fileSize-imSize)
586    if dType == 'UnsignedShort':       
587        image = np.array(ar.array('H',File.read(imSize)),dtype=np.int32)
588    elif dType == 'UnsignedInt':
589        image = np.array(ar.array('L',File.read(imSize)),dtype=np.int32)
590    elif dType == 'UnsignedLong':
591        image = np.array(ar.array('L',File.read(imSize)),dtype=np.int32)
592    elif dType == 'SignedInteger':
593        image = np.array(ar.array('l',File.read(imSize)),dtype=np.int32)
594    image = np.reshape(image,(sizexy[1],sizexy[0]))
595    data = {'pixelSize':pixSize,'wavelength':wave,'distance':dist,'center':cent,'size':sizexy}
596    Npix = sizexy[0]*sizexy[1]
597    File.close()   
598    if imageOnly:
599        return image
600    else:
601        return head,data,Npix,image
602       
603# should get moved to importer when ready to test
604def GetRigaku(filename,imageOnly=False):
605    'Read Rigaku R-Axis IV image file'
606    import struct as st
607    import array as ar
608    if not imageOnly:
609        print 'Read Rigaku R-Axis IV file: ',filename   
610    File = open(filename,'rb')
611    fileSize = os.stat(filename).st_size
612    Npix = (fileSize-6000)/2
613    Head = File.read(6000)
614    head = ['Rigaku R-Axis IV detector data',]
615    image = np.array(ar.array('H',File.read(fileSize-6000)),dtype=np.int32)
616    print fileSize,image.shape
617    print head
618    if Npix == 9000000:
619        sizexy = [3000,3000]
620        pixSize = [100.,100.]       
621    elif Npix == 2250000:
622        sizexy = [1500,1500]
623        pixSize = [200.,200.]
624    else:
625        sizexy = [6000,6000]
626        pixSize = [50.,50.] 
627    image = np.reshape(image,(sizexy[1],sizexy[0]))       
628    data = {'pixelSize':pixSize,'wavelength':1.5428,'distance':250.0,'center':[150.,150.],'size':sizexy} 
629    File.close()   
630    if imageOnly:
631        return image
632    else:
633        return head,data,Npix,image
634   
635# should get moved to importer when ready to test       
636def GetImgData(filename,imageOnly=False):
637    'Read an ADSC image file'
638    import struct as st
639    import array as ar
640    if not imageOnly:
641        print 'Read ADSC img file: ',filename
642    File = open(filename,'rb')
643    head = File.read(511)
644    lines = head.split('\n')
645    head = []
646    center = [0,0]
647    for line in lines[1:-2]:
648        line = line.strip()[:-1]
649        if line:
650            if 'SIZE1' in line:
651                size = int(line.split('=')[1])
652                Npix = size*size
653            elif 'WAVELENGTH' in line:
654                wave = float(line.split('=')[1])
655            elif 'BIN' in line:
656                if line.split('=')[1] == '2x2':
657                    pixel=(102,102)
658                else:
659                    pixel = (51,51)
660            elif 'DISTANCE' in line:
661                distance = float(line.split('=')[1])
662            elif 'CENTER_X' in line:
663                center[0] = float(line.split('=')[1])
664            elif 'CENTER_Y' in line:
665                center[1] = float(line.split('=')[1])
666            head.append(line)
667    data = {'pixelSize':pixel,'wavelength':wave,'distance':distance,'center':center,'size':[size,size]}
668    image = []
669    row = 0
670    pos = 512
671    File.seek(pos)
672    image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
673    image = np.reshape(image,(sizexy[1],sizexy[0]))
674#    image = np.zeros(shape=(size,size),dtype=np.int32)   
675#    while row < size:
676#        File.seek(pos)
677#        line = ar.array('H',File.read(2*size))
678#        image[row] = np.asarray(line)
679#        row += 1
680#        pos += 2*size
681    File.close()
682    if imageOnly:
683        return image
684    else:
685        return lines[1:-2],data,Npix,image
686       
687# should get moved to importer when ready to test
688def GetMAR345Data(filename,imageOnly=False):
689    'Read a MAR-345 image plate image'
690    import array as ar
691    import struct as st
692    try:
693        import pack_f as pf
694    except:
695        msg = wx.MessageDialog(None, message="Unable to load the GSAS MAR image decompression, pack_f",
696                               caption="Import Error",
697                               style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
698        msg.ShowModal()
699        return None,None,None,None
700
701    if not imageOnly:
702        print 'Read Mar345 file: ',filename
703    File = open(filename,'rb')
704    head = File.read(4095)
705    numbers = st.unpack('<iiiiiiiiii',head[:40])
706    lines = head[128:].split('\n')
707    head = []
708    for line in lines:
709        line = line.strip()
710        if 'PIXEL' in line:
711            values = line.split()
712            pixel = (int(values[2]),int(values[4]))     #in microns
713        elif 'WAVELENGTH' in line:
714            wave = float(line.split()[1])
715        elif 'DISTANCE' in line:
716            distance = float(line.split()[1])           #in mm
717            if not distance:
718                distance = 500.
719        elif 'CENTER' in line:
720            values = line.split()
721            center = [float(values[2])/10.,float(values[4])/10.]    #make in mm from pixels
722        if line: 
723            head.append(line)
724    data = {'pixelSize':pixel,'wavelength':wave,'distance':distance,'center':center}
725    for line in head:
726        if 'FORMAT' in line[0:6]:
727            items = line.split()
728            sizex = int(items[1])
729            Npix = int(items[3])
730            sizey = int(Npix/sizex)
731    pos = 4096
732    data['size'] = [sizex,sizey]
733    File.seek(pos)
734    line = File.read(8)
735    while 'CCP4' not in line:       #get past overflow list for now
736        line = File.read(8)
737        pos += 8
738    pos += 37
739    File.seek(pos)
740    raw = File.read()
741    File.close()
742    image = np.zeros(shape=(sizex,sizey),dtype=np.int32)
743   
744    image = np.flipud(pf.pack_f(len(raw),raw,sizex,sizey,image).T)  #transpose to get it right way around & flip
745    if imageOnly:
746        return image
747    else:
748        return head,data,Npix,image
749       
750def ProjFileOpen(G2frame,showProvenance=True):
751    'Read a GSAS-II project file and load into the G2 data tree'
752    if not os.path.exists(G2frame.GSASprojectfile):
753        print ('\n*** Error attempt to open project file that does not exist:\n   '+
754               str(G2frame.GSASprojectfile))
755        return
756    LastSavedUsing = None
757    file = open(G2frame.GSASprojectfile,'rb')
758    if showProvenance: print 'loading from file: ',G2frame.GSASprojectfile
759    G2frame.SetTitle("GSAS-II data tree: "+
760                     os.path.split(G2frame.GSASprojectfile)[1])
761    wx.BeginBusyCursor()
762    try:
763        while True:
764            try:
765                data = cPickle.load(file)
766            except EOFError:
767                break
768            datum = data[0]
769           
770            Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=datum[0])
771            if 'PWDR' in datum[0]:               
772                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
773                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
774                G2frame.PatternTree.SetItemPyData(Id,datum[1][:3])  #temp. trim off junk (patch?)
775            elif datum[0].startswith('HKLF'): 
776                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
777                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
778                G2frame.PatternTree.SetItemPyData(Id,datum[1])
779            else:
780                G2frame.PatternTree.SetItemPyData(Id,datum[1])             
781                if datum[0] == 'Controls' and 'LastSavedUsing' in datum[1]:
782                    LastSavedUsing = datum[1]['LastSavedUsing']
783                if datum[0] == 'Controls' and 'PythonVersions' in datum[1] and GSASIIpath.GetConfigValue('debug') and showProvenance:
784                    print('Packages used to create .GPX file:')
785                    if 'dict' in str(type(datum[1]['PythonVersions'])):  #patch
786                        for p in sorted(datum[1]['PythonVersions'],key=lambda s: s.lower()):
787                            print("{:>14s}: {:s}".format(p[0],p[1]))
788                    else:
789                        for p in datum[1]['PythonVersions']:
790                            print("{:<12s} {:s}".format(p[0]+':',p[1]))
791            for datus in data[1:]:
792                sub = G2frame.PatternTree.AppendItem(Id,datus[0])
793#patch
794                if datus[0] == 'Instrument Parameters' and len(datus[1]) == 1:
795                    if 'PWDR' in datum[0]:
796                        datus[1] = [dict(zip(datus[1][3],zip(datus[1][0],datus[1][1],datus[1][2]))),{}]
797                    else:
798                        datus[1] = [dict(zip(datus[1][2],zip(datus[1][0],datus[1][1]))),{}]
799                    for item in datus[1][0]:               #zip makes tuples - now make lists!
800                        datus[1][0][item] = list(datus[1][0][item])
801#end patch
802                G2frame.PatternTree.SetItemPyData(sub,datus[1])
803            if 'IMG' in datum[0]:                   #retrieve image default flag & data if set
804                Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
805                if Data['setDefault']:
806                    G2frame.imageDefault = Data               
807        file.close()
808        if LastSavedUsing:
809            print('GPX load successful. Last saved with GSAS-II version '+LastSavedUsing)
810        else:
811            print('project load successful')
812        G2frame.NewPlot = True
813    except:
814        msg = wx.MessageDialog(G2frame,message="Error reading file "+
815            str(G2frame.GSASprojectfile)+". This is not a GSAS-II .gpx file",
816            caption="Load Error",style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
817        msg.ShowModal()
818    finally:
819        wx.EndBusyCursor()
820        G2frame.Status.SetStatusText('To reorder tree items, use mouse RB to drag/drop them')
821   
822def ProjFileSave(G2frame):
823    'Save a GSAS-II project file'
824    if not G2frame.PatternTree.IsEmpty():
825        file = open(G2frame.GSASprojectfile,'wb')
826        print 'save to file: ',G2frame.GSASprojectfile
827        # stick the file name into the tree and version info into tree so they are saved.
828        # (Controls should always be created at this point)
829        try:
830            Controls = G2frame.PatternTree.GetItemPyData(
831                G2gd.GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
832            Controls['LastSavedAs'] = os.path.abspath(G2frame.GSASprojectfile)
833            Controls['LastSavedUsing'] = str(GSASIIpath.GetVersionNumber())
834            Controls['PythonVersions'] = G2frame.PackageVersions
835        except:
836            pass
837        wx.BeginBusyCursor()
838        try:
839            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
840            while item:
841                data = []
842                name = G2frame.PatternTree.GetItemText(item)
843                data.append([name,G2frame.PatternTree.GetItemPyData(item)])
844                item2, cookie2 = G2frame.PatternTree.GetFirstChild(item)
845                while item2:
846                    name = G2frame.PatternTree.GetItemText(item2)
847                    data.append([name,G2frame.PatternTree.GetItemPyData(item2)])
848                    item2, cookie2 = G2frame.PatternTree.GetNextChild(item, cookie2)                           
849                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)                           
850                cPickle.dump(data,file,1)
851            file.close()
852            pth = os.path.split(os.path.abspath(G2frame.GSASprojectfile))[0]
853            if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
854            G2frame.LastGPXdir = pth
855        finally:
856            wx.EndBusyCursor()
857        print('project save successful')
858
859def SaveIntegration(G2frame,PickId,data,Overwrite=False):
860    'Save image integration results as powder pattern(s)'
861    azms = G2frame.Integrate[1]
862    X = G2frame.Integrate[2][:-1]
863    N = len(X)
864    Id = G2frame.PatternTree.GetItemParent(PickId)
865    name = G2frame.PatternTree.GetItemText(Id)
866    name = name.replace('IMG ',data['type']+' ')
867    Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Comments'))
868    if 'PWDR' in name:
869        names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
870        codes = [0 for i in range(11)]
871    elif 'SASD' in name:
872        names = ['Type','Lam','Zero','Azimuth'] 
873        codes = [0 for i in range(4)]
874        X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
875    Xminmax = [X[0],X[-1]]
876    LRazm = data['LRazimuth']
877    Azms = []
878    dazm = 0.
879    if data['fullIntegrate'] and data['outAzimuths'] == 1:
880        Azms = [45.0,]                              #a poor man's average?
881    else:
882        for i,azm in enumerate(azms[:-1]):
883            if azm > 360. and azms[i+1] > 360.:
884                Azms.append(G2img.meanAzm(azm%360.,azms[i+1]%360.))
885            else:   
886                Azms.append(G2img.meanAzm(azm,azms[i+1]))
887        dazm = np.min(np.abs(np.diff(azms)))/2.
888    G2frame.IntgOutList = []
889    for i,azm in enumerate(azms[:-1]):
890        Aname = name+" Azm= %.2f"%((azm+dazm)%360.)
891        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
892        # if Overwrite delete any duplicate
893        if Overwrite and G2gd.GetPatternTreeItemId(G2frame,G2frame.root,Aname):
894            print('Replacing '+Aname)
895            item = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,Aname)
896            G2frame.PatternTree.Delete(item)
897        else:
898            nOcc = 0
899            while item:
900                Name = G2frame.PatternTree.GetItemText(item)
901                if Aname in Name:
902                    nOcc += 1
903                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
904            if nOcc:
905                Aname += '(%d)'%(nOcc)
906        Sample = G2pdG.SetDefaultSample()
907        Sample['Gonio. radius'] = data['distance']
908        Sample['Omega'] = data['GonioAngles'][0]
909        Sample['Chi'] = data['GonioAngles'][1]
910        Sample['Phi'] = data['GonioAngles'][2]
911        Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
912        if 'PWDR' in Aname:
913            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!
914        elif 'SASD' in Aname:
915            Sample['Trans'] = data['SampleAbs'][0]
916            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
917        Y = G2frame.Integrate[0][i]
918        W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
919        Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=Aname)
920        G2frame.IntgOutList.append(Id)
921        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
922        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
923        if 'PWDR' in Aname:
924            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
925                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
926        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
927        for item in inst[0]:
928            inst[0][item] = list(inst[0][item])
929        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
930        if 'PWDR' in Aname:
931            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
932            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Peak List'),{'sigDict':{},'peaks':[]})
933            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
934            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Unit Cells List'),[])
935            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Reflection Lists'),{})
936        elif 'SASD' in Aname:             
937            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
938            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
939            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
940        valuesdict = {
941            'wtFactor':1.0,
942            'Dummy':False,
943            'ranId':ran.randint(0,sys.maxint),
944            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
945            'qPlot':False,'dPlot':False,'sqrtPlot':False
946            }
947        G2frame.PatternTree.SetItemPyData(
948            Id,[valuesdict,
949                [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
950    return Id       #last powder pattern generated
951           
952def PDFSave(G2frame,exports):
953    'Save a PDF G(r) and S(Q) in column formats'
954    for export in exports:
955        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
956        SQname = 'S(Q)'+export[4:]
957        GRname = 'G(R)'+export[4:]
958        sqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.sq')
959        grfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.gr')
960        sqId = G2gd.GetPatternTreeItemId(G2frame, PickId, SQname)
961        grId = G2gd.GetPatternTreeItemId(G2frame, PickId, GRname)
962        sqdata = np.array(G2frame.PatternTree.GetItemPyData(sqId)[1][:2]).T
963        grdata = np.array(G2frame.PatternTree.GetItemPyData(grId)[1][:2]).T
964        sqfile = open(sqfilename,'w')
965        grfile = open(grfilename,'w')
966        sqfile.write('#T S(Q) %s\n'%(export))
967        grfile.write('#T G(R) %s\n'%(export))
968        sqfile.write('#L Q     S(Q)\n')
969        grfile.write('#L R     G(R)\n')
970        for q,sq in sqdata:
971            sqfile.write("%15.6g %15.6g\n" % (q,sq))
972        sqfile.close()
973        for r,gr in grdata:
974            grfile.write("%15.6g %15.6g\n" % (r,gr))
975        grfile.close()
976   
977def PeakListSave(G2frame,file,peaks):
978    'Save powder peaks to a data file'
979    print 'save peak list to file: ',G2frame.peaklistfile
980    if not peaks:
981        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
982        try:
983            result = dlg.ShowModal()
984        finally:
985            dlg.Destroy()
986        return
987    for peak in peaks:
988        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
989            (peak[0],peak[2],peak[4],peak[6]))
990    print 'peak list saved'
991             
992def IndexPeakListSave(G2frame,peaks):
993    'Save powder peaks from the indexing list'
994    file = open(G2frame.peaklistfile,'wa')
995    print 'save index peak list to file: ',G2frame.peaklistfile
996    wx.BeginBusyCursor()
997    try:
998        if not peaks:
999            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1000            try:
1001                result = dlg.ShowModal()
1002            finally:
1003                dlg.Destroy()
1004            return
1005        for peak in peaks:
1006            file.write("%12.6f\n" % (peak[7]))
1007        file.close()
1008    finally:
1009        wx.EndBusyCursor()
1010    print 'index peak list saved'
1011   
1012def SetNewPhase(Name='New Phase',SGData=None,cell=None,Super=None):
1013    '''Create a new phase dict with default values for various parameters
1014
1015    :param str Name: Name for new Phase
1016
1017    :param dict SGData: space group data from :func:`GSASIIspc:SpcGroup`;
1018      defaults to data for P 1
1019
1020    :param list cell: unit cell parameter list; defaults to
1021      [1.0,1.0,1.0,90.,90,90.,1.]
1022
1023    '''
1024    if SGData is None: SGData = G2spc.SpcGroup('P 1')[1]
1025    if cell is None: cell=[1.0,1.0,1.0,90.,90,90.,1.]
1026    phaseData = {
1027        'ranId':ran.randint(0,sys.maxint),
1028        'General':{
1029            'Name':Name,
1030            'Type':'nuclear',
1031            'AtomPtrs':[3,1,7,9],
1032            'SGData':SGData,
1033            'Cell':[False,]+cell,
1034            'Pawley dmin':1.0,
1035            'Data plot type':'None',
1036            'SH Texture':{
1037                'Order':0,
1038                'Model':'cylindrical',
1039                'Sample omega':[False,0.0],
1040                'Sample chi':[False,0.0],
1041                'Sample phi':[False,0.0],
1042                'SH Coeff':[False,{}],
1043                'SHShow':False,
1044                'PFhkl':[0,0,1],
1045                'PFxyz':[0,0,1],
1046                'PlotType':'Pole figure',
1047                'Penalty':[['',],0.1,False,1.0]}},
1048        'Atoms':[],
1049        'Drawing':{},
1050        'Histograms':{},
1051        'Pawley ref':[],
1052        'RBModels':{},
1053        }
1054    if Super and Super.get('Use',False):
1055        phaseData['General'].update({'Type':'modulated','Super':True,'SuperSg':Super['ssSymb']})
1056        phaseData['General']['SSGData'] = G2spc.SSpcGroup(SGData,Super['ssSymb'])
1057        phaseData['General']['SuperVec'] = [Super['ModVec'],False,Super['maxH']]
1058
1059    return phaseData
1060       
1061class MultipleChoicesDialog(wx.Dialog):
1062    '''A dialog that offers a series of choices, each with a
1063    title and a wx.Choice widget. Intended to be used Modally.
1064    typical input:
1065
1066        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1067        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1068       
1069    selections are placed in self.chosen when OK is pressed
1070    '''
1071    def __init__(self,choicelist,headinglist,
1072                 head='Select options',
1073                 title='Please select from options below',
1074                 parent=None):
1075        self.chosen = []
1076        wx.Dialog.__init__(
1077            self,parent,wx.ID_ANY,head, 
1078            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1079        panel = wx.Panel(self)
1080        mainSizer = wx.BoxSizer(wx.VERTICAL)
1081        mainSizer.Add((10,10),1)
1082        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1083        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1084        self.ChItems = []
1085        for choice,lbl in zip(choicelist,headinglist):
1086            mainSizer.Add((10,10),1)
1087            self.chosen.append(0)
1088            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1089            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1090            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1091            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1092
1093        OkBtn = wx.Button(panel,-1,"Ok")
1094        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1095        cancelBtn = wx.Button(panel,-1,"Cancel")
1096        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1097        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1098        btnSizer.Add((20,20),1)
1099        btnSizer.Add(OkBtn)
1100        btnSizer.Add((20,20),1)
1101        btnSizer.Add(cancelBtn)
1102        btnSizer.Add((20,20),1)
1103        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1104        panel.SetSizer(mainSizer)
1105        panel.Fit()
1106        self.Fit()
1107       
1108    def OnOk(self,event):
1109        parent = self.GetParent()
1110        if parent is not None: parent.Raise()
1111        # save the results from the choice widgets
1112        self.chosen = []
1113        for w in self.ChItems:
1114            self.chosen.append(w.GetSelection())
1115        self.EndModal(wx.ID_OK)             
1116           
1117    def OnCancel(self,event):
1118        parent = self.GetParent()
1119        if parent is not None: parent.Raise()
1120        self.chosen = []
1121        self.EndModal(wx.ID_CANCEL)             
1122           
1123def ExtractFileFromZip(filename, selection=None, confirmread=True,
1124                       confirmoverwrite=True, parent=None,
1125                       multipleselect=False):
1126    '''If the filename is a zip file, extract a file from that
1127    archive.
1128
1129    :param list Selection: used to predefine the name of the file
1130      to be extracted. Filename case and zip directory name are
1131      ignored in selection; the first matching file is used.
1132
1133    :param bool confirmread: if True asks the user to confirm before expanding
1134      the only file in a zip
1135
1136    :param bool confirmoverwrite: if True asks the user to confirm
1137      before overwriting if the extracted file already exists
1138
1139    :param bool multipleselect: if True allows more than one zip
1140      file to be extracted, a list of file(s) is returned.
1141      If only one file is present, do not ask which one, otherwise
1142      offer a list of choices (unless selection is used).
1143   
1144    :returns: the name of the file that has been created or a
1145      list of files (see multipleselect)
1146
1147    If the file is not a zipfile, return the name of the input file.
1148    If the zipfile is empty or no file has been selected, return None
1149    '''
1150    import zipfile # do this now, since we can save startup time by doing this only on need
1151    import shutil
1152    zloc = os.path.split(filename)[0]
1153    if not zipfile.is_zipfile(filename):
1154        #print("not zip")
1155        return filename
1156
1157    z = zipfile.ZipFile(filename,'r')
1158    zinfo = z.infolist()
1159
1160    if len(zinfo) == 0:
1161        #print('Zip has no files!')
1162        zlist = [-1]
1163    if selection:
1164        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1165        if selection.lower() in choices:
1166            zlist = [choices.index(selection.lower())]
1167        else:
1168            print('debug: file '+str(selection)+' was not found in '+str(filename))
1169            zlist = [-1]
1170    elif len(zinfo) == 1 and confirmread:
1171        result = wx.ID_NO
1172        dlg = wx.MessageDialog(
1173            parent,
1174            'Is file '+str(zinfo[0].filename)+
1175            ' what you want to extract from '+
1176            str(os.path.split(filename)[1])+'?',
1177            'Confirm file', 
1178            wx.YES_NO | wx.ICON_QUESTION)
1179        try:
1180            result = dlg.ShowModal()
1181        finally:
1182            dlg.Destroy()
1183        if result == wx.ID_NO:
1184            zlist = [-1]
1185        else:
1186            zlist = [0]
1187    elif len(zinfo) == 1:
1188        zlist = [0]
1189    elif multipleselect:
1190        # select one or more from a from list
1191        choices = [i.filename for i in zinfo]
1192        dlg = G2G.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1193            'Choose file(s)',choices)
1194        if dlg.ShowModal() == wx.ID_OK:
1195            zlist = dlg.GetSelections()
1196        else:
1197            zlist = []
1198        dlg.Destroy()
1199    else:
1200        # select one from a from list
1201        choices = [i.filename for i in zinfo]
1202        dlg = wx.SingleChoiceDialog(parent,
1203            'Select file to extract from zip file'+str(filename),'Choose file',
1204            choices,)
1205        if dlg.ShowModal() == wx.ID_OK:
1206            zlist = [dlg.GetSelection()]
1207        else:
1208            zlist = [-1]
1209        dlg.Destroy()
1210       
1211    outlist = []
1212    for zindex in zlist:
1213        if zindex >= 0:
1214            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1215            if os.path.exists(efil) and confirmoverwrite:
1216                result = wx.ID_NO
1217                dlg = wx.MessageDialog(parent,
1218                    'File '+str(efil)+' already exists. OK to overwrite it?',
1219                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1220                try:
1221                    result = dlg.ShowModal()
1222                finally:
1223                    dlg.Destroy()
1224                if result == wx.ID_NO:
1225                    zindex = -1
1226        if zindex >= 0:
1227            # extract the file to the current directory, regardless of it's original path
1228            #z.extract(zinfo[zindex],zloc)
1229            eloc,efil = os.path.split(zinfo[zindex].filename)
1230            outfile = os.path.join(zloc, efil)
1231            fpin = z.open(zinfo[zindex])
1232            fpout = file(outfile, "wb")
1233            shutil.copyfileobj(fpin, fpout)
1234            fpin.close()
1235            fpout.close()
1236            outlist.append(outfile)
1237    z.close()
1238    if multipleselect and len(outlist) >= 1:
1239        return outlist
1240    elif len(outlist) == 1:
1241        return outlist[0]
1242    else:
1243        return None
1244
1245######################################################################
1246# base classes for reading various types of data files
1247#   not used directly, only by subclassing
1248######################################################################
1249try:
1250    E,SGData = G2spc.SpcGroup('P 1') # data structure for default space group
1251except: # errors on doc build
1252    SGData = None
1253P1SGData = SGData
1254######################################################################
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    UseReader = True  # in __init__ set value of self.UseReader to False to skip use of current importer
1270    def __init__(self,formatName,longFormatName=None,
1271                 extensionlist=[],strictExtension=False,):
1272        self.formatName = formatName # short string naming file type
1273        if longFormatName: # longer string naming file type
1274            self.longFormatName = longFormatName
1275        else:
1276            self.longFormatName = formatName
1277        # define extensions that are allowed for the file type
1278        # for windows, remove any extensions that are duplicate, as case is ignored
1279        if sys.platform == 'windows' and extensionlist:
1280            extensionlist = list(set([s.lower() for s in extensionlist]))
1281        self.extensionlist = extensionlist
1282        # If strictExtension is True, the file will not be read, unless
1283        # the extension matches one in the extensionlist
1284        self.strictExtension = strictExtension
1285        self.errors = ''
1286        self.warnings = ''
1287        # used for readers that will use multiple passes to read
1288        # more than one data block
1289        self.repeat = False
1290        self.selections = []
1291        self.repeatcount = 0
1292        self.readfilename = '?'
1293        #print 'created',self.__class__
1294
1295    def ReInitialize(self):
1296        'Reinitialize the Reader to initial settings'
1297        self.errors = ''
1298        self.warnings = ''
1299        self.repeat = False
1300        self.repeatcount = 0
1301        self.readfilename = '?'
1302
1303    def BlockSelector(self, ChoiceList, ParentFrame=None,title='Select a block',
1304        size=None, header='Block Selector',useCancel=True):
1305        ''' Provide a wx dialog to select a block if the file contains more
1306        than one set of data and one must be selected
1307        '''
1308        if useCancel:
1309            dlg = wx.SingleChoiceDialog(
1310                ParentFrame,title, header,ChoiceList)
1311        else:
1312            dlg = wx.SingleChoiceDialog(
1313                ParentFrame,title, header,ChoiceList,
1314                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1315        if size: dlg.SetSize(size)
1316        dlg.CenterOnParent()
1317        if dlg.ShowModal() == wx.ID_OK:
1318            sel = dlg.GetSelection()
1319            return sel
1320        else:
1321            return None
1322        dlg.Destroy()
1323
1324    def MultipleBlockSelector(self, ChoiceList, ParentFrame=None,
1325        title='Select a block',size=None, header='Block Selector'):
1326        '''Provide a wx dialog to select a block of data if the
1327        file contains more than one set of data and one must be
1328        selected.
1329
1330        :returns: a list of the selected blocks
1331        '''
1332        dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1333            wx.CHOICEDLG_STYLE)
1334        dlg.CenterOnScreen()
1335        if size: dlg.SetSize(size)
1336        if dlg.ShowModal() == wx.ID_OK:
1337            sel = dlg.GetSelections()
1338        else:
1339            return []
1340        dlg.Destroy()
1341        selected = []
1342        if len(ChoiceList) in sel:
1343            return range(len(ChoiceList))
1344        else:
1345            return sel
1346        return selected
1347
1348    def MultipleChoicesDialog(self, choicelist, headinglist, ParentFrame=None, **kwargs):
1349        '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1350        widget. Typical input:
1351       
1352           * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1353           
1354           * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1355           
1356        optional keyword parameters are: head (window title) and title
1357        returns a list of selected indicies for each choice (or None)
1358        '''
1359        result = None
1360        dlg = MultipleChoicesDialog(choicelist,headinglist,
1361            parent=ParentFrame, **kwargs)         
1362        dlg.CenterOnParent()
1363        if dlg.ShowModal() == wx.ID_OK:
1364            result = dlg.chosen
1365        dlg.Destroy()
1366        return result
1367
1368    def ShowBusy(self):
1369        wx.BeginBusyCursor()
1370#        wx.Yield() # make it happen now!
1371
1372    def DoneBusy(self):
1373        wx.EndBusyCursor()
1374        wx.Yield() # make it happen now!
1375       
1376#    def Reader(self, filename, filepointer, ParentFrame=None, **unused):
1377#        '''This method must be supplied in the child class to read the file.
1378#        if the read fails either return False or raise an Exception
1379#        preferably of type ImportException.
1380#        '''
1381#        #start reading
1382#        raise ImportException("Error occurred while...")
1383#        self.errors += "Hint for user on why the error occur
1384#        return False # if an error occurs
1385#        return True # if read OK
1386
1387    def ExtensionValidator(self, filename):
1388        '''This methods checks if the file has the correct extension
1389        Return False if this filename will not be supported by this reader
1390        Return True if the extension matches the list supplied by the reader
1391        Return None if the reader allows un-registered extensions
1392        '''
1393        if filename:
1394            ext = os.path.splitext(filename)[1]
1395            if sys.platform == 'windows': ext = ext.lower()
1396            if ext in self.extensionlist: return True
1397            if self.strictExtension: return False
1398        return None
1399
1400    def ContentsValidator(self, filepointer):
1401        '''This routine will attempt to determine if the file can be read
1402        with the current format.
1403        This will typically be overridden with a method that
1404        takes a quick scan of [some of]
1405        the file contents to do a "sanity" check if the file
1406        appears to match the selected format.
1407        Expected to be called via self.Validator()
1408        '''
1409        #filepointer.seek(0) # rewind the file pointer
1410        return True
1411
1412    def CIFValidator(self, filepointer):
1413        '''A :meth:`ContentsValidator` for use to validate CIF files.
1414        '''
1415        for i,l in enumerate(filepointer):
1416            if i >= 1000: return True
1417            '''Encountered only blank lines or comments in first 1000
1418            lines. This is unlikely, but assume it is CIF anyway, since we are
1419            even less likely to find a file with nothing but hashes and
1420            blank lines'''
1421            line = l.strip()
1422            if len(line) == 0: # ignore blank lines
1423                continue 
1424            elif line.startswith('#'): # ignore comments
1425                continue 
1426            elif line.startswith('data_'): # on the right track, accept this file
1427                return True
1428            else: # found something invalid
1429                self.errors = 'line '+str(i+1)+' contains unexpected data:\n'
1430                self.errors += '  '+str(l)
1431                self.errors += '  Note: a CIF should only have blank lines or comments before'
1432                self.errors += '        a data_ statement begins a block.'
1433                return False 
1434
1435######################################################################
1436class ImportPhase(ImportBaseclass):
1437    '''Defines a base class for the reading of files with coordinates
1438
1439    Objects constructed that subclass this (in import/G2phase_*.py etc.) will be used
1440    in :meth:`GSASII.GSASII.OnImportPhase`.
1441    See :ref:`Writing a Import Routine<Import_Routines>`
1442    for an explanation on how to use this class.
1443
1444    '''
1445    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1446        strictExtension=False,):
1447        # call parent __init__
1448        ImportBaseclass.__init__(self,formatName,longFormatName,
1449            extensionlist,strictExtension)
1450        self.Phase = None # a phase must be created with G2IO.SetNewPhase in the Reader
1451        self.Constraints = None
1452
1453    def PhaseSelector(self, ChoiceList, ParentFrame=None,
1454        title='Select a phase', size=None,header='Phase Selector'):
1455        ''' Provide a wx dialog to select a phase if the file contains more
1456        than one phase
1457        '''
1458        return self.BlockSelector(ChoiceList,ParentFrame,title,
1459            size,header)
1460
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    '''
1480    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1481        strictExtension=False,):
1482        ImportBaseclass.__init__(self,formatName,longFormatName,
1483            extensionlist,strictExtension)
1484
1485        # define contents of Structure Factor entry
1486        self.Parameters = []
1487        'self.Parameters is a list with two dicts for data parameter settings'
1488        self.InitParameters()
1489        self.RefDict = {'RefList':[],'FF':{},'Super':0}
1490        self.Banks = []             #for multi bank data (usually TOF)
1491        '''self.RefDict is a dict containing the reflection information, as read from the file.
1492        Item 'RefList' contains the reflection information. See the
1493        :ref:`Single Crystal Reflection Data Structure<XtalRefl_table>`
1494        for the contents of each row. Dict element 'FF'
1495        contains the form factor values for each element type; if this entry
1496        is left as initialized (an empty list) it will be initialized as needed later.
1497        '''
1498    def ReInitialize(self):
1499        'Reinitialize the Reader to initial settings'
1500        ImportBaseclass.ReInitialize(self)
1501        self.InitParameters()
1502        self.Banks = []             #for multi bank data (usually TOF)
1503        self.RefDict = {'RefList':[],'FF':{},'Super':0}
1504       
1505    def InitParameters(self):
1506        'initialize the instrument parameters structure'
1507        Lambda = 0.70926
1508        HistType = 'SXC'
1509        self.Parameters = [{'Type':[HistType,HistType], # create the structure
1510                            'Lam':[Lambda,Lambda]
1511                            }, {}]
1512        'Parameters is a list with two dicts for data parameter settings'
1513
1514    def UpdateParameters(self,Type=None,Wave=None):
1515        'Revise the instrument parameters'
1516        if Type is not None:
1517            self.Parameters[0]['Type'] = [Type,Type]
1518        if Wave is not None:
1519            self.Parameters[0]['Lam'] = [Wave,Wave]           
1520                       
1521######################################################################
1522class ImportPowderData(ImportBaseclass):
1523    '''Defines a base class for the reading of files with powder data.
1524
1525    Objects constructed that subclass this (in import/G2pwd_*.py etc.) will be used
1526    in :meth:`GSASII.GSASII.OnImportPowder`.
1527    See :ref:`Writing a Import Routine<Import_Routines>`
1528    for an explanation on how to use this class.
1529    '''
1530    def __init__(self,formatName,longFormatName=None,
1531        extensionlist=[],strictExtension=False,):
1532        ImportBaseclass.__init__(self,formatName,longFormatName,
1533            extensionlist,strictExtension)
1534        self.clockWd = None  # used in TOF
1535        self.ReInitialize()
1536       
1537    def ReInitialize(self):
1538        'Reinitialize the Reader to initial settings'
1539        ImportBaseclass.ReInitialize(self)
1540        self.powderentry = ['',None,None] #  (filename,Pos,Bank)
1541        self.powderdata = [] # Powder dataset
1542        '''A powder data set is a list with items [x,y,w,yc,yb,yd]:
1543                np.array(x), # x-axis values
1544                np.array(y), # powder pattern intensities
1545                np.array(w), # 1/sig(intensity)^2 values (weights)
1546                np.array(yc), # calc. intensities (zero)
1547                np.array(yb), # calc. background (zero)
1548                np.array(yd), # obs-calc profiles
1549        '''                           
1550        self.comments = []
1551        self.idstring = ''
1552        self.Sample = G2pdG.SetDefaultSample() # default sample parameters
1553        self.Controls = {}  # items to be placed in top-level Controls
1554        self.GSAS = None     # used in TOF
1555        self.repeat_instparm = True # Should a parm file be
1556        #                             used for multiple histograms?
1557        self.instparm = None # name hint from file of instparm to use
1558        self.instfile = '' # full path name to instrument parameter file
1559        self.instbank = '' # inst parm bank number
1560        self.instmsg = ''  # a label that gets printed to show
1561                           # where instrument parameters are from
1562        self.numbanks = 1
1563        self.instdict = {} # place items here that will be transferred to the instrument parameters
1564        self.pwdparms = {} # place parameters that are transferred directly to the tree
1565                           # here (typically from an existing GPX file)
1566######################################################################
1567class ImportSmallAngleData(ImportBaseclass):
1568    '''Defines a base class for the reading of files with small angle data.
1569    See :ref:`Writing a Import Routine<Import_Routines>`
1570    for an explanation on how to use this class.
1571    '''
1572    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1573        strictExtension=False,):
1574           
1575        ImportBaseclass.__init__(self,formatName,longFormatName,extensionlist,
1576            strictExtension)
1577        self.ReInitialize()
1578       
1579    def ReInitialize(self):
1580        'Reinitialize the Reader to initial settings'
1581        ImportBaseclass.ReInitialize(self)
1582        self.smallangleentry = ['',None,None] #  (filename,Pos,Bank)
1583        self.smallangledata = [] # SASD dataset
1584        '''A small angle data set is a list with items [x,y,w,yc,yd]:
1585                np.array(x), # x-axis values
1586                np.array(y), # powder pattern intensities
1587                np.array(w), # 1/sig(intensity)^2 values (weights)
1588                np.array(yc), # calc. intensities (zero)
1589                np.array(yd), # obs-calc profiles
1590                np.array(yb), # preset bkg
1591        '''                           
1592        self.comments = []
1593        self.idstring = ''
1594        self.Sample = G2pdG.SetDefaultSample()
1595        self.GSAS = None     # used in TOF
1596        self.clockWd = None  # used in TOF
1597        self.numbanks = 1
1598        self.instdict = {} # place items here that will be transferred to the instrument parameters
1599
1600######################################################################
1601class ImportImage(ImportBaseclass):
1602    '''Defines a base class for the reading of images
1603
1604    Images are read in only these places:
1605   
1606      * Initial reading is typically done from a menu item
1607        with a call to :meth:`GSASII.GSASII.OnImportImage`
1608        which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`. That calls
1609        methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1610        :meth:`Reader`. This returns a list of reader objects for each read image.
1611
1612      * Images are read alternatively in :func:`GSASIIIO.ReadImages`, which puts image info
1613        directly into the data tree.
1614
1615      * Images are reloaded with :func:`GSASIIIO.GetImageData`.
1616
1617    .. _Image_import_routines:
1618
1619    When reading an image, the ``Reader()`` routine in the ImportImage class
1620    should set:
1621   
1622      * :attr:`Comments`: a list of strings (str),
1623      * :attr:`Npix`: the number of pixels in the image (int),
1624      * :attr:`Image`: the actual image as a numpy array (np.array)
1625      * :attr:`Data`: a dict defining image parameters (dict). Within this dict the following
1626        data items are needed:
1627       
1628         * 'pixelSize': size of each pixel in microns (such as ``[200,200]``.
1629         * 'wavelength': wavelength in Angstoms.
1630         * 'distance': distance of detector from sample in cm.
1631         * 'center': uncalibrated center of beam on detector (such as ``[204.8,204.8]``.
1632         * 'size': size of image (such as ``[2048,2048]``).
1633         * 'ImageTag': image number or other keyword used to retrieve image from
1634           a multi-image data file (defaults to ``1`` if not specified).
1635         * 'sumfile': holds sum image file name if a sum was produced from a multi image file
1636
1637    optional data items:
1638   
1639      * :attr:`repeat`: set to True if there are additional images to
1640        read in the file, False otherwise
1641      * :attr:`repeatcount`: set to the number of the image.
1642     
1643    Note that the above is initialized with :meth:`InitParameters`.
1644    (Also see :ref:`Writing a Import Routine<Import_Routines>`
1645    for an explanation on how to use import classes in general.)
1646    '''
1647    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1648        strictExtension=False,):
1649        ImportBaseclass.__init__(self,formatName,longFormatName,
1650            extensionlist,strictExtension)
1651        self.InitParameters()
1652       
1653    def ReInitialize(self):
1654        'Reinitialize the Reader to initial settings -- not used at present'
1655        ImportBaseclass.ReInitialize(self)
1656        self.InitParameters()
1657       
1658    def InitParameters(self):
1659        'initialize the instrument parameters structure'
1660        self.Comments = ['No comments']
1661        self.Data = {}
1662        self.Npix = 0
1663        self.Image = None
1664        self.repeat = False
1665        self.repeatcount = 1
1666        self.sumfile = ''
1667
1668    def LoadImage(self,ParentFrame,imagefile,imagetag=None):
1669        '''Optionally, call this after reading in an image to load it into the tree.
1670        This saves time by preventing a reread of the same information.
1671        '''
1672        if ParentFrame:
1673            ParentFrame.ImageZ = self.Image   # store the image for plotting
1674            ParentFrame.oldImagefile = imagefile # save the name of the last image file read
1675            ParentFrame.oldImageTag = imagetag   # save the tag of the last image file read           
1676
1677######################################################################
1678class ExportBaseclass(object):
1679    '''Defines a base class for the exporting of GSAS-II results.
1680
1681    This class is subclassed in the various exports/G2export_*.py files. Those files
1682    are imported in :meth:`GSASII.GSASII._init_Exports` which defines the
1683    appropriate menu items for each one and the .Exporter method is called
1684    directly from the menu item.
1685
1686    Routines may also define a .Writer method, which is used to write a single
1687    file without invoking any GUI objects.
1688    '''
1689    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1690        self.G2frame = G2frame
1691        self.formatName = formatName # short string naming file type
1692        self.extension = extension
1693        if longFormatName: # longer string naming file type
1694            self.longFormatName = longFormatName
1695        else:
1696            self.longFormatName = formatName
1697        self.OverallParms = {}
1698        self.Phases = {}
1699        self.Histograms = {}
1700        self.powderDict = {}
1701        self.xtalDict = {}
1702        self.parmDict = {}
1703        self.sigDict = {}
1704        # updated in InitExport:
1705        self.currentExportType = None # type of export that has been requested
1706        # updated in ExportSelect (when used):
1707        self.phasenam = None # a list of selected phases
1708        self.histnam = None # a list of selected histograms
1709        self.filename = None # name of file to be written (single export) or template (multiple files)
1710        self.dirname = '' # name of directory where file(s) will be written
1711        self.fullpath = '' # name of file being written -- full path
1712       
1713        # items that should be defined in a subclass of this class
1714        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1715        # The following types are defined: 'project', "phase", "powder", "single"
1716        self.multiple = False # set as True if the class can export multiple phases or histograms
1717        # self.multiple is ignored for "project" exports
1718
1719    def InitExport(self,event):
1720        '''Determines the type of menu that called the Exporter and
1721        misc initialization.
1722        '''
1723        self.filename = None # name of file to be written (single export)
1724        self.dirname = '' # name of file to be written (multiple export)
1725        if event:
1726            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1727
1728    def MakePWDRfilename(self,hist):
1729        '''Make a filename root (no extension) from a PWDR histogram name
1730
1731        :param str hist: the histogram name in data tree (starts with "PWDR ")
1732        '''
1733        file0 = ''
1734        file1 = hist[5:]
1735        # replace repeated blanks
1736        while file1 != file0:
1737            file0 = file1
1738            file1 = file0.replace('  ',' ').strip()
1739        file0 = file1.replace('Azm= ','A')
1740        # if angle has unneeded decimal places on aziumuth, remove them
1741        if file0[-3:] == '.00': file0 = file0[:-3]
1742        file0 = file0.replace('.','_')
1743        file0 = file0.replace(' ','_')
1744        return file0
1745
1746    def ExportSelect(self,AskFile='ask'):
1747        '''Selects histograms or phases when needed. Sets a default file name when
1748        requested in self.filename; always sets a default directory in self.dirname.
1749
1750        :param bool AskFile: Determines how this routine processes getting a
1751          location to store the current export(s).
1752         
1753          * if AskFile is 'ask' (default option), get the name of the file to be written;
1754            self.filename and self.dirname are always set. In the case where
1755            multiple files must be generated, the export routine should do this
1756            based on self.filename as a template.
1757          * if AskFile is 'dir', get the name of the directory to be used;
1758            self.filename is not used, but self.dirname is always set. The export routine
1759            will always generate the file name.
1760          * if AskFile is 'single', get only the name of the directory to be used when
1761            multiple items will be written (as multiple files) are used
1762            *or* a complete file name is requested when a single file
1763            name is selected. self.dirname is always set and self.filename used
1764            only when a single file is selected.
1765          * if AskFile is 'default', creates a name of the file to be used from
1766            the name of the project (.gpx) file. If the project has not been saved,
1767            then the name of file is requested.
1768            self.filename and self.dirname are always set. In the case where
1769            multiple file names must be generated, the export routine should do this
1770            based on self.filename.
1771          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1772            file. If the project has not been saved, then a directory is requested.
1773            self.filename is not used.
1774
1775        :returns: True in case of an error
1776        '''
1777       
1778        numselected = 1
1779        if self.currentExportType == 'phase':
1780            if len(self.Phases) == 0:
1781                self.G2frame.ErrorDialog(
1782                    'Empty project',
1783                    'Project does not contain any phases.')
1784                return True
1785            elif len(self.Phases) == 1:
1786                self.phasenam = self.Phases.keys()
1787            elif self.multiple: 
1788                choices = sorted(self.Phases.keys())
1789                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1790                if phasenum is None: return True
1791                self.phasenam = [choices[i] for i in phasenum]
1792                if not self.phasenam: return True
1793                numselected = len(self.phasenam)
1794            else:
1795                choices = sorted(self.Phases.keys())
1796                phasenum = G2G.ItemSelector(choices,self.G2frame)
1797                if phasenum is None: return True
1798                self.phasenam = [choices[phasenum]]
1799                numselected = len(self.phasenam)
1800        elif self.currentExportType == 'single':
1801            if len(self.xtalDict) == 0:
1802                self.G2frame.ErrorDialog(
1803                    'Empty project',
1804                    'Project does not contain any single crystal data.')
1805                return True
1806            elif len(self.xtalDict) == 1:
1807                self.histnam = self.xtalDict.values()
1808            elif self.multiple:
1809                choices = sorted(self.xtalDict.values())
1810                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1811                if not hnum: return True
1812                self.histnam = [choices[i] for i in hnum]
1813                numselected = len(self.histnam)
1814            else:
1815                choices = sorted(self.xtalDict.values())
1816                hnum = G2G.ItemSelector(choices,self.G2frame)
1817                if hnum is None: return True
1818                self.histnam = [choices[hnum]]
1819                numselected = len(self.histnam)
1820        elif self.currentExportType == 'powder':
1821            if len(self.powderDict) == 0:
1822                self.G2frame.ErrorDialog(
1823                    'Empty project',
1824                    'Project does not contain any powder data.')
1825                return True
1826            elif len(self.powderDict) == 1:
1827                self.histnam = self.powderDict.values()
1828            elif self.multiple:
1829                choices = sorted(self.powderDict.values())
1830                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1831                if not hnum: return True
1832                self.histnam = [choices[i] for i in hnum]
1833                numselected = len(self.histnam)
1834            else:
1835                choices = sorted(self.powderDict.values())
1836                hnum = G2G.ItemSelector(choices,self.G2frame)
1837                if hnum is None: return True
1838                self.histnam = [choices[hnum]]
1839                numselected = len(self.histnam)
1840        elif self.currentExportType == 'image':
1841            if len(self.Histograms) == 0:
1842                self.G2frame.ErrorDialog(
1843                    'Empty project',
1844                    'Project does not contain any images.')
1845                return True
1846            elif len(self.Histograms) == 1:
1847                self.histnam = self.Histograms.keys()
1848            else:
1849                choices = sorted(self.Histograms.keys())
1850                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1851                if self.multiple:
1852                    if not hnum: return True
1853                    self.histnam = [choices[i] for i in hnum]
1854                else:
1855                    if hnum is None: return True
1856                    self.histnam = [choices[hnum]]
1857                numselected = len(self.histnam)
1858        if self.currentExportType == 'map':
1859            # search for phases with maps
1860            mapPhases = []
1861            choices = []
1862            for phasenam in sorted(self.Phases):
1863                phasedict = self.Phases[phasenam] # pointer to current phase info           
1864                if len(phasedict['General']['Map'].get('rho',[])):
1865                    mapPhases.append(phasenam)
1866                    if phasedict['General']['Map'].get('Flip'):
1867                        choices.append('Charge flip map: '+str(phasenam))
1868                    elif phasedict['General']['Map'].get('MapType'):
1869                        choices.append(
1870                            str(phasedict['General']['Map'].get('MapType'))
1871                            + ' map: ' + str(phasenam))
1872                    else:
1873                        choices.append('unknown map: '+str(phasenam))
1874            # select a map if needed
1875            if len(mapPhases) == 0:
1876                self.G2frame.ErrorDialog(
1877                    'Empty project',
1878                    'Project does not contain any maps.')
1879                return True
1880            elif len(mapPhases) == 1:
1881                self.phasenam = mapPhases
1882            else: 
1883                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
1884                if self.multiple:
1885                    if not phasenum: return True
1886                    self.phasenam = [mapPhases[i] for i in phasenum]
1887                else:
1888                    if phasenum is None: return True
1889                    self.phasenam = [mapPhases[phasenum]]
1890            numselected = len(self.phasenam)
1891
1892        # items selected, now set self.dirname and usually self.filename
1893        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
1894            AskFile == 'default' and not self.G2frame.GSASprojectfile
1895            ):
1896            filename = self.askSaveFile()
1897            if not filename: return True
1898            self.dirname,self.filename = os.path.split(filename)
1899        elif AskFile == 'dir' or AskFile == 'single' or (
1900            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
1901            ):
1902            self.dirname = self.askSaveDirectory()
1903            if not self.dirname: return True
1904        elif AskFile == 'default-dir' or AskFile == 'default':
1905            self.dirname,self.filename = os.path.split(
1906                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
1907                )
1908        else:
1909            raise Exception('This should not happen!')
1910       
1911    def loadParmDict(self):
1912        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
1913        refined values to those from the last cycle and set the uncertainties for the
1914        refined parameters in another dict (self.sigDict).
1915
1916        Expands the parm & sig dicts to include values derived from constraints.
1917        '''
1918        self.parmDict = {}
1919        self.sigDict = {}
1920        rigidbodyDict = {}
1921        covDict = {}
1922        consDict = {}
1923        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
1924        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
1925        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
1926        while item:
1927            name = self.G2frame.PatternTree.GetItemText(item)
1928            if name == 'Rigid bodies':
1929                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
1930            elif name == 'Covariance':
1931                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
1932            elif name == 'Constraints':
1933                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
1934            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
1935        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1936        self.parmDict.update(rbDict)
1937        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1938        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,maxSSwave =  G2stIO.GetPhaseData(
1939            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
1940        self.parmDict.update(phaseDict)
1941        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
1942            Phases,Histograms,Print=False,resetRefList=False)
1943        self.parmDict.update(hapDict)
1944        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
1945        self.parmDict.update(histDict)
1946        self.parmDict.update(zip(
1947            covDict.get('varyList',[]),
1948            covDict.get('variables',[])))
1949        self.sigDict = dict(zip(
1950            covDict.get('varyList',[]),
1951            covDict.get('sig',[])))
1952        # expand to include constraints: first compile a list of constraints
1953        constList = []
1954        for item in consDict:
1955            if item.startswith('_'): continue
1956            constList += consDict[item]
1957        # now process the constraints
1958        G2mv.InitVars()
1959        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
1960        varyList = covDict.get('varyListStart')
1961        if varyList is None and len(constDict) == 0:
1962            # no constraints can use varyList
1963            varyList = covDict.get('varyList')
1964        elif varyList is None:
1965            # old GPX file from before pre-constraint varyList is saved
1966            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
1967            raise Exception(' *** Export aborted ***')
1968        else:
1969            varyList = list(varyList)
1970        try:
1971            groups,parmlist = G2mv.GroupConstraints(constDict)
1972            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,self.parmDict)
1973        except:
1974            # this really should not happen
1975            print ' *** ERROR - constraints are internally inconsistent ***'
1976            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
1977            print 'Errors',errmsg
1978            if warnmsg: print 'Warnings',warnmsg
1979            raise Exception(' *** CIF creation aborted ***')
1980        # add the constrained values to the parameter dictionary
1981        G2mv.Dict2Map(self.parmDict,varyList)
1982        # and add their uncertainties into the esd dictionary (sigDict)
1983        if covDict.get('covMatrix') is not None:
1984            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
1985
1986    def loadTree(self):
1987        '''Load the contents of the data tree into a set of dicts
1988        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
1989        & self.xtalDict)
1990       
1991        * The childrenless data tree items are overall parameters/controls for the
1992          entire project and are placed in self.OverallParms
1993        * Phase items are placed in self.Phases
1994        * Data items are placed in self.Histogram. The key for these data items
1995          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
1996        '''
1997        self.OverallParms = {}
1998        self.powderDict = {}
1999        self.xtalDict = {}
2000        self.Phases = {}
2001        self.Histograms = {}
2002        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2003        histType = None       
2004        if self.currentExportType == 'phase':
2005            # if exporting phases load them here
2006            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
2007            if not sub:
2008                print 'no phases found'
2009                return True
2010            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
2011            while item:
2012                phaseName = self.G2frame.PatternTree.GetItemText(item)
2013                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
2014                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
2015            return
2016        elif self.currentExportType == 'single':
2017            histType = 'HKLF'
2018        elif self.currentExportType == 'powder':
2019            histType = 'PWDR'
2020        elif self.currentExportType == 'image':
2021            histType = 'IMG'
2022
2023        if histType: # Loading just one kind of tree entry
2024            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2025            while item:
2026                name = self.G2frame.PatternTree.GetItemText(item)
2027                if name.startswith(histType):
2028                    if self.Histograms.get(name): # there is already an item with this name
2029                        print('Histogram name '+str(name)+' is repeated. Renaming')
2030                        if name[-1] == '9':
2031                            name = name[:-1] + '10'
2032                        elif name[-1] in '012345678':
2033                            name = name[:-1] + str(int(name[-1])+1)
2034                        else:                           
2035                            name += '-1'
2036                    self.Histograms[name] = {}
2037                    # the main info goes into Data, but the 0th
2038                    # element contains refinement results, carry
2039                    # that over too now.
2040                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
2041                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
2042                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2043                    while item2: 
2044                        child = self.G2frame.PatternTree.GetItemText(item2)
2045                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
2046                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
2047                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2048            # index powder and single crystal histograms by number
2049            for hist in self.Histograms:
2050                if hist.startswith("PWDR"): 
2051                    d = self.powderDict
2052                elif hist.startswith("HKLF"): 
2053                    d = self.xtalDict
2054                else:
2055                    return                   
2056                i = self.Histograms[hist].get('hId')
2057                if i is None and not d.keys():
2058                    i = 0
2059                elif i is None or i in d.keys():
2060                    i = max(d.keys())+1
2061                d[i] = hist
2062            return
2063        # else standard load: using all interlinked phases and histograms
2064        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2065        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2066        while item:
2067            name = self.G2frame.PatternTree.GetItemText(item)
2068            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2069            if not item2: 
2070                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
2071            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2072        # index powder and single crystal histograms
2073        for hist in self.Histograms:
2074            i = self.Histograms[hist]['hId']
2075            if hist.startswith("PWDR"): 
2076                self.powderDict[i] = hist
2077            elif hist.startswith("HKLF"): 
2078                self.xtalDict[i] = hist
2079
2080    def dumpTree(self,mode='type'):
2081        '''Print out information on the data tree dicts loaded in loadTree
2082        '''
2083        print '\nOverall'
2084        if mode == 'type':
2085            def Show(arg): return type(arg)
2086        else:
2087            def Show(arg): return arg
2088        for key in self.OverallParms:
2089            print '  ',key,Show(self.OverallParms[key])
2090        print 'Phases'
2091        for key1 in self.Phases:
2092            print '    ',key1,Show(self.Phases[key1])
2093        print 'Histogram'
2094        for key1 in self.Histograms:
2095            print '    ',key1,Show(self.Histograms[key1])
2096            for key2 in self.Histograms[key1]:
2097                print '      ',key2,Show(self.Histograms[key1][key2])
2098
2099    def defaultSaveFile(self):
2100        return os.path.abspath(
2101            os.path.splitext(self.G2frame.GSASprojectfile
2102                             )[0]+self.extension)
2103       
2104    def askSaveFile(self):
2105        '''Ask the user to supply a file name
2106
2107        :returns: a file name (str) or None if Cancel is pressed
2108        '''
2109       
2110        pth = G2G.GetExportPath(self.G2frame)
2111        defnam = os.path.splitext(
2112            os.path.split(self.G2frame.GSASprojectfile)[1]
2113            )[0]+self.extension
2114        dlg = wx.FileDialog(
2115            self.G2frame, 'Input name for file to write', pth, defnam,
2116            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2117            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2118        dlg.CenterOnParent()
2119        try:
2120            if dlg.ShowModal() == wx.ID_OK:
2121                filename = dlg.GetPath()
2122                self.G2frame.LastExportDir = os.path.split(filename)[0]
2123                filename = os.path.splitext(filename)[0]+self.extension # make sure extension is correct
2124            else:
2125                filename = None
2126        finally:
2127            dlg.Destroy()
2128        return filename
2129
2130    def askSaveDirectory(self):
2131        '''Ask the user to supply a directory name. Path name is used as the
2132        starting point for the next export path search.
2133
2134        :returns: a directory name (str) or None if Cancel is pressed
2135        '''
2136        pth = G2G.GetExportPath(self.G2frame)
2137        dlg = wx.DirDialog(
2138            self.G2frame, 'Input directory where file(s) will be written', pth,
2139            wx.DD_DEFAULT_STYLE)
2140        dlg.CenterOnParent()
2141        try:
2142            if dlg.ShowModal() == wx.ID_OK:
2143                filename = dlg.GetPath()
2144                self.G2frame.LastExportDir = filename
2145            else:
2146                filename = None
2147        finally:
2148            dlg.Destroy()
2149        return filename
2150
2151    # Tools for file writing.
2152    def OpenFile(self,fil=None,mode='w'):
2153        '''Open the output file
2154
2155        :param str fil: The name of the file to open. If None (default)
2156          the name defaults to self.dirname + self.filename.
2157          If an extension is supplied, it is not overridded,
2158          but if not, the default extension is used.
2159        :returns: the file object opened by the routine which is also
2160          saved as self.fp
2161        '''
2162        if not fil:
2163            if not os.path.splitext(self.filename)[1]:
2164                self.filename += self.extension
2165            fil = os.path.join(self.dirname,self.filename)
2166        self.fullpath = os.path.abspath(fil)
2167        self.fp = open(self.fullpath,mode)
2168        return self.fp
2169
2170    def Write(self,line):
2171        '''write a line of output, attaching a line-end character
2172
2173        :param str line: the text to be written.
2174        '''
2175        self.fp.write(line+'\n')
2176    def CloseFile(self,fp=None):
2177        '''Close a file opened in OpenFile
2178
2179        :param file fp: the file object to be closed. If None (default)
2180          file object self.fp is closed.
2181        '''
2182        if fp is None:
2183            fp = self.fp
2184            self.fp = None
2185        fp.close()
2186    # Tools to pull information out of the data arrays
2187    def GetCell(self,phasenam):
2188        """Gets the unit cell parameters and their s.u.'s for a selected phase
2189
2190        :param str phasenam: the name for the selected phase
2191        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2192          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2193          cell values and `cellSig` has their uncertainties.
2194        """
2195        phasedict = self.Phases[phasenam] # pointer to current phase info
2196        try:
2197            pfx = str(phasedict['pId'])+'::'
2198            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2199            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
2200                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2201            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2202            return cellList,cellSig
2203        except KeyError:
2204            cell = phasedict['General']['Cell'][1:]
2205            return cell,7*[0]
2206   
2207    def GetAtoms(self,phasenam):
2208        """Gets the atoms associated with a phase. Can be used with standard
2209        or macromolecular phases
2210
2211        :param str phasenam: the name for the selected phase
2212        :returns: a list of items for eac atom where each item is a list containing:
2213          label, typ, mult, xyz, and td, where
2214
2215          * label and typ are the atom label and the scattering factor type (str)
2216          * mult is the site multiplicity (int)
2217          * xyz is contains a list with four pairs of numbers:
2218            x, y, z and fractional occupancy and
2219            their standard uncertainty (or a negative value)
2220          * td is contains a list with either one or six pairs of numbers:
2221            if one number it is U\ :sub:`iso` and with six numbers it is
2222            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2223            paired with their standard uncertainty (or a negative value)
2224        """
2225        phasedict = self.Phases[phasenam] # pointer to current phase info           
2226        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2227        cfrac = cx+3
2228        fpfx = str(phasedict['pId'])+'::Afrac:'       
2229        atomslist = []
2230        for i,at in enumerate(phasedict['Atoms']):
2231            if phasedict['General']['Type'] == 'macromolecular':
2232                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2233            else:
2234                label = at[ct-1]
2235            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2236            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2237            mult = at[cs+1]
2238            typ = at[ct]
2239            xyz = []
2240            for j,v in enumerate(('x','y','z')):
2241                val = at[cx+j]
2242                pfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2243                sig = self.sigDict.get(pfx,-0.000009)
2244                xyz.append((val,sig))
2245            xyz.append((fval,fsig))
2246            td = []
2247            if at[cia] == 'I':
2248                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2249                val = self.parmDict.get(pfx,at[cia+1])
2250                sig = self.sigDict.get(pfx,-0.0009)
2251                td.append((val,sig))
2252            else:
2253                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2254                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2255                    val = self.parmDict.get(pfx,at[cia+2+i])
2256                    sig = self.sigDict.get(pfx,-0.0009)
2257                    td.append((val,sig))
2258            atomslist.append((label,typ,mult,xyz,td))
2259        return atomslist
2260######################################################################
2261def ExportPowderList(G2frame):
2262    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
2263   
2264    :param wx.Frame G2frame: the GSAS-II main data tree window
2265    '''
2266    extList = []
2267    for obj in G2frame.exporterlist:
2268        if 'powder' in obj.exporttype:
2269            try:
2270                obj.Writer
2271                extList.append(obj.extension)
2272            except AttributeError:
2273                pass
2274    return extList
2275
2276def ExportPowder(G2frame,TreeName,fileroot,extension):
2277    '''Writes a single powder histogram using the Export routines
2278
2279    :param wx.Frame G2frame: the GSAS-II main data tree window
2280    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
2281    :param str fileroot: name for file to be written, extension ignored
2282    :param str extension: extension for file to be written (start with '.'). Must
2283      match a powder export routine that has a Writer object.
2284    '''
2285    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2286    for obj in G2frame.exporterlist:
2287        if obj.extension == extension and 'powder' in obj.exporttype:
2288            obj.currentExportType = 'powder'
2289            obj.InitExport(None)
2290            obj.loadTree() # load all histograms in tree into dicts
2291            if TreeName not in obj.Histograms:
2292                raise Exception('Histogram not found: '+str(TreeName))
2293            try:
2294                obj.Writer
2295            except AttributeError:
2296                continue
2297            try:
2298                obj.Writer(TreeName,filename)
2299                return
2300            except Exception,err:
2301                print('Export Routine for '+extension+' failed.')
2302                print err
2303    else:
2304        print('No Export routine supports extension '+extension)
2305       
2306def ReadDIFFaX(DIFFaXfile):
2307    print 'read ',DIFFaXfile
2308    Layer = {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],'Width':[[10.,10.],[False,False]],
2309        'Layers':[],'Stacking':[],'Transitions':[],'Toler':0.01,'AtInfo':{}}
2310    df = open(DIFFaXfile,'r')
2311    lines = df.readlines()
2312    df.close()
2313    struct = False
2314    Struct = []
2315    stack = False
2316    Stack = []
2317    trans = False
2318    Trans = []
2319    instr = False
2320    for diff in lines:
2321        diff = diff[:-1].lower()
2322        if '!'  in diff:
2323            continue
2324        while '}' in diff: #strip comments
2325            iB = diff.index('{')
2326            iF = diff.index('}')+1
2327            if iB:
2328                diff = diff[:iB]
2329            else:
2330                diff = diff[iF:]
2331        if not diff:
2332            continue
2333        if diff.strip() == 'instrumental':
2334            instr = True
2335            continue
2336        if diff.strip() == 'structural':
2337            instr = False
2338            struct = True
2339            continue
2340        elif diff.strip() == 'stacking':
2341            struct = False
2342            stack = True
2343            continue
2344        elif diff.strip() == 'transitions':
2345            stack = False
2346            trans = True
2347            continue
2348        diff = diff.strip()
2349        if struct:
2350            if diff:
2351                Struct.append(diff)
2352        elif stack:
2353            if diff:
2354                Stack.append(diff)
2355        elif trans:
2356            if diff:
2357                Trans.append(diff)
2358   
2359#STRUCTURE records
2360    laueRec = Struct[1].split()
2361    Layer['Laue'] = laueRec[0]
2362    if Layer['Laue'] == 'unknown' and len(laueRec) > 1:
2363        Layer['Toler'] = float(laueRec[1])    #tolerance for 'unknown'?
2364    if Layer['Laue'] == '2/m(1)': Layer['Laue'] = '2/m(c)'
2365    if Layer['Laue'] == '2/m(2)': Layer['Laue'] = '2/m(ab)'
2366    cell = Struct[0].split()
2367    Layer['Cell'] = [False,float(cell[0]),float(cell[1]),float(cell[2]),90.,90.,float(cell[3]),1.0]
2368    nLayers = int(Struct[2])
2369    N = 3
2370    if 'layer' not in Struct[3]:
2371        N = 4
2372        if Struct[3] != 'infinite':
2373            width = Struct[3].split()
2374            Layer['Width'][0] = [float(width[0]),float(width[1])]
2375    for nL in range(nLayers):
2376        if '=' in Struct[N]:
2377            name = Struct[N].split('=')
2378            sameas = int(name[1])-1
2379            Layer['Layers'].append({'Name':name[0],'SameAs':Layer['Layers'][sameas]['Name'],'Symm':'None','Atoms':[]})
2380            N += 1
2381            continue
2382        Symm = 'None'
2383        if 'centro' in Struct[N+1]: Symm = '-1'
2384        Layer['Layers'].append({'Name':Struct[N],'SameAs':'','Symm':Symm,'Atoms':[]})
2385        N += 2
2386        iatm = 0
2387        while 'layer' not in Struct[N]:
2388            atom = Struct[N][4:].split()
2389            atomType = G2el.FixValence(Struct[N][:4].replace(' ','').strip().capitalize())
2390            if atomType not in Layer['AtInfo']:
2391                Layer['AtInfo'][atomType] = G2el.GetAtomInfo(atomType)
2392            atomName = '%s(%s)'%(atomType,atom[0])
2393            newVals = []
2394            for val in atom[1:6]:
2395                if '/' in val:
2396                    newVals.append(eval(val+'.'))
2397                else:
2398                    newVals.append(float(val))               
2399            atomRec = [atomName,atomType,newVals[0],newVals[1],newVals[2],newVals[4],newVals[3]/78.9568]
2400            Layer['Layers'][-1]['Atoms'].append(atomRec)
2401            N += 1
2402            if N > len(Struct)-1:
2403                break
2404#TRANSITIONS records
2405    transArray = []
2406    N = 0
2407    for i in range(nLayers):
2408        transArray.append([])
2409        for j in range(nLayers):
2410            vals = Trans[N].split()
2411            newVals = []
2412            for val in vals[:4]:
2413                if '/' in val:
2414                    newVals.append(eval(val+'.'))
2415                else:
2416                    newVals.append(float(val))
2417            transArray[-1].append(newVals+['',False])
2418            N += 1
2419    Layer['Transitions'] = transArray
2420#STACKING records
2421    Layer['Stacking'] = [Stack[0],'']
2422    if Stack[0] == 'recursive':
2423        Layer['Stacking'][1] = Stack[1]
2424    elif Stack[0] == 'explicit':
2425        if Stack[1] == 'random':
2426            Layer['Stacking'][1] = Stack[1]
2427        else:
2428            Layer['Stacking'][1] = 'list'
2429            Layer['Stacking'].append('')
2430            for stack in Stack[2:]:
2431                Layer['Stacking'][2] += ' '+stack
2432    return Layer
2433
2434def ReadCIF(URLorFile):
2435    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2436    (from James Hester).
2437    The open routine gets confused with DOS names that begin with a letter and colon
2438    "C:\dir\" so this routine will try to open the passed name as a file and if that
2439    fails, try it as a URL
2440
2441    :param str URLorFile: string containing a URL or a file name. Code will try first
2442      to open it as a file and then as a URL.
2443
2444    :returns: a PyCifRW CIF object.
2445    '''
2446    import CifFile as cif # PyCifRW from James Hester
2447
2448    # alternate approach:
2449    #import urllib
2450    #ciffile = 'file:'+urllib.pathname2url(filename)
2451   
2452    try:
2453        fp = open(URLorFile,'r')
2454        cf = cif.ReadCif(fp)
2455        fp.close()
2456        return cf
2457    except IOError:
2458        return cif.ReadCif(URLorFile)
2459
2460if __name__ == '__main__':
2461    import GSASII
2462    application = GSASII.GSASIImain(0)
2463    G2frame = application.main
2464    #app = wx.PySimpleApp()
2465    #G2frame = wx.Frame(None) # create a frame
2466    #frm.Show(True)
2467    #filename = '/tmp/notzip.zip'
2468    #filename = '/tmp/all.zip'
2469    #filename = '/tmp/11bmb_7652.zip'
2470   
2471    #selection=None, confirmoverwrite=True, parent=None
2472    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2473    #print ExtractFileFromZip(filename,multipleselect=True)
2474    #                         #confirmread=False, confirmoverwrite=False)
2475
2476    # choicelist=[ ('a','b','c'),
2477    #              ('test1','test2'),('no choice',)]
2478    # titles = [ 'a, b or c', 'tests', 'No option here']
2479    # dlg = MultipleChoicesDialog(
2480    #     choicelist,titles,
2481    #     parent=frm)
2482    # if dlg.ShowModal() == wx.ID_OK:
2483    #     print 'Got OK'
2484    imagefile = '/tmp/NDC5_00237_3.ge3'
2485    Comments, Data, Npix, Image = GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None)
2486
2487    print("\n\nResults loaded to Comments, Data, Npix and Image\n\n")
2488
2489    GSASIIpath.IPyBreak_base()
Note: See TracBrowser for help on using the repository browser.