source: trunk/GSASIIimgGUI.py @ 2823

Last change on this file since 2823 was 2823, checked in by vondreele, 5 years ago

implement hold flag for dzero adjustment in 2D strain fitting

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 161.2 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII - image data display routines
3########### SVN repository information ###################
4# $Date: 2017-05-09 20:41:38 +0000 (Tue, 09 May 2017) $
5# $Author: vondreele $
6# $Revision: 2823 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 2823 2017-05-09 20:41:38Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIimgGUI: Image GUI*
12-------------------------
13
14Control image display and processing
15
16'''
17import os
18import copy
19import glob
20import re
21import math
22import sys
23import wx
24import wx.lib.scrolledpanel as wxscroll
25import wx.lib.mixins.listctrl  as  listmix
26import matplotlib as mpl
27import numpy as np
28import GSASIIpath
29GSASIIpath.SetVersionNumber("$Revision: 2823 $")
30import GSASIIimage as G2img
31import GSASIImath as G2mth
32import GSASIIElem as G2elem
33import GSASIIpwdGUI as G2pdG
34import GSASIIplot as G2plt
35import GSASIIIO as G2IO
36import GSASIIgrid as G2gd
37import GSASIIctrls as G2G
38import GSASIIobj as G2obj
39import GSASIIpy3 as G2py3
40
41VERY_LIGHT_GREY = wx.Colour(235,235,235)
42WACV = wx.ALIGN_CENTER_VERTICAL
43
44# trig functions in degrees
45sind = lambda x: math.sin(x*math.pi/180.)
46tand = lambda x: math.tan(x*math.pi/180.)
47cosd = lambda x: math.cos(x*math.pi/180.)
48asind = lambda x: 180.*math.asin(x)/math.pi
49tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
50tof2q = lambda t,C:2.0*math.pi*C/t
51atand = lambda x: 180.*math.atan(x)/math.pi
52atan2d = lambda y,x: 180.*math.atan2(y,x)/math.pi
53
54################################################################################
55##### Image Data
56################################################################################
57
58def GetImageZ(G2frame,data,newRange=False):
59    '''Gets image & applies dark, background & flat background corrections
60    :param wx.Frame G2frame: main GSAS-II frame
61    param: dict data: Image Controls dictionary
62    return: array sumImg: corrected image for background/dark/flat back
63    '''
64   
65    Npix,imagefile,imagetag = G2IO.GetCheckImageFile(G2frame,G2frame.Image)
66    formatName = data.get('formatName','')
67    sumImg = G2IO.GetImageData(G2frame,imagefile,True,ImageTag=imagetag,FormatName=formatName)
68    if sumImg is None:
69        return []
70    darkImg = False
71    if 'dark image' in data:
72        darkImg,darkScale = data['dark image']
73        if darkImg:
74            Did = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, darkImg)
75            if Did:
76                Ddata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Did,'Image Controls'))
77                dformatName = Ddata.get('formatName','')
78                Npix,darkfile,imagetag = G2IO.GetCheckImageFile(G2frame,Did)
79                darkImage = G2IO.GetImageData(G2frame,darkfile,True,ImageTag=imagetag,FormatName=dformatName)
80                if darkImg is not None:               
81                    sumImg += np.array(darkImage*darkScale,dtype='int32')
82    if 'background image' in data:
83        backImg,backScale = data['background image']           
84        if backImg:     #ignores any transmission effect in the background image
85            Bid = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, backImg)
86            if Bid:
87                Npix,backfile,imagetag = G2IO.GetCheckImageFile(G2frame,Bid)
88                Bdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Bid,'Image Controls'))
89                bformatName = Bdata.get('formatName','')
90                backImage = G2IO.GetImageData(G2frame,backfile,True,ImageTag=imagetag,FormatName=bformatName)
91                if darkImg and backImage is not None:
92                    backImage += np.array(darkImage*darkScale/backScale,dtype='int32')
93                if backImage is not None:
94                    sumImg += np.array(backImage*backScale,dtype='int32')
95#    if darkImg: del darkImg         #force cleanup
96#    if backImg: del backImg
97    sumImg -= int(data.get('Flat Bkg',0))
98    Imax = np.max(sumImg)
99    if 'range' not in data or newRange:
100        data['range'] = [(0,Imax),[0,Imax]]
101    return np.asarray(sumImg,dtype='int32')
102
103def UpdateImageData(G2frame,data):
104   
105    def OnPixVal(invalid,value,tc):
106        G2plt.PlotExposedImage(G2frame,newPlot=True,event=tc.event)
107       
108    if G2frame.dataDisplay:
109        G2frame.dataDisplay.Destroy()
110    if not G2frame.dataFrame.GetStatusBar():
111        G2frame.dataFrame.CreateStatusBar()
112    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
113    G2frame.ImageZ = GetImageZ(G2frame,data)
114    mainSizer = wx.BoxSizer(wx.VERTICAL)
115    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,
116        label='Do not change anything here unless you are absolutely sure!'),0,WACV)
117    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Image size: %d by %d'%(data['size'][0],data['size'][1])),0,WACV)
118    pixSize = wx.FlexGridSizer(0,4,5,5)
119    pixLabels = [u' Pixel X-dimension (\xb5m)',u' Pixel Y-dimension (\xb5m)']
120    for i,[pixLabel,pix] in enumerate(zip(pixLabels,data['pixelSize'])):
121        pixSize.Add(wx.StaticText(G2frame.dataDisplay,label=pixLabel),0,WACV)
122        pixVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['pixelSize'],i,nDig=(10,3),
123            typeHint=float,OnLeave=OnPixVal)
124        pixSize.Add(pixVal,0,WACV)
125    mainSizer.Add(pixSize,0)
126    distSizer = wx.BoxSizer(wx.HORIZONTAL)
127    distSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Set detector distance: '),0,WACV)
128    if 'setdist' not in data:
129        data['setdist'] = data['distance']
130    distSizer.Add(G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'setdist',nDig=(10,4),
131            typeHint=float),0,WACV)
132    distSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Polarization: '),0,WACV)
133    if 'PolaVal' not in data:       #patch
134        data['PolaVal'] = [0.99,False]
135    distSizer.Add(G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['PolaVal'],0,nDig=(10,4),
136        min=0.,max=1.,typeHint=float),0,WACV)
137    mainSizer.Add(distSizer,0)
138    mainSizer.Layout()   
139    G2frame.dataDisplay.SetSizer(mainSizer)
140    fitSize = mainSizer.Fit(G2frame.dataFrame)
141    G2frame.dataFrame.setSizePosLeft(fitSize)
142
143################################################################################
144##### Image Controls
145################################################################################                   
146def UpdateImageControls(G2frame,data,masks,IntegrateOnly=False):
147    '''Shows and handles the controls on the "Image Controls"
148    data tree entry
149    '''
150    import ImageCalibrants as calFile
151#patch
152    if 'Flat Bkg' not in data:
153        data['Flat Bkg'] = 0.0
154    if 'GonioAngles' not in data:
155        data['GonioAngles'] = [0.,0.,0.]
156    if 'DetDepth' not in data:
157        data['DetDepth'] = 0.
158    if 'SampleAbs' not in data:
159        data['SampleShape'] = 'Cylinder'
160        data['SampleAbs'] = [0.0,False]
161    if 'binType' not in data:
162        if 'PWDR' in data['type']:
163            data['binType'] = '2-theta'
164        elif 'SASD' in data['type']:
165            data['binType'] = 'log(q)'
166    if 'varyList' not in data:
167        data['varyList'] = {'dist':True,'det-X':True,'det-Y':True,'tilt':True,'phi':True,'dep':False,'wave':False}
168    if data['DetDepth'] > 0.5:
169        data['DetDepth'] /= data['distance']
170#end patch
171
172# Menu items
173           
174    def OnCalibrate(event):
175        G2frame.dataFrame.GetStatusBar().SetStatusText('Select > 4 points on 1st used ring; LB to pick, RB on point to delete else RB to finish')
176        G2frame.ifGetRing = True
177               
178    def OnRecalibrate(event):
179        G2img.ImageRecalibrate(G2frame,data,masks)
180        wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
181       
182    def OnRecalibAll(event):
183        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
184        dlg = G2G.G2MultiChoiceDialog(G2frame,'Image calibration controls','Select images to recalibrate:',Names)
185        try:
186            if dlg.ShowModal() == wx.ID_OK:
187                Id =  G2gd.GetPatternTreeItemId(G2frame,G2frame.root,'Sequential image calibration results')
188                if Id:
189                    SeqResult = G2frame.PatternTree.GetItemPyData(Id)
190                else:
191                    Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text='Sequential image calibration results')
192                SeqResult = {'SeqPseudoVars':{},'SeqParFitEqList':[]}
193                items = dlg.GetSelections()
194                G2frame.EnablePlot = False
195                for item in items:
196                    name = Names[item]
197                    print 'calibrating',name
198                    G2frame.Image = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
199                    Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Image Controls'))
200                    G2frame.ImageZ = GetImageZ(G2frame,Data)
201                    Data['setRings'] = True
202                    Mid = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Masks')
203                    Masks = G2frame.PatternTree.GetItemPyData(Mid)
204                    vals,varyList,sigList,parmDict = G2img.ImageRecalibrate(G2frame,Data,Masks)
205                    sigList = list(sigList)
206                    if 'dist' not in varyList:
207                        vals.append(parmDict['dist'])
208                        varyList.append('dist')
209                        sigList.append(None)
210                    vals.append(Data['setdist'])
211                    varyList.append('setdist')
212                    sigList.append(None)
213                    SeqResult[name] = {'variables':vals,'varyList':varyList,'sig':sigList,'Rvals':[],
214                        'covMatrix':np.eye(len(varyList)),'title':name,'parmDict':parmDict}
215                SeqResult['histNames'] = Names               
216                G2frame.PatternTree.SetItemPyData(Id,SeqResult)
217        finally:
218            dlg.Destroy()
219        print 'All selected images recalibrated - results in Sequential image calibration results'
220        G2frame.G2plotNB.Delete('Sequential refinement')    #clear away probably invalid plot
221        G2plt.PlotExposedImage(G2frame,event=None)
222        G2frame.PatternTree.SelectItem(Id)
223       
224    def OnClearCalib(event):
225        data['ring'] = []
226        data['rings'] = []
227        data['ellipses'] = []
228        G2plt.PlotExposedImage(G2frame,event=event)
229           
230    def ResetThresholds():
231        Imin = max(0.,np.min(G2frame.ImageZ))
232        Imax = np.max(G2frame.ImageZ)
233        data['range'] = [(0,Imax),[Imin,Imax]]
234        masks['Thresholds'] = [(0,Imax),[Imin,Imax]]
235        MaxSizer.GetChildren()[2].Window.SetValue(Imax)   #tricky
236        MaxSizer.GetChildren()[5].Window.SetValue(Imin)   #tricky
237         
238    def OnIntegrate(event):
239        '''Integrate image in response to a menu event or from the AutoIntegrate
240        dialog. In the latter case, event=None.
241        '''
242        CleanupMasks(masks)
243        blkSize = 128   #this seems to be optimal; will break in polymask if >1024
244        sumImg = GetImageZ(G2frame,data)
245        wx.BeginBusyCursor()
246        try:
247            G2frame.Integrate = G2img.ImageIntegrate(sumImg,data,masks,blkSize)           
248        finally:
249            wx.EndBusyCursor()   
250        G2frame.PauseIntegration = G2frame.Integrate[-1]
251        del sumImg  #force cleanup
252        Id = G2IO.SaveIntegration(G2frame,G2frame.PickId,data,(event is None))
253        G2frame.PatternId = Id
254        G2frame.PatternTree.SelectItem(Id)
255        G2frame.PatternTree.Expand(Id)
256        for item in G2frame.MakePDF: item.Enable(True)
257       
258    def OnIntegrateAll(event):
259        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
260        dlg = G2G.G2MultiChoiceDialog(G2frame,'Image integration controls','Select images to integrate:',Names)
261        try:
262            if dlg.ShowModal() == wx.ID_OK:
263                items = dlg.GetSelections()
264                G2frame.EnablePlot = False
265                dlgp = wx.ProgressDialog("Elapsed time","2D image integrations",len(items)+1,
266                    style = wx.PD_ELAPSED_TIME|wx.PD_CAN_ABORT)
267                try:
268                    for icnt,item in enumerate(items):
269                        GoOn = dlgp.Update(icnt)
270                        if not GoOn[0]:
271                            break
272                        name = Names[item]
273                        G2frame.Image = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
274                        CId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Image Controls')
275                        Data = G2frame.PatternTree.GetItemPyData(CId)
276                        Masks = G2frame.PatternTree.GetItemPyData(
277                            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Masks'))
278                        image = GetImageZ(G2frame,Data)
279                        blkSize = 128   #this seems to be optimal; will break in polymask if >1024
280                        G2frame.Integrate = G2img.ImageIntegrate(image,Data,Masks,blkSize)
281                        del image   #force cleanup
282                        pId = G2IO.SaveIntegration(G2frame,CId,Data)
283                finally:   
284                    dlgp.Destroy()
285                    G2frame.EnablePlot = True
286                    G2frame.PatternTree.SelectItem(pId)
287                    G2frame.PatternTree.Expand(pId)
288                    G2frame.PatternId = pId
289        finally:
290            dlg.Destroy()
291       
292    def OnCopyControls(event):
293        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
294        if len(Names) == 1:
295            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
296            return
297        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
298        Names.pop(Names.index(Source))
299# select targets & do copy
300        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy image controls','Copy controls from '+Source+' to:',Names)
301        try:
302            if dlg.ShowModal() == wx.ID_OK:
303                items = dlg.GetSelections()
304                G2frame.EnablePlot = False
305                for item in items:
306                    name = Names[item]
307                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
308                    CId = G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls')
309                    oldData = copy.deepcopy(G2frame.PatternTree.GetItemPyData(CId))
310                    Data = copy.deepcopy(data)
311                    Data['range'][0] = oldData['range'][0]
312                    Data['size'] = oldData['size']
313                    Data['GonioAngles'] = oldData.get('GonioAngles', [0.,0.,0.])
314                    Data['ring'] = []
315                    Data['rings'] = []
316                    Data['ellipses'] = []
317                    if name == Data['dark image'][0]:
318                        Data['dark image'] = ['',-1.]
319                    if name == Data['background image'][0]:
320                        Data['background image'] = ['',-1.]
321                    G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Image Controls'),Data)
322        finally:
323            dlg.Destroy()
324            G2frame.PatternTree.SelectItem(G2frame.PickId)
325           
326    def OnCopySelected(event):
327        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
328        if len(Names) == 1:
329            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
330            return
331        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
332        # Assemble a list of item labels
333        keyList = ['type','wavelength','calibrant','distance','center','Oblique',
334                    'tilt','rotation','azmthOff','fullIntegrate','LRazimuth','setdist',
335                    'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
336                    'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList',
337                    'binType','SampleShape','PolaVal','SampleAbs','dark image','background image']
338        keyList.sort(key=lambda s: s.lower())
339        keyText = [i+' = '+str(data[i]) for i in keyList]
340        # sort both lists together, ordered by keyText
341        selectedKeys = []
342        dlg = G2G.G2MultiChoiceDialog(G2frame.dataFrame,'Select which image controls\nto copy',
343            'Select image controls', keyText)
344        try:
345            if dlg.ShowModal() == wx.ID_OK:
346                selectedKeys = [keyList[i] for i in dlg.GetSelections()]
347        finally:
348            dlg.Destroy()
349        if not selectedKeys: return # nothing to copy
350        copyDict = {}
351        for parm in selectedKeys:
352            copyDict[parm] = data[parm]
353        dlg = G2G.G2MultiChoiceDialog(G2frame.dataFrame,'Copy image controls from\n'+Source+' to...',
354            'Copy image controls', Names)
355        try:
356            if dlg.ShowModal() == wx.ID_OK:
357                result = dlg.GetSelections()
358                for i in result: 
359                    item = Names[i]
360                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,item)
361                    Controls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
362                    Controls.update(copy.deepcopy(copyDict))
363        finally:
364            dlg.Destroy()           
365               
366    def OnSaveControls(event):
367        pth = G2G.GetExportPath(G2frame)
368        dlg = wx.FileDialog(G2frame, 'Choose image controls file', pth, '', 
369            'image control files (*.imctrl)|*.imctrl',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
370        try:
371            if dlg.ShowModal() == wx.ID_OK:
372                filename = dlg.GetPath()
373                # make sure extension is .imctrl
374                filename = os.path.splitext(filename)[0]+'.imctrl'
375                WriteControls(filename,data)
376        finally:
377            dlg.Destroy()
378
379    def WriteControls(filename,data):
380        File = open(filename,'w')
381        keys = ['type','wavelength','calibrant','distance','center','Oblique',
382            'tilt','rotation','azmthOff','fullIntegrate','LRazimuth','setdist',
383            'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
384            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList',
385            'binType','SampleShape','PolaVal','SampleAbs','dark image','background image',
386            ]
387        for key in keys:
388            if key not in data:     #uncalibrated!
389                continue
390            File.write(key+':'+str(data[key])+'\n')
391        File.close()
392       
393    def OnSaveMultiControls(event):
394        '''Save controls from multiple images
395        '''
396        imglist = []
397        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
398        while item:
399            name = G2frame.PatternTree.GetItemText(item)
400            if name.startswith('IMG '): 
401                imglist.append(name)               
402            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
403        if not imglist:
404            print('No images!')
405            return
406        dlg = G2G.G2MultiChoiceDialog(G2frame, 'Which images to select?',
407            'Select images', imglist, wx.CHOICEDLG_STYLE)
408        try:
409            if dlg.ShowModal() == wx.ID_OK:
410                treeEntries = [imglist[i] for i in dlg.GetSelections()]
411        finally:
412            dlg.Destroy()
413        if not treeEntries:
414            print('No images selected!')
415            return
416        pth = G2G.GetExportPath(G2frame)
417        dlg = wx.DirDialog(
418            G2frame, 'Select directory for output files',pth,wx.DD_DEFAULT_STYLE)
419        dlg.CenterOnParent()
420        outdir = None
421        try:
422            if dlg.ShowModal() == wx.ID_OK:
423                outdir = dlg.GetPath()
424        finally:
425            dlg.Destroy()
426        if not outdir:
427            print('No directory')
428            return
429        for img in treeEntries:
430            item = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
431            data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
432                G2frame,item,'Image Controls'))
433            Npix,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(item)
434            filename = os.path.join(outdir,
435                                    os.path.splitext(os.path.split(imagefile)[1])[0]
436                                    + '.imctrl')
437            print('writing '+filename)
438            WriteControls(filename,data)
439           
440    def OnLoadControls(event):
441        cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type','Oblique',
442            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
443            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList','setdist',
444            'PolaVal','SampleAbs','dark image','background image']
445        pth = G2G.GetImportPath(G2frame)
446        if not pth: pth = '.'
447        dlg = wx.FileDialog(G2frame, 'Choose image controls file', pth, '', 
448            'image control files (*.imctrl)|*.imctrl',wx.OPEN)
449        try:
450            if dlg.ShowModal() == wx.ID_OK:
451                filename = dlg.GetPath()
452                File = open(filename,'r')
453                save = {}
454                S = File.readline()
455                while S:
456                    if S[0] == '#':
457                        S = File.readline()
458                        continue
459                    [key,val] = S.strip().split(':',1)
460                    if key in ['type','calibrant','binType','SampleShape',]:    #strings
461                        save[key] = val
462                    elif key in ['varyList',]:
463                        save[key] = eval(val)   #dictionary
464                    elif key in ['rotation']:
465                        save[key] = float(val)
466                    elif key in ['center',]:
467                        if ',' in val:
468                            save[key] = eval(val)
469                        else:
470                            vals = val.strip('[] ').split()
471                            save[key] = [float(vals[0]),float(vals[1])] 
472                    elif key in cntlList:
473                        save[key] = eval(val)
474                    S = File.readline()
475                data.update(save)
476                # next line removed. Previous updates tree contents. The next
477                # makes a copy of data, puts it into tree and "disconnects" data
478                # from tree contents (later changes to data are lost!)
479                #G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'),copy.deepcopy(data))
480                File.close()
481        finally:
482            dlg.Destroy()
483        G2frame.ImageZ = GetImageZ(G2frame,data)
484        ResetThresholds()
485        G2plt.PlotExposedImage(G2frame,event=event)
486        wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
487       
488    def OnTransferAngles(event):
489        '''Sets the integration range for the selected Images based on the difference in detector distance
490        '''
491        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
492        if len(Names) == 1:
493            G2frame.ErrorDialog('No images to transfer integration angles to','Need more "IMG"s')
494            return
495        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
496        Names.pop(Names.index(Source))
497        # select targets & do copy
498        extraopts = {"label_1":"Xfer scaled calib d-min", "value_1":False,
499                     "label_2":"Xfer scaled 2-theta min", "value_2":False,
500                     "label_3":"Xfer scaled 2-theta max", "value_3":True,
501                     }
502        dlg = G2G.G2MultiChoiceDialog(G2frame,'Xfer angles','Transfer integration range from '+Source+' to:',
503            Names,extraOpts=extraopts)
504        try:
505            if dlg.ShowModal() == wx.ID_OK:
506                for i in '_1','_2','_3':
507                    if extraopts['value'+i]: break
508                else:
509                    G2G.G2MessageBox(G2frame,'Nothing to do!')
510                    return
511                xferAng = lambda tth,dist1,dist2: atand(dist1 * tand(tth) / dist2)
512                items = dlg.GetSelections()
513                G2frame.EnablePlot = False
514                Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,Source)
515                data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
516                ttmin0,ttmax0 = data['IOtth']
517                dist0 = data['distance']
518                wave0 = data['wavelength']
519                dsp0 = data['calibdmin']
520                print('distance = {:.2f} integration range: [{:.4f}, {:.4f}], calib dmin {:.3f}'
521                            .format(dist0,ttmin0,ttmax0,dsp0))
522                for item in items:
523                    name = Names[item]
524                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
525                    data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
526                    dist1 = data['distance']
527                    if extraopts["value_2"]:
528                        data['IOtth'][0] = xferAng(ttmin0,dist0,dist1)
529                    if extraopts["value_3"]:
530                        data['IOtth'][1] = xferAng(ttmax0,dist0,dist1)
531                    if extraopts["value_1"]:
532                        ang1 = xferAng(2.0*asind(wave0/(2.*dsp0)),dist0,dist1)
533                        data['calibdmin'] = data['wavelength']/(2.*sind(ang1/2.))
534                        print('distance = {:.2f} integration range: [{:.4f}, {:.4f}], calib dmin {:.3f}'
535                            .format(dist1,data['IOtth'][0],data['IOtth'][1],data['calibdmin']))
536                    else:
537                        print('distance = {:.2f} integration range: [{:.4f}, {:.4f}]'
538                            .format(dist1,data['IOtth'][0],data['IOtth'][1]))
539        finally:
540            dlg.Destroy()
541            G2frame.PatternTree.SelectItem(G2frame.PickId)       
542           
543    def OnResetDist(event):
544        dlg = wx.MessageDialog(G2frame,'Are you sure you want to do this?',caption='Reset dist to set dist',style=wx.YES_NO|wx.ICON_EXCLAMATION)
545        if dlg.ShowModal() != wx.ID_YES:
546            dlg.Destroy()
547            return
548        dlg.Destroy()
549        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
550        dlg = G2G.G2MultiChoiceDialog(G2frame,'Reset dist','Reset dist to set dist for:',Names)
551        try:
552            if dlg.ShowModal() == wx.ID_OK:
553                items = dlg.GetSelections()
554                for item in items:
555                    name = Names[item]
556                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
557                    Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
558                    Data['distance'] = Data['setdist']
559        finally:
560            dlg.Destroy()
561        wx.CallAfter(UpdateImageControls,G2frame,data,masks)
562           
563# Sizers
564    Indx = {}                                   
565    def ComboSizer():
566
567        def OnDataType(event):
568            data['type'] = typeSel.GetValue()[:4]
569            if 'SASD' in data['type']:
570                data['SampleAbs'][0] = np.exp(-data['SampleAbs'][0]) #switch from muT to trans!
571                if data['binType'] == '2-theta': data['binType'] = 'log(q)'  #switch default bin type
572            elif 'PWDR' in data['type']:
573                data['SampleAbs'][0] = -np.log(data['SampleAbs'][0])  #switch from trans to muT!
574                if data['binType'] == 'log(q)': data['binType'] = '2-theta'  #switch default bin type                 
575            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
576   
577        def OnNewColorBar(event):
578            data['color'] = colSel.GetValue()
579            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
580       
581        def OnAzmthOff(invalid,value,tc):
582            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
583       
584        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
585        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Type of image data: '),0,WACV)
586        typeSel = wx.ComboBox(parent=G2frame.dataDisplay,value=typeDict[data['type']],choices=typeList,
587            style=wx.CB_READONLY|wx.CB_DROPDOWN)
588        typeSel.SetValue(data['type'])
589        typeSel.Bind(wx.EVT_COMBOBOX, OnDataType)
590        comboSizer.Add(typeSel,0,WACV)
591        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Color bar '),0,WACV)
592        colSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['color'],choices=colorList,
593            style=wx.CB_READONLY|wx.CB_DROPDOWN)
594        colSel.Bind(wx.EVT_COMBOBOX, OnNewColorBar)
595        comboSizer.Add(colSel,0,WACV)
596        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Azimuth offset '),0,WACV)
597        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),
598            typeHint=float,OnLeave=OnAzmthOff)
599        comboSizer.Add(azmthOff,0,WACV)
600        return comboSizer
601       
602    def MaxSizer():
603        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
604        for the image, as well as callback routines.
605        '''
606        def OnNewVal(invalid,value,tc):
607            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
608            the value into the data['range'] nested list).
609            This adjusts the slider positions to match the current values
610            '''
611            scaleSel.SetSelection(len(scaleChoices)-1)
612            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
613            if r11 != Range[1][1]:
614                Range[1][1] = r11
615                maxVal.SetValue(int(Range[1][1]))
616            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
617            if r10 != Range[1][0]:
618                Range[1][0] = r10
619                minVal.SetValue(int(Range[1][0]))
620            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
621            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
622            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
623            maxSel.SetValue(sv1)
624            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
625            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
626            minSel.SetValue(sv0)
627            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
628            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
629            if mplOld:
630                Page.canvas.draw()
631            else:
632                Page.canvas.draw_idle()
633           
634        G2frame.prevMaxValue = None   
635        def OnMaxSlider(event):
636            val = maxSel.GetValue()
637            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
638            scaleSel.SetSelection(len(scaleChoices)-1)
639            G2frame.prevMaxValue = val
640            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
641            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
642            maxVal.SetValue(int(0.5+Range[1][1]))
643            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
644            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
645            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
646            minSel.SetValue(sv0)
647            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
648            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
649            if mplOld:
650                Page.canvas.draw()
651            else:
652                Page.canvas.draw_idle()
653           
654        G2frame.prevMinValue = None   
655        def OnMinSlider(event):
656            val = minSel.GetValue()
657            scaleSel.SetSelection(len(scaleChoices)-1)
658            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
659            G2frame.prevMinValue = val
660            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
661            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
662            minVal.SetValue(int(Range[1][0]))
663            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
664            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
665            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
666            maxSel.SetValue(sv1)
667            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
668            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
669            if mplOld:
670                Page.canvas.draw()
671            else:
672                Page.canvas.draw_idle()
673           
674        def OnAutoSet(event):
675            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
676            for the image so that 95% (etc.) of pixels are inside the color map limits.
677            An equal number of pixels are dropped at the minimum and maximum levels.
678            '''
679            try:
680                val = int(event.GetEventObject().GetStringSelection()[:-1])
681                margin = (100-val)/2.
682            except:
683                margin = 0
684                event.GetEventObject().SetSelection(0)
685            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
686            if margin == 0:
687                Range[1] = list(Range[0])
688            else:
689                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
690                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
691            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
692            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
693            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
694            maxSel.SetValue(sv1)
695            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
696            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
697            minSel.SetValue(sv0)
698            minVal.SetValue(int(Range[1][0]))
699            maxVal.SetValue(int(Range[1][1]))
700            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
701            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
702            if mplOld:
703                Page.canvas.draw()
704            else:
705                Page.canvas.draw_idle()
706
707        mplv = mpl.__version__.split('.')
708        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
709        # Plot color scaling uses limits as below:
710        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
711        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
712        maxSizer = wx.GridBagSizer(0,0)
713        r = c = 0
714        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Max intensity'),(r,c))
715        c += 1
716        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
717        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
718        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
719        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
720        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
721        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
722        maxSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv1,size=(300,-1))
723        maxSizer.Add(maxSel,(r,c),flag=wx.EXPAND)
724        maxSizer.AddGrowableCol(c)
725        c += 1
726        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
727        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],1,min=Range[0][0]+1,
728            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
729        maxSizer.Add(maxVal,(r,c))
730        c += 1
731        scaleChoices = ("100%","99%","95%","90%","80%","?")
732        scaleSel = wx.Choice(G2frame.dataDisplay,choices=scaleChoices,size=(-1,-1))
733        if (Range[1][0] == Range[0][0] and
734            Range[1][1] == Range[0][1]):
735            scaleSel.SetSelection(0)
736        else:
737            scaleSel.SetSelection(len(scaleChoices)-1)
738        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
739        maxSizer.Add(scaleSel,(r,c),(2,1),flag=wx.ALIGN_CENTER)
740        c = 0
741        r = 1
742        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min intensity'),(r,c))
743        c += 1
744        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
745        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
746        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
747        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
748        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
749        minSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv0,size=(300,-1))
750        maxSizer.Add(minSel,(r,c),flag=wx.EXPAND|wx.ALL)
751        c += 1
752        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
753        minVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],0,
754            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
755        maxSizer.Add(minVal,(r,c))
756        return maxSizer
757       
758    def CalibCoeffSizer():
759       
760        def OnCalRef(event):
761            Obj = event.GetEventObject()
762            name = Indx[Obj]
763            data['varyList'][name] = Obj.GetValue()
764           
765        calibSizer = wx.FlexGridSizer(0,2,5,5)
766        calibSizer.SetFlexibleDirection(wx.HORIZONTAL)
767        calibSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calibration coefficients'),0,WACV)   
768        calibSizer.Add((5,0),0)
769        Names = ['det-X','det-Y','wave','dist','tilt','phi']
770        if 'PWDR' in data['type']:
771            Names.append('dep') 
772        Parms = {'dist':['Distance',(10,3),data,'distance'],'det-X':['Beam center X',(10,3),data['center'],0],
773            'det-Y':['Beam center Y',(10,3),data['center'],1],'tilt':['Tilt angle',(10,3),data,'tilt'],
774            'phi':['Tilt rotation',(10,2),data,'rotation'],'dep':['Penetration',(10,4),data,'DetDepth'],
775            'wave':['Wavelength',(10,6),data,'wavelength']}
776        for name in Names:
777            calSel = wx.CheckBox(parent=G2frame.dataDisplay,label=Parms[name][0])
778            calibSizer.Add(calSel,0,WACV)
779            calSel.Bind(wx.EVT_CHECKBOX, OnCalRef)
780            calSel.SetValue(data['varyList'][name])
781            Indx[calSel] = name
782            if name == 'wave':
783                calVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Parms[name][2],
784                    Parms[name][3],min=0.01,max=10.,nDig=Parms[name][1],typeHint=float)
785            elif name == 'dep':
786                calVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Parms[name][2],
787                    Parms[name][3],min=0.0,max=0.2,nDig=Parms[name][1],typeHint=float)
788            else:
789                calVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Parms[name][2],
790                    Parms[name][3],nDig=Parms[name][1],typeHint=float)
791            calibSizer.Add(calVal,0,WACV)
792        return calibSizer
793   
794    def IntegrateSizer():
795       
796        def OnNewBinType(event):
797            data['binType'] = binSel.GetValue()
798            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
799       
800        def OnIOtth(invalid,value,tc):
801            Ltth = float(G2frame.InnerTth.GetValue())
802            Utth = float(G2frame.OuterTth.GetValue())
803            if Ltth > Utth:
804                Ltth,Utth = Utth,Ltth
805            if 'q' in data['binType'].lower():
806                data['IOtth'] = [2.*asind(Ltth*wave/(4.*math.pi)),2.*asind(Utth*wave/(4.*math.pi))]
807            else:
808                data['IOtth'] = [Ltth,Utth]
809            G2frame.InnerTth.SetValue(Ltth)
810            G2frame.OuterTth.SetValue(Utth)
811            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
812       
813        def OnLRazim(invalid,value,tc):
814            Lazm = float(G2frame.Lazim.GetValue())%360.
815            Razm = float(G2frame.Razim.GetValue())%360.
816            if Lazm > Razm:
817                Razm += 360.
818            if data['fullIntegrate']:
819                Razm = Lazm+360.
820            G2frame.Lazim.SetValue(Lazm)
821            G2frame.Razim.SetValue(Razm)
822            data['LRazimuth'] = [Lazm,Razm]
823            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
824               
825        def OnNumOutAzms(invalid,value,tc):
826            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
827       
828        def OnOblique(event):
829            data['Oblique'][1] = not data['Oblique'][1]
830               
831        def OnSampleShape(event):
832            data['SampleShape'] = samShape.GetValue()
833            if 'Cylind' in data['SampleShape']:
834                data['SampleAbs'][0] = 0.0
835            elif 'Fixed' in data['SampleShape']:
836                data['SampleAbs'][0] = 1.0
837            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
838                           
839        def OnSamAbs(event):
840            data['SampleAbs'][1] = not data['SampleAbs'][1]
841                           
842        def OnShowLines(event):
843            data['showLines'] = not data['showLines']
844            G2plt.PlotExposedImage(G2frame,event=event)
845           
846        def OnFullIntegrate(event):
847            Lazm = float(G2frame.Lazim.GetValue())
848            if data['fullIntegrate']:
849                data['fullIntegrate'] = False
850                data['LRazimuth'] = [Lazm,Lazm+20.]
851            else:
852                data['fullIntegrate'] = True
853                data['LRazimuth'] = [Lazm,Lazm+360.]
854            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
855            G2plt.PlotExposedImage(G2frame,event=event)
856           
857        def OnSetDefault(event):
858            if data['setDefault']:
859                G2frame.imageDefault = {}
860                data['setDefault'] = False
861            else:
862                G2frame.imageDefault = copy.copy(data)
863                data['setDefault'] = True
864               
865        def OnCenterAzm(event):
866            data['centerAzm'] = not data['centerAzm']
867            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
868               
869        def OnApplyPola(event):
870            data['PolaVal'][1] = not data['PolaVal'][1]
871               
872        dataSizer = wx.FlexGridSizer(0,2,5,3)
873        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Integration coefficients'),0,WACV)   
874        dataSizer.Add((5,0),0)
875        if 'PWDR' in data['type']:
876            binChoice = ['2-theta','Q']
877        elif 'SASD' in data['type']:
878            binChoice = ['2-theta','Q','log(q)']
879        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Bin style: Constant step bins in'),0,WACV)           
880        binSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['binType'],choices=binChoice,
881            style=wx.CB_READONLY|wx.CB_DROPDOWN)
882        binSel.Bind(wx.EVT_COMBOBOX, OnNewBinType)
883        dataSizer.Add(binSel,0,WACV)
884        binType = '2-theta'
885        if 'q' in data['binType'].lower():
886            binType = 'Q'
887        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Inner/Outer '+binType),0,WACV)           
888        IOtth = data['IOtth'][:]
889        if 'q' in data['binType'].lower():
890            wave = data['wavelength']
891            IOtth = [4.*math.pi*sind(IOtth[0]/2.)/wave,4.*math.pi*sind(IOtth[1]/2.)/wave]
892        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
893        G2frame.InnerTth = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,IOtth,0,nDig=(8,3,'f'),min=0.001,typeHint=float,OnLeave=OnIOtth)
894        littleSizer.Add(G2frame.InnerTth,0,WACV)
895        G2frame.OuterTth = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,IOtth,1,nDig=(8,3,'f'),min=0.001,typeHint=float,OnLeave=OnIOtth)
896        littleSizer.Add(G2frame.OuterTth,0,WACV)
897        dataSizer.Add(littleSizer,0,)
898        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Start/End azimuth'),0,WACV)
899        LRazim = data['LRazimuth']
900        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
901        G2frame.Lazim = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,LRazim,0,nDig=(6,1,'f'),typeHint=float,OnLeave=OnLRazim)
902        littleSizer.Add(G2frame.Lazim,0,WACV)
903        G2frame.Razim = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,LRazim,1,nDig=(6,1,'f'),typeHint=float,OnLeave=OnLRazim)
904        if data['fullIntegrate']:
905            G2frame.Razim.Enable(False)
906            G2frame.Razim.SetBackgroundColour(VERY_LIGHT_GREY)
907            G2frame.Razim.SetValue(LRazim[0]+360.)
908        littleSizer.Add(G2frame.Razim,0,WACV)
909        dataSizer.Add(littleSizer,0,)
910        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' No. 2-theta/azimuth bins'),0,WACV)
911        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
912        outChan = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'outChannels',typeHint=int,min=10)
913        littleSizer.Add(outChan,0,WACV)
914        outAzim = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'outAzimuths',nDig=(10,4),min=1,typeHint=int,OnLeave=OnNumOutAzms)
915        littleSizer.Add(outAzim,0,WACV)
916        dataSizer.Add(littleSizer,0,)
917        samplechoice = ['Cylinder','Fixed flat plate',]
918        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label='Select sample shape'),0,WACV)
919        samShape = wx.ComboBox(G2frame.dataDisplay,value=data['SampleShape'],choices=samplechoice,
920            style=wx.CB_READONLY|wx.CB_DROPDOWN)
921        samShape.Bind(wx.EVT_COMBOBOX,OnSampleShape)
922        dataSizer.Add(samShape,0,WACV)
923        #SampleShape - cylinder or flat plate choice?
924        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
925        samabs = wx.CheckBox(parent=G2frame.dataDisplay,label='Apply sample absorption?')
926        dataSizer.Add(samabs,0,WACV)
927        samabs.Bind(wx.EVT_CHECKBOX, OnSamAbs)
928        samabs.SetValue(data['SampleAbs'][1])
929        minmax = [0.,2.]
930        if 'Cylind' in data['SampleShape']: #cylinder mu*R; flat plate transmission
931            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='mu*R (0.00-2.0) '),0,WACV)
932        elif 'Fixed' in data['SampleShape']:
933            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='transmission '),0,WACV) #for flat plate
934            minmax = [.05,1.0]
935        samabsVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['SampleAbs'],0,nDig=(10,3),
936            typeHint=float,min=minmax[0],max=minmax[1])
937        littleSizer.Add(samabsVal,0,WACV)
938        dataSizer.Add(littleSizer,0,)       
939        if 'PWDR' in data['type']:
940            littleSizer = wx.BoxSizer(wx.HORIZONTAL)
941            oblique = wx.CheckBox(parent=G2frame.dataDisplay,label='Apply detector absorption?')
942            dataSizer.Add(oblique,0,WACV)
943            oblique.Bind(wx.EVT_CHECKBOX, OnOblique)
944            oblique.SetValue(data['Oblique'][1])
945            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='Value (0.01-0.99)  '),0,WACV)
946            obliqVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['Oblique'],0,nDig=(10,3),typeHint=float,min=0.01,max=0.99)
947            littleSizer.Add(obliqVal,0,WACV)
948            dataSizer.Add(littleSizer,0,)
949        if 'SASD' in data['type']:
950            littleSizer = wx.BoxSizer(wx.HORIZONTAL)
951            setPolariz = wx.CheckBox(parent=G2frame.dataDisplay,label='Apply polarization?')
952            dataSizer.Add(setPolariz,0,WACV)
953            setPolariz.Bind(wx.EVT_CHECKBOX, OnApplyPola)
954            setPolariz.SetValue(data['PolaVal'][1])
955            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='Value (0.001-0.999)  '),0,WACV)
956            polaVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['PolaVal'],0,nDig=(10,3),typeHint=float,min=0.001,max=0.999)
957            littleSizer.Add(polaVal,0,WACV)
958            dataSizer.Add(littleSizer,0,)
959       
960        showLines = wx.CheckBox(parent=G2frame.dataDisplay,label='Show integration limits?')
961        dataSizer.Add(showLines,0,WACV)
962        showLines.Bind(wx.EVT_CHECKBOX, OnShowLines)
963        showLines.SetValue(data['showLines'])
964        fullIntegrate = wx.CheckBox(parent=G2frame.dataDisplay,label='Do full integration?')
965        dataSizer.Add(fullIntegrate,0,WACV)
966        fullIntegrate.Bind(wx.EVT_CHECKBOX, OnFullIntegrate)
967        fullIntegrate.SetValue(data['fullIntegrate'])
968        setDefault = wx.CheckBox(parent=G2frame.dataDisplay,label='Use for all new images?')
969        dataSizer.Add(setDefault,0,WACV)
970        setDefault.Bind(wx.EVT_CHECKBOX, OnSetDefault)
971        setDefault.SetValue(data['setDefault'])
972        centerAzm = wx.CheckBox(parent=G2frame.dataDisplay,label='Azimuth at bin center?')
973        dataSizer.Add(centerAzm,0,WACV)
974        centerAzm.Bind(wx.EVT_CHECKBOX, OnCenterAzm)
975        centerAzm.SetValue(data['centerAzm'])
976        return dataSizer
977       
978    def BackSizer():
979       
980        global oldFlat
981        def OnBackImage(event):
982            data['background image'][0] = backImage.GetValue()
983            G2frame.ImageZ = GetImageZ(G2frame,data,newRange=True)
984            ResetThresholds()
985            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
986           
987        def OnDarkImage(event):
988            data['dark image'][0] = darkImage.GetValue()
989            G2frame.ImageZ = GetImageZ(G2frame,data,newRange=True)
990            ResetThresholds()
991            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
992           
993        def OnFlatBkg(invalid,value,tc):
994            global oldFlat
995            G2frame.ImageZ += int(oldFlat-data['Flat Bkg'])
996            oldFlat = data['Flat Bkg']
997            ResetThresholds()
998            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
999
1000        def OnMult(invalid,value,tc):
1001            G2frame.ImageZ = GetImageZ(G2frame,data,newRange=True)
1002            ResetThresholds()
1003            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
1004       
1005        backSizer = wx.FlexGridSizer(0,6,5,5)
1006        oldFlat = data.get('Flat Bkg',0.)
1007
1008        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Dark image'),0,WACV)
1009        Choices = ['',]+G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1010        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
1011        Choices.pop(Choices.index(Source))
1012        darkImage = wx.ComboBox(parent=G2frame.dataDisplay,value=data['dark image'][0],choices=Choices,
1013            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1014        darkImage.Bind(wx.EVT_COMBOBOX,OnDarkImage)
1015        backSizer.Add(darkImage)
1016        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' multiplier'),0,WACV)
1017        darkMult = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['dark image'],1,nDig=(10,3),
1018            typeHint=float,OnLeave=OnMult)
1019        backSizer.Add(darkMult,0,WACV)
1020        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Flat Bkg: '),0,WACV)
1021        flatbkg = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Flat Bkg',nDig=(10,0),
1022            typeHint=float,OnLeave=OnFlatBkg)
1023        backSizer.Add(flatbkg,0,WACV)
1024
1025        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Background image'),0,WACV)
1026        backImage = wx.ComboBox(parent=G2frame.dataDisplay,value=data['background image'][0],choices=Choices,
1027            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1028        backImage.Bind(wx.EVT_COMBOBOX,OnBackImage)
1029        backSizer.Add(backImage)
1030        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' multiplier'),0,WACV)
1031        backMult = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['background image'],1,nDig=(10,3),
1032            typeHint=float,OnLeave=OnMult)
1033        backSizer.Add(backMult,0,WACV)
1034        return backSizer
1035                       
1036    def CalibSizer():
1037               
1038        def OnNewCalibrant(event):
1039            data['calibrant'] = calSel.GetValue().strip()
1040            if data['calibrant']:
1041                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBRATE,enable=True)
1042                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMCALIBRATE,enable=True)
1043                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBALL,enable=True)
1044                data['calibskip'] = calFile.Calibrants[data['calibrant']][3]
1045                limits = calFile.Calibrants[data['calibrant']][4]
1046                data['calibdmin'],data['pixLimit'],data['cutoff'] = limits
1047                pixLimit.SetValue(str(limits[1]))
1048                cutOff.SetValue(limits[2])
1049                calibSkip.SetValue(str(data['calibskip']))
1050                G2frame.calibDmin.SetValue(limits[0])
1051            else:
1052                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBRATE,enable=False)
1053                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMCALIBRATE,enable=False)
1054                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBALL,enable=False)
1055               
1056        def OnCalibSkip(event):
1057            data['calibskip'] = int(calibSkip.GetValue())
1058           
1059        def OnPixLimit(event):
1060            data['pixLimit'] = int(pixLimit.GetValue())
1061           
1062        def OnSetRings(event):
1063            data['setRings'] = not data['setRings']
1064            G2plt.PlotExposedImage(G2frame,event=event)
1065   
1066        calibSizer = wx.FlexGridSizer(0,3,5,5)
1067        comboSizer = wx.BoxSizer(wx.HORIZONTAL)   
1068        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calibrant '),0,WACV)
1069        calSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['calibrant'],choices=calList,
1070            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1071        calSel.Bind(wx.EVT_COMBOBOX, OnNewCalibrant)
1072        comboSizer.Add(calSel,0,WACV)
1073        calibSizer.Add(comboSizer,0)
1074       
1075        comboSizer = wx.BoxSizer(wx.HORIZONTAL)   
1076        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calib lines to skip   '),0,WACV)
1077        calibSkip  = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['calibskip']),choices=[str(i) for i in range(25)],
1078            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1079        calibSkip.Bind(wx.EVT_COMBOBOX, OnCalibSkip)
1080        comboSizer.Add(calibSkip,0,WACV)
1081        calibSizer.Add(comboSizer,0)
1082       
1083        comboSizer = wx.BoxSizer(wx.HORIZONTAL)       
1084        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min calib d-spacing '),0,WACV)
1085        G2frame.calibDmin = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'calibdmin',nDig=(10,2),typeHint=float,min=0.25)
1086        comboSizer.Add(G2frame.calibDmin,0,WACV)
1087        calibSizer.Add(comboSizer,0)
1088       
1089        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1090        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min ring I/Ib '),0,WACV)
1091        cutOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'cutoff',nDig=(10,2),min=0.1)
1092        comboSizer.Add(cutOff,0,WACV)
1093        calibSizer.Add(comboSizer,0)
1094       
1095        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1096        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Pixel search range '),0,WACV)
1097        pixLimit = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['pixLimit']),choices=['1','2','5','10','15','20'],
1098            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1099        pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
1100        comboSizer.Add(pixLimit,0,WACV)
1101        calibSizer.Add(comboSizer,0)
1102       
1103        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1104        setRings = wx.CheckBox(parent=G2frame.dataDisplay,label='Show ring picks?')
1105        comboSizer.Add(setRings,0)
1106        setRings.Bind(wx.EVT_CHECKBOX, OnSetRings)
1107        setRings.SetValue(data['setRings'])
1108        calibSizer.Add(comboSizer,0)
1109        return calibSizer
1110       
1111    def GonioSizer():
1112       
1113        def OnGlobalEdit(event):
1114            Names = []
1115            Items = []
1116            if G2frame.PatternTree.GetCount():
1117                id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1118                while id:
1119                    name = G2frame.PatternTree.GetItemText(id)
1120                    if 'IMG' in name:
1121                        ctrls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,id,'Image Controls'))
1122                        Names.append(name)
1123                        Items.append(ctrls['GonioAngles'])
1124                    id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1125                if len(Names) == 1:
1126                    G2frame.ErrorDialog('Nothing for global editing','There must be more than one "IMG" pattern')
1127                    return
1128                dlg = G2G.G2HistoDataDialog(G2frame,' Edit sample goniometer data:',
1129                    'Edit data',['Omega','Chi','Phi'],['%.2f','%.2f','%.2f'],Names,Items)
1130                try:
1131                    if dlg.ShowModal() == wx.ID_OK:
1132                        id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1133                        while id:
1134                            name = G2frame.PatternTree.GetItemText(id)
1135                            if 'IMG' in name:
1136                                ctrls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,id,'Image Controls'))
1137                                vals = Items[Names.index(name)]
1138                                ctrls['GonioAngles'] = vals
1139                            id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1140                finally:
1141                    dlg.Destroy()
1142                    G2frame.PatternTree.SelectItem(G2frame.PickId)
1143       
1144        gonioSizer = wx.BoxSizer(wx.HORIZONTAL)
1145        names = ['Omega','Chi','Phi']
1146        gonioSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,'Sample goniometer angles: '),0,WACV)
1147        for i,name in enumerate(names):
1148            gonioSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,name),0,WACV)
1149            angle = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['GonioAngles'],i,nDig=(8,2),typeHint=float)
1150            gonioSizer.Add(angle,0,WACV)
1151        globEdit = wx.Button(G2frame.dataDisplay,-1,'Global edit')
1152        globEdit.Bind(wx.EVT_BUTTON,OnGlobalEdit)
1153        gonioSizer.Add(globEdit,0,WACV)
1154        return gonioSizer
1155       
1156# Image Controls main code             
1157                           
1158    #fix for old files:
1159    if 'azmthOff' not in data:
1160        data['azmthOff'] = 0.0
1161    if 'background image' not in data:
1162        data['background image'] = ['',-1.0]
1163    if 'dark image' not in data:
1164        data['dark image'] = ['',-1.0]
1165    if 'centerAzm' not in data:
1166        data['centerAzm'] = False
1167    if 'Oblique' not in data:
1168        data['Oblique'] = [0.5,False]
1169    if 'PolaVal' not in data:
1170        data['PolaVal'] = [0.99,False]
1171    #end fix
1172   
1173    if IntegrateOnly:
1174        OnIntegrate(None)
1175        return
1176   
1177    colorList = sorted([m for m in mpl.cm.datad.keys() if not m.endswith("_r")],key=lambda s: s.lower())
1178    calList = sorted([m for m in calFile.Calibrants.keys()],key=lambda s: s.lower())
1179    typeList = ['PWDR - powder diffraction data','SASD - small angle scattering data',]
1180    if not data.get('type'):                        #patch for old project files
1181        data['type'] = 'PWDR'
1182    typeDict = {'PWDR':typeList[0],'SASD':typeList[1],}
1183    if G2frame.dataDisplay:
1184        G2frame.dataDisplay.Destroy()
1185    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.ImageMenu)
1186    if not G2frame.dataFrame.GetStatusBar():
1187        G2frame.dataFrame.CreateStatusBar()
1188    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCalibrate, id=G2gd.wxID_IMCALIBRATE)
1189    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRecalibrate, id=G2gd.wxID_IMRECALIBRATE)
1190    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRecalibAll, id=G2gd.wxID_IMRECALIBALL)
1191    G2frame.dataFrame.Bind(wx.EVT_MENU, OnClearCalib, id=G2gd.wxID_IMCLEARCALIB)
1192    if data.get('calibrant'):
1193        mode = True
1194    else:
1195        mode = False
1196    G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBRATE,enable=mode)
1197    G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMCALIBRATE,enable=mode)
1198    G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBALL,enable=mode)
1199    G2frame.dataFrame.Bind(wx.EVT_MENU, OnIntegrate, id=G2gd.wxID_IMINTEGRATE)
1200    G2frame.dataFrame.Bind(wx.EVT_MENU, OnIntegrateAll, id=G2gd.wxID_INTEGRATEALL)
1201    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyControls, id=G2gd.wxID_IMCOPYCONTROLS)
1202    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopySelected, id=G2gd.wxID_IMCOPYSELECTED)
1203    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveControls, id=G2gd.wxID_IMSAVECONTROLS)
1204    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveMultiControls, id=G2gd.wxID_SAVESELECTEDCONTROLS)
1205    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadControls, id=G2gd.wxID_IMLOADCONTROLS)
1206    G2frame.dataFrame.Bind(wx.EVT_MENU, OnTransferAngles, id=G2gd.wxID_IMXFERCONTROLS)
1207    G2frame.dataFrame.Bind(wx.EVT_MENU, OnResetDist, id=G2gd.wxID_IMRESETDIST)
1208    def OnDestroy(event):
1209        G2frame.autoIntFrame = None
1210    def OnAutoInt(event):
1211        if G2frame.autoIntFrame: # ensure only one open at a time
1212            G2frame.autoIntFrame.Raise()
1213            return
1214        PollTime = GSASIIpath.GetConfigValue('Autoint_PollTime',30.)
1215        G2frame.autoIntFrame = AutoIntFrame(G2frame,PollTime=PollTime)
1216        G2frame.autoIntFrame.Bind(wx.EVT_WINDOW_DESTROY,OnDestroy) # clean up name on window close
1217    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAutoInt, id=G2gd.wxID_IMAUTOINTEG)
1218    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
1219
1220    mainSizer = wx.BoxSizer(wx.VERTICAL)
1221    mainSizer.Add((5,10),0)   
1222    mainSizer.Add(ComboSizer(),0,wx.ALIGN_LEFT)
1223    mainSizer.Add((5,5),0)
1224    Range = data['range'] # allows code to be same in Masks
1225    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1226    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1227   
1228    mainSizer.Add((5,5),0)
1229    DataSizer = wx.FlexGridSizer(0,2,5,0)
1230    DataSizer.Add(CalibCoeffSizer(),0)
1231    DataSizer.Add(IntegrateSizer(),0)       
1232    mainSizer.Add(DataSizer,0)
1233    mainSizer.Add((5,5),0)           
1234    mainSizer.Add(BackSizer(),0)
1235    mainSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calibration controls:'),0,WACV)
1236    mainSizer.Add((5,5),0)
1237    mainSizer.Add(CalibSizer(),0,WACV)
1238    mainSizer.Add((5,5),0)
1239    mainSizer.Add(GonioSizer(),0,WACV)   
1240       
1241    mainSizer.Layout()   
1242    G2frame.dataDisplay.SetSizer(mainSizer)
1243    fitSize = mainSizer.Fit(G2frame.dataFrame)
1244    G2frame.dataFrame.setSizePosLeft(fitSize)
1245    G2frame.dataFrame.SendSizeEvent()
1246   
1247################################################################################
1248##### Masks
1249################################################################################
1250def CleanupMasks(data):
1251    '''If a mask creation is not completed, an empty mask entry is created in the
1252    masks array. This cleans them out. It is called when the masks page is first loaded
1253    and before saving them or after reading them in. This should also probably be done
1254    before they are used for integration.
1255    '''
1256    for key in ['Points','Rings','Arcs','Polygons']:
1257        data[key] = data.get(key,[])
1258        l1 = len(data[key])
1259        data[key] = [i for i in data[key] if len(i)]
1260        l2 = len(data[key])
1261        if GSASIIpath.GetConfigValue('debug') and l1 != l2:
1262            print 'Mask Cleanup:',key,'was',l1,'entries','now',l2
1263   
1264def UpdateMasks(G2frame,data):
1265    '''Shows and handles the controls on the "Masks" data tree entry
1266    '''
1267   
1268    def OnTextMsg(event):
1269        Obj = event.GetEventObject()
1270        Obj.SetToolTipString('Drag this mask on 2D Powder Image with mouse to change ')
1271
1272    def Replot(*args,**kwargs):
1273        wx.CallAfter(G2plt.PlotExposedImage,G2frame)
1274
1275    def newReplot(*args,**kwargs):
1276        wx.CallAfter(G2plt.PlotExposedImage,G2frame,newPlot=True)
1277
1278    def onDeleteMask(event):
1279        Obj = event.GetEventObject()
1280        typ = Obj.locationcode.split('+')[1]
1281        num = int(Obj.locationcode.split('+')[2])
1282        del(data[typ][num])
1283        wx.CallAfter(UpdateMasks,G2frame,data)
1284        G2plt.PlotExposedImage(G2frame,event=event)
1285
1286    def onDeleteFrame(event):
1287        data['Frames'] = []
1288        wx.CallAfter(UpdateMasks,G2frame,data)
1289        G2plt.PlotExposedImage(G2frame,event=event)
1290
1291    def OnCopyMask(event):
1292        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1293        if len(Names) == 1:
1294            G2frame.ErrorDialog('Nothing to copy masks to','There must be more than one "IMG" pattern')
1295            return
1296        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
1297        Names.pop(Names.index(Source))
1298        Data = copy.deepcopy(data)
1299        Thresh = Data.pop('Thresholds')     # & remove it as well
1300        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy mask data','Copy masks from '+Source+' to:',Names)
1301        try:
1302            if dlg.ShowModal() == wx.ID_OK:
1303                items = dlg.GetSelections()
1304                for item in items:
1305                    name = Names[item]
1306                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
1307                    MId = G2gd.GetPatternTreeItemId(G2frame,Id,'Masks')
1308                    Mask = G2frame.PatternTree.GetItemPyData(MId)
1309                    Mask.update(copy.deepcopy(Data))
1310                    Mask['Thresholds'][1][0] = Thresh[1][0]  #copy only lower threshold
1311                    G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Masks'),Mask)
1312        finally:
1313            dlg.Destroy()
1314
1315    def OnSaveMask(event):
1316        CleanupMasks(data)
1317        pth = G2G.GetExportPath(G2frame)
1318        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1319            'image mask files (*.immask)|*.immask',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1320        try:
1321            if dlg.ShowModal() == wx.ID_OK:
1322                filename = dlg.GetPath()
1323                filename = os.path.splitext(filename)[0]+'.immask'
1324                File = open(filename,'w')
1325                keys = ['Points','Rings','Arcs','Polygons','Frames','Thresholds']
1326                for key in keys:
1327                    File.write(key+':'+str(data[key])+'\n')
1328                File.close()
1329        finally:
1330            dlg.Destroy()
1331       
1332    def OnLoadMask(event):
1333        if event.Id == G2gd.wxID_MASKLOADNOT:
1334            ignoreThreshold = True
1335        else:
1336            ignoreThreshold = False
1337        pth = G2G.GetImportPath(G2frame)
1338        if not pth: pth = '.'
1339        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1340            'image mask files (*.immask)|*.immask',wx.OPEN)
1341        try:
1342            if dlg.ShowModal() == wx.ID_OK:
1343                filename = dlg.GetPath()
1344                File = open(filename,'r')
1345                save = {}
1346                oldThreshold = data['Thresholds'][0]
1347                S = File.readline()
1348                while S:
1349                    if S[0] == '#':
1350                        S = File.readline()
1351                        continue
1352                    [key,val] = S.strip().split(':',1)
1353                    if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
1354                        if ignoreThreshold and key == 'Thresholds':
1355                            S = File.readline() 
1356                            continue
1357                        save[key] = eval(val)
1358                        if key == 'Thresholds':
1359                            save[key][0] = oldThreshold
1360                            save[key][1][1] = min(oldThreshold[1],save[key][1][1])
1361                    S = File.readline()
1362                File.close()
1363                data.update(save)
1364                CleanupMasks(data)
1365                wx.CallAfter(UpdateMasks,G2frame,data)
1366                G2plt.PlotExposedImage(G2frame,event=event)               
1367        finally:
1368            dlg.Destroy()
1369           
1370    def OnAutoSpotMask(event):
1371        'Do auto search for spot masks'
1372        if wx.MessageDialog(G2frame.dataDisplay,'NB: This will clear any old spot masks',
1373            'Auto Spot Masks', wx.OK|wx.CANCEL).ShowModal() == wx.ID_OK:
1374            Controls = copy.deepcopy(G2frame.PatternTree.GetItemPyData( 
1375                G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Image Controls')))
1376            Error = G2img.AutoSpotMasks(G2frame.ImageZ,data,Controls)
1377            if not Error is None:
1378                G2frame.ErrorDialog('Auto spot search error',Error)
1379            wx.CallAfter(UpdateMasks,G2frame,data)
1380            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
1381
1382    def OnDeleteSpotMask(event):
1383        data['Points'] = []
1384        wx.CallAfter(UpdateMasks,G2frame,data)
1385        wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)         
1386           
1387    def ToggleSpotMaskMode(event):
1388        G2plt.ToggleMultiSpotMask(G2frame)
1389       
1390    def OnNewArcMask(event):
1391        'Start a new arc mask'
1392        G2frame.MaskKey = 'a'
1393        G2plt.OnStartMask(G2frame)
1394       
1395    def OnNewRingMask(event):
1396        'Start a new ring mask'
1397        G2frame.MaskKey = 'r'
1398        G2plt.OnStartMask(G2frame)
1399       
1400    def OnNewPolyMask(event):
1401        'Start a new polygon mask'
1402        G2frame.MaskKey = 'p'
1403        G2plt.OnStartMask(G2frame)
1404       
1405    def OnNewFrameMask(event):
1406        'Start a new Frame mask'
1407        G2frame.MaskKey = 'f'
1408        G2plt.OnStartMask(G2frame)
1409       
1410    def MaxSizer():
1411        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
1412        for the image, as well as callback routines.
1413        '''
1414        def OnNewVal(invalid,value,tc):
1415            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
1416            the value into the data['range'] nested list).
1417            This adjusts the slider positions to match the current values
1418            '''
1419            scaleSel.SetSelection(len(scaleChoices)-1)
1420            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
1421            if r11 != Range[1][1]:
1422                Range[1][1] = r11
1423                maxVal.SetValue(int(Range[1][1]))
1424            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
1425            if r10 != Range[1][0]:
1426                Range[1][0] = r10
1427                minVal.SetValue(int(Range[1][0]))
1428            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1429            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1430            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1431            maxSel.SetValue(sv1)
1432            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1433            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1434            minSel.SetValue(sv0)
1435            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1436            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1437            if mplOld:
1438                Page.canvas.draw()
1439            else:
1440                Page.canvas.draw_idle()
1441           
1442        G2frame.prevMaxValue = None   
1443        def OnMaxSlider(event):
1444            val = maxSel.GetValue()
1445            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
1446            scaleSel.SetSelection(len(scaleChoices)-1)
1447            G2frame.prevMaxValue = val
1448            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1449            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
1450            maxVal.SetValue(int(0.5+Range[1][1]))
1451            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1452            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
1453            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1454            minSel.SetValue(sv0)
1455            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1456            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1457            if mplOld:
1458                Page.canvas.draw()
1459            else:
1460                Page.canvas.draw_idle()
1461           
1462        G2frame.prevMinValue = None   
1463        def OnMinSlider(event):
1464            val = minSel.GetValue()
1465            scaleSel.SetSelection(len(scaleChoices)-1)
1466            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
1467            G2frame.prevMinValue = val
1468            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1469            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
1470            minVal.SetValue(int(Range[1][0]))
1471            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1472            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1473            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1474            maxSel.SetValue(sv1)
1475            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1476            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1477            if mplOld:
1478                Page.canvas.draw()
1479            else:
1480                Page.canvas.draw_idle()
1481           
1482        def OnAutoSet(event):
1483            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
1484            for the image so that 95% (etc.) of pixels are inside the color map limits.
1485            An equal number of pixels are dropped at the minimum and maximum levels.
1486            '''
1487            try:
1488                val = int(event.GetEventObject().GetStringSelection()[:-1])
1489                margin = (100-val)/2.
1490            except:
1491                margin = 0
1492                event.GetEventObject().SetSelection(0)
1493            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1494            if margin == 0:
1495                Range[1] = list(Range[0])
1496            else:
1497                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
1498                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
1499            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1500            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1501            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1502            maxSel.SetValue(sv1)
1503            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1504            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1505            minSel.SetValue(sv0)
1506            minVal.SetValue(int(Range[1][0]))
1507            maxVal.SetValue(int(Range[1][1]))
1508            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1509            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1510            if mplOld:
1511                Page.canvas.draw()
1512            else:
1513                Page.canvas.draw_idle()
1514
1515        mplv = mpl.__version__.split('.')
1516        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
1517        # Plot color scaling uses limits as below:
1518        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
1519        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
1520        maxSizer = wx.GridBagSizer(0,0)
1521        r = c = 0
1522        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Max intensity'),(r,c))
1523        c += 1
1524        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
1525        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
1526        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
1527        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1528        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1529        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1530        maxSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv1,size=(300,-1))
1531        maxSizer.Add(maxSel,(r,c),flag=wx.EXPAND)
1532        maxSizer.AddGrowableCol(c)
1533        c += 1
1534        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
1535        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],1,min=Range[0][0]+1,
1536            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1537        maxSizer.Add(maxVal,(r,c))
1538        c += 1
1539        scaleChoices = ("100%","99%","95%","90%","80%","?")
1540        scaleSel = wx.Choice(G2frame.dataDisplay,choices=scaleChoices,size=(-1,-1))
1541        if (Range[1][0] == Range[0][0] and
1542            Range[1][1] == Range[0][1]):
1543            scaleSel.SetSelection(0)
1544        else:
1545            scaleSel.SetSelection(len(scaleChoices)-1)
1546        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
1547        maxSizer.Add(scaleSel,(r,c),(2,1),flag=wx.ALIGN_CENTER)
1548        c = 0
1549        r = 1
1550        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min intensity'),(r,c))
1551        c += 1
1552        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
1553        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
1554        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
1555        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1556        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1557        minSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv0,size=(300,-1))
1558        maxSizer.Add(minSel,(r,c),flag=wx.EXPAND|wx.ALL)
1559        c += 1
1560        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
1561        minVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],0,
1562            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1563        maxSizer.Add(minVal,(r,c))
1564        return maxSizer
1565
1566    startScroll = None
1567    if G2frame.dataDisplay:
1568        startScroll = G2frame.dataDisplay.GetScrollPos(wx.VERTICAL) # save scroll position
1569        G2frame.dataDisplay.Destroy()
1570    else:
1571        CleanupMasks(data) # posting page for 1st time; clean out anything unfinished
1572    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.MaskMenu)
1573    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyMask, id=G2gd.wxID_MASKCOPY)
1574    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadMask, id=G2gd.wxID_MASKLOAD)
1575    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadMask, id=G2gd.wxID_MASKLOADNOT)
1576    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveMask, id=G2gd.wxID_MASKSAVE)
1577    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAutoSpotMask, id=G2gd.wxID_FINDSPOTS)
1578    G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteSpotMask, id=G2gd.wxID_DELETESPOTS)
1579    G2frame.dataFrame.Bind(wx.EVT_MENU, ToggleSpotMaskMode, id=G2gd.wxID_NEWMASKSPOT)
1580    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewArcMask, id=G2gd.wxID_NEWMASKARC)
1581    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewRingMask, id=G2gd.wxID_NEWMASKRING)
1582    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewPolyMask, id=G2gd.wxID_NEWMASKPOLY)
1583    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewFrameMask, id=G2gd.wxID_NEWMASKFRAME)
1584    if not G2frame.dataFrame.GetStatusBar():
1585        G2frame.dataFrame.CreateStatusBar()
1586    if G2frame.MaskKey == 'f':
1587        G2frame.dataFrame.GetStatusBar().SetStatusText('Frame mask active - LB pick next point, RB close polygon')
1588    elif G2frame.MaskKey == 'p':
1589        G2frame.dataFrame.GetStatusBar().SetStatusText('Polygon mask active - LB pick next point, RB close polygon')
1590    elif G2frame.MaskKey == 'a':
1591        G2frame.dataFrame.GetStatusBar().SetStatusText('Arc mask active - LB pick arc location')
1592    elif G2frame.MaskKey == 'r':
1593        G2frame.dataFrame.GetStatusBar().SetStatusText('Ring mask active - LB pick ring location')
1594    else:
1595        G2frame.dataFrame.GetStatusBar().SetStatusText("To add mask: press a,r,s,p or f on 2D image for arc/ring/spot/polygon/frame")
1596    G2frame.dataDisplay = wxscroll.ScrolledPanel(G2frame.dataFrame)
1597    mainSizer = wx.BoxSizer(wx.VERTICAL)
1598    mainSizer.Add((5,10),0)
1599
1600    thresh = data['Thresholds']         #min/max intensity range
1601    Spots = data['Points']               #x,y,radius in mm
1602    Rings = data['Rings']               #radius, thickness
1603    Polygons = data['Polygons']         #3+ x,y pairs
1604    if 'Frames' not in data:
1605        data['Frames'] = []
1606    frame = data['Frames']             #3+ x,y pairs
1607    Arcs = data['Arcs']                 #radius, start/end azimuth, thickness
1608
1609    ######################################################################
1610    CId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Image Controls')
1611    controlData = G2frame.PatternTree.GetItemPyData(CId)
1612    Range = controlData['range']
1613    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1614    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1615
1616    littleSizer = wx.FlexGridSizer(0,3,0,5)
1617    littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Lower/Upper limits '),0,WACV)
1618    Text = wx.TextCtrl(G2frame.dataDisplay,value=str(thresh[0][0]),style=wx.TE_READONLY)
1619    littleSizer.Add(Text,0,WACV)
1620    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1621    Text = wx.TextCtrl(G2frame.dataDisplay,value=str(thresh[0][1]),style=wx.TE_READONLY)
1622    littleSizer.Add(Text,0,WACV)
1623    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1624    littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Lower/Upper thresholds '),0,WACV)
1625    lowerThreshold = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=thresh[1],key=0,
1626        min=thresh[0][0],OnLeave=newReplot,typeHint=int)
1627    littleSizer.Add(lowerThreshold,0,WACV)
1628    upperThreshold = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=thresh[1],key=1,
1629        max=thresh[0][1],OnLeave=newReplot,typeHint=int)
1630    littleSizer.Add(upperThreshold,0,WACV)
1631    mainSizer.Add(littleSizer,0,)
1632    if len(Spots):
1633        lbl = wx.StaticText(parent=G2frame.dataDisplay,label=' Spot masks')
1634        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1635        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1636        littleSizer = wx.FlexGridSizer(0,3,0,5)
1637        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' position, mm'),0,WACV)
1638        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' diameter, mm'),0,WACV)
1639        littleSizer.Add((5,0),0)
1640        for i in range(len(Spots)):
1641            if len(Spots[i]):
1642                x,y,d = Spots[i]
1643                spotText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%.2f,%.2f" % (x,y)),
1644                    style=wx.TE_READONLY)
1645                spotText.SetBackgroundColour(VERY_LIGHT_GREY)
1646                littleSizer.Add(spotText,0,WACV)
1647                spotText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1648                spotDiameter = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=Spots[i],key=2,
1649                    max=100.,OnLeave=Replot,nDig=[8,2])
1650                littleSizer.Add(spotDiameter,0,WACV)
1651                spotDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1652                    locationcode='Delete+Points+'+str(i),handler=onDeleteMask)
1653                littleSizer.Add(spotDelete,0,WACV)
1654        mainSizer.Add(littleSizer,0,)
1655    if Rings:
1656        lbl = wx.StaticText(parent=G2frame.dataDisplay,label=' Ring masks')
1657        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1658        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1659        littleSizer = wx.FlexGridSizer(0,3,0,5)
1660        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' 2-theta,deg'),0,WACV)
1661        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' thickness, deg'),0,WACV)
1662        littleSizer.Add((5,0),0)
1663        for i in range(len(Rings)):
1664            if Rings[i]:
1665                ringText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%.3f" % (Rings[i][0])),
1666                    style=wx.TE_READONLY)
1667                ringText.SetBackgroundColour(VERY_LIGHT_GREY)
1668                ringText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1669                littleSizer.Add(ringText,0,WACV)
1670                ringThick = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=Rings[i],key=1,
1671                    min=0.001,max=1.,OnLeave=Replot,nDig=[8,3])
1672                littleSizer.Add(ringThick,0,WACV)
1673                ringDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1674                    locationcode='Delete+Rings+'+str(i),handler=onDeleteMask)
1675                littleSizer.Add(ringDelete,0,WACV)
1676        mainSizer.Add(littleSizer,0,)
1677    if Arcs:
1678        lbl = wx.StaticText(parent=G2frame.dataDisplay,label=' Arc masks')
1679        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1680        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1681        littleSizer = wx.FlexGridSizer(0,4,0,5)
1682        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' 2-theta,deg'),0,WACV)
1683        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' azimuth, deg'),0,WACV)
1684        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' thickness, deg'),0,WACV)
1685        littleSizer.Add((5,0),0)
1686        for i in range(len(Arcs)):
1687            if Arcs[i]:
1688                tth,azimuth,thick = Arcs[i]
1689                arcText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%.3f" % (tth)),
1690                    style=wx.TE_READONLY)
1691                arcText.SetBackgroundColour(VERY_LIGHT_GREY)
1692                arcText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1693                littleSizer.Add(arcText,0,WACV)
1694                azmText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%d,%d" % (azimuth[0],azimuth[1])),
1695                    style=wx.TE_READONLY)
1696                azmText.SetBackgroundColour(VERY_LIGHT_GREY)
1697                azmText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1698                littleSizer.Add(azmText,0,WACV)
1699                arcThick = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=Arcs[i],key=2,
1700                    min=0.001,max=20.,OnLeave=Replot,nDig=[8,3])
1701                littleSizer.Add(arcThick,0,WACV)
1702                arcDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1703                    locationcode='Delete+Arcs+'+str(i),handler=onDeleteMask)
1704                littleSizer.Add(arcDelete,0,WACV)
1705        mainSizer.Add(littleSizer,0,)
1706    if Polygons:
1707        lbl = wx.StaticText(parent=G2frame.dataDisplay,
1708            label=' Polygon masks (on plot RB vertex drag to move, LB vertex drag to insert)')
1709        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1710        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1711        littleSizer = wx.FlexGridSizer(0,2,0,5)
1712        for i in range(len(Polygons)):
1713            if Polygons[i]:
1714                polyList = []
1715                for x,y in Polygons[i]:
1716                    polyList.append("%.2f, %.2f"%(x,y))
1717                polyText = wx.ComboBox(G2frame.dataDisplay,value=polyList[0],choices=polyList,style=wx.CB_READONLY)
1718                littleSizer.Add(polyText,0,WACV)
1719                polyDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1720                    locationcode='Delete+Polygons+'+str(i),handler=onDeleteMask)
1721                littleSizer.Add(polyDelete,0,WACV)
1722        mainSizer.Add(littleSizer,0,)
1723    if frame:
1724        lbl = wx.StaticText(parent=G2frame.dataDisplay,
1725            label=' Frame mask (on plot RB vertex drag to move, LB vertex drag to insert)')
1726        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1727        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1728        littleSizer = wx.FlexGridSizer(0,2,0,5)
1729        frameList = []
1730        for x,y in frame:
1731            frameList.append("%.2f, %.2f"%(x,y))
1732        frameText = wx.ComboBox(G2frame.dataDisplay,value=frameList[0],choices=frameList,style=wx.CB_READONLY)
1733        littleSizer.Add(frameText,0,WACV)
1734        frameDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1735            locationcode='Delete+Frame',handler=onDeleteFrame)
1736        littleSizer.Add(frameDelete,0,WACV)
1737        mainSizer.Add(littleSizer,0,)
1738    mainSizer.Layout()   
1739    G2frame.dataDisplay.SetSizer(mainSizer)
1740    G2frame.dataDisplay.SetupScrolling()
1741    Size = mainSizer.Fit(G2frame.dataFrame)
1742    Size[0] += 50 # room for scrollbar & status msg
1743    Size[1] = min(Size[1],500)
1744    G2frame.dataFrame.setSizePosLeft(Size)   
1745    if startScroll: # reset scroll to saved position
1746        G2frame.dataDisplay.Scroll(0,startScroll) # set to saved scroll position
1747        wx.Yield()
1748
1749################################################################################
1750##### Stress/Strain
1751################################################################################
1752
1753def UpdateStressStrain(G2frame,data):
1754    '''Shows and handles the controls on the "Stress/Strain"
1755    data tree entry
1756    '''
1757   
1758    def OnAppendDzero(event):
1759        data['d-zero'].append({'Dset':1.0,'Dcalc':0.0,'pixLimit':10,'cutoff':1.0,
1760            'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]],'Emat':[1.0,1.0,1.0],'fixDset':True})
1761        UpdateStressStrain(G2frame,data)
1762       
1763    def OnUpdateDzero(event):
1764        for item in data['d-zero']:
1765            if item['Dcalc']:   #skip unrefined ones
1766                item['Dset'] = item['Dcalc']
1767        UpdateStressStrain(G2frame,data)
1768           
1769    def OnCopyStrSta(event):
1770        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1771        if len(Names) == 1:
1772            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
1773            return
1774        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
1775        Names.pop(Names.index(Source))
1776        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy stress/strain controls','Copy controls from '+Source+' to:',Names)
1777        try:
1778            if dlg.ShowModal() == wx.ID_OK:
1779                items = dlg.GetSelections()
1780                for item in items:
1781                    name = Names[item]
1782                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
1783                    CId = G2gd.GetPatternTreeItemId(G2frame,Id,'Stress/Strain')
1784                    oldData = G2frame.PatternTree.GetItemPyData(CId)
1785                    load = oldData.get('Sample load',0.0)
1786                    Data = copy.deepcopy(data)
1787                    Data['Sample load'] = load
1788                    G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Stress/Strain'),Data)
1789        finally:
1790            dlg.Destroy()
1791            G2frame.PatternTree.SelectItem(G2frame.PickId)
1792
1793    def OnLoadStrSta(event):
1794        pth = G2G.GetImportPath(G2frame)
1795        if not pth: pth = '.'
1796        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1797            'image control files (*.strsta)|*.strsta',wx.OPEN)
1798        try:
1799            if dlg.ShowModal() == wx.ID_OK:
1800                filename = dlg.GetPath()
1801                File = open(filename,'r')
1802                S = File.read()
1803                data = eval(S)
1804                Controls = G2frame.PatternTree.GetItemPyData(
1805                    G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1806                G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1807                UpdateStressStrain(G2frame,data)
1808                G2plt.PlotExposedImage(G2frame,event=event)
1809                G2plt.PlotStrain(G2frame,data,newPlot=True)
1810                File.close()
1811        finally:
1812            dlg.Destroy()
1813
1814    def OnSaveStrSta(event):
1815        pth = G2G.GetExportPath(G2frame)
1816        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1817            'image control files (*.strsta)|*.strsta',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1818        try:
1819            if dlg.ShowModal() == wx.ID_OK:
1820                filename = dlg.GetPath()
1821                File = open(filename,'w')
1822                keys = ['Type','Sample phi','Sample z','Sample load']
1823                keys2 = ['Dset','Dcalc','pixLimit','cutoff','Emat','fixDset']
1824                File.write('{\n\t')
1825                for key in keys:
1826                    if key in 'Type':
1827                        File.write("'"+key+"':'"+data[key]+"',")
1828                    else:
1829                        File.write("'"+key+"':"+str(data[key])+',')
1830                File.write('\n\t'+"'d-zero':[\n")
1831                for data2 in data['d-zero']:
1832                    File.write('\t\t{')
1833                    for key in keys2:
1834                        File.write("'"+key+"':"+str(data2[key])+',')
1835                    File.write("'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]]},\n")
1836                File.write('\t]\n}')
1837                File.close()
1838        finally:
1839            dlg.Destroy()
1840           
1841    def OnStrStaSample(event):
1842        filename = ''
1843        pth = G2G.GetImportPath(G2frame)
1844        if not pth: pth = '.'
1845        dlg = wx.FileDialog(G2frame, 'Choose multihistogram metadata text file', pth, '', 
1846            'metadata file (*.*)|*.*',wx.OPEN)
1847        try:
1848            if dlg.ShowModal() == wx.ID_OK:
1849                filename = dlg.GetPath()
1850                File = open(filename,'r')
1851                S = File.readline()
1852                newItems = []
1853                itemNames = []
1854                Comments = []
1855                while S:
1856                    if S[0] == '#':
1857                        Comments.append(S)
1858                        S = File.readline()
1859                        continue
1860                    S = S.replace(',',' ').replace('\t',' ')
1861                    Stuff = S[:-1].split()
1862                    itemNames.append(Stuff[0])
1863                    newItems.append(Stuff[1:])
1864                    S = File.readline()               
1865                File.close()
1866        finally:
1867            dlg.Destroy()
1868        if not filename:
1869            G2frame.ErrorDialog('Nothing to do','No file selected')
1870            return
1871        dataDict = dict(zip(itemNames,newItems))
1872        ifany = False
1873        Names = [' ','Sample phi','Sample z','Sample load']
1874        dlg = G2G.G2ColumnIDDialog( G2frame,' Choose multihistogram metadata columns:',
1875            'Select columns',Comments,Names,np.array(newItems).T)
1876        try:
1877            if dlg.ShowModal() == wx.ID_OK:
1878                colNames,newData = dlg.GetSelection()
1879                dataDict = dict(zip(itemNames,newData.T))
1880                for item in colNames:
1881                    if item != ' ':
1882                        ifany = True
1883        finally:
1884            dlg.Destroy()
1885        if not ifany:
1886            G2frame.ErrorDialog('Nothing to do','No columns identified')
1887            return
1888        histList = []
1889        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)       
1890        while item:
1891            name = G2frame.PatternTree.GetItemText(item)
1892            if name.startswith('IMG'):
1893                histList.append(name)
1894            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1895        colIds = {}
1896        for i,name in enumerate(colNames):
1897            if name != ' ':
1898                colIds[name] = i
1899        for hist in histList:
1900            name = hist.split()[1]  #this is file name
1901            if name in dataDict:
1902                newItems = {}
1903                for item in colIds:
1904                    newItems[item] = float(dataDict[name][colIds[item]])
1905                Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,hist)
1906                stsrData = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Stress/Strain'))
1907                stsrData.update(newItems)       
1908        UpdateStressStrain(G2frame,data)       
1909   
1910    def OnPlotStrSta(event):
1911        Controls = G2frame.PatternTree.GetItemPyData(
1912            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1913        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1914        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1915        G2plt.PlotExposedImage(G2frame,event=event)
1916        G2frame.G2plotNB.Delete('Ring Intensities')
1917        G2plt.PlotXY(G2frame,RingInt,labelX='Azimuth',
1918            labelY='MRD',newPlot=True,Title='Ring Intensities',
1919            names=Names,lines=True)
1920       
1921    def OnSaveStrRing(event):
1922        Controls = G2frame.PatternTree.GetItemPyData(
1923            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1924        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1925        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1926        pth = G2G.GetExportPath(G2frame)
1927        dlg = wx.FileDialog(G2frame, 'Choose strain ring intensity file', pth, '', 
1928            'ring intensity file (*.txt)|*.txt',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1929        try:
1930            if dlg.ShowModal() == wx.ID_OK:
1931                filename = dlg.GetPath()
1932                File = open(filename,'w')
1933                for i,name in enumerate(Names):
1934                    File.write('%s%s\n'%(' Ring intensity for ',name))
1935                    File.write('%12s %12s\n'%('Azimuth','RMD'))
1936                    for item in RingInt[i].T:
1937                        File.write(' %12.3f %12.3f\n'%(item[0],item[1]))
1938                    File.write('\n')
1939                File.close()
1940        finally:
1941            dlg.Destroy()
1942               
1943               
1944    def OnFitStrSta(event):
1945        Controls = G2frame.PatternTree.GetItemPyData(
1946            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1947        G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1948        print 'Strain fitting finished'
1949        UpdateStressStrain(G2frame,data)
1950        G2plt.PlotExposedImage(G2frame,event=event)
1951        G2plt.PlotStrain(G2frame,data,newPlot=True)
1952       
1953    def OnFitAllStrSta(event):
1954        choices = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1955        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices)
1956        names = []
1957        if dlg.ShowModal() == wx.ID_OK:
1958            for sel in dlg.GetSelections():
1959                names.append(choices[sel])
1960            Id =  G2gd.GetPatternTreeItemId(G2frame,G2frame.root,'Sequential strain fit results')
1961            if Id:
1962                SeqResult = G2frame.PatternTree.GetItemPyData(Id)
1963            else:
1964                SeqResult = {}
1965                Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text='Sequential strain fit results')
1966            SeqResult.update({'SeqPseudoVars':{},'SeqParFitEqList':[]})
1967        else:
1968            dlg.Destroy()
1969            return
1970        dlg.Destroy()
1971        if not names:
1972            return
1973        Reverse = False
1974        CopyForward = False
1975        choice = ['Reverse sequence','Copy from prev.',]
1976        dlg = wx.MultiChoiceDialog(G2frame.dataFrame,'Sequential controls','Select controls',choice)
1977        if dlg.ShowModal() == wx.ID_OK:
1978            for sel in dlg.GetSelections():
1979                if sel:
1980                    CopyForward = True
1981                else:
1982                    Reverse = True
1983        dlg.Destroy()
1984        dlg = wx.ProgressDialog('Sequential IMG Strain fit','Data set name = '+names[0],len(names), 
1985            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT)         
1986        wx.BeginBusyCursor()
1987        goodnames = []
1988        if Reverse:
1989            names.reverse()
1990        try:
1991            for i,name in enumerate(names):
1992                print ' Sequential strain fit for ',name
1993                GoOn = dlg.Update(i,newmsg='Data set name = '+name)[0]
1994                if not GoOn:
1995                    break
1996                sId =  G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
1997                G2frame.Image = sId
1998                Controls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,sId, 'Image Controls'))
1999                StaCtrls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,sId, 'Stress/Strain'))
2000                if not len(StaCtrls['d-zero']):
2001                    continue
2002                goodnames.append(name)
2003                Npix,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(sId)
2004                image = GetImageZ(G2frame,Controls)
2005                sig = []
2006                varyList = []
2007                variables = []
2008                if i and CopyForward:
2009                    for j,ring in enumerate(StaCtrls['d-zero']):
2010                        ring['Emat'] = variables[4*j:4*j+3]
2011                #get results from previous & put in StaCtrls
2012                G2img.FitStrSta(image,StaCtrls,Controls)
2013                G2plt.PlotStrain(G2frame,StaCtrls,newPlot=True)
2014                parmDict = {'Sample load':StaCtrls['Sample load'],}
2015                varyNames = ['e11','e12','e22']
2016                for j,item in enumerate(StaCtrls['d-zero']):
2017                    variables += item['Emat']
2018                    sig += item['Esig']
2019                    varylist = ['%d;%s'%(j,Name) for Name in varyNames]
2020                    varyList += varylist
2021                    parmDict.update(dict(zip(varylist,item['Emat'])))
2022                    parmDict['%d;Dcalc'%(j)] = item['Dcalc']
2023                    parmDict['%d;Ivar'%(j)] = item['Ivar']
2024                    variables.append(item['Ivar'])
2025                    varyList.append('%d;Ivar'%(j))
2026                    sig.append(None)
2027                SeqResult[name] = {'variables':variables,'varyList':varyList,'sig':sig,'Rvals':[],
2028                    'covMatrix':np.eye(len(variables)),'title':name,'parmDict':parmDict}
2029            else:
2030                SeqResult['histNames'] = goodnames
2031                dlg.Destroy()
2032                print ' ***** Sequential strain refinement successful *****'
2033        finally:
2034            wx.EndBusyCursor()   
2035        SeqResult['histNames'] = choices
2036        G2frame.PatternTree.SetItemPyData(Id,SeqResult)
2037        G2frame.PatternTree.SelectItem(Id)
2038        print 'All images fitted'
2039       
2040    def SamSizer():
2041       
2042        def OnStrainType(event):
2043            data['Type'] = strType.GetValue()
2044       
2045        samSizer = wx.BoxSizer(wx.HORIZONTAL)
2046        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Strain type: '),0,WACV)
2047        strType = wx.ComboBox(G2frame.dataDisplay,value=data['Type'],choices=['True','Conventional'],
2048            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2049        strType.SetValue(data['Type'])
2050        strType.Bind(wx.EVT_COMBOBOX, OnStrainType)
2051        samSizer.Add(strType,0,WACV)
2052       
2053        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample phi: '),0,WACV)
2054        samPhi = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample phi',nDig=(10,3),typeHint=float,min=-360.,max=360.)
2055        samSizer.Add(samPhi,0,WACV)
2056        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample delta-z(mm): '),0,WACV)
2057        samZ = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample z',nDig=(10,3),typeHint=float)
2058        samSizer.Add(samZ,0,WACV)
2059        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample load(MPa): '),0,WACV)
2060        samLoad = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample load',
2061                nDig=[8,3],typeHint=float,)
2062        samSizer.Add(samLoad,0,WACV)
2063
2064        return samSizer
2065       
2066    def DzeroSizer():
2067   
2068        def OnDzero(invalid,value,tc):
2069            data['d-zero'] = G2mth.sortArray(data['d-zero'],'Dset',reverse=True)
2070            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[tc.GetId()]],G2frame.ImageZ,Controls)
2071            if len(Ring):
2072                data['d-zero'][Indx[tc.GetId()]].update(R)
2073            else:
2074                G2frame.ErrorDialog('Strain peak selection','WARNING - No points found for this ring selection')
2075               
2076            wx.CallAfter(UpdateStressStrain,G2frame,data)
2077            G2plt.PlotExposedImage(G2frame,event=tc.event,newPlot=False)
2078            G2plt.PlotStrain(G2frame,data,newPlot=True)
2079           
2080        def OnDeleteDzero(event):
2081            Obj = event.GetEventObject()
2082            del(data['d-zero'][delIndx.index(Obj)])
2083            UpdateStressStrain(G2frame,data)
2084            G2plt.PlotExposedImage(G2frame,event=event,newPlot=True)
2085            G2plt.PlotStrain(G2frame,data,newPlot=True)
2086       
2087        def OnCutOff(invalid,value,tc):
2088            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[tc.GetId()]],G2frame.ImageZ,Controls)
2089            G2plt.PlotExposedImage(G2frame,event=tc.event)
2090            G2plt.PlotStrain(G2frame,data,newPlot=True)
2091       
2092        def OnPixLimit(event):
2093            Obj = event.GetEventObject()
2094            data['d-zero'][Indx[Obj.GetId()]]['pixLimit'] = int(Obj.GetValue())
2095            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[Obj.GetId()]],G2frame.ImageZ,Controls)
2096            G2plt.PlotExposedImage(G2frame,event=event)
2097            G2plt.PlotStrain(G2frame,data,newPlot=True)
2098           
2099        def OnFixDset(event):
2100            Obj = event.GetEventObject()
2101            data['d-zero'][Indx[Obj.GetId()]]['fixDset'] = Obj.GetValue()
2102           
2103        Indx = {}
2104        delIndx = []   
2105        dzeroSizer = wx.FlexGridSizer(0,8,5,5)
2106        for id,dzero in enumerate(data['d-zero']):
2107            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' d-zero #%d: '%(id))),0,WACV)
2108            dZero = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['d-zero'][id],'Dset',
2109                min=0.25,max=20.,nDig=(10,5),typeHint=float,OnLeave=OnDzero)
2110            dzeroSizer.Add(dZero,0,WACV)
2111            Indx[dZero.GetId()] = id
2112            dfix = wx.CheckBox(G2frame.dataDisplay,label='Hold?')
2113            dfix.SetValue(dzero.get('fixDset',True))
2114            dfix.Bind(wx.EVT_CHECKBOX,OnFixDset)
2115            Indx[dfix.GetId()] = id
2116            dzeroSizer.Add(dfix,0,WACV)
2117            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' d-zero ave: %.5f'%(dzero['Dcalc']))),0,WACV)
2118               
2119            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min ring I/Ib '),0,WACV)
2120            cutOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['d-zero'][id],'cutoff',
2121                    min=0.5,max=20.,nDig=(10,1),typeHint=float,OnLeave=OnCutOff)
2122            Indx[cutOff.GetId()] = id
2123            dzeroSizer.Add(cutOff,0,WACV)
2124       
2125            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Pixel search range '),0,WACV)
2126            pixLimit = wx.ComboBox(parent=G2frame.dataDisplay,value=str(dzero['pixLimit']),choices=['1','2','5','10','15','20'],
2127                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2128            pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
2129            Indx[pixLimit.GetId()] = id
2130            dzeroSizer.Add(pixLimit,0,WACV)               
2131               
2132            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' Strain tensor:')),WACV)
2133            names = ['e11','e12','e22']
2134            for i in range(3):
2135                dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=names[i]),0,WACV)
2136                tensorElem = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(dzero['Emat'][i]),style=wx.TE_READONLY)
2137                tensorElem.SetBackgroundColour(VERY_LIGHT_GREY)
2138                dzeroSizer.Add(tensorElem,0,WACV)
2139            dzeroDelete = wx.CheckBox(parent=G2frame.dataDisplay,label='delete?')
2140            dzeroDelete.Bind(wx.EVT_CHECKBOX,OnDeleteDzero)
2141            delIndx.append(dzeroDelete)
2142            dzeroSizer.Add(dzeroDelete,0,WACV)
2143           
2144        return dzeroSizer
2145       
2146# patches
2147    if 'Sample load' not in data:
2148        data['Sample load'] = 0.0
2149# end patches
2150   
2151    if G2frame.dataDisplay:
2152        G2frame.dataDisplay.Destroy()
2153    Controls = G2frame.PatternTree.GetItemPyData(
2154        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))       
2155    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.StrStaMenu)
2156    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAppendDzero, id=G2gd.wxID_APPENDDZERO)
2157    G2frame.dataFrame.Bind(wx.EVT_MENU, OnUpdateDzero, id=G2gd.wxID_UPDATEDZERO)
2158    G2frame.dataFrame.Bind(wx.EVT_MENU, OnFitStrSta, id=G2gd.wxID_STRSTAFIT)
2159    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotStrSta, id=G2gd.wxID_STRSTAPLOT) 
2160    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveStrRing, id=G2gd.wxID_STRRINGSAVE) 
2161    G2frame.dataFrame.Bind(wx.EVT_MENU, OnFitAllStrSta, id=G2gd.wxID_STRSTAALLFIT)
2162    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyStrSta, id=G2gd.wxID_STRSTACOPY)
2163    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadStrSta, id=G2gd.wxID_STRSTALOAD)
2164    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveStrSta, id=G2gd.wxID_STRSTASAVE)
2165    G2frame.dataFrame.Bind(wx.EVT_MENU, OnStrStaSample, id=G2gd.wxID_STRSTSAMPLE)       
2166    if not G2frame.dataFrame.GetStatusBar():
2167        G2frame.dataFrame.CreateStatusBar()
2168    if G2frame.StrainKey == 'a':    #probably doesn't happen
2169        G2frame.dataFrame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value')
2170    else:
2171        G2frame.dataFrame.GetStatusBar().SetStatusText("To add strain data: On 2D Powder Image, key a:add ring")
2172       
2173    G2frame.dataDisplay = wxscroll.ScrolledPanel(G2frame.dataFrame)
2174    mainSizer = wx.BoxSizer(wx.VERTICAL)
2175    mainSizer.Add((5,10),0)
2176    mainSizer.Add(SamSizer())
2177    mainSizer.Add((5,10),0)
2178    mainSizer.Add(DzeroSizer())
2179   
2180    mainSizer.Layout()   
2181    G2frame.dataDisplay.SetSizer(mainSizer)
2182    G2frame.dataDisplay.SetAutoLayout(1)
2183    G2frame.dataDisplay.SetupScrolling()
2184    Size = mainSizer.Fit(G2frame.dataFrame)
2185    Size[0] += 25
2186    G2frame.dataFrame.setSizePosLeft(Size)   
2187
2188###########################################################################
2189# Autointegration
2190###########################################################################
2191def ReadMask(filename):
2192    'Read a mask (.immask) file'
2193    File = open(filename,'r')
2194    save = {}
2195    S = File.readline()
2196    while S:
2197        if S[0] == '#':
2198            S = File.readline()
2199            continue
2200        [key,val] = S.strip().split(':',1)
2201        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
2202            save[key] = eval(val)
2203        S = File.readline()
2204    File.close()
2205    CleanupMasks(save)
2206    return save
2207
2208def ReadControls(filename):
2209    'read an image controls (.imctrl) file'
2210    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2211            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2212            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2213            'PolaVal','SampleAbs','dark image','background image']
2214    File = open(filename,'r')
2215    save = {}
2216    S = File.readline()
2217    while S:
2218        if S[0] == '#':
2219            S = File.readline()
2220            continue
2221        [key,val] = S.strip().split(':',1)
2222        if key in ['type','calibrant','binType','SampleShape',]:    #strings
2223            save[key] = val
2224        elif key in ['rotation']:
2225            save[key] = float(val)
2226        elif key in ['center',]:
2227            if ',' in val:
2228                save[key] = eval(val)
2229            else:
2230                vals = val.strip('[] ').split()
2231                save[key] = [float(vals[0]),float(vals[1])] 
2232        elif key in cntlList:
2233            save[key] = eval(val)
2234        S = File.readline()
2235    File.close()
2236    return save
2237
2238def Read_imctrl(imctrl_file):
2239    '''Read an image control file and record control parms into a dict, with some simple
2240    type conversions
2241    '''
2242    save = {'filename':imctrl_file}
2243    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
2244    if os.path.exists(immask_file):
2245        save['maskfile'] = immask_file
2246    else:
2247        save['maskfile'] = '(none)'
2248    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2249                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2250                        'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2251                        'PolaVal','SampleAbs','dark image','background image','setdist']
2252    File = open(imctrl_file,'r')
2253    fullIntegrate = False
2254    try:
2255        S = File.readline()
2256        while S:
2257            if S[0] == '#':
2258                S = File.readline()
2259                continue
2260            [key,val] = S.strip().split(':',1)
2261            if val.find(':') != -1:
2262                #print 'rejecting ',key,val
2263                S = File.readline()
2264                continue
2265            if key in ['type','calibrant','binType','SampleShape',]:    #strings
2266                save[key] = val
2267            elif key == 'rotation':
2268                save[key] = float(val)
2269            elif key == 'fullIntegrate':
2270                fullIntegrate = eval(val)
2271            elif key == 'LRazimuth':
2272                save['LRazimuth_min'],save['LRazimuth_max'] = eval(val)[0:2]
2273            elif key == 'IOtth':
2274                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
2275            elif key == 'center':
2276                if ',' in val:
2277                    vals = eval(val)
2278                else:
2279                    vals = val.strip('[] ').split()
2280                    vals = [float(vals[0]),float(vals[1])] 
2281                save['center_x'],save['center_y'] = vals[0:2]
2282            elif key in cntlList:
2283                save[key] = eval(val)
2284            S = File.readline()
2285    finally:
2286        File.close()
2287        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0.,0.
2288    return save
2289   
2290class AutoIntFrame(wx.Frame):
2291    '''Creates a wx.Frame window for the Image AutoIntegration.
2292    The intent is that this will be used as a non-modal dialog window.
2293   
2294    Implements a Start button that morphs into a pause and resume button.
2295    This button starts a processing loop that is repeated every
2296    :meth:`PollTime` seconds.
2297
2298    :param wx.Frame G2frame: main GSAS-II frame
2299    :param float PollTime: frequency in seconds to repeat calling the
2300      processing loop. (Default is 30.0 seconds.)
2301    '''
2302    def __init__(self,G2frame,PollTime=30.0):
2303        def OnStart(event):
2304            '''Called when the start button is pressed. Changes button label
2305            to Pause. When Pause is pressed the label changes to Resume.
2306            When either Start or Resume is pressed, the processing loop
2307            is started. When Pause is pressed, the loop is stopped.
2308            '''
2309            # check inputs for errors before starting
2310            #err = ''
2311            #if not any([self.params[fmt] for fmt in self.fmtlist]):
2312            #    err += '\nPlease select at least one output format\n'
2313            #if err:
2314            #    G2G.G2MessageBox(self,err)
2315            #    return
2316            self.Pause = False
2317            # change button label
2318            if self.btnstart.GetLabel() != 'Pause':
2319                self.btnstart.SetLabel('Pause')
2320                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
2321                if self.timer.IsRunning(): self.timer.Stop()
2322                self.PreventReEntryTimer = False
2323                if self.StartLoop():
2324                    G2G.G2MessageBox(self,'Error in setting up integration. See console')
2325                    return
2326                self.OnTimerLoop(None) # run once immediately
2327                if not self.Pause:
2328                    # no pause, so start timer to check for new files
2329                    self.timer.Start(int(1000*PollTime),oneShot=False)
2330                    return
2331            # we will get to this point if Paused
2332            self.OnPause()
2333           
2334        def OnReset(event):
2335            '''Called when Reset button is pressed. This stops the
2336            processing loop and resets the list of integrated files so
2337            all images can be reintegrated.
2338            '''
2339            self.btnstart.SetLabel('Restart')
2340            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
2341            if self.timer.IsRunning(): self.timer.Stop()
2342            self.Reset = True
2343            self.Pause = True
2344           
2345        def OnQuit(event):
2346            '''Stop the processing loop and close the Frame
2347            '''
2348            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
2349            wx.CallAfter(self.Destroy)
2350           
2351        def OnBrowse(event):
2352            '''Responds when the Browse button is pressed to load a file.
2353            The routine determines which button was pressed and gets the
2354            appropriate file type and loads it into the appropriate place
2355            in the dict.
2356            '''
2357            if btn3 == event.GetEventObject():
2358                dlg = wx.DirDialog(
2359                    self, 'Select directory for output files',
2360                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
2361                dlg.CenterOnParent()
2362                try:
2363                    if dlg.ShowModal() == wx.ID_OK:
2364                        self.params['outdir'] = dlg.GetPath()
2365                        fInp3.SetValue(self.params['outdir'])
2366                finally:
2367                    dlg.Destroy()
2368                return
2369            elif btn4 == event.GetEventObject():
2370                msg = ''
2371                pth = G2G.GetExportPath(G2frame)
2372                dlg = wx.FileDialog(
2373                    self, 'Select a PDF parameter file',
2374                    pth, self.params['pdfprm'], 
2375                    "PDF controls file (*.pdfprm)|*.pdfprm",
2376                    wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
2377                dlg.CenterOnParent()
2378                try:
2379                    if dlg.ShowModal() == wx.ID_OK:
2380                        self.params['pdfprm'] = dlg.GetPath()
2381                        fInp4.SetValue(self.params['pdfprm'])
2382                        scanPDFprm()
2383                        msg = self.checkPDFprm(True)
2384                finally:
2385                    dlg.Destroy()
2386                if 'Error' in msg:
2387                    print(msg)
2388                    lbl = 'PDFPRM error'
2389                else:
2390                    msg = 'Information from file {}\n\n{}'.format(self.params['pdfprm'],msg)
2391                    lbl = 'PDFPRM information'
2392                G2G.G2MessageBox(self,msg,lbl)
2393                return
2394               
2395        def OnRadioSelect(event):
2396            '''Respond to a radiobutton selection and when in table
2397            mode, get distance-dependent parameters from user.
2398            '''
2399            self.Evaluator = None
2400            if self.useTable.GetValue():
2401                dlg = None
2402                try:
2403                    dlg = IntegParmTable(self) # create the dialog
2404                    dlg.CenterOnParent()
2405                    if dlg.ShowModal() == wx.ID_OK:
2406                        self.ImgTblParms = dlg.parms
2407                        self.IMfileList = dlg.IMfileList
2408                        self.Evaluator = DefineEvaluator(dlg)
2409                        self.params['Mode'] = 'table'
2410                        self.editTable.Enable(True)
2411                    else:
2412                        self.useActive.SetValue(True)
2413                finally:
2414                    if dlg: dlg.Destroy()
2415            elif self.useActive.GetValue():
2416                self.params['Mode'] = 'active'
2417                self.imageBase = G2frame.Image
2418                self.useActive.SetLabel("Active Image: "+
2419                        G2frame.PatternTree.GetItemText(self.imageBase))
2420                self.editTable.Enable(False)
2421            else:
2422                print('unexpected mode in OnRadioSelect')
2423
2424        def OnEditTable(event):
2425            '''Called to edit the distance-dependent parameter look-up table.
2426            Should be called only when table is defined and active.
2427            '''
2428            dlg = None
2429            try:
2430                dlg = IntegParmTable(self,self.ImgTblParms,self.IMfileList)
2431                dlg.CenterOnParent()
2432                if dlg.ShowModal() == wx.ID_OK:
2433                    self.ImgTblParms = dlg.parms
2434                    self.IMfileList = dlg.IMfileList
2435                    self.Evaluator = DefineEvaluator(dlg)
2436                    self.params['Mode'] = 'table'
2437                    self.editTable.Enable(True)
2438                else:
2439                    self.useActive.SetValue(True)
2440                    self.params['Mode'] = 'active'
2441                    self.imageBase = G2frame.Image
2442                    self.useActive.SetLabel("Active Image: "+
2443                            G2frame.PatternTree.GetItemText(self.imageBase))
2444                    self.editTable.Enable(False)
2445            finally:
2446                if dlg: dlg.Destroy()
2447               
2448        def showPDFctrls(event):
2449            '''Called to show or hide AutoPDF widgets. Note that fInp4 must be included in the
2450            sizer layout with .Show(True) before .Show(False) will work properly.
2451            '''
2452            fInp4.Enable(self.params['ComputePDF'])
2453            fInp4.Show(self.params['ComputePDF'])
2454            fInp4a.Enable(self.params['ComputePDF'])
2455            btn4.Enable(self.params['ComputePDF'])
2456            cOpt.Enable(self.params['ComputePDF'])
2457            if self.params['ComputePDF']:
2458                lbl4.SetForegroundColour("black")
2459                lbl4a.SetForegroundColour("black")
2460            else:
2461                lbl4.SetForegroundColour("gray")
2462                lbl4a.SetForegroundColour("gray")
2463                                   
2464        def scanPDFprm(**kw):
2465            fInp4.invalid = not os.path.exists(fInp4.GetValue())
2466            fInp4._IndicateValidity()
2467           
2468        def OnAutoScale(event):
2469            self.AutoScale = autoscale.GetValue()
2470           
2471        def OnAutoScaleName(event):
2472            self.AutoScaleName = scalename.GetValue()
2473            self.Scale[0] = self.AutoScales[self.AutoScaleName]
2474            scaleval.SetValue(self.Scale[0])
2475               
2476        ##################################################
2477        # beginning of __init__ processing
2478        ##################################################
2479        self.G2frame = G2frame
2480        self.ImgTblParms = None
2481        self.IMfileList = None
2482        self.Evaluator = None
2483        self.params = {}
2484        self.Reset = False
2485        self.Pause = False
2486        self.PreventReEntryShowMatch = False
2487        self.PreventReEntryTimer = False
2488        self.params['IMGfile'] = ''
2489        self.params['MaskFile'] = ''
2490        self.params['IgnoreMask'] = True
2491        self.fmtlist = G2IO.ExportPowderList(G2frame)
2492        self.timer = wx.Timer()
2493        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
2494        self.imageBase = G2frame.Image
2495        self.params['ComputePDF'] = False
2496        self.params['pdfDmax'] = 0.0
2497        self.params['pdfprm'] = ''
2498        self.params['optPDF'] = True
2499        self.pdfControls = {}
2500        self.AutoScale = False
2501        self.Scale = [1.0,]
2502
2503        G2frame.PatternTree.GetSelection()
2504        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(self.imageBase) 
2505        self.imagedir,fileroot = os.path.split(imagefile)
2506        Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2507            G2frame,self.imageBase, 'Comments'))
2508        DefaultAutoScaleNames = GSASIIpath.GetConfigValue('Autoscale_ParmNames')
2509        self.AutoScaleName = GSASIIpath.GetConfigValue('DefaultAutoScale')
2510        self.AutoScales = {}
2511        if DefaultAutoScaleNames is not None:
2512            for comment in Comments:
2513                if '=' in comment:
2514                    name,val = comment.split('=',1) 
2515                    if name in DefaultAutoScaleNames:
2516                        try:
2517                            self.AutoScales[name] = float(val)
2518                            if name == self.AutoScaleName:
2519                                self.Scale[0] = float(val)
2520                        except ValueError:
2521                            continue
2522        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2523        self.params['outdir'] = os.path.abspath(self.imagedir)
2524        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2525                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2526        self.Status = self.CreateStatusBar()
2527        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2528        mnpnl = wx.Panel(self)
2529        mnsizer = wx.BoxSizer(wx.VERTICAL)
2530        # box for integration controls & masks input
2531        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2532        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2533        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2534        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2535                            style = wx.RB_GROUP)
2536        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2537        self.useActive.SetLabel("Active Image: "+
2538                                G2frame.PatternTree.GetItemText(self.imageBase))
2539        lblsizr.Add(self.useActive,1,wx.EXPAND,1)
2540        self.useActive.SetValue(True)
2541        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2542        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2543        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2544        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2545        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2546        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2547        self.editTable.Enable(False)
2548        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2549        # bind button and deactivate be default
2550        lblsizr.Add(minisizer)
2551        mnsizer.Add(lblsizr,1,wx.EXPAND,1)
2552
2553        # file filter stuff
2554        sizer = wx.BoxSizer(wx.HORIZONTAL)
2555        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Image filter'))
2556        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',OnLeave=self.ShowMatchingFiles)
2557        sizer.Add(flterInp)
2558        mnsizer.Add(sizer,0,wx.ALIGN_RIGHT,1)
2559        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2560        mnsizer.Add(self.ListBox,0,wx.EXPAND,1)
2561        self.ShowMatchingFiles(self.params['filter'])
2562        # box for output selections
2563        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2564        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2565        sizer = wx.BoxSizer(wx.HORIZONTAL)
2566        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2567        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2568        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2569        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2570        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2571        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2572        lblsizr.Add(sizer,0,wx.EXPAND)
2573        sizer = wx.BoxSizer(wx.HORIZONTAL)
2574        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
2575        for dfmt in self.fmtlist:
2576            fmt = dfmt[1:]
2577            self.params[fmt] = False
2578            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2579            sizer.Add(btn)
2580        lblsizr.Add(sizer)
2581        sizer = wx.BoxSizer(wx.HORIZONTAL)
2582        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2583        self.params['SeparateDir'] = False
2584        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2585        lblsizr.Add(sizer)
2586        if self.AutoScales:
2587            sizer = wx.BoxSizer(wx.HORIZONTAL)
2588            autoscale = wx.CheckBox(mnpnl,label='Do autoscaling with:')
2589            autoscale.Bind(wx.EVT_CHECKBOX,OnAutoScale)
2590            sizer.Add(autoscale,0,WACV)
2591            scalename = wx.ComboBox(mnpnl,value=self.AutoScaleName,choices=self.AutoScales.keys(),
2592                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2593            scalename.Bind(wx.EVT_COMBOBOX,OnAutoScaleName)
2594            sizer.Add(scalename,0,WACV)
2595            sizer.Add(wx.StaticText(mnpnl,label=' to '),0,WACV)
2596            scaleval = G2G.ValidatedTxtCtrl(mnpnl,self.Scale,0,nDig=(10,2),min=1.)
2597            sizer.Add(scaleval,0,WACV)
2598            lblsizr.Add(sizer,0)
2599        #ToDO: Autonormalize, parm name?, scaling value?
2600        sizer = wx.BoxSizer(wx.HORIZONTAL)
2601        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
2602        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2603        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2604        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2605        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2606        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2607        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
2608        sizer.Add(cOpt)
2609        lblsizr.Add(sizer,0)
2610        sizer = wx.BoxSizer(wx.HORIZONTAL)
2611        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2612        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2613        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2614                                     OnLeave=scanPDFprm)
2615        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2616        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2617        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2618        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2619        lblsizr.Add(sizer,0,wx.EXPAND)
2620        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2621        # buttons on bottom
2622        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2623        sizer = wx.BoxSizer(wx.HORIZONTAL)
2624        sizer.Add((20,-1))
2625        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2626        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2627        sizer.Add(self.btnstart)
2628        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2629        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2630        sizer.Add(self.btnreset)
2631        sizer.Add((20,-1),wx.EXPAND,1)
2632        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
2633        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
2634        sizer.Add(self.btnclose)
2635        sizer.Add((20,-1))
2636        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
2637        # finish up window
2638        mnpnl.SetSizer(mnsizer)
2639        OnRadioSelect(None) # disable widgets
2640        mnsizer.Fit(self)
2641        self.CenterOnParent()
2642        self.Show()
2643        showPDFctrls(None)
2644
2645    def checkPDFprm(self,ShowContents=False):
2646        '''Read in the PDF (.pdfprm) parameter file and check for problems.
2647        If ShowContents is True, a formatted text version of some of the file
2648        contents is returned. If errors are found, the return string will contain
2649        the string "Error:" at least once.
2650        '''
2651        self.pdfControls = {}
2652        msg = ''
2653        File = None
2654        try:
2655            File = open(self.params['pdfprm'],'r')
2656            S = File.readline()
2657            while S:
2658                if '#' in S:
2659                    S = File.readline()
2660                    continue
2661                key,val = S.split(':',1)
2662                try:
2663                    self.pdfControls[key] = eval(val)
2664                except:
2665                    self.pdfControls[key] = val
2666                S = File.readline()
2667        except Exception as err:
2668            msg += 'PDF Processing Error: error with open or read of {}'.format(self.params['pdfprm'])
2669            if GSASIIpath.GetConfigValue('debug'):
2670                print(msg)
2671                print(err)
2672            self.pdfControls = {}
2673            return msg
2674        finally:
2675            if File: File.close()
2676        formula = ''
2677        for el in self.pdfControls['ElList']:
2678            if self.pdfControls['ElList'][el]['FormulaNo'] <= 0: continue
2679            if formula: formula += ' '
2680            formula += '{}({:.1f})'.format(el,self.pdfControls['ElList'][el]['FormulaNo'])
2681        if not formula:
2682            msg += 'Error: no chemical formula in file'
2683        for key in ['Sample Bkg.','Container','Container Bkg.']:
2684            if key not in self.pdfControls:
2685                if msg: msg += '\n'
2686                msg += 'Error: missing key in self.pdfControls: '+key
2687                continue
2688        if msg or not ShowContents: return msg  # stop on error
2689        msg += 'Default formula: '+formula+'\n'
2690        for key in ['Sample Bkg.','Container','Container Bkg.']:
2691            name = self.pdfControls[key]['Name']
2692            mult = self.pdfControls[key].get('Mult',0.0)
2693            if not name: continue
2694            msg += '\n{}: {:.2f} * "{}"'.format(key,mult,name)
2695            if not G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,name):
2696                msg += ' *** missing ***'
2697        return msg
2698   
2699    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
2700        G2frame = self.G2frame
2701        if invalid: return
2702        msg = ''
2703        if self.PreventReEntryShowMatch: return
2704        self.PreventReEntryShowMatch = True
2705        imageFileList = []
2706        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
2707            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2708            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2709            if imagefile not in imageFileList: imageFileList.append(imagefile)
2710            if img not in G2frame.IntegratedList:
2711                if msg: msg += '\n'
2712                msg += '  ' + img
2713        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
2714        msg1 = ""
2715        try:
2716            imageList = sorted(
2717                glob.glob(os.path.join(self.imagedir,value)))
2718            if not imageList:
2719                msg1 = 'Warning: No files match search string '+os.path.join(self.imagedir,value)
2720            else:
2721                for fil in imageList:
2722                    if fil not in imageFileList: msg1 += '\n  '+fil
2723                if msg1:
2724                    msg += 'Files to integrate from '+os.path.join(self.imagedir,value)+msg1
2725                else:
2726                    msg += 'No files found to read'
2727        except IndexError:
2728            msg += 'Error searching for files named '+os.path.join(self.imagedir,value)
2729        self.ListBox.Clear()
2730        self.ListBox.AppendItems(msg.split('\n'))
2731        self.PreventReEntryShowMatch = False
2732        return
2733       
2734    def OnPause(self):
2735        '''Respond to Pause, changes text on button/Status line, if needed
2736        Stops timer
2737        self.Pause should already be True
2738        '''
2739        if self.timer.IsRunning(): self.timer.Stop()
2740        if self.btnstart.GetLabel() == 'Restart':
2741            return
2742        if self.btnstart.GetLabel() != 'Resume':
2743            print('\nPausing autointegration\n')
2744            self.btnstart.SetLabel('Resume')
2745            self.Status.SetStatusText(
2746                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
2747        self.Pause = True
2748           
2749    def IntegrateImage(self,img):
2750        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
2751        are placed in G2frame.IntgOutList
2752        '''
2753        G2frame = self.G2frame
2754        imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2755        G2frame.Image = imgId
2756        G2frame.PickId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls')
2757        # do integration
2758        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2759        if self.AutoScale:
2760            Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2761                G2frame,imgId, 'Comments'))
2762            for comment in Comments:
2763                if '=' in comment:
2764                    name,val = comment.split('=',1) 
2765                    if name == self.AutoScaleName:
2766                        val = float(val)
2767                        if val > 0.:
2768                            Scale = self.Scale[0]/val
2769                        break
2770        masks = G2frame.PatternTree.GetItemPyData(
2771            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
2772        data = G2frame.PatternTree.GetItemPyData(G2frame.PickId)
2773        # simulate a Image Controls press, since that is where the
2774        # integration is hidden
2775        UpdateImageControls(G2frame,data,masks,IntegrateOnly=True)
2776        G2frame.IntegratedList.append(img) # note this as integrated
2777        # split name and control number
2778        s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
2779        namepre = s[0]
2780        if len(s) > 1:
2781            namenum = s[1]
2782        else:
2783            namenum = ''
2784        for Id in G2frame.IntgOutList: # loop over newly created PDWR entry(ies)
2785            # save the created PWDR tree names so that a reset can delete them
2786            G2frame.Image = Id
2787            treename = G2frame.PatternTree.GetItemText(Id)
2788            G2frame.AutointPWDRnames.append(treename)
2789            # write out the images in the selected formats
2790            Sdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Sample Parameters'))
2791            if self.AutoScale:
2792                print 'Rescale by %.4f'%(Scale)
2793                y,w = G2frame.PatternTree.GetItemPyData(Id)[1][1:3]
2794                y *= Scale
2795                w /= Scale**2
2796            # determine the name for the current file
2797            fileroot = namepre
2798            if len(G2frame.IntgOutList) > 1:
2799                fileroot += "_AZM"
2800                if 'Azimuth' in Sdata:
2801                    fileroot += str(int(10*Sdata['Azimuth']))
2802                fileroot += "_" 
2803            fileroot += namenum
2804            # loop over selected formats
2805            for dfmt in self.fmtlist:
2806                if not self.params[dfmt[1:]]: continue
2807                if self.params['SeparateDir']:
2808                    subdir = dfmt[1:]
2809                else:
2810                    subdir = ''
2811                fil = os.path.join(self.params['outdir'],subdir,fileroot)
2812                print('writing file '+fil+dfmt)
2813                G2IO.ExportPowder(G2frame,treename,fil,dfmt)
2814               
2815    def EnableButtons(self,flag):
2816        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
2817        '''
2818        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
2819        # on windows
2820        if sys.platform != "win32":
2821            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
2822        self.btnstart.SetLabel('Pause')
2823        wx.Yield()
2824               
2825    def ResetFromTable(self,dist):
2826        '''Sets integration parameters based on values from
2827        the lookup table
2828        '''
2829        #dist = self.controlsDict['distance']
2830        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
2831        if GSASIIpath.GetConfigValue('debug'):
2832            print 'interpolated values: ',interpDict
2833        self.ImageControls = ReadControls(imgctrl)
2834        self.ImageControls.update(interpDict)
2835        self.ImageControls['showLines'] = True
2836        self.ImageControls['ring'] = []
2837        self.ImageControls['rings'] = []
2838        self.ImageControls['ellipses'] = []
2839        self.ImageControls['setDefault'] = False
2840        for i in 'range','size','GonioAngles':
2841            if i in self.ImageControls:
2842                del self.ImageControls[i]
2843        # load copy of Image Masks
2844        if immask:
2845            self.ImageMasks = ReadMask(immask)
2846            if list(self.ImageMasks['Thresholds'][0]) == self.ImageMasks['Thresholds'][1]:     #avoid copy of unchanged thresholds
2847                del self.ImageMasks['Thresholds']
2848        else:
2849            self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
2850       
2851    def StartLoop(self):
2852        '''Prepare to start autointegration timer loop.
2853        Save current Image params for use in future integrations
2854        also label the window so users understand what is being used
2855        '''
2856        print('\nStarting new autointegration\n')
2857        G2frame = self.G2frame
2858        # show current IMG base
2859        if self.params['Mode'] != 'table':
2860            self.useActive.SetLabel("Active Image: "+
2861                                    G2frame.PatternTree.GetItemText(self.imageBase))
2862            # load copy of Image Controls from current image and clean up
2863            # items that should not be copied
2864            self.ImageControls = copy.deepcopy(
2865                G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2866                    G2frame,self.imageBase, 'Image Controls')))
2867            self.ImageControls['showLines'] = True
2868            self.ImageControls['ring'] = []
2869            self.ImageControls['rings'] = []
2870            self.ImageControls['ellipses'] = []
2871            self.ImageControls['setDefault'] = False
2872            del self.ImageControls['range']
2873            del self.ImageControls['size']
2874            del self.ImageControls['GonioAngles']
2875            # load copy of Image Masks, keep thresholds
2876            self.ImageMasks = copy.deepcopy(
2877                G2frame.PatternTree.GetItemPyData(
2878                    G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Masks')))
2879            self.Thresholds = self.ImageMasks['Thresholds'][:]
2880            if list(self.Thresholds[0]) == self.Thresholds[1]:     #avoid copy of unchanged thresholds
2881                del self.ImageMasks['Thresholds']   
2882        # make sure all output directories exist
2883        if self.params['SeparateDir']:
2884            for dfmt in self.fmtlist:
2885                if not self.params[dfmt[1:]]: continue
2886                dir = os.path.join(self.params['outdir'],dfmt[1:])
2887                if not os.path.exists(dir): os.makedirs(dir)
2888        else:
2889            if not os.path.exists(self.params['outdir']):
2890                os.makedirs(self.params['outdir'])
2891        if self.Reset: # special things to do after Reset has been pressed
2892            self.G2frame.IntegratedList = []
2893           
2894            if self.params['Mode'] != 'table': # reset controls and masks for all IMG items in tree to master
2895                for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
2896                    # update controls from master
2897                    controlsDict = G2frame.PatternTree.GetItemPyData(
2898                        G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Image Controls'))
2899                    controlsDict.update(self.ImageControls)
2900                    # update masks from master
2901                    ImageMasks = G2frame.PatternTree.GetItemPyData(
2902                        G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Masks'))
2903                    ImageMasks.update(self.ImageMasks)
2904            # delete all PWDR items created after last Start was pressed
2905            idlist = []
2906            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
2907            while item:
2908                itemName = G2frame.PatternTree.GetItemText(item)
2909                if itemName in G2frame.AutointPWDRnames:
2910                    idlist.append(item)
2911                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
2912            for item in idlist:
2913                G2frame.PatternTree.Delete(item)
2914            wx.Yield()
2915            self.Reset = False
2916        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
2917        G2frame.AutointPDFnames = [] # list of created PWDR tree item names
2918        # check that AutoPDF input is OK, offer chance to use alternate PDWRs if referenced ones
2919        # are not present
2920        if self.params['ComputePDF']:
2921            msg = self.checkPDFprm()
2922            if 'Error:' in msg:
2923                print(msg)
2924                return True
2925            fileList = []
2926            id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
2927            while id:
2928                name = G2frame.PatternTree.GetItemText(id)
2929                if name.startswith('PWDR '): fileList.append(name)
2930                id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
2931            if not fileList:
2932                print(msg)
2933                print('No PWDR entries to select')
2934                return True
2935            for key in ['Sample Bkg.','Container','Container Bkg.']:
2936                name = self.pdfControls[key]['Name']
2937                if not name: continue
2938                if not G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name):
2939                    indx = G2G.ItemSelector(fileList, self, header='Select PDWR item',
2940                                    title='Select a PDWR tree item for '+key+'\n(or cancel to quit)')
2941                    if indx is None:
2942                        print('No PWDR entry selected for '+key)
2943                        return True
2944                    self.pdfControls[key]['Name'] = fileList[indx]
2945        return False
2946               
2947    def OnTimerLoop(self,event):
2948        '''A method that is called every :meth:`PollTime` seconds that is
2949        used to check for new files and process them. Integrates new images.
2950        Also optionally sets up and computes PDF.
2951        This is called only after the "Start" button is pressed (then its label reads "Pause").
2952        '''
2953        def AutoIntegrateImage(imgId):
2954            '''Integrates an image that has been read into the data tree and updates the
2955            AutoInt window.
2956            '''
2957            img = G2frame.PatternTree.GetItemText(imgId)
2958            controlsDict = G2frame.PatternTree.GetItemPyData(
2959                G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
2960            ImageMasks = G2frame.PatternTree.GetItemPyData(
2961                G2gd.GetPatternTreeItemId(G2frame,imgId, 'Masks'))
2962            if self.params['Mode'] == 'table': # look up parameter values from table
2963                self.ResetFromTable(controlsDict['setdist'])
2964            # update controls from master
2965            controlsDict.update(self.ImageControls)
2966            # update masks from master w/o Thresholds
2967            ImageMasks.update(self.ImageMasks)
2968            self.EnableButtons(False)
2969            try:
2970                self.IntegrateImage(img)
2971            finally:
2972                self.EnableButtons(True)
2973            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
2974            wx.Yield()
2975            self.ShowMatchingFiles(self.params['filter'])
2976            wx.Yield()
2977           
2978        def AutoComputePDF(imgId):
2979            '''Computes a PDF for a PWDR data tree tree item
2980            '''
2981            for pwdr in G2frame.AutointPWDRnames[:]:
2982                if not pwdr.startswith('PWDR '): continue
2983                if pwdr in G2frame.AutointPDFnames: continue
2984                PWid = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,pwdr)
2985                controlsDict = G2frame.PatternTree.GetItemPyData(
2986                    G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
2987                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
2988                    print('Skipping PDF for '+pwdr+' due to detector position')
2989                    continue
2990                # Setup PDF
2991                Data = G2frame.PatternTree.GetItemPyData(PWid)[1]
2992                pwdrMin = np.min(Data[1])
2993                Parms = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2994                    G2frame,PWid,'Instrument Parameters'))[0]
2995                fullLimits = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2996                    G2frame,PWid,'Limits'))[0]
2997                if 'C' in Parms['Type'][0]:
2998                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
2999                else:
3000                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
3001                Qlimits = [0.9*qMax,qMax]
3002
3003                item = pwdr
3004                Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
3005                    G2frame,imgId, 'Comments'))
3006                ElList = {}
3007                sumnum = 1.0
3008                for item in Comments:           #grab chemical formula from Comments, if there
3009                    if 'formula' in item[:15].lower():
3010                        formula = item.split('=')[1].split()
3011                        elems = formula[::2]
3012                        nums = formula[1::2]
3013                        formula = zip(elems,nums)
3014                        sumnum = 0.
3015                        for [elem,num] in formula:
3016                            ElData = G2elem.GetElInfo(elem,Parms)
3017                            ElData['FormulaNo'] = float(num)
3018                            sumnum += float(num)
3019                            ElList[elem] = ElData
3020                PDFnames = G2gd.GetPatternTreeDataNames(G2frame,['PDF ',])
3021                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits,sumnum,pwdrMin,PDFnames)
3022                if not PDFid: continue
3023                PDFdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
3024                    G2frame,PDFid, 'PDF Controls'))
3025                PDFdata.update(self.pdfControls)
3026                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
3027                PDFdata['Sample']['Name'] = pwdr
3028                # compute PDF
3029                wx.Yield()
3030                G2pdG.computePDF(G2frame,PDFdata)
3031                wx.Yield()
3032                G2frame.PatternId = PDFid
3033                G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3034                if self.params['optPDF']:
3035                    G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
3036                    wx.Yield()
3037                    G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3038                G2frame.AutointPDFnames.append(pwdr)
3039                # save names of PDF entry to be deleted later if needed
3040                G2frame.AutointPWDRnames.append(G2frame.PatternTree.GetItemText(PDFid))
3041           
3042        G2frame = self.G2frame
3043        try:
3044            self.currImageList = sorted(
3045                glob.glob(os.path.join(self.imagedir,self.params['filter'])))
3046            self.ShowMatchingFiles(self.params['filter'])
3047        except IndexError:
3048            self.currImageList = []
3049            return
3050
3051        if self.PreventReEntryTimer: return
3052        self.PreventReEntryTimer = True
3053        imageFileList = []
3054        # integrate the images that have already been read in, but
3055        # have not yet been processed           
3056        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
3057            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
3058            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
3059            # Create a list of image files that have been read in
3060            if imagefile not in imageFileList: imageFileList.append(imagefile)
3061            # skip if already integrated
3062            if img in G2frame.IntegratedList: continue
3063            AutoIntegrateImage(imgId)
3064            #self.Pause |= G2frame.PauseIntegration
3065            #if self.Pause:
3066            #    self.OnPause()
3067            #    self.PreventReEntryTimer = False
3068            #    return
3069            if self.pdfControls: AutoComputePDF(imgId)
3070            self.Pause |= G2frame.PauseIntegration
3071            if self.Pause:
3072                self.OnPause()
3073                self.PreventReEntryTimer = False
3074                return
3075
3076        # loop over image files matching glob, reading in any new ones
3077        for newImage in self.currImageList:
3078            if newImage in imageFileList or self.Pause: continue # already read?
3079            for imgId in G2IO.ReadImages(G2frame,newImage):
3080                AutoIntegrateImage(imgId)           
3081                #self.Pause |= G2frame.PauseIntegration
3082                #if self.Pause:
3083                #    self.OnPause()
3084                #    self.PreventReEntryTimer = False
3085                #    return
3086                if self.pdfControls: AutoComputePDF(imgId)
3087                self.Pause |= G2frame.PauseIntegration
3088                if self.Pause:
3089                    self.OnPause()
3090                    self.PreventReEntryTimer = False
3091                    return
3092        if GSASIIpath.GetConfigValue('debug'):
3093            import datetime
3094            print ("Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
3095        self.PreventReEntryTimer = False
3096
3097def DefineEvaluator(dlg):
3098    '''Creates a function that provides interpolated values for a given distance value
3099    '''
3100    def Evaluator(dist):
3101        '''Interpolate image parameters for a supplied distance value
3102
3103        :param float dist: distance to use for interpolation
3104        :returns: a list with 3 items:
3105
3106          * a dict with parameter values,
3107          * the closest imctrl and
3108          * the closest maskfile (or None)
3109        '''           
3110        x = np.array([float(i) for i in parms[0]])
3111        closest = abs(x-dist).argmin()
3112        D = {'setdist':dist}
3113        imctfile = IMfileList[closest]
3114        if parms[-1][closest].lower() != '(none)':
3115            maskfile = parms[-1][closest]
3116        else:
3117            maskfile = None
3118        for c in range(1,cols-1):
3119            lbl = ParmList[c]
3120            if lbl in nonInterpVars:
3121                if lbl in ['outChannels',]:
3122                    D[lbl] = int(float(parms[c][closest]))
3123                else:
3124                    D[lbl] = float(parms[c][closest])
3125            else:
3126                y = np.array([float(i) for i in parms[c]])
3127                D[lbl] = np.interp(dist,x,y)
3128        # full integration when angular range is 0
3129        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
3130        # conversion for paired values
3131        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
3132            r = a.split('_')[0]
3133            D[r] = [D[a],D[b]]
3134            if r in ['LRazimuth',]:
3135                D[r] = [int(D[a]),int(D[b])]
3136            del D[a]
3137            del D[b]
3138        return D,imctfile,maskfile
3139    # save local copies of values needed in Evaluator
3140    parms = dlg.ReadImageParmTable()
3141    IMfileList = dlg.IMfileList
3142    cols = dlg.list.GetColumnCount()
3143    ParmList = dlg.ParmList
3144    nonInterpVars = dlg.nonInterpVars
3145    return Evaluator
3146
3147class IntegParmTable(wx.Dialog):
3148    '''Creates a dialog window with a table of integration parameters.
3149    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
3150    In this case, :func:`DefineEvaluator` should be called to obtain a function that
3151    creates a dictionary with interpolated parameter values.
3152    '''
3153    ParmList = ('setdist','distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
3154            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
3155            'maskfile',
3156            )
3157    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
3158                     'outChannels')  # values in this list are taken from nearest rather than interpolated
3159    HeaderList = ('Set Dist','Calib Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
3160            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
3161            'Mask File',
3162            )
3163    def __init__(self,parent,parms=None,IMfileList=None):
3164        self.G2frame = parent.G2frame
3165        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
3166        if parms:
3167            self.parms = parms # list of values by column
3168            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
3169        else:
3170            self.parms = [] # list of values by column
3171            self.IMfileList = [] # list of .imctrl file names for each entry in table
3172            files = []
3173            try:
3174                pth = G2G.GetImportPath(self.G2frame)
3175                if not pth: pth = '.'
3176                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3177                    style=wx.OPEN| wx.MULTIPLE,
3178                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3179                dlg.CenterOnParent()
3180                if dlg.ShowModal() == wx.ID_OK:
3181                    files = dlg.GetPaths()
3182                    self.parms,self.IMfileList = self.ReadFiles(files)
3183            finally:
3184                dlg.Destroy()
3185            if not files:
3186                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3187                return
3188        mainSizer = wx.BoxSizer(wx.VERTICAL)
3189        self.list = ImgIntLstCtrl(self, wx.ID_ANY,
3190                      style=wx.LC_REPORT
3191                          | wx.BORDER_SUNKEN
3192                         #| wx.BORDER_NONE
3193                         )
3194        mainSizer.Add(self.list,1,wx.EXPAND,1)
3195        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3196        btn = wx.Button(self, wx.ID_OK)
3197        btnsizer.Add(btn)
3198        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3199        btn.Bind(wx.EVT_BUTTON,self._onSave)
3200        btnsizer.Add(btn)
3201        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3202        btn.Bind(wx.EVT_BUTTON,self._onClose)
3203        btnsizer.Add(btn)
3204        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3205        self.SetSizer(mainSizer)
3206        self.list.FillList(self.parms)
3207        mainSizer.Layout()
3208        mainSizer.Fit(self)
3209       
3210    def ReadFiles(self,files):
3211        '''Reads a list of .imctrl files or a single .imtbl file
3212        '''
3213        tmpDict = {}
3214        if not files: return
3215        # option 1, a dump from a previous save
3216        if os.path.splitext(files[0])[1] == '.imtbl':
3217            fp = open(files[0],'r')
3218            S = fp.readline()
3219            while S:
3220                if S[0] != '#':
3221                    [key,val] = S[:-1].split(':',1)
3222                    tmpDict[key] = eval(val)
3223                S = fp.readline()
3224            fp.close()
3225            # delete entries where files do not exist
3226            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3227            if m1:
3228                print('\nimctrl file not found:')
3229                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3230            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3231            if m2:
3232                print('\nmask file not found')
3233                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3234            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3235            if m3:
3236                print('\nDropping entries due to negative distance: '+str(m3))
3237            m = sorted(set(m1 + m2 + m3))
3238            m.reverse()
3239            for c in m:
3240                for key in tmpDict:
3241                    del tmpDict[key][c]
3242            fileList = tmpDict.get('filenames','[]')
3243            parms = []
3244            if 'setdist' not in tmpDict:
3245                print(u'Old file, recreate before using: {}'.format(files[0]))
3246                return [[]],[]
3247            for key in self.ParmList:
3248                try:
3249                    float(tmpDict[key][0])
3250                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3251                except ValueError:
3252                    parms.append(tmpDict[key])
3253                except IndexError:
3254                    print('No valid image control entries read')
3255                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3256                    return [[]],[]
3257            return parms,fileList
3258        # option 2, read in a list of files
3259        for file in files: # read all files; place in dict by distance
3260            imgDict = Read_imctrl(file)
3261            dist = imgDict.get('setdist')
3262            if dist is None:
3263                print('Skipping old file, redo: {}'.format(file))
3264            tmpDict[dist] = imgDict
3265        parms = [[] for key in self.ParmList]
3266        fileList = []
3267        for d in sorted(tmpDict):
3268            fileList.append(tmpDict[d].get('filename'))
3269            if d is None: continue
3270            if d < 0: continue
3271            for i,key in enumerate(self.ParmList):
3272                val = tmpDict[d].get(key)
3273                try:
3274                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3275                except:
3276                    val = str(val)
3277                parms[i].append(val)
3278        return parms,fileList
3279   
3280    def ReadImageParmTable(self):
3281        '''Reads possibly edited values from the ListCtrl table and returns a list
3282        of values for each column.
3283        '''
3284        rows = self.list.GetItemCount()
3285        cols = self.list.GetColumnCount()
3286        parms = []
3287        for c in range(cols):
3288            parms.append([])
3289            for r in range(rows):
3290                parms[c].append(self.list.GetItem(r,c).GetText())
3291        return parms
3292
3293    def _onClose(self,event):
3294        'Called when Cancel button is pressed'
3295        self.EndModal(wx.ID_CANCEL)
3296       
3297    def _onSave(self,event):
3298        'Called when save button is pressed; creates a .imtbl file'
3299        fil = ''
3300        if self.G2frame.GSASprojectfile:
3301            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3302        dir,f = os.path.split(fil)
3303        pth = G2G.GetExportPath(self.G2frame)
3304        try:
3305            dlg = wx.FileDialog(self, 'Save table data as',
3306                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3307                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3308            dlg.CenterOnParent()
3309            if dlg.ShowModal() != wx.ID_OK: return
3310            fil = dlg.GetPath()
3311            fil = os.path.splitext(fil)[0]+'.imtbl'
3312        finally:
3313            dlg.Destroy()       
3314        parms = self.ReadImageParmTable()
3315        print('Writing image parameter table as '+fil)
3316        fp = open(fil,'w')
3317        for c in range(len(parms)-1):
3318            lbl = self.ParmList[c]
3319            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3320        lbl = self.ParmList[c+1]
3321        fp.write(lbl+': '+str(parms[c+1])+'\n')
3322        lbl = 'filenames'
3323        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3324        fp.close()
3325   
3326class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3327    '''Creates a custom ListCtrl for editing Image Integration parameters
3328    '''
3329    def __init__(self, parent, ID, pos=wx.DefaultPosition,
3330                 size=(1000,200), style=0):
3331        self.parent=parent
3332        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3333        listmix.ListCtrlAutoWidthMixin.__init__(self)
3334        listmix.TextEditMixin.__init__(self)
3335        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3336        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3337    def FillList(self,parms):
3338        'Places the current parms into the table'
3339        self.ClearAll()
3340        self.rowlen = len(self.parent.ParmList)
3341        for i,lbl in enumerate(self.parent.HeaderList):
3342            self.InsertColumn(i, lbl)
3343        for r,d in enumerate(parms[0]):
3344            if d is None: continue
3345            if float(d) < 0: continue
3346            index = self.InsertStringItem(sys.maxint, d)
3347            for j in range(1,len(parms)):
3348                self.SetStringItem(index, j, parms[j][r])
3349        for i,lbl in enumerate(self.parent.ParmList):
3350            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3351
3352    def OnDouble(self,evt):
3353        'respond to a double-click'
3354        self.CloseEditor()
3355        fil = '(none)'
3356        pth = G2G.GetImportPath(self.parent.G2frame)
3357        if not pth: pth = '.'
3358        try:
3359            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3360                                style=wx.OPEN,
3361                                wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3362            dlg.CenterOnParent()
3363            if dlg.ShowModal() == wx.ID_OK:
3364                fil = dlg.GetPath()
3365        finally:
3366            dlg.Destroy()
3367        if os.path.splitext(fil)[1] != '.imctrl':
3368            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3369            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3370        else:
3371            # insert or overwrite an instrument parameter set
3372            if not os.path.exists(fil):
3373                print('Does not exist: '+fil)
3374                return
3375            imgDict = Read_imctrl(fil)
3376            dist = imgDict['distance']
3377            parms = self.parent.ReadImageParmTable()
3378            x = np.array([float(i) for i in parms[0]])
3379            closest = abs(x-dist).argmin()
3380            closeX = x[closest]
3381            # fix IMfileList
3382            for c,lbl in enumerate(self.parent.ParmList):
3383                try:
3384                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3385                except ValueError:
3386                    vali = imgDict[lbl]
3387                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3388                    parms[c][closest] = vali
3389                elif dist > closeX: # insert after
3390                    parms[c].insert(closest+1,vali)
3391                else:
3392                    parms[c].insert(closest,vali)
3393            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3394                self.parent.IMfileList[closest] = fil
3395            elif dist > closeX: # insert after
3396                self.parent.IMfileList.insert(closest+1,fil)
3397            else:
3398                self.parent.IMfileList.insert(closest,fil)
3399            self.FillList(parms)
3400# Autointegration end
3401###########################################################################
Note: See TracBrowser for help on using the repository browser.