source: trunk/GSASIIimgGUI.py @ 2610

Last change on this file since 2610 was 2610, checked in by toby, 5 years ago

fix bug on change in PDF files; save location when images are moved

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