source: trunk/GSASIIimgGUI.py @ 2611

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

Change PDF defaults; option for multiple element addition (added to PDF); replace old single-spot mask option with new; stop overwriting image limits on Copy Controls

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