source: trunk/GSASIIimgGUI.py @ 2759

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

fix problem viewing LS parameters with constraints
fix problem accessing auto spot masks (NB: working on auto spot masks)

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