source: trunk/GSASIIimgGUI.py @ 2627

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

remove progress bar stuff from image Integrate
put progress bar around integrate all
put wx.BusyCursor? around individual integrations & autointegrate

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