source: trunk/GSASIIimgGUI.py @ 2863

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

extend contour color selection to include reversed ones (doubles the list!)
add Contour_color to configuration items; modify the Preferences GUI to present a ComboBox? for the color maps
continue implementation of protein validator
np.max --> np.fmax in some places (there may be more to be found); new numpy won't allow np.max to be used to compare an array with a value
fix one use of float as array index - not allowed now: there may be others in G2plot

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 161.6 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII - image data display routines
3########### SVN repository information ###################
4# $Date: 2017-06-14 21:11:42 +0000 (Wed, 14 Jun 2017) $
5# $Author: vondreele $
6# $Revision: 2863 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 2863 2017-06-14 21:11:42Z 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: 2863 $")
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 (shift key to force 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() ],key=lambda s: s.lower())   #if not m.endswith("_r")
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':False})
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                Nvar = 5*len(StaCtrls['d-zero'])
2017                coVar = np.zeros((Nvar,Nvar))
2018                for j,item in enumerate(StaCtrls['d-zero']):
2019                    variables += item['Emat']
2020                    sig += item['Esig']
2021                    varylist = ['%d;%s'%(j,Name) for Name in varyNames]
2022                    varyList += varylist
2023                    parmDict.update(dict(zip(varylist,item['Emat'])))
2024                    parmDict['%d;Dcalc'%(j)] = item['Dcalc']
2025                    variables.append(1.e6*(item['Dcalc']/item['Dset']-1.))
2026                    varyList.append('%d;h-mstrain'%(j))
2027                    sig.append(0)
2028                    parmDict['%d;Ivar'%(j)] = item['Ivar']
2029                    variables.append(item['Ivar'])
2030                    varyList.append('%d;Ivar'%(j))
2031                    sig.append(0)
2032                    j4 = j*5
2033                    coVar[j4:j4+3,j4:j4+3] = item['covMat']
2034                SeqResult[name] = {'variables':variables,'varyList':varyList,'sig':sig,'Rvals':[],
2035                    'covMatrix':coVar,'title':name,'parmDict':parmDict}
2036            else:
2037                SeqResult['histNames'] = goodnames
2038                dlg.Destroy()
2039                print ' ***** Sequential strain refinement successful *****'
2040        finally:
2041            wx.EndBusyCursor()   
2042        SeqResult['histNames'] = choices
2043        G2frame.PatternTree.SetItemPyData(Id,SeqResult)
2044        G2frame.PatternTree.SelectItem(Id)
2045        print 'All images fitted'
2046       
2047    def SamSizer():
2048       
2049        def OnStrainType(event):
2050            data['Type'] = strType.GetValue()
2051       
2052        samSizer = wx.BoxSizer(wx.HORIZONTAL)
2053        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Strain type: '),0,WACV)
2054        strType = wx.ComboBox(G2frame.dataDisplay,value=data['Type'],choices=['True','Conventional'],
2055            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2056        strType.SetValue(data['Type'])
2057        strType.Bind(wx.EVT_COMBOBOX, OnStrainType)
2058        samSizer.Add(strType,0,WACV)
2059       
2060        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample phi: '),0,WACV)
2061        samPhi = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample phi',nDig=(10,3),typeHint=float,min=-360.,max=360.)
2062        samSizer.Add(samPhi,0,WACV)
2063        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample delta-z(mm): '),0,WACV)
2064        samZ = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample z',nDig=(10,3),typeHint=float)
2065        samSizer.Add(samZ,0,WACV)
2066        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample load(MPa): '),0,WACV)
2067        samLoad = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample load',
2068                nDig=[8,3],typeHint=float,)
2069        samSizer.Add(samLoad,0,WACV)
2070
2071        return samSizer
2072       
2073    def DzeroSizer():
2074   
2075        def OnDzero(invalid,value,tc):
2076            data['d-zero'] = G2mth.sortArray(data['d-zero'],'Dset',reverse=True)
2077            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[tc.GetId()]],G2frame.ImageZ,Controls)
2078            if len(Ring):
2079                data['d-zero'][Indx[tc.GetId()]].update(R)
2080            else:
2081                G2frame.ErrorDialog('Strain peak selection','WARNING - No points found for this ring selection')
2082               
2083            wx.CallAfter(UpdateStressStrain,G2frame,data)
2084            G2plt.PlotExposedImage(G2frame,event=tc.event,newPlot=False)
2085            G2plt.PlotStrain(G2frame,data,newPlot=True)
2086           
2087        def OnDeleteDzero(event):
2088            Obj = event.GetEventObject()
2089            del(data['d-zero'][delIndx.index(Obj)])
2090            UpdateStressStrain(G2frame,data)
2091            G2plt.PlotExposedImage(G2frame,event=event,newPlot=True)
2092            G2plt.PlotStrain(G2frame,data,newPlot=True)
2093       
2094        def OnCutOff(invalid,value,tc):
2095            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[tc.GetId()]],G2frame.ImageZ,Controls)
2096            G2plt.PlotExposedImage(G2frame,event=tc.event)
2097            G2plt.PlotStrain(G2frame,data,newPlot=True)
2098       
2099        def OnPixLimit(event):
2100            Obj = event.GetEventObject()
2101            data['d-zero'][Indx[Obj.GetId()]]['pixLimit'] = int(Obj.GetValue())
2102            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[Obj.GetId()]],G2frame.ImageZ,Controls)
2103            G2plt.PlotExposedImage(G2frame,event=event)
2104            G2plt.PlotStrain(G2frame,data,newPlot=True)
2105           
2106        def OnFixDset(event):
2107            Obj = event.GetEventObject()
2108            data['d-zero'][Indx[Obj.GetId()]]['fixDset'] = Obj.GetValue()
2109           
2110        Indx = {}
2111        delIndx = []   
2112        dzeroSizer = wx.FlexGridSizer(0,8,5,5)
2113        for id,dzero in enumerate(data['d-zero']):
2114            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' d-zero #%d: '%(id))),0,WACV)
2115            dZero = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['d-zero'][id],'Dset',
2116                min=0.25,max=20.,nDig=(10,5),typeHint=float,OnLeave=OnDzero)
2117            dzeroSizer.Add(dZero,0,WACV)
2118            Indx[dZero.GetId()] = id
2119            dfix = wx.CheckBox(G2frame.dataDisplay,label='Use Poisson mean?')
2120            dfix.SetValue(dzero.get('fixDset',False))
2121            dfix.Bind(wx.EVT_CHECKBOX,OnFixDset)
2122            Indx[dfix.GetId()] = id
2123            dzeroSizer.Add(dfix,0,WACV)
2124            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' d-zero ave: %.5f'%(dzero['Dcalc']))),0,WACV)
2125               
2126            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min ring I/Ib '),0,WACV)
2127            cutOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['d-zero'][id],'cutoff',
2128                    min=0.5,max=20.,nDig=(10,1),typeHint=float,OnLeave=OnCutOff)
2129            Indx[cutOff.GetId()] = id
2130            dzeroSizer.Add(cutOff,0,WACV)
2131       
2132            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Pixel search range '),0,WACV)
2133            pixLimit = wx.ComboBox(parent=G2frame.dataDisplay,value=str(dzero['pixLimit']),choices=['1','2','5','10','15','20'],
2134                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2135            pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
2136            Indx[pixLimit.GetId()] = id
2137            dzeroSizer.Add(pixLimit,0,WACV)               
2138               
2139            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' Strain tensor:')),WACV)
2140            names = ['e11','e12','e22']
2141            for i in range(3):
2142                dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=names[i]),0,WACV)
2143                tensorElem = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(dzero['Emat'][i]),style=wx.TE_READONLY)
2144                tensorElem.SetBackgroundColour(VERY_LIGHT_GREY)
2145                dzeroSizer.Add(tensorElem,0,WACV)
2146            dzeroDelete = wx.CheckBox(parent=G2frame.dataDisplay,label='delete?')
2147            dzeroDelete.Bind(wx.EVT_CHECKBOX,OnDeleteDzero)
2148            delIndx.append(dzeroDelete)
2149            dzeroSizer.Add(dzeroDelete,0,WACV)
2150           
2151        return dzeroSizer
2152       
2153# patches
2154    if 'Sample load' not in data:
2155        data['Sample load'] = 0.0
2156# end patches
2157   
2158    if G2frame.dataDisplay:
2159        G2frame.dataDisplay.Destroy()
2160    Controls = G2frame.PatternTree.GetItemPyData(
2161        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))       
2162    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.StrStaMenu)
2163    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAppendDzero, id=G2gd.wxID_APPENDDZERO)
2164    G2frame.dataFrame.Bind(wx.EVT_MENU, OnUpdateDzero, id=G2gd.wxID_UPDATEDZERO)
2165    G2frame.dataFrame.Bind(wx.EVT_MENU, OnFitStrSta, id=G2gd.wxID_STRSTAFIT)
2166    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotStrSta, id=G2gd.wxID_STRSTAPLOT) 
2167    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveStrRing, id=G2gd.wxID_STRRINGSAVE) 
2168    G2frame.dataFrame.Bind(wx.EVT_MENU, OnFitAllStrSta, id=G2gd.wxID_STRSTAALLFIT)
2169    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyStrSta, id=G2gd.wxID_STRSTACOPY)
2170    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadStrSta, id=G2gd.wxID_STRSTALOAD)
2171    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveStrSta, id=G2gd.wxID_STRSTASAVE)
2172    G2frame.dataFrame.Bind(wx.EVT_MENU, OnStrStaSample, id=G2gd.wxID_STRSTSAMPLE)       
2173    if not G2frame.dataFrame.GetStatusBar():
2174        G2frame.dataFrame.CreateStatusBar()
2175    if G2frame.StrainKey == 'a':    #probably doesn't happen
2176        G2frame.dataFrame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value')
2177    else:
2178        G2frame.dataFrame.GetStatusBar().SetStatusText("To add strain data: On 2D Powder Image, key a:add ring")
2179       
2180    G2frame.dataDisplay = wxscroll.ScrolledPanel(G2frame.dataFrame)
2181    mainSizer = wx.BoxSizer(wx.VERTICAL)
2182    mainSizer.Add((5,10),0)
2183    mainSizer.Add(SamSizer())
2184    mainSizer.Add((5,10),0)
2185    mainSizer.Add(DzeroSizer())
2186   
2187    mainSizer.Layout()   
2188    G2frame.dataDisplay.SetSizer(mainSizer)
2189    G2frame.dataDisplay.SetAutoLayout(1)
2190    G2frame.dataDisplay.SetupScrolling()
2191    Size = mainSizer.Fit(G2frame.dataFrame)
2192    Size[0] += 25
2193    G2frame.dataFrame.setSizePosLeft(Size)   
2194
2195###########################################################################
2196# Autointegration
2197###########################################################################
2198def ReadMask(filename):
2199    'Read a mask (.immask) file'
2200    File = open(filename,'r')
2201    save = {}
2202    S = File.readline()
2203    while S:
2204        if S[0] == '#':
2205            S = File.readline()
2206            continue
2207        [key,val] = S.strip().split(':',1)
2208        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
2209            save[key] = eval(val)
2210        S = File.readline()
2211    File.close()
2212    CleanupMasks(save)
2213    return save
2214
2215def ReadControls(filename):
2216    'read an image controls (.imctrl) file'
2217    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2218            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2219            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2220            'PolaVal','SampleAbs','dark image','background image']
2221    File = open(filename,'r')
2222    save = {}
2223    S = File.readline()
2224    while S:
2225        if S[0] == '#':
2226            S = File.readline()
2227            continue
2228        [key,val] = S.strip().split(':',1)
2229        if key in ['type','calibrant','binType','SampleShape',]:    #strings
2230            save[key] = val
2231        elif key in ['rotation']:
2232            save[key] = float(val)
2233        elif key in ['center',]:
2234            if ',' in val:
2235                save[key] = eval(val)
2236            else:
2237                vals = val.strip('[] ').split()
2238                save[key] = [float(vals[0]),float(vals[1])] 
2239        elif key in cntlList:
2240            save[key] = eval(val)
2241        S = File.readline()
2242    File.close()
2243    return save
2244
2245def Read_imctrl(imctrl_file):
2246    '''Read an image control file and record control parms into a dict, with some simple
2247    type conversions
2248    '''
2249    save = {'filename':imctrl_file}
2250    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
2251    if os.path.exists(immask_file):
2252        save['maskfile'] = immask_file
2253    else:
2254        save['maskfile'] = '(none)'
2255    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2256                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2257                        'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2258                        'PolaVal','SampleAbs','dark image','background image','setdist']
2259    File = open(imctrl_file,'r')
2260    fullIntegrate = False
2261    try:
2262        S = File.readline()
2263        while S:
2264            if S[0] == '#':
2265                S = File.readline()
2266                continue
2267            [key,val] = S.strip().split(':',1)
2268            if val.find(':') != -1:
2269                #print 'rejecting ',key,val
2270                S = File.readline()
2271                continue
2272            if key in ['type','calibrant','binType','SampleShape',]:    #strings
2273                save[key] = val
2274            elif key == 'rotation':
2275                save[key] = float(val)
2276            elif key == 'fullIntegrate':
2277                fullIntegrate = eval(val)
2278            elif key == 'LRazimuth':
2279                save['LRazimuth_min'],save['LRazimuth_max'] = eval(val)[0:2]
2280            elif key == 'IOtth':
2281                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
2282            elif key == 'center':
2283                if ',' in val:
2284                    vals = eval(val)
2285                else:
2286                    vals = val.strip('[] ').split()
2287                    vals = [float(vals[0]),float(vals[1])] 
2288                save['center_x'],save['center_y'] = vals[0:2]
2289            elif key in cntlList:
2290                save[key] = eval(val)
2291            S = File.readline()
2292    finally:
2293        File.close()
2294        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0.,0.
2295    return save
2296   
2297class AutoIntFrame(wx.Frame):
2298    '''Creates a wx.Frame window for the Image AutoIntegration.
2299    The intent is that this will be used as a non-modal dialog window.
2300   
2301    Implements a Start button that morphs into a pause and resume button.
2302    This button starts a processing loop that is repeated every
2303    :meth:`PollTime` seconds.
2304
2305    :param wx.Frame G2frame: main GSAS-II frame
2306    :param float PollTime: frequency in seconds to repeat calling the
2307      processing loop. (Default is 30.0 seconds.)
2308    '''
2309    def __init__(self,G2frame,PollTime=30.0):
2310        def OnStart(event):
2311            '''Called when the start button is pressed. Changes button label
2312            to Pause. When Pause is pressed the label changes to Resume.
2313            When either Start or Resume is pressed, the processing loop
2314            is started. When Pause is pressed, the loop is stopped.
2315            '''
2316            # check inputs for errors before starting
2317            #err = ''
2318            #if not any([self.params[fmt] for fmt in self.fmtlist]):
2319            #    err += '\nPlease select at least one output format\n'
2320            #if err:
2321            #    G2G.G2MessageBox(self,err)
2322            #    return
2323            self.Pause = False
2324            # change button label
2325            if self.btnstart.GetLabel() != 'Pause':
2326                self.btnstart.SetLabel('Pause')
2327                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
2328                if self.timer.IsRunning(): self.timer.Stop()
2329                self.PreventReEntryTimer = False
2330                if self.StartLoop():
2331                    G2G.G2MessageBox(self,'Error in setting up integration. See console')
2332                    return
2333                self.OnTimerLoop(None) # run once immediately
2334                if not self.Pause:
2335                    # no pause, so start timer to check for new files
2336                    self.timer.Start(int(1000*PollTime),oneShot=False)
2337                    return
2338            # we will get to this point if Paused
2339            self.OnPause()
2340           
2341        def OnReset(event):
2342            '''Called when Reset button is pressed. This stops the
2343            processing loop and resets the list of integrated files so
2344            all images can be reintegrated.
2345            '''
2346            self.btnstart.SetLabel('Restart')
2347            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
2348            if self.timer.IsRunning(): self.timer.Stop()
2349            self.Reset = True
2350            self.Pause = True
2351           
2352        def OnQuit(event):
2353            '''Stop the processing loop and close the Frame
2354            '''
2355            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
2356            wx.CallAfter(self.Destroy)
2357           
2358        def OnBrowse(event):
2359            '''Responds when the Browse button is pressed to load a file.
2360            The routine determines which button was pressed and gets the
2361            appropriate file type and loads it into the appropriate place
2362            in the dict.
2363            '''
2364            if btn3 == event.GetEventObject():
2365                dlg = wx.DirDialog(
2366                    self, 'Select directory for output files',
2367                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
2368                dlg.CenterOnParent()
2369                try:
2370                    if dlg.ShowModal() == wx.ID_OK:
2371                        self.params['outdir'] = dlg.GetPath()
2372                        fInp3.SetValue(self.params['outdir'])
2373                finally:
2374                    dlg.Destroy()
2375                return
2376            elif btn4 == event.GetEventObject():
2377                msg = ''
2378                pth = G2G.GetExportPath(G2frame)
2379                dlg = wx.FileDialog(
2380                    self, 'Select a PDF parameter file',
2381                    pth, self.params['pdfprm'], 
2382                    "PDF controls file (*.pdfprm)|*.pdfprm",
2383                    wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
2384                dlg.CenterOnParent()
2385                try:
2386                    if dlg.ShowModal() == wx.ID_OK:
2387                        self.params['pdfprm'] = dlg.GetPath()
2388                        fInp4.SetValue(self.params['pdfprm'])
2389                        scanPDFprm()
2390                        msg = self.checkPDFprm(True)
2391                finally:
2392                    dlg.Destroy()
2393                if 'Error' in msg:
2394                    print(msg)
2395                    lbl = 'PDFPRM error'
2396                else:
2397                    msg = 'Information from file {}\n\n{}'.format(self.params['pdfprm'],msg)
2398                    lbl = 'PDFPRM information'
2399                G2G.G2MessageBox(self,msg,lbl)
2400                return
2401               
2402        def OnRadioSelect(event):
2403            '''Respond to a radiobutton selection and when in table
2404            mode, get distance-dependent parameters from user.
2405            '''
2406            self.Evaluator = None
2407            if self.useTable.GetValue():
2408                dlg = None
2409                try:
2410                    dlg = IntegParmTable(self) # create the dialog
2411                    dlg.CenterOnParent()
2412                    if dlg.ShowModal() == wx.ID_OK:
2413                        self.ImgTblParms = dlg.parms
2414                        self.IMfileList = dlg.IMfileList
2415                        self.Evaluator = DefineEvaluator(dlg)
2416                        self.params['Mode'] = 'table'
2417                        self.editTable.Enable(True)
2418                    else:
2419                        self.useActive.SetValue(True)
2420                finally:
2421                    if dlg: dlg.Destroy()
2422            elif self.useActive.GetValue():
2423                self.params['Mode'] = 'active'
2424                self.imageBase = G2frame.Image
2425                self.useActive.SetLabel("Active Image: "+
2426                        G2frame.PatternTree.GetItemText(self.imageBase))
2427                self.editTable.Enable(False)
2428            else:
2429                print('unexpected mode in OnRadioSelect')
2430
2431        def OnEditTable(event):
2432            '''Called to edit the distance-dependent parameter look-up table.
2433            Should be called only when table is defined and active.
2434            '''
2435            dlg = None
2436            try:
2437                dlg = IntegParmTable(self,self.ImgTblParms,self.IMfileList)
2438                dlg.CenterOnParent()
2439                if dlg.ShowModal() == wx.ID_OK:
2440                    self.ImgTblParms = dlg.parms
2441                    self.IMfileList = dlg.IMfileList
2442                    self.Evaluator = DefineEvaluator(dlg)
2443                    self.params['Mode'] = 'table'
2444                    self.editTable.Enable(True)
2445                else:
2446                    self.useActive.SetValue(True)
2447                    self.params['Mode'] = 'active'
2448                    self.imageBase = G2frame.Image
2449                    self.useActive.SetLabel("Active Image: "+
2450                            G2frame.PatternTree.GetItemText(self.imageBase))
2451                    self.editTable.Enable(False)
2452            finally:
2453                if dlg: dlg.Destroy()
2454               
2455        def showPDFctrls(event):
2456            '''Called to show or hide AutoPDF widgets. Note that fInp4 must be included in the
2457            sizer layout with .Show(True) before .Show(False) will work properly.
2458            '''
2459            fInp4.Enable(self.params['ComputePDF'])
2460            fInp4.Show(self.params['ComputePDF'])
2461            fInp4a.Enable(self.params['ComputePDF'])
2462            btn4.Enable(self.params['ComputePDF'])
2463            cOpt.Enable(self.params['ComputePDF'])
2464            if self.params['ComputePDF']:
2465                lbl4.SetForegroundColour("black")
2466                lbl4a.SetForegroundColour("black")
2467            else:
2468                lbl4.SetForegroundColour("gray")
2469                lbl4a.SetForegroundColour("gray")
2470                                   
2471        def scanPDFprm(**kw):
2472            fInp4.invalid = not os.path.exists(fInp4.GetValue())
2473            fInp4._IndicateValidity()
2474           
2475        def OnAutoScale(event):
2476            self.AutoScale = autoscale.GetValue()
2477           
2478        def OnAutoScaleName(event):
2479            self.AutoScaleName = scalename.GetValue()
2480            self.Scale[0] = self.AutoScales[self.AutoScaleName]
2481            scaleval.SetValue(self.Scale[0])
2482               
2483        ##################################################
2484        # beginning of __init__ processing
2485        ##################################################
2486        self.G2frame = G2frame
2487        self.ImgTblParms = None
2488        self.IMfileList = None
2489        self.Evaluator = None
2490        self.params = {}
2491        self.Reset = False
2492        self.Pause = False
2493        self.PreventReEntryShowMatch = False
2494        self.PreventReEntryTimer = False
2495        self.params['IMGfile'] = ''
2496        self.params['MaskFile'] = ''
2497        self.params['IgnoreMask'] = True
2498        self.fmtlist = G2IO.ExportPowderList(G2frame)
2499        self.timer = wx.Timer()
2500        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
2501        self.imageBase = G2frame.Image
2502        self.params['ComputePDF'] = False
2503        self.params['pdfDmax'] = 0.0
2504        self.params['pdfprm'] = ''
2505        self.params['optPDF'] = True
2506        self.pdfControls = {}
2507        self.AutoScale = False
2508        self.Scale = [1.0,]
2509
2510        G2frame.PatternTree.GetSelection()
2511        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(self.imageBase) 
2512        self.imagedir,fileroot = os.path.split(imagefile)
2513        Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2514            G2frame,self.imageBase, 'Comments'))
2515        DefaultAutoScaleNames = GSASIIpath.GetConfigValue('Autoscale_ParmNames')
2516        self.AutoScaleName = GSASIIpath.GetConfigValue('DefaultAutoScale')
2517        self.AutoScales = {}
2518        if DefaultAutoScaleNames is not None:
2519            for comment in Comments:
2520                if '=' in comment:
2521                    name,val = comment.split('=',1) 
2522                    if name in DefaultAutoScaleNames:
2523                        try:
2524                            self.AutoScales[name] = float(val)
2525                            if name == self.AutoScaleName:
2526                                self.Scale[0] = float(val)
2527                        except ValueError:
2528                            continue
2529        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2530        self.params['outdir'] = os.path.abspath(self.imagedir)
2531        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2532                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2533        self.Status = self.CreateStatusBar()
2534        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2535        mnpnl = wx.Panel(self)
2536        mnsizer = wx.BoxSizer(wx.VERTICAL)
2537        # box for integration controls & masks input
2538        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2539        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2540        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2541        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2542                            style = wx.RB_GROUP)
2543        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2544        self.useActive.SetLabel("Active Image: "+
2545                                G2frame.PatternTree.GetItemText(self.imageBase))
2546        lblsizr.Add(self.useActive,1,wx.EXPAND,1)
2547        self.useActive.SetValue(True)
2548        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2549        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2550        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2551        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2552        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2553        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2554        self.editTable.Enable(False)
2555        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2556        # bind button and deactivate be default
2557        lblsizr.Add(minisizer)
2558        mnsizer.Add(lblsizr,1,wx.EXPAND,1)
2559
2560        # file filter stuff
2561        sizer = wx.BoxSizer(wx.HORIZONTAL)
2562        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Image filter'))
2563        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',OnLeave=self.ShowMatchingFiles)
2564        sizer.Add(flterInp)
2565        mnsizer.Add(sizer,0,wx.ALIGN_RIGHT,1)
2566        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2567        mnsizer.Add(self.ListBox,0,wx.EXPAND,1)
2568        self.ShowMatchingFiles(self.params['filter'])
2569        # box for output selections
2570        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2571        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2572        sizer = wx.BoxSizer(wx.HORIZONTAL)
2573        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2574        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2575        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2576        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2577        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2578        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2579        lblsizr.Add(sizer,0,wx.EXPAND)
2580        sizer = wx.BoxSizer(wx.HORIZONTAL)
2581        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
2582        for dfmt in self.fmtlist:
2583            fmt = dfmt[1:]
2584            self.params[fmt] = False
2585            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2586            sizer.Add(btn)
2587        lblsizr.Add(sizer)
2588        sizer = wx.BoxSizer(wx.HORIZONTAL)
2589        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2590        self.params['SeparateDir'] = False
2591        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2592        lblsizr.Add(sizer)
2593        if self.AutoScales:
2594            sizer = wx.BoxSizer(wx.HORIZONTAL)
2595            autoscale = wx.CheckBox(mnpnl,label='Do autoscaling with:')
2596            autoscale.Bind(wx.EVT_CHECKBOX,OnAutoScale)
2597            sizer.Add(autoscale,0,WACV)
2598            scalename = wx.ComboBox(mnpnl,value=self.AutoScaleName,choices=self.AutoScales.keys(),
2599                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2600            scalename.Bind(wx.EVT_COMBOBOX,OnAutoScaleName)
2601            sizer.Add(scalename,0,WACV)
2602            sizer.Add(wx.StaticText(mnpnl,label=' to '),0,WACV)
2603            scaleval = G2G.ValidatedTxtCtrl(mnpnl,self.Scale,0,nDig=(10,2),min=1.)
2604            sizer.Add(scaleval,0,WACV)
2605            lblsizr.Add(sizer,0)
2606        #ToDO: Autonormalize, parm name?, scaling value?
2607        sizer = wx.BoxSizer(wx.HORIZONTAL)
2608        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
2609        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2610        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2611        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2612        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2613        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2614        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
2615        sizer.Add(cOpt)
2616        lblsizr.Add(sizer,0)
2617        sizer = wx.BoxSizer(wx.HORIZONTAL)
2618        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2619        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2620        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2621                                     OnLeave=scanPDFprm)
2622        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2623        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2624        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2625        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2626        lblsizr.Add(sizer,0,wx.EXPAND)
2627        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2628        # buttons on bottom
2629        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2630        sizer = wx.BoxSizer(wx.HORIZONTAL)
2631        sizer.Add((20,-1))
2632        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2633        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2634        sizer.Add(self.btnstart)
2635        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2636        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2637        sizer.Add(self.btnreset)
2638        sizer.Add((20,-1),wx.EXPAND,1)
2639        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
2640        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
2641        sizer.Add(self.btnclose)
2642        sizer.Add((20,-1))
2643        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
2644        # finish up window
2645        mnpnl.SetSizer(mnsizer)
2646        OnRadioSelect(None) # disable widgets
2647        mnsizer.Fit(self)
2648        self.CenterOnParent()
2649        self.Show()
2650        showPDFctrls(None)
2651
2652    def checkPDFprm(self,ShowContents=False):
2653        '''Read in the PDF (.pdfprm) parameter file and check for problems.
2654        If ShowContents is True, a formatted text version of some of the file
2655        contents is returned. If errors are found, the return string will contain
2656        the string "Error:" at least once.
2657        '''
2658        self.pdfControls = {}
2659        msg = ''
2660        File = None
2661        try:
2662            File = open(self.params['pdfprm'],'r')
2663            S = File.readline()
2664            while S:
2665                if '#' in S:
2666                    S = File.readline()
2667                    continue
2668                key,val = S.split(':',1)
2669                try:
2670                    self.pdfControls[key] = eval(val)
2671                except:
2672                    self.pdfControls[key] = val
2673                S = File.readline()
2674        except Exception as err:
2675            msg += 'PDF Processing Error: error with open or read of {}'.format(self.params['pdfprm'])
2676            if GSASIIpath.GetConfigValue('debug'):
2677                print(msg)
2678                print(err)
2679            self.pdfControls = {}
2680            return msg
2681        finally:
2682            if File: File.close()
2683        formula = ''
2684        for el in self.pdfControls['ElList']:
2685            if self.pdfControls['ElList'][el]['FormulaNo'] <= 0: continue
2686            if formula: formula += ' '
2687            formula += '{}({:.1f})'.format(el,self.pdfControls['ElList'][el]['FormulaNo'])
2688        if not formula:
2689            msg += 'Error: no chemical formula in file'
2690        for key in ['Sample Bkg.','Container','Container Bkg.']:
2691            if key not in self.pdfControls:
2692                if msg: msg += '\n'
2693                msg += 'Error: missing key in self.pdfControls: '+key
2694                continue
2695        if msg or not ShowContents: return msg  # stop on error
2696        msg += 'Default formula: '+formula+'\n'
2697        for key in ['Sample Bkg.','Container','Container Bkg.']:
2698            name = self.pdfControls[key]['Name']
2699            mult = self.pdfControls[key].get('Mult',0.0)
2700            if not name: continue
2701            msg += '\n{}: {:.2f} * "{}"'.format(key,mult,name)
2702            if not G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,name):
2703                msg += ' *** missing ***'
2704        return msg
2705   
2706    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
2707        G2frame = self.G2frame
2708        if invalid: return
2709        msg = ''
2710        if self.PreventReEntryShowMatch: return
2711        self.PreventReEntryShowMatch = True
2712        imageFileList = []
2713        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
2714            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2715            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2716            if imagefile not in imageFileList: imageFileList.append(imagefile)
2717            if img not in G2frame.IntegratedList:
2718                if msg: msg += '\n'
2719                msg += '  ' + img
2720        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
2721        msg1 = ""
2722        try:
2723            imageList = sorted(
2724                glob.glob(os.path.join(self.imagedir,value)))
2725            if not imageList:
2726                msg1 = 'Warning: No files match search string '+os.path.join(self.imagedir,value)
2727            else:
2728                for fil in imageList:
2729                    if fil not in imageFileList: msg1 += '\n  '+fil
2730                if msg1:
2731                    msg += 'Files to integrate from '+os.path.join(self.imagedir,value)+msg1
2732                else:
2733                    msg += 'No files found to read'
2734        except IndexError:
2735            msg += 'Error searching for files named '+os.path.join(self.imagedir,value)
2736        self.ListBox.Clear()
2737        self.ListBox.AppendItems(msg.split('\n'))
2738        self.PreventReEntryShowMatch = False
2739        return
2740       
2741    def OnPause(self):
2742        '''Respond to Pause, changes text on button/Status line, if needed
2743        Stops timer
2744        self.Pause should already be True
2745        '''
2746        if self.timer.IsRunning(): self.timer.Stop()
2747        if self.btnstart.GetLabel() == 'Restart':
2748            return
2749        if self.btnstart.GetLabel() != 'Resume':
2750            print('\nPausing autointegration\n')
2751            self.btnstart.SetLabel('Resume')
2752            self.Status.SetStatusText(
2753                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
2754        self.Pause = True
2755           
2756    def IntegrateImage(self,img):
2757        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
2758        are placed in G2frame.IntgOutList
2759        '''
2760        G2frame = self.G2frame
2761        imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2762        G2frame.Image = imgId
2763        G2frame.PickId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls')
2764        # do integration
2765        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2766        if self.AutoScale:
2767            Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2768                G2frame,imgId, 'Comments'))
2769            for comment in Comments:
2770                if '=' in comment:
2771                    name,val = comment.split('=',1) 
2772                    if name == self.AutoScaleName:
2773                        val = float(val)
2774                        if val > 0.:
2775                            Scale = self.Scale[0]/val
2776                        break
2777        masks = G2frame.PatternTree.GetItemPyData(
2778            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
2779        data = G2frame.PatternTree.GetItemPyData(G2frame.PickId)
2780        # simulate a Image Controls press, since that is where the
2781        # integration is hidden
2782        UpdateImageControls(G2frame,data,masks,IntegrateOnly=True)
2783        G2frame.IntegratedList.append(img) # note this as integrated
2784        # split name and control number
2785        s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
2786        namepre = s[0]
2787        if len(s) > 1:
2788            namenum = s[1]
2789        else:
2790            namenum = ''
2791        for Id in G2frame.IntgOutList: # loop over newly created PDWR entry(ies)
2792            # save the created PWDR tree names so that a reset can delete them
2793            G2frame.Image = Id
2794            treename = G2frame.PatternTree.GetItemText(Id)
2795            G2frame.AutointPWDRnames.append(treename)
2796            # write out the images in the selected formats
2797            Sdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Sample Parameters'))
2798            if self.AutoScale:
2799                print 'Rescale by %.4f'%(Scale)
2800                y,w = G2frame.PatternTree.GetItemPyData(Id)[1][1:3]
2801                y *= Scale
2802                w /= Scale**2
2803            # determine the name for the current file
2804            fileroot = namepre
2805            if len(G2frame.IntgOutList) > 1:
2806                fileroot += "_AZM"
2807                if 'Azimuth' in Sdata:
2808                    fileroot += str(int(10*Sdata['Azimuth']))
2809                fileroot += "_" 
2810            fileroot += namenum
2811            # loop over selected formats
2812            for dfmt in self.fmtlist:
2813                if not self.params[dfmt[1:]]: continue
2814                if self.params['SeparateDir']:
2815                    subdir = dfmt[1:]
2816                else:
2817                    subdir = ''
2818                fil = os.path.join(self.params['outdir'],subdir,fileroot)
2819                print('writing file '+fil+dfmt)
2820                G2IO.ExportPowder(G2frame,treename,fil,dfmt)
2821               
2822    def EnableButtons(self,flag):
2823        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
2824        '''
2825        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
2826        # on windows
2827        if sys.platform != "win32":
2828            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
2829        self.btnstart.SetLabel('Pause')
2830        wx.Yield()
2831               
2832    def ResetFromTable(self,dist):
2833        '''Sets integration parameters based on values from
2834        the lookup table
2835        '''
2836        #dist = self.controlsDict['distance']
2837        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
2838        if GSASIIpath.GetConfigValue('debug'):
2839            print 'interpolated values: ',interpDict
2840        self.ImageControls = ReadControls(imgctrl)
2841        self.ImageControls.update(interpDict)
2842        self.ImageControls['showLines'] = True
2843        self.ImageControls['ring'] = []
2844        self.ImageControls['rings'] = []
2845        self.ImageControls['ellipses'] = []
2846        self.ImageControls['setDefault'] = False
2847        for i in 'range','size','GonioAngles':
2848            if i in self.ImageControls:
2849                del self.ImageControls[i]
2850        # load copy of Image Masks
2851        if immask:
2852            self.ImageMasks = ReadMask(immask)
2853            if list(self.ImageMasks['Thresholds'][0]) == self.ImageMasks['Thresholds'][1]:     #avoid copy of unchanged thresholds
2854                del self.ImageMasks['Thresholds']
2855        else:
2856            self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
2857       
2858    def StartLoop(self):
2859        '''Prepare to start autointegration timer loop.
2860        Save current Image params for use in future integrations
2861        also label the window so users understand what is being used
2862        '''
2863        print('\nStarting new autointegration\n')
2864        G2frame = self.G2frame
2865        # show current IMG base
2866        if self.params['Mode'] != 'table':
2867            self.useActive.SetLabel("Active Image: "+
2868                                    G2frame.PatternTree.GetItemText(self.imageBase))
2869            # load copy of Image Controls from current image and clean up
2870            # items that should not be copied
2871            self.ImageControls = copy.deepcopy(
2872                G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2873                    G2frame,self.imageBase, 'Image Controls')))
2874            self.ImageControls['showLines'] = True
2875            self.ImageControls['ring'] = []
2876            self.ImageControls['rings'] = []
2877            self.ImageControls['ellipses'] = []
2878            self.ImageControls['setDefault'] = False
2879            del self.ImageControls['range']
2880            del self.ImageControls['size']
2881            del self.ImageControls['GonioAngles']
2882            # load copy of Image Masks, keep thresholds
2883            self.ImageMasks = copy.deepcopy(
2884                G2frame.PatternTree.GetItemPyData(
2885                    G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Masks')))
2886            self.Thresholds = self.ImageMasks['Thresholds'][:]
2887            if list(self.Thresholds[0]) == self.Thresholds[1]:     #avoid copy of unchanged thresholds
2888                del self.ImageMasks['Thresholds']   
2889        # make sure all output directories exist
2890        if self.params['SeparateDir']:
2891            for dfmt in self.fmtlist:
2892                if not self.params[dfmt[1:]]: continue
2893                dir = os.path.join(self.params['outdir'],dfmt[1:])
2894                if not os.path.exists(dir): os.makedirs(dir)
2895        else:
2896            if not os.path.exists(self.params['outdir']):
2897                os.makedirs(self.params['outdir'])
2898        if self.Reset: # special things to do after Reset has been pressed
2899            self.G2frame.IntegratedList = []
2900           
2901            if self.params['Mode'] != 'table': # reset controls and masks for all IMG items in tree to master
2902                for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
2903                    # update controls from master
2904                    controlsDict = G2frame.PatternTree.GetItemPyData(
2905                        G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Image Controls'))
2906                    controlsDict.update(self.ImageControls)
2907                    # update masks from master
2908                    ImageMasks = G2frame.PatternTree.GetItemPyData(
2909                        G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Masks'))
2910                    ImageMasks.update(self.ImageMasks)
2911            # delete all PWDR items created after last Start was pressed
2912            idlist = []
2913            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
2914            while item:
2915                itemName = G2frame.PatternTree.GetItemText(item)
2916                if itemName in G2frame.AutointPWDRnames:
2917                    idlist.append(item)
2918                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
2919            for item in idlist:
2920                G2frame.PatternTree.Delete(item)
2921            wx.Yield()
2922            self.Reset = False
2923        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
2924        G2frame.AutointPDFnames = [] # list of created PWDR tree item names
2925        # check that AutoPDF input is OK, offer chance to use alternate PDWRs if referenced ones
2926        # are not present
2927        if self.params['ComputePDF']:
2928            msg = self.checkPDFprm()
2929            if 'Error:' in msg:
2930                print(msg)
2931                return True
2932            fileList = []
2933            id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
2934            while id:
2935                name = G2frame.PatternTree.GetItemText(id)
2936                if name.startswith('PWDR '): fileList.append(name)
2937                id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
2938            if not fileList:
2939                print(msg)
2940                print('No PWDR entries to select')
2941                return True
2942            for key in ['Sample Bkg.','Container','Container Bkg.']:
2943                name = self.pdfControls[key]['Name']
2944                if not name: continue
2945                if not G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name):
2946                    indx = G2G.ItemSelector(fileList, self, header='Select PDWR item',
2947                                    title='Select a PDWR tree item for '+key+'\n(or cancel to quit)')
2948                    if indx is None:
2949                        print('No PWDR entry selected for '+key)
2950                        return True
2951                    self.pdfControls[key]['Name'] = fileList[indx]
2952        return False
2953               
2954    def OnTimerLoop(self,event):
2955        '''A method that is called every :meth:`PollTime` seconds that is
2956        used to check for new files and process them. Integrates new images.
2957        Also optionally sets up and computes PDF.
2958        This is called only after the "Start" button is pressed (then its label reads "Pause").
2959        '''
2960        def AutoIntegrateImage(imgId):
2961            '''Integrates an image that has been read into the data tree and updates the
2962            AutoInt window.
2963            '''
2964            img = G2frame.PatternTree.GetItemText(imgId)
2965            controlsDict = G2frame.PatternTree.GetItemPyData(
2966                G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
2967            ImageMasks = G2frame.PatternTree.GetItemPyData(
2968                G2gd.GetPatternTreeItemId(G2frame,imgId, 'Masks'))
2969            if self.params['Mode'] == 'table': # look up parameter values from table
2970                self.ResetFromTable(controlsDict['setdist'])
2971            # update controls from master
2972            controlsDict.update(self.ImageControls)
2973            # update masks from master w/o Thresholds
2974            ImageMasks.update(self.ImageMasks)
2975            self.EnableButtons(False)
2976            try:
2977                self.IntegrateImage(img)
2978            finally:
2979                self.EnableButtons(True)
2980            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
2981            wx.Yield()
2982            self.ShowMatchingFiles(self.params['filter'])
2983            wx.Yield()
2984           
2985        def AutoComputePDF(imgId):
2986            '''Computes a PDF for a PWDR data tree tree item
2987            '''
2988            for pwdr in G2frame.AutointPWDRnames[:]:
2989                if not pwdr.startswith('PWDR '): continue
2990                if pwdr in G2frame.AutointPDFnames: continue
2991                PWid = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,pwdr)
2992                controlsDict = G2frame.PatternTree.GetItemPyData(
2993                    G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
2994                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
2995                    print('Skipping PDF for '+pwdr+' due to detector position')
2996                    continue
2997                # Setup PDF
2998                Data = G2frame.PatternTree.GetItemPyData(PWid)[1]
2999                pwdrMin = np.min(Data[1])
3000                Parms = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
3001                    G2frame,PWid,'Instrument Parameters'))[0]
3002                fullLimits = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
3003                    G2frame,PWid,'Limits'))[0]
3004                if 'C' in Parms['Type'][0]:
3005                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
3006                else:
3007                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
3008                Qlimits = [0.9*qMax,qMax]
3009
3010                item = pwdr
3011                Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
3012                    G2frame,imgId, 'Comments'))
3013                ElList = {}
3014                sumnum = 1.0
3015                for item in Comments:           #grab chemical formula from Comments, if there
3016                    if 'formula' in item[:15].lower():
3017                        formula = item.split('=')[1].split()
3018                        elems = formula[::2]
3019                        nums = formula[1::2]
3020                        formula = zip(elems,nums)
3021                        sumnum = 0.
3022                        for [elem,num] in formula:
3023                            ElData = G2elem.GetElInfo(elem,Parms)
3024                            ElData['FormulaNo'] = float(num)
3025                            sumnum += float(num)
3026                            ElList[elem] = ElData
3027                PDFnames = G2gd.GetPatternTreeDataNames(G2frame,['PDF ',])
3028                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits,sumnum,pwdrMin,PDFnames)
3029                if not PDFid: continue
3030                PDFdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
3031                    G2frame,PDFid, 'PDF Controls'))
3032                PDFdata.update(self.pdfControls)
3033                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
3034                PDFdata['Sample']['Name'] = pwdr
3035                # compute PDF
3036                wx.Yield()
3037                G2pdG.computePDF(G2frame,PDFdata)
3038                wx.Yield()
3039                G2frame.PatternId = PDFid
3040                G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3041                if self.params['optPDF']:
3042                    G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
3043                    wx.Yield()
3044                    G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3045                G2frame.AutointPDFnames.append(pwdr)
3046                # save names of PDF entry to be deleted later if needed
3047                G2frame.AutointPWDRnames.append(G2frame.PatternTree.GetItemText(PDFid))
3048           
3049        G2frame = self.G2frame
3050        try:
3051            self.currImageList = sorted(
3052                glob.glob(os.path.join(self.imagedir,self.params['filter'])))
3053            self.ShowMatchingFiles(self.params['filter'])
3054        except IndexError:
3055            self.currImageList = []
3056            return
3057
3058        if self.PreventReEntryTimer: return
3059        self.PreventReEntryTimer = True
3060        imageFileList = []
3061        # integrate the images that have already been read in, but
3062        # have not yet been processed           
3063        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
3064            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
3065            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
3066            # Create a list of image files that have been read in
3067            if imagefile not in imageFileList: imageFileList.append(imagefile)
3068            # skip if already integrated
3069            if img in G2frame.IntegratedList: continue
3070            AutoIntegrateImage(imgId)
3071            #self.Pause |= G2frame.PauseIntegration
3072            #if self.Pause:
3073            #    self.OnPause()
3074            #    self.PreventReEntryTimer = False
3075            #    return
3076            if self.pdfControls: AutoComputePDF(imgId)
3077            self.Pause |= G2frame.PauseIntegration
3078            if self.Pause:
3079                self.OnPause()
3080                self.PreventReEntryTimer = False
3081                return
3082
3083        # loop over image files matching glob, reading in any new ones
3084        for newImage in self.currImageList:
3085            if newImage in imageFileList or self.Pause: continue # already read?
3086            for imgId in G2IO.ReadImages(G2frame,newImage):
3087                AutoIntegrateImage(imgId)           
3088                #self.Pause |= G2frame.PauseIntegration
3089                #if self.Pause:
3090                #    self.OnPause()
3091                #    self.PreventReEntryTimer = False
3092                #    return
3093                if self.pdfControls: AutoComputePDF(imgId)
3094                self.Pause |= G2frame.PauseIntegration
3095                if self.Pause:
3096                    self.OnPause()
3097                    self.PreventReEntryTimer = False
3098                    return
3099        if GSASIIpath.GetConfigValue('debug'):
3100            import datetime
3101            print ("Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
3102        self.PreventReEntryTimer = False
3103
3104def DefineEvaluator(dlg):
3105    '''Creates a function that provides interpolated values for a given distance value
3106    '''
3107    def Evaluator(dist):
3108        '''Interpolate image parameters for a supplied distance value
3109
3110        :param float dist: distance to use for interpolation
3111        :returns: a list with 3 items:
3112
3113          * a dict with parameter values,
3114          * the closest imctrl and
3115          * the closest maskfile (or None)
3116        '''           
3117        x = np.array([float(i) for i in parms[0]])
3118        closest = abs(x-dist).argmin()
3119        D = {'setdist':dist}
3120        imctfile = IMfileList[closest]
3121        if parms[-1][closest].lower() != '(none)':
3122            maskfile = parms[-1][closest]
3123        else:
3124            maskfile = None
3125        for c in range(1,cols-1):
3126            lbl = ParmList[c]
3127            if lbl in nonInterpVars:
3128                if lbl in ['outChannels',]:
3129                    D[lbl] = int(float(parms[c][closest]))
3130                else:
3131                    D[lbl] = float(parms[c][closest])
3132            else:
3133                y = np.array([float(i) for i in parms[c]])
3134                D[lbl] = np.interp(dist,x,y)
3135        # full integration when angular range is 0
3136        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
3137        # conversion for paired values
3138        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
3139            r = a.split('_')[0]
3140            D[r] = [D[a],D[b]]
3141            if r in ['LRazimuth',]:
3142                D[r] = [int(D[a]),int(D[b])]
3143            del D[a]
3144            del D[b]
3145        return D,imctfile,maskfile
3146    # save local copies of values needed in Evaluator
3147    parms = dlg.ReadImageParmTable()
3148    IMfileList = dlg.IMfileList
3149    cols = dlg.list.GetColumnCount()
3150    ParmList = dlg.ParmList
3151    nonInterpVars = dlg.nonInterpVars
3152    return Evaluator
3153
3154class IntegParmTable(wx.Dialog):
3155    '''Creates a dialog window with a table of integration parameters.
3156    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
3157    In this case, :func:`DefineEvaluator` should be called to obtain a function that
3158    creates a dictionary with interpolated parameter values.
3159    '''
3160    ParmList = ('setdist','distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
3161            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
3162            'maskfile',
3163            )
3164    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
3165                     'outChannels')  # values in this list are taken from nearest rather than interpolated
3166    HeaderList = ('Set Dist','Calib Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
3167            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
3168            'Mask File',
3169            )
3170    def __init__(self,parent,parms=None,IMfileList=None):
3171        self.G2frame = parent.G2frame
3172        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
3173        if parms:
3174            self.parms = parms # list of values by column
3175            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
3176        else:
3177            self.parms = [] # list of values by column
3178            self.IMfileList = [] # list of .imctrl file names for each entry in table
3179            files = []
3180            try:
3181                pth = G2G.GetImportPath(self.G2frame)
3182                if not pth: pth = '.'
3183                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3184                    style=wx.OPEN| wx.MULTIPLE,
3185                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3186                dlg.CenterOnParent()
3187                if dlg.ShowModal() == wx.ID_OK:
3188                    files = dlg.GetPaths()
3189                    self.parms,self.IMfileList = self.ReadFiles(files)
3190            finally:
3191                dlg.Destroy()
3192            if not files:
3193                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3194                return
3195        mainSizer = wx.BoxSizer(wx.VERTICAL)
3196        self.list = ImgIntLstCtrl(self, wx.ID_ANY,
3197                      style=wx.LC_REPORT
3198                          | wx.BORDER_SUNKEN
3199                         #| wx.BORDER_NONE
3200                         )
3201        mainSizer.Add(self.list,1,wx.EXPAND,1)
3202        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3203        btn = wx.Button(self, wx.ID_OK)
3204        btnsizer.Add(btn)
3205        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3206        btn.Bind(wx.EVT_BUTTON,self._onSave)
3207        btnsizer.Add(btn)
3208        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3209        btn.Bind(wx.EVT_BUTTON,self._onClose)
3210        btnsizer.Add(btn)
3211        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3212        self.SetSizer(mainSizer)
3213        self.list.FillList(self.parms)
3214        mainSizer.Layout()
3215        mainSizer.Fit(self)
3216       
3217    def ReadFiles(self,files):
3218        '''Reads a list of .imctrl files or a single .imtbl file
3219        '''
3220        tmpDict = {}
3221        if not files: return
3222        # option 1, a dump from a previous save
3223        if os.path.splitext(files[0])[1] == '.imtbl':
3224            fp = open(files[0],'r')
3225            S = fp.readline()
3226            while S:
3227                if S[0] != '#':
3228                    [key,val] = S[:-1].split(':',1)
3229                    tmpDict[key] = eval(val)
3230                S = fp.readline()
3231            fp.close()
3232            # delete entries where files do not exist
3233            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3234            if m1:
3235                print('\nimctrl file not found:')
3236                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3237            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3238            if m2:
3239                print('\nmask file not found')
3240                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3241            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3242            if m3:
3243                print('\nDropping entries due to negative distance: '+str(m3))
3244            m = sorted(set(m1 + m2 + m3))
3245            m.reverse()
3246            for c in m:
3247                for key in tmpDict:
3248                    del tmpDict[key][c]
3249            fileList = tmpDict.get('filenames','[]')
3250            parms = []
3251            if 'setdist' not in tmpDict:
3252                print(u'Old file, recreate before using: {}'.format(files[0]))
3253                return [[]],[]
3254            for key in self.ParmList:
3255                try:
3256                    float(tmpDict[key][0])
3257                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3258                except ValueError:
3259                    parms.append(tmpDict[key])
3260                except IndexError:
3261                    print('No valid image control entries read')
3262                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3263                    return [[]],[]
3264            return parms,fileList
3265        # option 2, read in a list of files
3266        for file in files: # read all files; place in dict by distance
3267            imgDict = Read_imctrl(file)
3268            dist = imgDict.get('setdist')
3269            if dist is None:
3270                print('Skipping old file, redo: {}'.format(file))
3271            tmpDict[dist] = imgDict
3272        parms = [[] for key in self.ParmList]
3273        fileList = []
3274        for d in sorted(tmpDict):
3275            fileList.append(tmpDict[d].get('filename'))
3276            if d is None: continue
3277            if d < 0: continue
3278            for i,key in enumerate(self.ParmList):
3279                val = tmpDict[d].get(key)
3280                try:
3281                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3282                except:
3283                    val = str(val)
3284                parms[i].append(val)
3285        return parms,fileList
3286   
3287    def ReadImageParmTable(self):
3288        '''Reads possibly edited values from the ListCtrl table and returns a list
3289        of values for each column.
3290        '''
3291        rows = self.list.GetItemCount()
3292        cols = self.list.GetColumnCount()
3293        parms = []
3294        for c in range(cols):
3295            parms.append([])
3296            for r in range(rows):
3297                parms[c].append(self.list.GetItem(r,c).GetText())
3298        return parms
3299
3300    def _onClose(self,event):
3301        'Called when Cancel button is pressed'
3302        self.EndModal(wx.ID_CANCEL)
3303       
3304    def _onSave(self,event):
3305        'Called when save button is pressed; creates a .imtbl file'
3306        fil = ''
3307        if self.G2frame.GSASprojectfile:
3308            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3309        dir,f = os.path.split(fil)
3310        pth = G2G.GetExportPath(self.G2frame)
3311        try:
3312            dlg = wx.FileDialog(self, 'Save table data as',
3313                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3314                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3315            dlg.CenterOnParent()
3316            if dlg.ShowModal() != wx.ID_OK: return
3317            fil = dlg.GetPath()
3318            fil = os.path.splitext(fil)[0]+'.imtbl'
3319        finally:
3320            dlg.Destroy()       
3321        parms = self.ReadImageParmTable()
3322        print('Writing image parameter table as '+fil)
3323        fp = open(fil,'w')
3324        for c in range(len(parms)-1):
3325            lbl = self.ParmList[c]
3326            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3327        lbl = self.ParmList[c+1]
3328        fp.write(lbl+': '+str(parms[c+1])+'\n')
3329        lbl = 'filenames'
3330        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3331        fp.close()
3332   
3333class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3334    '''Creates a custom ListCtrl for editing Image Integration parameters
3335    '''
3336    def __init__(self, parent, ID, pos=wx.DefaultPosition,
3337                 size=(1000,200), style=0):
3338        self.parent=parent
3339        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3340        listmix.ListCtrlAutoWidthMixin.__init__(self)
3341        listmix.TextEditMixin.__init__(self)
3342        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3343        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3344    def FillList(self,parms):
3345        'Places the current parms into the table'
3346        self.ClearAll()
3347        self.rowlen = len(self.parent.ParmList)
3348        for i,lbl in enumerate(self.parent.HeaderList):
3349            self.InsertColumn(i, lbl)
3350        for r,d in enumerate(parms[0]):
3351            if d is None: continue
3352            if float(d) < 0: continue
3353            index = self.InsertStringItem(sys.maxint, d)
3354            for j in range(1,len(parms)):
3355                self.SetStringItem(index, j, parms[j][r])
3356        for i,lbl in enumerate(self.parent.ParmList):
3357            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3358
3359    def OnDouble(self,evt):
3360        'respond to a double-click'
3361        self.CloseEditor()
3362        fil = '(none)'
3363        pth = G2G.GetImportPath(self.parent.G2frame)
3364        if not pth: pth = '.'
3365        try:
3366            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3367                                style=wx.OPEN,
3368                                wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3369            dlg.CenterOnParent()
3370            if dlg.ShowModal() == wx.ID_OK:
3371                fil = dlg.GetPath()
3372        finally:
3373            dlg.Destroy()
3374        if os.path.splitext(fil)[1] != '.imctrl':
3375            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3376            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3377        else:
3378            # insert or overwrite an instrument parameter set
3379            if not os.path.exists(fil):
3380                print('Does not exist: '+fil)
3381                return
3382            imgDict = Read_imctrl(fil)
3383            dist = imgDict['distance']
3384            parms = self.parent.ReadImageParmTable()
3385            x = np.array([float(i) for i in parms[0]])
3386            closest = abs(x-dist).argmin()
3387            closeX = x[closest]
3388            # fix IMfileList
3389            for c,lbl in enumerate(self.parent.ParmList):
3390                try:
3391                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3392                except ValueError:
3393                    vali = imgDict[lbl]
3394                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3395                    parms[c][closest] = vali
3396                elif dist > closeX: # insert after
3397                    parms[c].insert(closest+1,vali)
3398                else:
3399                    parms[c].insert(closest,vali)
3400            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3401                self.parent.IMfileList[closest] = fil
3402            elif dist > closeX: # insert after
3403                self.parent.IMfileList.insert(closest+1,fil)
3404            else:
3405                self.parent.IMfileList.insert(closest,fil)
3406            self.FillList(parms)
3407# Autointegration end
3408###########################################################################
Note: See TracBrowser for help on using the repository browser.