source: trunk/GSASIIIO.py @ 2196

Last change on this file since 2196 was 2196, checked in by toby, 7 years ago

redo plot window graphics to redraw after refinement (need to update more plotting routines & save plot zoom stack, etc.)

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