source: trunk/GSASIIIO.py @ 2083

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

Export multiple masks/controls

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