source: trunk/GSASIIimgGUI.py @ 2696

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

use F d 3 m for Si space group in ImageCalibrants? - should eliminate forbidden lines
add new menu item in Image Controls/Parms? to do reset of dist to set dist

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