source: trunk/GSASIIimgGUI.py @ 2626

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

fix dialog issues (again) in image integration

  • 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-12 17:56:41 +0000 (Thu, 12 Jan 2017) $
5# $Author: vondreele $
6# $Revision: 2626 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 2626 2017-01-12 17:56:41Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIimgGUI: Image GUI*
12-------------------------
13
14Control image display and processing
15
16'''
17import os
18import copy
19import glob
20import re
21import math
22import sys
23import wx
24import wx.lib.scrolledpanel as wxscroll
25import wx.lib.mixins.listctrl  as  listmix
26import matplotlib as mpl
27import numpy as np
28import GSASIIpath
29GSASIIpath.SetVersionNumber("$Revision: 2626 $")
30import GSASIIimage as G2img
31import GSASIImath as G2mth
32import GSASIIplot as G2plt
33import GSASIIIO as G2IO
34import GSASIIgrid as G2gd
35import GSASIIctrls as G2G
36import GSASIIpy3 as G2py3
37
38VERY_LIGHT_GREY = wx.Colour(235,235,235)
39WACV = wx.ALIGN_CENTER_VERTICAL
40
41# trig functions in degrees
42sind = lambda x: math.sin(x*math.pi/180.)
43tand = lambda x: math.tan(x*math.pi/180.)
44cosd = lambda x: math.cos(x*math.pi/180.)
45asind = lambda x: 180.*math.asin(x)/math.pi
46   
47################################################################################
48##### Image Data
49################################################################################
50
51def GetImageZ(G2frame,data,newRange=False):
52    '''Gets image & applies dark, background & flat background corrections
53    :param wx.Frame G2frame: main GSAS-II frame
54    param: dict data: Image Controls dictionary
55    return: array sumImg: corrected image for background/dark/flat back
56    '''
57   
58    Npix,imagefile,imagetag = G2IO.GetCheckImageFile(G2frame,G2frame.Image)
59    formatName = data.get('formatName','')
60    sumImg = G2IO.GetImageData(G2frame,imagefile,True,ImageTag=imagetag,FormatName=formatName)
61    if sumImg is None:
62        return []
63    if not 'dark image' in data:
64        return sumImg
65    darkImg,darkScale = data['dark image']
66    if darkImg:
67        Did = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, darkImg)
68        if Did:
69            Ddata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Did,'Image Controls'))
70            dformatName = Ddata.get('formatName','')
71            Npix,darkfile,imagetag = G2IO.GetCheckImageFile(G2frame,Did)
72            darkImage = G2IO.GetImageData(G2frame,darkfile,True,ImageTag=imagetag,FormatName=dformatName)
73            if darkImg is not None:               
74                sumImg += np.array(darkImage*darkScale,dtype='int32')
75    if not 'background image' in data:
76        return sumImg
77    backImg,backScale = data['background image']           
78    if backImg:     #ignores any transmission effect in the background image
79        Bid = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, backImg)
80        if Bid:
81            Npix,backfile,imagetag = G2IO.GetCheckImageFile(G2frame,Bid)
82            Bdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Bid,'Image Controls'))
83            bformatName = Bdata.get('formatName','')
84            backImage = G2IO.GetImageData(G2frame,backfile,True,ImageTag=imagetag,FormatName=bformatName)
85            if darkImg and backImage is not None:
86                Did = G2gd.GetPatternTreeItemId(G2frame, G2frame.root,darkImg)
87                if Did:
88                    Ddata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Did,'Image Controls'))
89                    dformatName = Ddata.get('formatName','')
90                    Npix,darkfile,imagetag = G2IO.GetCheckImageFile(G2frame,Did)
91                    darkImage = G2IO.GetImageData(G2frame,darkfile,True,ImageTag=imagetag,FormatName=dformatName)
92                    if darkImage is not None:
93                        backImage += np.array(darkImage*darkScale,dtype='int32')
94            if backImage is not None:
95                sumImg += np.array(backImage*backScale,dtype='int32')
96    if darkImg: del darkImg         #force cleanup
97    if backImg: del backImg
98    sumImg -= int(data.get('Flat Bkg',0))
99    Imax = np.max(sumImg)
100    if 'range' not in data or newRange:
101        data['range'] = [(0,Imax),[0,Imax]]
102    return sumImg
103
104def UpdateImageData(G2frame,data):
105   
106    def OnPixVal(invalid,value,tc):
107        G2plt.PlotExposedImage(G2frame,newPlot=True,event=tc.event)
108       
109    if G2frame.dataDisplay:
110        G2frame.dataDisplay.Destroy()
111    if not G2frame.dataFrame.GetStatusBar():
112        G2frame.dataFrame.CreateStatusBar()
113    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
114    G2frame.ImageZ = GetImageZ(G2frame,data)
115    mainSizer = wx.BoxSizer(wx.VERTICAL)
116    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,
117        label='Do not change anything here unless you are absolutely sure!'),0,WACV)
118    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Image size: %d by %d'%(data['size'][0],data['size'][1])),0,WACV)
119    pixSize = wx.FlexGridSizer(0,4,5,5)
120    pixLabels = [u' Pixel X-dimension (\xb5m)',u' Pixel Y-dimension (\xb5m)']
121    for i,[pixLabel,pix] in enumerate(zip(pixLabels,data['pixelSize'])):
122        pixSize.Add(wx.StaticText(G2frame.dataDisplay,label=pixLabel),0,WACV)
123        pixVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['pixelSize'],i,nDig=(10,3),
124            typeHint=float,OnLeave=OnPixVal)
125        pixSize.Add(pixVal,0,WACV)
126    mainSizer.Add(pixSize,0)
127   
128    mainSizer.Layout()   
129    G2frame.dataDisplay.SetSizer(mainSizer)
130    fitSize = mainSizer.Fit(G2frame.dataFrame)
131    G2frame.dataFrame.setSizePosLeft(fitSize)
132
133################################################################################
134##### Image Controls
135################################################################################                   
136def UpdateImageControls(G2frame,data,masks,IntegrateOnly=False):
137    '''Shows and handles the controls on the "Image Controls"
138    data tree entry
139    '''
140    import ImageCalibrants as calFile
141#patch
142    if 'Flat Bkg' not in data:
143        data['Flat Bkg'] = 0.0
144    if 'GonioAngles' not in data:
145        data['GonioAngles'] = [0.,0.,0.]
146    if 'DetDepth' not in data:
147        data['DetDepth'] = 0.
148    if 'SampleAbs' not in data:
149        data['SampleShape'] = 'Cylinder'
150        data['SampleAbs'] = [0.0,False]
151    if 'binType' not in data:
152        if 'PWDR' in data['type']:
153            data['binType'] = '2-theta'
154        elif 'SASD' in data['type']:
155            data['binType'] = 'log(q)'
156    if 'varyList' not in data:
157        data['varyList'] = {'dist':True,'det-X':True,'det-Y':True,'tilt':True,'phi':True,'dep':False,'wave':False}
158#end patch
159
160# Menu items
161           
162    def OnCalibrate(event):
163        G2frame.dataFrame.GetStatusBar().SetStatusText('Select > 4 points on 1st used ring; LB to pick, RB on point to delete else RB to finish')
164        G2frame.ifGetRing = True
165               
166    def OnRecalibrate(event):
167        G2img.ImageRecalibrate(G2frame,data,masks)
168        wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
169       
170    def OnRecalibAll(event):
171        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
172        dlg = G2G.G2MultiChoiceDialog(G2frame,'Image calibration controls','Select images to recalibrate:',Names)
173        try:
174            if dlg.ShowModal() == wx.ID_OK:
175                items = dlg.GetSelections()
176                G2frame.EnablePlot = False
177                for item in items:
178                    name = Names[item]
179                    print 'calibrating',name
180                    G2frame.Image = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
181                    CId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Image Controls')
182                    Data = G2frame.PatternTree.GetItemPyData(CId)
183                    G2frame.ImageZ = GetImageZ(G2frame,Data)
184                    Data['setRings'] = True
185                    Mid = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Masks')
186                    Masks = G2frame.PatternTree.GetItemPyData(Mid)
187                    G2img.ImageRecalibrate(G2frame,Data,Masks)
188        finally:
189            dlg.Destroy()
190        G2plt.PlotExposedImage(G2frame,event=None)
191        wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
192       
193    def OnClearCalib(event):
194        data['ring'] = []
195        data['rings'] = []
196        data['ellipses'] = []
197        G2plt.PlotExposedImage(G2frame,event=event)
198           
199    def ResetThresholds():
200        Imin = max(0.,np.min(G2frame.ImageZ))
201        Imax = np.max(G2frame.ImageZ)
202        data['range'] = [(0,Imax),[Imin,Imax]]
203        masks['Thresholds'] = [(0,Imax),[Imin,Imax]]
204        MaxSizer.GetChildren()[2].Window.SetValue(Imax)   #tricky
205        MaxSizer.GetChildren()[5].Window.SetValue(Imin)   #tricky
206         
207    def OnIntegrate(event):
208        '''Integrate image in response to a menu event or from the AutoIntegrate
209        dialog. In the latter case, event=None.
210        '''
211        CleanupMasks(masks)
212        blkSize = 128   #this seems to be optimal; will break in polymask if >1024
213        Nx,Ny = data['size']
214        nXBlks = (Nx-1)/blkSize+1
215        nYBlks = (Ny-1)/blkSize+1
216        Nup = nXBlks*nYBlks*3+1     #exact count expected so AUTO_HIDE works!
217        sumImg = GetImageZ(G2frame,data)
218        if IntegrateOnly:
219            dlg = 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            dlg = wx.ProgressDialog("Elapsed time","2D image integration",Nup,
223                style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE)
224        try:
225            G2frame.Integrate = G2img.ImageIntegrate(sumImg,data,masks,blkSize,dlg)           
226        finally:
227            dlg.Destroy()
228        G2frame.PauseIntegration = G2frame.Integrate[-1]
229        del sumImg  #force cleanup
230        Id = G2IO.SaveIntegration(G2frame,G2frame.PickId,data,(event is None))
231        G2frame.PatternId = Id
232        G2frame.PatternTree.SelectItem(Id)
233        G2frame.PatternTree.Expand(Id)
234        for item in G2frame.MakePDF: item.Enable(True)
235       
236    def OnIntegrateAll(event):
237        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
238        dlg = G2G.G2MultiChoiceDialog(G2frame,'Image integration controls','Select images to integrate:',Names)
239        try:
240            if dlg.ShowModal() == wx.ID_OK:
241                items = dlg.GetSelections()
242                G2frame.EnablePlot = False
243                for item in items:
244                    name = Names[item]
245                    G2frame.Image = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
246                    CId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Image Controls')
247                    Data = G2frame.PatternTree.GetItemPyData(CId)
248                    blkSize = 128   #this seems to be optimal; will break in polymask if >1024
249                    Nx,Ny = Data['size']
250                    nXBlks = (Nx-1)/blkSize+1
251                    nYBlks = (Ny-1)/blkSize+1
252                    Nup = nXBlks*nYBlks*3+3
253                    image = GetImageZ(G2frame,Data)
254                    Masks = G2frame.PatternTree.GetItemPyData(
255                        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Masks'))
256                    image = GetImageZ(G2frame,Data)
257                    dlg = wx.ProgressDialog("Elapsed time","2D image integration",Nup,
258                        style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
259                    try:
260                        G2frame.Integrate = G2img.ImageIntegrate(image,Data,Masks,blkSize,dlg)
261                    finally:
262                        dlg.Destroy()
263                    del image   #force cleanup
264                    pId = G2IO.SaveIntegration(G2frame,CId,Data)
265                    if G2frame.Integrate[-1]:       #Cancel from progress bar?
266                        break
267                else:
268                    G2frame.EnablePlot = True
269                    G2frame.PatternTree.SelectItem(pId)
270                    G2frame.PatternTree.Expand(pId)
271                    G2frame.PatternId = pId
272        finally:
273            dlg.Destroy()
274       
275    def OnCopyControls(event):
276        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
277        if len(Names) == 1:
278            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
279            return
280        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
281        Names.pop(Names.index(Source))
282# select targets & do copy
283        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy image controls','Copy controls from '+Source+' to:',Names)
284        try:
285            if dlg.ShowModal() == wx.ID_OK:
286                items = dlg.GetSelections()
287                G2frame.EnablePlot = False
288                for item in items:
289                    name = Names[item]
290                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
291                    CId = G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls')
292                    oldData = copy.deepcopy(G2frame.PatternTree.GetItemPyData(CId))
293                    Data = copy.deepcopy(data)
294                    Data['range'][0] = oldData['range'][0]
295                    Data['size'] = oldData['size']
296                    Data['GonioAngles'] = oldData.get('GonioAngles', [0.,0.,0.])
297                    Data['ring'] = []
298                    Data['rings'] = []
299                    Data['ellipses'] = []
300                    if name == Data['dark image'][0]:
301                        Data['dark image'] = ['',-1.]
302                    if name == Data['background image'][0]:
303                        Data['background image'] = ['',-1.]
304                    G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Image Controls'),Data)
305        finally:
306            dlg.Destroy()
307            G2frame.PatternTree.SelectItem(G2frame.PickId)
308           
309    def OnCopySelected(event):
310        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
311        if len(Names) == 1:
312            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
313            return
314        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
315        # Assemble a list of item labels
316        keyList = ['type','wavelength','calibrant','distance','center',
317                    'tilt','rotation','azmthOff','fullIntegrate','LRazimuth',
318                    'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
319                    'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList',
320                    'binType','SampleShape','PolaVal','SampleAbs','dark image','background image']
321        keyText = [i+'='+str(data[i]) for i in keyList]
322        # sort both lists together, ordered by keyText
323        selectedKeys = []
324        dlg = G2G.G2MultiChoiceDialog(
325            G2frame.dataFrame,
326            'Select which image controls\nto copy',
327            'Select image controls', keyText)
328        try:
329            if dlg.ShowModal() == wx.ID_OK:
330                selectedKeys = [keyList[i] for i in dlg.GetSelections()]
331        finally:
332            dlg.Destroy()
333        if not selectedKeys: return # nothing to copy
334        copyDict = {}
335        for parm in selectedKeys:
336            copyDict[parm] = data[parm]
337        dlg = G2G.G2MultiChoiceDialog(
338            G2frame.dataFrame,
339            'Copy image controls from\n'+Source+' to...',
340            'Copy image controls', Names)
341        try:
342            if dlg.ShowModal() == wx.ID_OK:
343                result = dlg.GetSelections()
344                for i in result: 
345                    item = Names[i]
346                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,item)
347                    Controls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
348                    Controls.update(copy.deepcopy(copyDict))
349        finally:
350            dlg.Destroy()           
351               
352    def OnSaveControls(event):
353        pth = G2G.GetExportPath(G2frame)
354        dlg = wx.FileDialog(G2frame, 'Choose image controls file', pth, '', 
355            'image control files (*.imctrl)|*.imctrl',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
356        try:
357            if dlg.ShowModal() == wx.ID_OK:
358                filename = dlg.GetPath()
359                # make sure extension is .imctrl
360                filename = os.path.splitext(filename)[0]+'.imctrl'
361                WriteControls(filename,data)
362        finally:
363            dlg.Destroy()
364
365    def WriteControls(filename,data):
366        File = open(filename,'w')
367        keys = ['type','wavelength','calibrant','distance','center',
368            'tilt','rotation','azmthOff','fullIntegrate','LRazimuth',
369            'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
370            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList',
371            'binType','SampleShape','PolaVal','SampleAbs','dark image','background image']
372        for key in keys:
373            if key not in data:     #uncalibrated!
374                continue
375            File.write(key+':'+str(data[key])+'\n')
376        File.close()
377       
378    def OnSaveMultiControls(event):
379        '''Save controls from multiple images
380        '''
381        imglist = []
382        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
383        while item:
384            name = G2frame.PatternTree.GetItemText(item)
385            if name.startswith('IMG '): 
386                imglist.append(name)               
387            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
388        if not imglist:
389            print('No images!')
390            return
391        dlg = G2G.G2MultiChoiceDialog(G2frame, 'Which images to select?',
392                                      'Select images', imglist, wx.CHOICEDLG_STYLE)
393        try:
394            if dlg.ShowModal() == wx.ID_OK:
395                treeEntries = [imglist[i] for i in dlg.GetSelections()]
396        finally:
397            dlg.Destroy()
398        if not treeEntries:
399            print('No images selected!')
400            return
401        pth = G2G.GetExportPath(G2frame)
402        dlg = wx.DirDialog(
403            G2frame, 'Select directory for output files',pth,wx.DD_DEFAULT_STYLE)
404        dlg.CenterOnParent()
405        outdir = None
406        try:
407            if dlg.ShowModal() == wx.ID_OK:
408                outdir = dlg.GetPath()
409        finally:
410            dlg.Destroy()
411        if not outdir:
412            print('No directory')
413            return
414        for img in treeEntries:
415            item = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
416            data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
417                G2frame,item,'Image Controls'))
418            Npix,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(item)
419            filename = os.path.join(outdir,
420                                    os.path.splitext(os.path.split(imagefile)[1])[0]
421                                    + '.imctrl')
422            print('writing '+filename)
423            WriteControls(filename,data)
424           
425    def OnLoadControls(event):
426        cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
427            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
428            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList',
429            'PolaVal','SampleAbs','dark image','background image']
430        pth = G2G.GetImportPath(G2frame)
431        if not pth: pth = '.'
432        dlg = wx.FileDialog(G2frame, 'Choose image controls file', pth, '', 
433            'image control files (*.imctrl)|*.imctrl',wx.OPEN)
434        try:
435            if dlg.ShowModal() == wx.ID_OK:
436                filename = dlg.GetPath()
437                File = open(filename,'r')
438                save = {}
439                S = File.readline()
440                while S:
441                    if S[0] == '#':
442                        S = File.readline()
443                        continue
444                    [key,val] = S.strip().split(':',1)
445                    if key in ['type','calibrant','binType','SampleShape',]:    #strings
446                        save[key] = val
447                    elif key in ['varyList',]:
448                        save[key] = eval(val)   #dictionary
449                    elif key in ['rotation']:
450                        save[key] = float(val)
451                    elif key in ['center',]:
452                        if ',' in val:
453                            save[key] = eval(val)
454                        else:
455                            vals = val.strip('[] ').split()
456                            save[key] = [float(vals[0]),float(vals[1])] 
457                    elif key in cntlList:
458                        save[key] = eval(val)
459                    S = File.readline()
460                data.update(save)
461                # next line removed. Previous updates tree contents. The next
462                # makes a copy of data, puts it into tree and "disconnects" data
463                # from tree contents (later changes to data are lost!)
464                #G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'),copy.deepcopy(data))
465                File.close()
466        finally:
467            dlg.Destroy()
468        G2frame.ImageZ = GetImageZ(G2frame,data)
469        ResetThresholds()
470        G2plt.PlotExposedImage(G2frame,event=event)
471        wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
472       
473    def OnTransferAngles(event):
474        '''Sets the integration range for the selected Images based on the difference in detector distance
475        '''
476        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
477        if len(Names) == 1:
478            G2frame.ErrorDialog('Nothing images to transfer integration angles to','Need more "IMG"s')
479            return
480        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
481        Names.pop(Names.index(Source))
482# select targets & do copy
483        dlg = G2G.G2MultiChoiceDialog(G2frame,'Xfer angles','Transfer integration range from '+Source+' to:',Names)
484        try:
485            if dlg.ShowModal() == wx.ID_OK:
486                xferAng = lambda tth,dist1,dist2: asind(dist1 * sind(tth) / dist2)
487                items = dlg.GetSelections()
488                G2frame.EnablePlot = False
489                Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,Source)
490                data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
491                ttmin0,ttmax0 = data['IOtth']
492                dist0 = data['distance']
493                print 'distance =',dist0,' integration range: ',data['IOtth']
494                for item in items:
495                    name = Names[item]
496                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
497                    data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
498                    dist1 = data['distance']
499                    data['IOtth'] = [xferAng(ttmin0,dist0,dist1),xferAng(ttmax0,dist0,dist1)]
500                    print 'distance =',dist1,' integration range: ',data['IOtth']
501        finally:
502            dlg.Destroy()
503            G2frame.PatternTree.SelectItem(G2frame.PickId)       
504           
505# Sizers
506    Indx = {}                                   
507    def ComboSizer():
508
509        def OnDataType(event):
510            data['type'] = typeSel.GetValue()[:4]
511            if 'SASD' in data['type']:
512                data['SampleAbs'][0] = np.exp(-data['SampleAbs'][0]) #switch from muT to trans!
513                if data['binType'] == '2-theta': data['binType'] = 'log(q)'  #switch default bin type
514            elif 'PWDR' in data['type']:
515                data['SampleAbs'][0] = -np.log(data['SampleAbs'][0])  #switch from trans to muT!
516                if data['binType'] == 'log(q)': data['binType'] = '2-theta'  #switch default bin type                 
517            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
518   
519        def OnNewColorBar(event):
520            data['color'] = colSel.GetValue()
521            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
522       
523        def OnAzmthOff(invalid,value,tc):
524            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
525       
526        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
527        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Type of image data: '),0,WACV)
528        typeSel = wx.ComboBox(parent=G2frame.dataDisplay,value=typeDict[data['type']],choices=typeList,
529            style=wx.CB_READONLY|wx.CB_DROPDOWN)
530        typeSel.SetValue(data['type'])
531        typeSel.Bind(wx.EVT_COMBOBOX, OnDataType)
532        comboSizer.Add(typeSel,0,WACV)
533        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Color bar '),0,WACV)
534        colSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['color'],choices=colorList,
535            style=wx.CB_READONLY|wx.CB_DROPDOWN)
536        colSel.Bind(wx.EVT_COMBOBOX, OnNewColorBar)
537        comboSizer.Add(colSel,0,WACV)
538        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Azimuth offset '),0,WACV)
539        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),
540            typeHint=float,OnLeave=OnAzmthOff)
541        comboSizer.Add(azmthOff,0,WACV)
542        return comboSizer
543       
544    def MaxSizer():
545        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
546        for the image, as well as callback routines.
547        '''
548        def OnNewVal(invalid,value,tc):
549            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
550            the value into the data['range'] nested list).
551            This adjusts the slider positions to match the current values
552            '''
553            scaleSel.SetSelection(len(scaleChoices)-1)
554            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
555            if r11 != Range[1][1]:
556                Range[1][1] = r11
557                maxVal.SetValue(int(Range[1][1]))
558            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
559            if r10 != Range[1][0]:
560                Range[1][0] = r10
561                minVal.SetValue(int(Range[1][0]))
562            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
563            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
564            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
565            maxSel.SetValue(sv1)
566            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
567            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
568            minSel.SetValue(sv0)
569            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
570            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
571            if mplOld:
572                Page.canvas.draw()
573            else:
574                Page.canvas.draw_idle()
575           
576        G2frame.prevMaxValue = None   
577        def OnMaxSlider(event):
578            val = maxSel.GetValue()
579            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
580            scaleSel.SetSelection(len(scaleChoices)-1)
581            G2frame.prevMaxValue = val
582            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
583            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
584            maxVal.SetValue(int(0.5+Range[1][1]))
585            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
586            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
587            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
588            minSel.SetValue(sv0)
589            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
590            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
591            if mplOld:
592                Page.canvas.draw()
593            else:
594                Page.canvas.draw_idle()
595           
596        G2frame.prevMinValue = None   
597        def OnMinSlider(event):
598            val = minSel.GetValue()
599            scaleSel.SetSelection(len(scaleChoices)-1)
600            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
601            G2frame.prevMinValue = val
602            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
603            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
604            minVal.SetValue(int(Range[1][0]))
605            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
606            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
607            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
608            maxSel.SetValue(sv1)
609            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
610            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
611            if mplOld:
612                Page.canvas.draw()
613            else:
614                Page.canvas.draw_idle()
615           
616        def OnAutoSet(event):
617            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
618            for the image so that 95% (etc.) of pixels are inside the color map limits.
619            An equal number of pixels are dropped at the minimum and maximum levels.
620            '''
621            try:
622                val = int(event.GetEventObject().GetStringSelection()[:-1])
623                margin = (100-val)/2.
624            except:
625                margin = 0
626                event.GetEventObject().SetSelection(0)
627            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
628            if margin == 0:
629                Range[1] = list(Range[0])
630            else:
631                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
632                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
633            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
634            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
635            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
636            maxSel.SetValue(sv1)
637            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
638            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
639            minSel.SetValue(sv0)
640            minVal.SetValue(int(Range[1][0]))
641            maxVal.SetValue(int(Range[1][1]))
642            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
643            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
644            if mplOld:
645                Page.canvas.draw()
646            else:
647                Page.canvas.draw_idle()
648
649        mplv = mpl.__version__.split('.')
650        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
651        # Plot color scaling uses limits as below:
652        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
653        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
654        maxSizer = wx.GridBagSizer(0,0)
655        r = c = 0
656        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Max intensity'),(r,c))
657        c += 1
658        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
659        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
660        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
661        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
662        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
663        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
664        maxSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv1,size=(300,-1))
665        maxSizer.Add(maxSel,(r,c),flag=wx.EXPAND)
666        maxSizer.AddGrowableCol(c)
667        c += 1
668        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
669        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],1,min=Range[0][0]+1,
670            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
671        maxSizer.Add(maxVal,(r,c))
672        c += 1
673        scaleChoices = ("100%","99%","95%","90%","80%","?")
674        scaleSel = wx.Choice(G2frame.dataDisplay,choices=scaleChoices,size=(-1,-1))
675        if (Range[1][0] == Range[0][0] and
676            Range[1][1] == Range[0][1]):
677            scaleSel.SetSelection(0)
678        else:
679            scaleSel.SetSelection(len(scaleChoices)-1)
680        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
681        maxSizer.Add(scaleSel,(r,c),(2,1),flag=wx.ALIGN_CENTER)
682        c = 0
683        r = 1
684        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min intensity'),(r,c))
685        c += 1
686        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
687        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
688        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
689        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
690        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
691        minSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv0,size=(300,-1))
692        maxSizer.Add(minSel,(r,c),flag=wx.EXPAND|wx.ALL)
693        c += 1
694        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
695        minVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],0,
696            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
697        maxSizer.Add(minVal,(r,c))
698        return maxSizer
699       
700    def CalibCoeffSizer():
701       
702        def OnCalRef(event):
703            Obj = event.GetEventObject()
704            name = Indx[Obj]
705            data['varyList'][name] = Obj.GetValue()
706           
707        calibSizer = wx.FlexGridSizer(0,2,5,5)
708        calibSizer.SetFlexibleDirection(wx.HORIZONTAL)
709        calibSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calibration coefficients'),0,WACV)   
710        calibSizer.Add((5,0),0)
711        Names = ['det-X','det-Y','wave','dist','tilt','phi']
712        if 'PWDR' in data['type']:
713            Names.append('dep') 
714        Parms = {'dist':['Distance',(10,3),data,'distance'],'det-X':['Beam center X',(10,3),data['center'],0],
715            'det-Y':['Beam center Y',(10,3),data['center'],1],'tilt':['Tilt angle',(10,3),data,'tilt'],
716            'phi':['Tilt rotation',(10,2),data,'rotation'],'dep':['Penetration',(10,2),data,'DetDepth'],
717            'wave':['Wavelength',(10,6),data,'wavelength']}
718        for name in Names:
719            calSel = wx.CheckBox(parent=G2frame.dataDisplay,label=Parms[name][0])
720            calibSizer.Add(calSel,0,WACV)
721            calSel.Bind(wx.EVT_CHECKBOX, OnCalRef)
722            calSel.SetValue(data['varyList'][name])
723            Indx[calSel] = name
724            if name == 'wave':
725                calVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Parms[name][2],
726                    Parms[name][3],min=0.01,max=10.,nDig=Parms[name][1],typeHint=float)
727            else:
728                calVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Parms[name][2],
729                    Parms[name][3],nDig=Parms[name][1],typeHint=float)
730            calibSizer.Add(calVal,0,WACV)
731        return calibSizer
732   
733    def IntegrateSizer():
734       
735        def OnNewBinType(event):
736            data['binType'] = binSel.GetValue()
737            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
738       
739        def OnIOtth(invalid,value,tc):
740            Ltth = float(G2frame.InnerTth.GetValue())
741            Utth = float(G2frame.OuterTth.GetValue())
742            if Ltth > Utth:
743                Ltth,Utth = Utth,Ltth
744            if 'Q' in data['binType']:
745                data['IOtth'] = [2.*asind(Ltth*wave/(4.*math.pi)),2.*asind(Utth*wave/(4.*math.pi))]
746            else:
747                data['IOtth'] = [Ltth,Utth]
748            G2frame.InnerTth.SetValue(Ltth)
749            G2frame.OuterTth.SetValue(Utth)
750            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
751       
752        def OnLRazim(invalid,value,tc):
753            Lazm = float(G2frame.Lazim.GetValue())%360.
754            Razm = float(G2frame.Razim.GetValue())%360.
755            if Lazm > Razm:
756                Razm += 360.
757            if data['fullIntegrate']:
758                Razm = Lazm+360.
759            G2frame.Lazim.SetValue(Lazm)
760            G2frame.Razim.SetValue(Razm)
761            data['LRazimuth'] = [Lazm,Razm]
762            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
763               
764        def OnNumOutAzms(invalid,value,tc):
765            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
766       
767        def OnOblique(event):
768            data['Oblique'][1] = not data['Oblique'][1]
769               
770        def OnSampleShape(event):
771            data['SampleShape'] = samShape.GetValue()
772            if 'Cylind' in data['SampleShape']:
773                data['SampleAbs'][0] = 0.0
774            elif 'Fixed' in data['SampleShape']:
775                data['SampleAbs'][0] = 1.0
776            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
777                           
778        def OnSamAbs(event):
779            data['SampleAbs'][1] = not data['SampleAbs'][1]
780                           
781        def OnShowLines(event):
782            data['showLines'] = not data['showLines']
783            G2plt.PlotExposedImage(G2frame,event=event)
784           
785        def OnFullIntegrate(event):
786            Lazm = float(G2frame.Lazim.GetValue())
787            if data['fullIntegrate']:
788                data['fullIntegrate'] = False
789                data['LRazimuth'] = [Lazm,Lazm+20.]
790            else:
791                data['fullIntegrate'] = True
792                data['LRazimuth'] = [Lazm,Lazm+360.]
793            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
794            G2plt.PlotExposedImage(G2frame,event=event)
795           
796        def OnSetDefault(event):
797            if data['setDefault']:
798                G2frame.imageDefault = {}
799                data['setDefault'] = False
800            else:
801                G2frame.imageDefault = copy.copy(data)
802                data['setDefault'] = True
803               
804        def OnCenterAzm(event):
805            data['centerAzm'] = not data['centerAzm']
806            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
807               
808        def OnApplyPola(event):
809            data['PolaVal'][1] = not data['PolaVal'][1]
810               
811        dataSizer = wx.FlexGridSizer(0,2,5,3)
812        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Integration coefficients'),0,WACV)   
813        dataSizer.Add((5,0),0)
814        if 'PWDR' in data['type']:
815            binChoice = ['2-theta','Q']
816        elif 'SASD' in data['type']:
817            binChoice = ['2-theta','Q','log(q)']
818        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Bin style: Constant step bins in'),0,WACV)           
819        binSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['binType'],choices=binChoice,
820            style=wx.CB_READONLY|wx.CB_DROPDOWN)
821        binSel.Bind(wx.EVT_COMBOBOX, OnNewBinType)
822        dataSizer.Add(binSel,0,WACV)
823        binType = '2-theta'
824        if 'q' in data['binType'].lower():
825            binType = 'Q'
826        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Inner/Outer '+binType),0,WACV)           
827        IOtth = data['IOtth'][:]
828        if 'Q' in data['binType']:
829            wave = data['wavelength']
830            IOtth = [4.*math.pi*sind(IOtth[0]/2.)/wave,4.*math.pi*sind(IOtth[1]/2.)/wave]
831        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
832        G2frame.InnerTth = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,IOtth,0,nDig=(8,3,'f'),min=0.001,typeHint=float,OnLeave=OnIOtth)
833        littleSizer.Add(G2frame.InnerTth,0,WACV)
834        G2frame.OuterTth = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,IOtth,1,nDig=(8,3,'f'),min=0.001,typeHint=float,OnLeave=OnIOtth)
835        littleSizer.Add(G2frame.OuterTth,0,WACV)
836        dataSizer.Add(littleSizer,0,)
837        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Start/End azimuth'),0,WACV)
838        LRazim = data['LRazimuth']
839        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
840        G2frame.Lazim = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,LRazim,0,nDig=(6,1,'f'),typeHint=float,OnLeave=OnLRazim)
841        littleSizer.Add(G2frame.Lazim,0,WACV)
842        G2frame.Razim = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,LRazim,1,nDig=(6,1,'f'),typeHint=float,OnLeave=OnLRazim)
843        if data['fullIntegrate']:
844            G2frame.Razim.Enable(False)
845            G2frame.Razim.SetBackgroundColour(VERY_LIGHT_GREY)
846            G2frame.Razim.SetValue(LRazim[0]+360.)
847        littleSizer.Add(G2frame.Razim,0,WACV)
848        dataSizer.Add(littleSizer,0,)
849        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' No. 2-theta/azimuth bins'),0,WACV)
850        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
851        outChan = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'outChannels',typeHint=int,min=10)
852        littleSizer.Add(outChan,0,WACV)
853        outAzim = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'outAzimuths',nDig=(10,4),min=1,typeHint=int,OnLeave=OnNumOutAzms)
854        littleSizer.Add(outAzim,0,WACV)
855        dataSizer.Add(littleSizer,0,)
856        samplechoice = ['Cylinder','Fixed flat plate',]
857        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label='Select sample shape'),0,WACV)
858        samShape = wx.ComboBox(G2frame.dataDisplay,value=data['SampleShape'],choices=samplechoice,
859            style=wx.CB_READONLY|wx.CB_DROPDOWN)
860        samShape.Bind(wx.EVT_COMBOBOX,OnSampleShape)
861        dataSizer.Add(samShape,0,WACV)
862        #SampleShape - cylinder or flat plate choice?
863        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
864        samabs = wx.CheckBox(parent=G2frame.dataDisplay,label='Apply sample absorption?')
865        dataSizer.Add(samabs,0,WACV)
866        samabs.Bind(wx.EVT_CHECKBOX, OnSamAbs)
867        samabs.SetValue(data['SampleAbs'][1])
868        minmax = [0.,2.]
869        if 'Cylind' in data['SampleShape']: #cylinder mu*R; flat plate transmission
870            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='mu*R (0.00-2.0) '),0,WACV)
871        elif 'Fixed' in data['SampleShape']:
872            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='transmission '),0,WACV) #for flat plate
873            minmax = [.05,1.0]
874        samabsVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['SampleAbs'],0,nDig=(10,3),
875            typeHint=float,min=minmax[0],max=minmax[1])
876        littleSizer.Add(samabsVal,0,WACV)
877        dataSizer.Add(littleSizer,0,)       
878        if 'PWDR' in data['type']:
879            littleSizer = wx.BoxSizer(wx.HORIZONTAL)
880            oblique = wx.CheckBox(parent=G2frame.dataDisplay,label='Apply detector absorption?')
881            dataSizer.Add(oblique,0,WACV)
882            oblique.Bind(wx.EVT_CHECKBOX, OnOblique)
883            oblique.SetValue(data['Oblique'][1])
884            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='Value (0.01-0.99)  '),0,WACV)
885            obliqVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['Oblique'],0,nDig=(10,3),typeHint=float,min=0.01,max=0.99)
886            littleSizer.Add(obliqVal,0,WACV)
887            dataSizer.Add(littleSizer,0,)
888        if 'SASD' in data['type']:
889            littleSizer = wx.BoxSizer(wx.HORIZONTAL)
890            setPolariz = wx.CheckBox(parent=G2frame.dataDisplay,label='Apply polarization?')
891            dataSizer.Add(setPolariz,0,WACV)
892            setPolariz.Bind(wx.EVT_CHECKBOX, OnApplyPola)
893            setPolariz.SetValue(data['PolaVal'][1])
894            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='Value (0.001-0.999)  '),0,WACV)
895            polaVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['PolaVal'],0,nDig=(10,3),typeHint=float,min=0.001,max=0.999)
896            littleSizer.Add(polaVal,0,WACV)
897            dataSizer.Add(littleSizer,0,)
898       
899        showLines = wx.CheckBox(parent=G2frame.dataDisplay,label='Show integration limits?')
900        dataSizer.Add(showLines,0,WACV)
901        showLines.Bind(wx.EVT_CHECKBOX, OnShowLines)
902        showLines.SetValue(data['showLines'])
903        fullIntegrate = wx.CheckBox(parent=G2frame.dataDisplay,label='Do full integration?')
904        dataSizer.Add(fullIntegrate,0,WACV)
905        fullIntegrate.Bind(wx.EVT_CHECKBOX, OnFullIntegrate)
906        fullIntegrate.SetValue(data['fullIntegrate'])
907        setDefault = wx.CheckBox(parent=G2frame.dataDisplay,label='Use for all new images?')
908        dataSizer.Add(setDefault,0,WACV)
909        setDefault.Bind(wx.EVT_CHECKBOX, OnSetDefault)
910        setDefault.SetValue(data['setDefault'])
911        centerAzm = wx.CheckBox(parent=G2frame.dataDisplay,label='Azimuth at bin center?')
912        dataSizer.Add(centerAzm,0,WACV)
913        centerAzm.Bind(wx.EVT_CHECKBOX, OnCenterAzm)
914        centerAzm.SetValue(data['centerAzm'])
915        return dataSizer
916       
917    def BackSizer():
918       
919        global oldFlat
920        def OnBackImage(event):
921            data['background image'][0] = backImage.GetValue()
922            G2frame.ImageZ = GetImageZ(G2frame,data)
923            ResetThresholds()
924            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
925           
926        def OnDarkImage(event):
927            data['dark image'][0] = darkImage.GetValue()
928            G2frame.ImageZ = GetImageZ(G2frame,data)
929            ResetThresholds()
930            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
931           
932        def OnFlatBkg(invalid,value,tc):
933            global oldFlat
934            G2frame.ImageZ += int(oldFlat-data['Flat Bkg'])
935            oldFlat = data['Flat Bkg']
936            ResetThresholds()
937            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
938
939        def OnMult(invalid,value,tc):
940            G2frame.ImageZ = GetImageZ(G2frame,data)
941            ResetThresholds()
942            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
943       
944        backSizer = wx.FlexGridSizer(0,6,5,5)
945        oldFlat = data.get('Flat Bkg',0.)
946
947        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Dark image'),0,WACV)
948        Choices = ['',]+G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
949        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
950        Choices.pop(Choices.index(Source))
951        darkImage = wx.ComboBox(parent=G2frame.dataDisplay,value=data['dark image'][0],choices=Choices,
952            style=wx.CB_READONLY|wx.CB_DROPDOWN)
953        darkImage.Bind(wx.EVT_COMBOBOX,OnDarkImage)
954        backSizer.Add(darkImage)
955        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' multiplier'),0,WACV)
956        darkMult = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['dark image'],1,nDig=(10,3),
957            typeHint=float,OnLeave=OnMult)
958        backSizer.Add(darkMult,0,WACV)
959        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Flat Bkg: '),0,WACV)
960        flatbkg = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Flat Bkg',nDig=(10,0),
961            typeHint=float,OnLeave=OnFlatBkg)
962        backSizer.Add(flatbkg,0,WACV)
963
964        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Background image'),0,WACV)
965        backImage = wx.ComboBox(parent=G2frame.dataDisplay,value=data['background image'][0],choices=Choices,
966            style=wx.CB_READONLY|wx.CB_DROPDOWN)
967        backImage.Bind(wx.EVT_COMBOBOX,OnBackImage)
968        backSizer.Add(backImage)
969        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' multiplier'),0,WACV)
970        backMult = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['background image'],1,nDig=(10,3),
971            typeHint=float,OnLeave=OnMult)
972        backSizer.Add(backMult,0,WACV)
973        return backSizer
974                       
975    def CalibSizer():
976               
977        def OnNewCalibrant(event):
978            data['calibrant'] = calSel.GetValue().strip()
979            if data['calibrant']:
980                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBRATE,enable=True)
981                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMCALIBRATE,enable=True)
982                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBALL,enable=True)
983                data['calibskip'] = calFile.Calibrants[data['calibrant']][3]
984                limits = calFile.Calibrants[data['calibrant']][4]
985                data['calibdmin'],data['pixLimit'],data['cutoff'] = limits
986                pixLimit.SetValue(str(limits[1]))
987                cutOff.SetValue(limits[2])
988                calibSkip.SetValue(str(data['calibskip']))
989                G2frame.calibDmin.SetValue(limits[0])
990            else:
991                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBRATE,enable=False)
992                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMCALIBRATE,enable=False)
993                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBALL,enable=False)
994               
995        def OnCalibSkip(event):
996            data['calibskip'] = int(calibSkip.GetValue())
997           
998        def OnPixLimit(event):
999            data['pixLimit'] = int(pixLimit.GetValue())
1000           
1001        def OnSetRings(event):
1002            data['setRings'] = not data['setRings']
1003            G2plt.PlotExposedImage(G2frame,event=event)
1004   
1005        calibSizer = wx.FlexGridSizer(0,3,5,5)
1006        comboSizer = wx.BoxSizer(wx.HORIZONTAL)   
1007        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calibrant '),0,WACV)
1008        calSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['calibrant'],choices=calList,
1009            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1010        calSel.Bind(wx.EVT_COMBOBOX, OnNewCalibrant)
1011        comboSizer.Add(calSel,0,WACV)
1012        calibSizer.Add(comboSizer,0)
1013       
1014        comboSizer = wx.BoxSizer(wx.HORIZONTAL)   
1015        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calib lines to skip   '),0,WACV)
1016        calibSkip  = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['calibskip']),choices=[str(i) for i in range(25)],
1017            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1018        calibSkip.Bind(wx.EVT_COMBOBOX, OnCalibSkip)
1019        comboSizer.Add(calibSkip,0,WACV)
1020        calibSizer.Add(comboSizer,0)
1021       
1022        comboSizer = wx.BoxSizer(wx.HORIZONTAL)       
1023        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min calib d-spacing '),0,WACV)
1024        G2frame.calibDmin = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'calibdmin',nDig=(10,2),typeHint=float,min=0.25)
1025        comboSizer.Add(G2frame.calibDmin,0,WACV)
1026        calibSizer.Add(comboSizer,0)
1027       
1028        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1029        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min ring I/Ib '),0,WACV)
1030        cutOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'cutoff',nDig=(10,1),typeHint=float,min=0.1)
1031        comboSizer.Add(cutOff,0,WACV)
1032        calibSizer.Add(comboSizer,0)
1033       
1034        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1035        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Pixel search range '),0,WACV)
1036        pixLimit = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['pixLimit']),choices=['1','2','5','10','15','20'],
1037            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1038        pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
1039        comboSizer.Add(pixLimit,0,WACV)
1040        calibSizer.Add(comboSizer,0)
1041       
1042        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1043        setRings = wx.CheckBox(parent=G2frame.dataDisplay,label='Show ring picks?')
1044        comboSizer.Add(setRings,0)
1045        setRings.Bind(wx.EVT_CHECKBOX, OnSetRings)
1046        setRings.SetValue(data['setRings'])
1047        calibSizer.Add(comboSizer,0)
1048        return calibSizer
1049       
1050    def GonioSizer():
1051       
1052        def OnGlobalEdit(event):
1053            Names = []
1054            Items = []
1055            if G2frame.PatternTree.GetCount():
1056                id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1057                while id:
1058                    name = G2frame.PatternTree.GetItemText(id)
1059                    if 'IMG' in name:
1060                        ctrls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,id,'Image Controls'))
1061                        Names.append(name)
1062                        Items.append(ctrls['GonioAngles'])
1063                    id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1064                if len(Names) == 1:
1065                    G2frame.ErrorDialog('Nothing for global editing','There must be more than one "IMG" pattern')
1066                    return
1067                dlg = G2G.G2HistoDataDialog(G2frame,' Edit sample goniometer data:',
1068                    'Edit data',['Omega','Chi','Phi'],['%.2f','%.2f','%.2f'],Names,Items)
1069            try:
1070                if dlg.ShowModal() == wx.ID_OK:
1071                    result = dlg.GetData()      #?? what was this for?
1072                    id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1073                    while id:
1074                        name = G2frame.PatternTree.GetItemText(id)
1075                        if 'IMG' in name:
1076                            ctrls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,id,'Image Controls'))
1077                            vals = Items[Names.index(name)]
1078                            ctrls['GonioAngles'] = vals
1079                        id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1080            finally:
1081                dlg.Destroy()
1082                G2frame.PatternTree.SelectItem(G2frame.PickId)
1083       
1084        gonioSizer = wx.BoxSizer(wx.HORIZONTAL)
1085        names = ['Omega','Chi','Phi']
1086        gonioSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,'Sample goniometer angles: '),0,WACV)
1087        for i,name in enumerate(names):
1088            gonioSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,name),0,WACV)
1089            angle = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['GonioAngles'],i,nDig=(8,2),typeHint=float)
1090            gonioSizer.Add(angle,0,WACV)
1091        globEdit = wx.Button(G2frame.dataDisplay,-1,'Global edit')
1092        globEdit.Bind(wx.EVT_BUTTON,OnGlobalEdit)
1093        gonioSizer.Add(globEdit,0,WACV)
1094        return gonioSizer
1095       
1096# Image Controls main code             
1097                           
1098    #fix for old files:
1099    if 'azmthOff' not in data:
1100        data['azmthOff'] = 0.0
1101    if 'background image' not in data:
1102        data['background image'] = ['',-1.0]
1103    if 'dark image' not in data:
1104        data['dark image'] = ['',-1.0]
1105    if 'centerAzm' not in data:
1106        data['centerAzm'] = False
1107    if 'Oblique' not in data:
1108        data['Oblique'] = [0.5,False]
1109    if 'PolaVal' not in data:
1110        data['PolaVal'] = [0.99,False]
1111    #end fix
1112   
1113    if IntegrateOnly:
1114        OnIntegrate(None)
1115        return
1116   
1117    colorList = sorted([m for m in mpl.cm.datad.keys() if not m.endswith("_r")],key=lambda s: s.lower())
1118    calList = sorted([m for m in calFile.Calibrants.keys()],key=lambda s: s.lower())
1119    typeList = ['PWDR - powder diffraction data','SASD - small angle scattering data',
1120        'REFL - reflectometry data']
1121    if not data.get('type'):                        #patch for old project files
1122        data['type'] = 'PWDR'
1123    typeDict = {'PWDR':typeList[0],'SASD':typeList[1],'REFL':typeList[2]}
1124    if G2frame.dataDisplay:
1125        G2frame.dataDisplay.Destroy()
1126    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.ImageMenu)
1127    if not G2frame.dataFrame.GetStatusBar():
1128        G2frame.dataFrame.CreateStatusBar()
1129    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCalibrate, id=G2gd.wxID_IMCALIBRATE)
1130    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRecalibrate, id=G2gd.wxID_IMRECALIBRATE)
1131    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRecalibAll, id=G2gd.wxID_IMRECALIBALL)
1132    G2frame.dataFrame.Bind(wx.EVT_MENU, OnClearCalib, id=G2gd.wxID_IMCLEARCALIB)
1133    if data.get('calibrant'):
1134        mode = True
1135    else:
1136        mode = False
1137    G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBRATE,enable=mode)
1138    G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMCALIBRATE,enable=mode)
1139    G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBALL,enable=mode)
1140    G2frame.dataFrame.Bind(wx.EVT_MENU, OnIntegrate, id=G2gd.wxID_IMINTEGRATE)
1141    G2frame.dataFrame.Bind(wx.EVT_MENU, OnIntegrateAll, id=G2gd.wxID_INTEGRATEALL)
1142    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyControls, id=G2gd.wxID_IMCOPYCONTROLS)
1143    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopySelected, id=G2gd.wxID_IMCOPYSELECTED)
1144    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveControls, id=G2gd.wxID_IMSAVECONTROLS)
1145    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveMultiControls, id=G2gd.wxID_SAVESELECTEDCONTROLS)
1146    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadControls, id=G2gd.wxID_IMLOADCONTROLS)
1147    G2frame.dataFrame.Bind(wx.EVT_MENU, OnTransferAngles, id=G2gd.wxID_IMXFERCONTROLS)
1148    def OnDestroy(event):
1149        G2frame.autoIntFrame = None
1150    def OnAutoInt(event):
1151        if G2frame.autoIntFrame: # ensure only one open at a time
1152            G2frame.autoIntFrame.Raise()
1153            return
1154        G2frame.autoIntFrame = AutoIntFrame(G2frame,PollTime=10.0)
1155        G2frame.autoIntFrame.Bind(wx.EVT_WINDOW_DESTROY,OnDestroy) # clean up name on window close
1156    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAutoInt, id=G2gd.wxID_IMAUTOINTEG)
1157    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
1158
1159    mainSizer = wx.BoxSizer(wx.VERTICAL)
1160    mainSizer.Add((5,10),0)   
1161    mainSizer.Add(ComboSizer(),0,wx.ALIGN_LEFT)
1162    mainSizer.Add((5,5),0)
1163    Range = data['range'] # allows code to be same in Masks
1164    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1165    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1166   
1167    mainSizer.Add((5,5),0)
1168    DataSizer = wx.FlexGridSizer(0,2,5,0)
1169    DataSizer.Add(CalibCoeffSizer(),0)
1170    DataSizer.Add(IntegrateSizer(),0)       
1171    mainSizer.Add(DataSizer,0)
1172    mainSizer.Add((5,5),0)           
1173    mainSizer.Add(BackSizer(),0)
1174    mainSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calibration controls:'),0,WACV)
1175    mainSizer.Add((5,5),0)
1176    mainSizer.Add(CalibSizer(),0,WACV)
1177    mainSizer.Add((5,5),0)
1178    mainSizer.Add(GonioSizer(),0,WACV)   
1179       
1180    mainSizer.Layout()   
1181    G2frame.dataDisplay.SetSizer(mainSizer)
1182    fitSize = mainSizer.Fit(G2frame.dataFrame)
1183    G2frame.dataFrame.setSizePosLeft(fitSize)
1184    G2frame.dataFrame.SendSizeEvent()
1185   
1186################################################################################
1187##### Masks
1188################################################################################
1189def CleanupMasks(data):
1190    '''If a mask creation is not completed, an empty mask entry is created in the
1191    masks array. This cleans them out. It is called when the masks page is first loaded
1192    and before saving them or after reading them in. This should also probably be done
1193    before they are used for integration.
1194    '''
1195    for key in ['Points','Rings','Arcs','Polygons']:
1196        data[key] = data.get(key,[])
1197        l1 = len(data[key])
1198        data[key] = [i for i in data[key] if len(i)]
1199        l2 = len(data[key])
1200        if GSASIIpath.GetConfigValue('debug') and l1 != l2:
1201            print 'Mask Cleanup:',key,'was',l1,'entries','now',l2
1202   
1203def UpdateMasks(G2frame,data):
1204    '''Shows and handles the controls on the "Masks" data tree entry
1205    '''
1206   
1207    def OnTextMsg(event):
1208        Obj = event.GetEventObject()
1209        Obj.SetToolTipString('Drag this mask on 2D Powder Image with mouse to change ')
1210
1211    def Replot(*args,**kwargs):
1212        wx.CallAfter(G2plt.PlotExposedImage,G2frame)
1213
1214    def onDeleteMask(event):
1215        Obj = event.GetEventObject()
1216        typ = Obj.locationcode.split('+')[1]
1217        num = int(Obj.locationcode.split('+')[2])
1218        del(data[typ][num])
1219        wx.CallAfter(UpdateMasks,G2frame,data)
1220        G2plt.PlotExposedImage(G2frame,event=event)
1221
1222    def onDeleteFrame(event):
1223        data['Frames'] = []
1224        wx.CallAfter(UpdateMasks,G2frame,data)
1225        G2plt.PlotExposedImage(G2frame,event=event)
1226
1227    def OnCopyMask(event):
1228        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1229        if len(Names) == 1:
1230            G2frame.ErrorDialog('Nothing to copy masks to','There must be more than one "IMG" pattern')
1231            return
1232        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
1233        Names.pop(Names.index(Source))
1234        Data = copy.deepcopy(data)
1235        Thresh = Data.pop('Thresholds')     # & remove it as well
1236        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy mask data','Copy masks from '+Source+' to:',Names)
1237        try:
1238            if dlg.ShowModal() == wx.ID_OK:
1239                items = dlg.GetSelections()
1240                for item in items:
1241                    name = Names[item]
1242                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
1243                    MId = G2gd.GetPatternTreeItemId(G2frame,Id,'Masks')
1244                    Mask = G2frame.PatternTree.GetItemPyData(MId)
1245                    Mask.update(copy.deepcopy(Data))
1246                    Mask['Thresholds'][1][0] = Thresh[1][0]  #copy only lower threshold
1247                    G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Masks'),Mask)
1248        finally:
1249            dlg.Destroy()
1250
1251    def OnSaveMask(event):
1252        CleanupMasks(data)
1253        pth = G2G.GetExportPath(G2frame)
1254        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1255            'image mask files (*.immask)|*.immask',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1256        try:
1257            if dlg.ShowModal() == wx.ID_OK:
1258                filename = dlg.GetPath()
1259                filename = os.path.splitext(filename)[0]+'.immask'
1260                File = open(filename,'w')
1261                keys = ['Points','Rings','Arcs','Polygons','Frames','Thresholds']
1262                for key in keys:
1263                    File.write(key+':'+str(data[key])+'\n')
1264                File.close()
1265        finally:
1266            dlg.Destroy()
1267       
1268    def OnLoadMask(event):
1269        if event.Id == G2gd.wxID_MASKLOADNOT:
1270            ignoreThreshold = True
1271        else:
1272            ignoreThreshold = False
1273        pth = G2G.GetImportPath(G2frame)
1274        if not pth: pth = '.'
1275        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1276            'image mask files (*.immask)|*.immask',wx.OPEN)
1277        try:
1278            if dlg.ShowModal() == wx.ID_OK:
1279                filename = dlg.GetPath()
1280                File = open(filename,'r')
1281                save = {}
1282                oldThreshold = data['Thresholds'][0]
1283                S = File.readline()
1284                while S:
1285                    if S[0] == '#':
1286                        S = File.readline()
1287                        continue
1288                    [key,val] = S.strip().split(':',1)
1289                    if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
1290                        if ignoreThreshold and key == 'Thresholds':
1291                            S = File.readline() 
1292                            continue
1293                        save[key] = eval(val)
1294                        if key == 'Thresholds':
1295                            save[key][0] = oldThreshold
1296                            save[key][1][1] = min(oldThreshold[1],save[key][1][1])
1297                    S = File.readline()
1298                File.close()
1299                data.update(save)
1300                CleanupMasks(data)
1301                wx.CallAfter(UpdateMasks,G2frame,data)
1302                G2plt.PlotExposedImage(G2frame,event=event)               
1303        finally:
1304            dlg.Destroy()
1305           
1306    def OnAutoSpotMask(event):
1307        'Do auto search for spot masks'
1308        if wx.MessageDialog(G2frame.dataDisplay,'NB: This will clear any old spot masks','Auto Spot Masks', wx.OK|wx.CANCEL).ShowModal() == wx.ID_OK:
1309            Controls = copy.deepcopy(G2frame.PatternTree.GetItemPyData( 
1310            G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId,'Image Controls')))
1311            Error = G2img.AutoSpotMasks(G2frame.ImageZ,data,Controls)
1312            if not Error is None:
1313                G2frame.ErrorDialog('Auto spot search error',Error)
1314            wx.CallAfter(UpdateMasks,G2frame,data)
1315            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)               
1316           
1317    def ToggleSpotMaskMode(event):
1318        G2plt.ToggleMultiSpotMask(G2frame)
1319       
1320    def OnNewArcMask(event):
1321        'Start a new arc mask'
1322        G2frame.MaskKey = 'a'
1323        G2plt.OnStartMask(G2frame)
1324       
1325    def OnNewRingMask(event):
1326        'Start a new ring mask'
1327        G2frame.MaskKey = 'r'
1328        G2plt.OnStartMask(G2frame)
1329       
1330    def OnNewPolyMask(event):
1331        'Start a new polygon mask'
1332        G2frame.MaskKey = 'p'
1333        G2plt.OnStartMask(G2frame)
1334       
1335    def OnNewFrameMask(event):
1336        'Start a new Frame mask'
1337        G2frame.MaskKey = 'f'
1338        G2plt.OnStartMask(G2frame)
1339       
1340    def MaxSizer():
1341        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
1342        for the image, as well as callback routines.
1343        '''
1344        def OnNewVal(invalid,value,tc):
1345            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
1346            the value into the data['range'] nested list).
1347            This adjusts the slider positions to match the current values
1348            '''
1349            scaleSel.SetSelection(len(scaleChoices)-1)
1350            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
1351            if r11 != Range[1][1]:
1352                Range[1][1] = r11
1353                maxVal.SetValue(int(Range[1][1]))
1354            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
1355            if r10 != Range[1][0]:
1356                Range[1][0] = r10
1357                minVal.SetValue(int(Range[1][0]))
1358            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1359            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1360            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1361            maxSel.SetValue(sv1)
1362            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1363            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1364            minSel.SetValue(sv0)
1365            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1366            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1367            if mplOld:
1368                Page.canvas.draw()
1369            else:
1370                Page.canvas.draw_idle()
1371           
1372        G2frame.prevMaxValue = None   
1373        def OnMaxSlider(event):
1374            val = maxSel.GetValue()
1375            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
1376            scaleSel.SetSelection(len(scaleChoices)-1)
1377            G2frame.prevMaxValue = val
1378            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1379            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
1380            maxVal.SetValue(int(0.5+Range[1][1]))
1381            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1382            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
1383            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1384            minSel.SetValue(sv0)
1385            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1386            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1387            if mplOld:
1388                Page.canvas.draw()
1389            else:
1390                Page.canvas.draw_idle()
1391           
1392        G2frame.prevMinValue = None   
1393        def OnMinSlider(event):
1394            val = minSel.GetValue()
1395            scaleSel.SetSelection(len(scaleChoices)-1)
1396            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
1397            G2frame.prevMinValue = val
1398            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1399            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
1400            minVal.SetValue(int(Range[1][0]))
1401            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1402            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1403            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1404            maxSel.SetValue(sv1)
1405            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1406            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1407            if mplOld:
1408                Page.canvas.draw()
1409            else:
1410                Page.canvas.draw_idle()
1411           
1412        def OnAutoSet(event):
1413            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
1414            for the image so that 95% (etc.) of pixels are inside the color map limits.
1415            An equal number of pixels are dropped at the minimum and maximum levels.
1416            '''
1417            try:
1418                val = int(event.GetEventObject().GetStringSelection()[:-1])
1419                margin = (100-val)/2.
1420            except:
1421                margin = 0
1422                event.GetEventObject().SetSelection(0)
1423            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1424            if margin == 0:
1425                Range[1] = list(Range[0])
1426            else:
1427                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
1428                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
1429            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1430            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1431            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1432            maxSel.SetValue(sv1)
1433            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1434            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1435            minSel.SetValue(sv0)
1436            minVal.SetValue(int(Range[1][0]))
1437            maxVal.SetValue(int(Range[1][1]))
1438            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1439            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1440            if mplOld:
1441                Page.canvas.draw()
1442            else:
1443                Page.canvas.draw_idle()
1444
1445        mplv = mpl.__version__.split('.')
1446        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
1447        # Plot color scaling uses limits as below:
1448        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
1449        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
1450        maxSizer = wx.GridBagSizer(0,0)
1451        r = c = 0
1452        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Max intensity'),(r,c))
1453        c += 1
1454        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
1455        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
1456        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
1457        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1458        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1459        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1460        maxSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv1,size=(300,-1))
1461        maxSizer.Add(maxSel,(r,c),flag=wx.EXPAND)
1462        maxSizer.AddGrowableCol(c)
1463        c += 1
1464        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
1465        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],1,min=Range[0][0]+1,
1466            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1467        maxSizer.Add(maxVal,(r,c))
1468        c += 1
1469        scaleChoices = ("100%","99%","95%","90%","80%","?")
1470        scaleSel = wx.Choice(G2frame.dataDisplay,choices=scaleChoices,size=(-1,-1))
1471        if (Range[1][0] == Range[0][0] and
1472            Range[1][1] == Range[0][1]):
1473            scaleSel.SetSelection(0)
1474        else:
1475            scaleSel.SetSelection(len(scaleChoices)-1)
1476        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
1477        maxSizer.Add(scaleSel,(r,c),(2,1),flag=wx.ALIGN_CENTER)
1478        c = 0
1479        r = 1
1480        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min intensity'),(r,c))
1481        c += 1
1482        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
1483        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
1484        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
1485        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1486        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1487        minSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv0,size=(300,-1))
1488        maxSizer.Add(minSel,(r,c),flag=wx.EXPAND|wx.ALL)
1489        c += 1
1490        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
1491        minVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],0,
1492            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1493        maxSizer.Add(minVal,(r,c))
1494        return maxSizer
1495
1496    startScroll = None
1497    if G2frame.dataDisplay:
1498        startScroll = G2frame.dataDisplay.GetScrollPos(wx.VERTICAL) # save scroll position
1499        G2frame.dataDisplay.Destroy()
1500    else:
1501        CleanupMasks(data) # posting page for 1st time; clean out anything unfinished
1502    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.MaskMenu)
1503    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyMask, id=G2gd.wxID_MASKCOPY)
1504    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadMask, id=G2gd.wxID_MASKLOAD)
1505    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadMask, id=G2gd.wxID_MASKLOADNOT)
1506    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveMask, id=G2gd.wxID_MASKSAVE)
1507    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAutoSpotMask, id=G2gd.wxID_FINDSPOTS)
1508    G2frame.dataFrame.Bind(wx.EVT_MENU, ToggleSpotMaskMode, id=G2gd.wxID_NEWMASKSPOT)
1509    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewArcMask, id=G2gd.wxID_NEWMASKARC)
1510    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewRingMask, id=G2gd.wxID_NEWMASKRING)
1511    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewPolyMask, id=G2gd.wxID_NEWMASKPOLY)
1512    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewFrameMask, id=G2gd.wxID_NEWMASKFRAME)
1513    if not G2frame.dataFrame.GetStatusBar():
1514        G2frame.dataFrame.CreateStatusBar()
1515    if G2frame.MaskKey == 'f':
1516        G2frame.dataFrame.GetStatusBar().SetStatusText('Frame mask active - LB pick next point, RB close polygon')
1517    elif G2frame.MaskKey == 'p':
1518        G2frame.dataFrame.GetStatusBar().SetStatusText('Polygon mask active - LB pick next point, RB close polygon')
1519    elif G2frame.MaskKey == 'a':
1520        G2frame.dataFrame.GetStatusBar().SetStatusText('Arc mask active - LB pick arc location')
1521    elif G2frame.MaskKey == 'r':
1522        G2frame.dataFrame.GetStatusBar().SetStatusText('Ring mask active - LB pick ring location')
1523    else:
1524        G2frame.dataFrame.GetStatusBar().SetStatusText("To add mask: press a,r,s,p or f on 2D image for arc/ring/spot/polygon/frame")
1525    G2frame.dataDisplay = wxscroll.ScrolledPanel(G2frame.dataFrame)
1526    mainSizer = wx.BoxSizer(wx.VERTICAL)
1527    mainSizer.Add((5,10),0)
1528
1529    thresh = data['Thresholds']         #min/max intensity range
1530    Spots = data['Points']               #x,y,radius in mm
1531    Rings = data['Rings']               #radius, thickness
1532    Polygons = data['Polygons']         #3+ x,y pairs
1533    if 'Frames' not in data:
1534        data['Frames'] = []
1535    frame = data['Frames']             #3+ x,y pairs
1536    Arcs = data['Arcs']                 #radius, start/end azimuth, thickness
1537
1538    ######################################################################
1539    CId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Image Controls')
1540    controlData = G2frame.PatternTree.GetItemPyData(CId)
1541    Range = controlData['range']
1542    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1543    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1544
1545    littleSizer = wx.FlexGridSizer(0,3,0,5)
1546    littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Lower/Upper limits '),0,WACV)
1547    Text = wx.TextCtrl(G2frame.dataDisplay,value=str(thresh[0][0]),style=wx.TE_READONLY)
1548    littleSizer.Add(Text,0,WACV)
1549    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1550    Text = wx.TextCtrl(G2frame.dataDisplay,value=str(thresh[0][1]),style=wx.TE_READONLY)
1551    littleSizer.Add(Text,0,WACV)
1552    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1553    littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Lower/Upper thresholds '),0,WACV)
1554    lowerThreshold = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=thresh[1],key=0,
1555        min=thresh[0][0],OnLeave=Replot,typeHint=int)
1556    littleSizer.Add(lowerThreshold,0,WACV)
1557    upperThreshold = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=thresh[1],key=1,
1558        max=thresh[0][1],OnLeave=Replot,typeHint=int)
1559    littleSizer.Add(upperThreshold,0,WACV)
1560    mainSizer.Add(littleSizer,0,)
1561    if len(Spots):
1562        lbl = wx.StaticText(parent=G2frame.dataDisplay,label=' Spot masks')
1563        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1564        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1565        littleSizer = wx.FlexGridSizer(0,3,0,5)
1566        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' position, mm'),0,WACV)
1567        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' diameter, mm'),0,WACV)
1568        littleSizer.Add((5,0),0)
1569        for i in range(len(Spots)):
1570            if len(Spots[i]):
1571                x,y,d = Spots[i]
1572                spotText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%.2f,%.2f" % (x,y)),
1573                    style=wx.TE_READONLY)
1574                spotText.SetBackgroundColour(VERY_LIGHT_GREY)
1575                littleSizer.Add(spotText,0,WACV)
1576                spotText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1577                spotDiameter = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=Spots[i],key=2,
1578                    max=100.,OnLeave=Replot,nDig=[8,2])
1579                littleSizer.Add(spotDiameter,0,WACV)
1580                spotDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1581                    locationcode='Delete+Points+'+str(i),handler=onDeleteMask)
1582                littleSizer.Add(spotDelete,0,WACV)
1583        mainSizer.Add(littleSizer,0,)
1584    if Rings:
1585        lbl = wx.StaticText(parent=G2frame.dataDisplay,label=' Ring masks')
1586        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1587        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1588        littleSizer = wx.FlexGridSizer(0,3,0,5)
1589        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' 2-theta,deg'),0,WACV)
1590        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' thickness, deg'),0,WACV)
1591        littleSizer.Add((5,0),0)
1592        for i in range(len(Rings)):
1593            if Rings[i]:
1594                ringText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%.3f" % (Rings[i][0])),
1595                    style=wx.TE_READONLY)
1596                ringText.SetBackgroundColour(VERY_LIGHT_GREY)
1597                ringText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1598                littleSizer.Add(ringText,0,WACV)
1599                ringThick = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=Rings[i],key=1,
1600                    min=0.001,max=1.,OnLeave=Replot,nDig=[8,3])
1601                littleSizer.Add(ringThick,0,WACV)
1602                ringDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1603                    locationcode='Delete+Rings+'+str(i),handler=onDeleteMask)
1604                littleSizer.Add(ringDelete,0,WACV)
1605        mainSizer.Add(littleSizer,0,)
1606    if Arcs:
1607        lbl = wx.StaticText(parent=G2frame.dataDisplay,label=' Arc masks')
1608        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1609        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1610        littleSizer = wx.FlexGridSizer(0,4,0,5)
1611        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' 2-theta,deg'),0,WACV)
1612        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' azimuth, deg'),0,WACV)
1613        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' thickness, deg'),0,WACV)
1614        littleSizer.Add((5,0),0)
1615        for i in range(len(Arcs)):
1616            if Arcs[i]:
1617                tth,azimuth,thick = Arcs[i]
1618                arcText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%.3f" % (tth)),
1619                    style=wx.TE_READONLY)
1620                arcText.SetBackgroundColour(VERY_LIGHT_GREY)
1621                arcText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1622                littleSizer.Add(arcText,0,WACV)
1623                azmText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%d,%d" % (azimuth[0],azimuth[1])),
1624                    style=wx.TE_READONLY)
1625                azmText.SetBackgroundColour(VERY_LIGHT_GREY)
1626                azmText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1627                littleSizer.Add(azmText,0,WACV)
1628                arcThick = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=Arcs[i],key=2,
1629                    min=0.001,max=20.,OnLeave=Replot,nDig=[8,3])
1630                littleSizer.Add(arcThick,0,WACV)
1631                arcDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1632                    locationcode='Delete+Arcs+'+str(i),handler=onDeleteMask)
1633                littleSizer.Add(arcDelete,0,WACV)
1634        mainSizer.Add(littleSizer,0,)
1635    if Polygons:
1636        lbl = wx.StaticText(parent=G2frame.dataDisplay,
1637            label=' Polygon masks (on plot RB vertex drag to move,\nLB vertex drag to insert)')
1638        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1639        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1640        littleSizer = wx.FlexGridSizer(0,2,0,5)
1641        for i in range(len(Polygons)):
1642            if Polygons[i]:
1643                polyList = []
1644                for x,y in Polygons[i]:
1645                    polyList.append("%.2f, %.2f"%(x,y))
1646                polyText = wx.ComboBox(G2frame.dataDisplay,value=polyList[0],choices=polyList,style=wx.CB_READONLY)
1647                littleSizer.Add(polyText,0,WACV)
1648                polyDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1649                    locationcode='Delete+Polygons+'+str(i),handler=onDeleteMask)
1650                littleSizer.Add(polyDelete,0,WACV)
1651        mainSizer.Add(littleSizer,0,)
1652    if frame:
1653        lbl = wx.StaticText(parent=G2frame.dataDisplay,
1654            label=' Frame mask (on plot RB vertex drag to move,LB vertex drag to insert)')
1655        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1656        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1657        littleSizer = wx.FlexGridSizer(0,2,0,5)
1658        frameList = []
1659        for x,y in frame:
1660            frameList.append("%.2f, %.2f"%(x,y))
1661        frameText = wx.ComboBox(G2frame.dataDisplay,value=frameList[0],choices=frameList,style=wx.CB_READONLY)
1662        littleSizer.Add(frameText,0,WACV)
1663        frameDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1664            locationcode='Delete+Frame',handler=onDeleteFrame)
1665        littleSizer.Add(frameDelete,0,WACV)
1666        mainSizer.Add(littleSizer,0,)
1667    mainSizer.Layout()   
1668    G2frame.dataDisplay.SetSizer(mainSizer)
1669    G2frame.dataDisplay.SetupScrolling()
1670    Size = mainSizer.Fit(G2frame.dataFrame)
1671    Size[0] += 50 # room for scrollbar & status msg
1672    Size[1] = min(Size[1],500)
1673    G2frame.dataFrame.setSizePosLeft(Size)   
1674    if startScroll: # reset scroll to saved position
1675        G2frame.dataDisplay.Scroll(0,startScroll) # set to saved scroll position
1676        wx.Yield()
1677
1678################################################################################
1679##### Stress/Strain
1680################################################################################
1681
1682def UpdateStressStrain(G2frame,data):
1683    '''Shows and handles the controls on the "Stress/Strain"
1684    data tree entry
1685    '''
1686   
1687    def OnAppendDzero(event):
1688        data['d-zero'].append({'Dset':1.0,'Dcalc':0.0,'pixLimit':10,'cutoff':1.0,
1689            'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]],'Emat':[1.0,1.0,1.0]})
1690        UpdateStressStrain(G2frame,data)
1691       
1692    def OnUpdateDzero(event):
1693        for item in data['d-zero']:
1694            if item['Dcalc']:   #skip unrefined ones
1695                item['Dset'] = item['Dcalc']
1696        UpdateStressStrain(G2frame,data)
1697           
1698    def OnCopyStrSta(event):
1699        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1700        if len(Names) == 1:
1701            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
1702            return
1703        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
1704        Names.pop(Names.index(Source))
1705        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy stress/strain controls','Copy controls from '+Source+' to:',Names)
1706        try:
1707            if dlg.ShowModal() == wx.ID_OK:
1708                items = dlg.GetSelections()
1709                for item in items:
1710                    name = Names[item]
1711                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
1712                    CId = G2gd.GetPatternTreeItemId(G2frame,Id,'Stress/Strain')
1713                    oldData = G2frame.PatternTree.GetItemPyData(CId)
1714                    load = oldData.get('Sample load',0.0)
1715                    Data = copy.deepcopy(data)
1716                    Data['Sample load'] = load
1717                    G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Stress/Strain'),Data)
1718        finally:
1719            dlg.Destroy()
1720            G2frame.PatternTree.SelectItem(G2frame.PickId)
1721
1722    def OnLoadStrSta(event):
1723        pth = G2G.GetImportPath(G2frame)
1724        if not pth: pth = '.'
1725        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1726            'image control files (*.strsta)|*.strsta',wx.OPEN)
1727        try:
1728            if dlg.ShowModal() == wx.ID_OK:
1729                filename = dlg.GetPath()
1730                File = open(filename,'r')
1731                S = File.read()
1732                data = eval(S)
1733                Controls = G2frame.PatternTree.GetItemPyData(
1734                    G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1735                G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1736                UpdateStressStrain(G2frame,data)
1737                G2plt.PlotExposedImage(G2frame,event=event)
1738                G2plt.PlotStrain(G2frame,data,newPlot=True)
1739                File.close()
1740        finally:
1741            dlg.Destroy()
1742
1743    def OnSaveStrSta(event):
1744        pth = G2G.GetExportPath(G2frame)
1745        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1746            'image control files (*.strsta)|*.strsta',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1747        try:
1748            if dlg.ShowModal() == wx.ID_OK:
1749                filename = dlg.GetPath()
1750                File = open(filename,'w')
1751                keys = ['Type','Sample phi','Sample z','Sample load']
1752                keys2 = ['Dset','Dcalc','pixLimit','cutoff','Emat']
1753                File.write('{\n\t')
1754                for key in keys:
1755                    if key in 'Type':
1756                        File.write("'"+key+"':'"+data[key]+"',")
1757                    else:
1758                        File.write("'"+key+"':"+str(data[key])+',')
1759                File.write('\n\t'+"'d-zero':[\n")
1760                for data2 in data['d-zero']:
1761                    File.write('\t\t{')
1762                    for key in keys2:
1763                        File.write("'"+key+"':"+str(data2[key])+',')
1764                    File.write("'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]]},\n")
1765                File.write('\t]\n}')
1766                File.close()
1767        finally:
1768            dlg.Destroy()
1769           
1770    def OnStrStaSample(event):
1771        filename = ''
1772        pth = G2G.GetImportPath(G2frame)
1773        if not pth: pth = '.'
1774        dlg = wx.FileDialog(G2frame, 'Choose multihistogram metadata text file', pth, '', 
1775            'metadata file (*.*)|*.*',wx.OPEN)
1776        try:
1777            if dlg.ShowModal() == wx.ID_OK:
1778                filename = dlg.GetPath()
1779                File = open(filename,'r')
1780                S = File.readline()
1781                newItems = []
1782                itemNames = []
1783                Comments = []
1784                while S:
1785                    if S[0] == '#':
1786                        Comments.append(S)
1787                        S = File.readline()
1788                        continue
1789                    S = S.replace(',',' ').replace('\t',' ')
1790                    Stuff = S[:-1].split()
1791                    itemNames.append(Stuff[0])
1792                    newItems.append(Stuff[1:])
1793                    S = File.readline()               
1794                File.close()
1795        finally:
1796            dlg.Destroy()
1797        if not filename:
1798            G2frame.ErrorDialog('Nothing to do','No file selected')
1799            return
1800        dataDict = dict(zip(itemNames,newItems))
1801        ifany = False
1802        Names = [' ','Sample phi','Sample z','Sample load']
1803        dlg = G2G.G2ColumnIDDialog( G2frame,' Choose multihistogram metadata columns:',
1804            'Select columns',Comments,Names,np.array(newItems).T)
1805        try:
1806            if dlg.ShowModal() == wx.ID_OK:
1807                colNames,newData = dlg.GetSelection()
1808                dataDict = dict(zip(itemNames,newData.T))
1809                for item in colNames:
1810                    if item != ' ':
1811                        ifany = True
1812        finally:
1813            dlg.Destroy()
1814        if not ifany:
1815            G2frame.ErrorDialog('Nothing to do','No columns identified')
1816            return
1817        histList = []
1818        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)       
1819        while item:
1820            name = G2frame.PatternTree.GetItemText(item)
1821            if name.startswith('IMG'):
1822                histList.append(name)
1823            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1824        colIds = {}
1825        for i,name in enumerate(colNames):
1826            if name != ' ':
1827                colIds[name] = i
1828        for hist in histList:
1829            name = hist.split()[1]  #this is file name
1830            if name in dataDict:
1831                newItems = {}
1832                for item in colIds:
1833                    newItems[item] = float(dataDict[name][colIds[item]])
1834                Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,hist)
1835                stsrData = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Stress/Strain'))
1836                stsrData.update(newItems)       
1837        UpdateStressStrain(G2frame,data)       
1838   
1839    def OnPlotStrSta(event):
1840        Controls = G2frame.PatternTree.GetItemPyData(
1841            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1842        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1843        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1844        G2plt.PlotExposedImage(G2frame,event=event)
1845        G2frame.G2plotNB.Delete('Ring Intensities')
1846        G2plt.PlotXY(G2frame,RingInt,labelX='Azimuth',
1847            labelY='MRD',newPlot=True,Title='Ring Intensities',
1848            names=Names,lines=True)
1849       
1850    def OnFitStrSta(event):
1851        Controls = G2frame.PatternTree.GetItemPyData(
1852            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1853        G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1854        print 'Strain fitting finished'
1855        UpdateStressStrain(G2frame,data)
1856        G2plt.PlotExposedImage(G2frame,event=event)
1857        G2plt.PlotStrain(G2frame,data,newPlot=True)
1858       
1859    def OnFitAllStrSta(event):
1860        choices = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1861        sel = []
1862        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices)
1863        dlg.SetSelections(sel)
1864        names = []
1865        if dlg.ShowModal() == wx.ID_OK:
1866            for sel in dlg.GetSelections():
1867                names.append(choices[sel])
1868        else:
1869            return
1870        if not names:
1871            return
1872        dlg.Destroy()
1873        SeqResult = {}
1874        Reverse = False
1875        CopyForward = False
1876        choice = ['Reverse sequence','Copy from prev.',]
1877        dlg = wx.MultiChoiceDialog(G2frame.dataFrame,'Sequential controls','Select controls',choice)
1878        if dlg.ShowModal() == wx.ID_OK:
1879            for sel in dlg.GetSelections():
1880                if sel:
1881                    CopyForward = True
1882                else:
1883                    Reverse = True
1884        dlg.Destroy()
1885        dlg = wx.ProgressDialog('Sequential IMG Strain fit','Data set name = '+names[0],len(names), 
1886            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT)         
1887        wx.BeginBusyCursor()
1888        goodnames = []
1889        if Reverse:
1890            names.reverse()
1891        try:
1892            for i,name in enumerate(names):
1893                print ' Sequential strain fit for ',name
1894                GoOn = dlg.Update(i,newmsg='Data set name = '+name)[0]
1895                if not GoOn:
1896                    break
1897                Id =  G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
1898                G2frame.Image = Id
1899                Controls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Image Controls'))
1900                StaCtrls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Stress/Strain'))
1901                if not len(StaCtrls['d-zero']):
1902                    continue
1903                goodnames.append(name)
1904                Npix,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(Id)
1905                image = GetImageZ(G2frame,Controls)
1906                sig = []
1907                varyList = []
1908                variables = []
1909                if i and CopyForward:
1910                    for j,ring in enumerate(StaCtrls['d-zero']):
1911                        ring['Emat'] = variables[4*j:4*j+3]
1912                #get results from previous & put in StaCtrls
1913                G2img.FitStrSta(image,StaCtrls,Controls)
1914                G2plt.PlotStrain(G2frame,StaCtrls,newPlot=True)
1915                parmDict = {'Sample load':StaCtrls['Sample load'],}
1916                varyNames = ['e11','e12','e22']
1917                for j,item in enumerate(StaCtrls['d-zero']):
1918                    variables += item['Emat']
1919                    sig += item['Esig']
1920                    varylist = ['%d;%s'%(j,Name) for Name in varyNames]
1921                    varyList += varylist
1922                    parmDict.update(dict(zip(varylist,item['Emat'])))
1923                    parmDict['%d;Dcalc'%(j)] = item['Dcalc']
1924                    parmDict['%d;Ivar'%(j)] = item['Ivar']
1925                    variables.append(item['Ivar'])
1926                    varyList.append('%d;Ivar'%(j))
1927                    sig.append(0.)
1928                SeqResult[name] = {'variables':variables,'varyList':varyList,'sig':sig,'Rvals':[],
1929                    'covMatrix':np.eye(len(variables)),'title':name,'parmDict':parmDict}
1930            else:
1931                SeqResult['histNames'] = goodnames
1932                dlg.Destroy()
1933                print ' ***** Sequential strain refinement successful *****'
1934        finally:
1935            wx.EndBusyCursor()   
1936        if Reverse:
1937            names.reverse()
1938        SeqResult['histNames'] = names
1939        Id =  G2gd.GetPatternTreeItemId(G2frame,G2frame.root,'Sequential strain fit results')
1940        if Id:
1941            G2frame.PatternTree.SetItemPyData(Id,SeqResult)
1942        else:
1943            Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text='Sequential strain fit results')
1944            G2frame.PatternTree.SetItemPyData(Id,SeqResult)
1945        G2frame.PatternTree.SelectItem(Id)
1946        print 'All images fitted'
1947       
1948    def SamSizer():
1949       
1950        def OnStrainType(event):
1951            data['Type'] = strType.GetValue()
1952       
1953        samSizer = wx.BoxSizer(wx.HORIZONTAL)
1954        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Strain type: '),0,WACV)
1955        strType = wx.ComboBox(G2frame.dataDisplay,value=data['Type'],choices=['True','Conventional'],
1956            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1957        strType.SetValue(data['Type'])
1958        strType.Bind(wx.EVT_COMBOBOX, OnStrainType)
1959        samSizer.Add(strType,0,WACV)
1960       
1961        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample phi: '),0,WACV)
1962        samPhi = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample phi',nDig=(10,3),typeHint=float,min=-360.,max=360.)
1963        samSizer.Add(samPhi,0,WACV)
1964        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample delta-z(mm): '),0,WACV)
1965        samZ = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample z',nDig=(10,3),typeHint=float)
1966        samSizer.Add(samZ,0,WACV)
1967        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample load(MPa): '),0,WACV)
1968        samLoad = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample load',
1969                nDig=[8,3],typeHint=float,)
1970        samSizer.Add(samLoad,0,WACV)
1971
1972        return samSizer
1973       
1974    def DzeroSizer():
1975   
1976        def OnDzero(invalid,value,tc):
1977            data['d-zero'] = G2mth.sortArray(data['d-zero'],'Dset',reverse=True)
1978            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[tc.GetId()]],G2frame.ImageZ,Controls)
1979            if len(Ring):
1980                data['d-zero'][Indx[tc.GetId()]].update(R)
1981            else:
1982                G2frame.ErrorDialog('Strain peak selection','WARNING - No points found for this ring selection')
1983               
1984            wx.CallAfter(UpdateStressStrain,G2frame,data)
1985            G2plt.PlotExposedImage(G2frame,event=tc.event,newPlot=False)
1986            G2plt.PlotStrain(G2frame,data,newPlot=True)
1987           
1988        def OnDeleteDzero(event):
1989            Obj = event.GetEventObject()
1990            del(data['d-zero'][delIndx.index(Obj)])
1991            UpdateStressStrain(G2frame,data)
1992            G2plt.PlotExposedImage(G2frame,event=event,newPlot=True)
1993            G2plt.PlotStrain(G2frame,data,newPlot=True)
1994       
1995        def OnCutOff(invalid,value,tc):
1996            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[tc.GetId()]],G2frame.ImageZ,Controls)
1997            G2plt.PlotExposedImage(G2frame,event=tc.event)
1998            G2plt.PlotStrain(G2frame,data,newPlot=True)
1999       
2000        def OnPixLimit(event):
2001            Obj = event.GetEventObject()
2002            data['d-zero'][Indx[Obj.GetId()]]['pixLimit'] = int(Obj.GetValue())
2003            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[Obj.GetId()]],G2frame.ImageZ,Controls)
2004            G2plt.PlotExposedImage(G2frame,event=event)
2005            G2plt.PlotStrain(G2frame,data,newPlot=True)
2006           
2007        Indx = {}
2008        delIndx = []   
2009        dzeroSizer = wx.FlexGridSizer(0,8,5,5)
2010        for id,dzero in enumerate(data['d-zero']):
2011            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' d-zero #%d: '%(id))),0,WACV)
2012            dZero = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['d-zero'][id],'Dset',
2013                min=0.25,max=20.,nDig=(10,5),typeHint=float,OnLeave=OnDzero)
2014            dzeroSizer.Add(dZero,0,WACV)
2015            Indx[dZero.GetId()] = id
2016            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' d-zero ave: %.5f'%(dzero['Dcalc']))),0,WACV)
2017               
2018            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min ring I/Ib '),0,WACV)
2019            cutOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['d-zero'][id],'cutoff',
2020                    min=0.5,max=20.,nDig=(10,1),typeHint=float,OnLeave=OnCutOff)
2021            Indx[cutOff.GetId()] = id
2022            dzeroSizer.Add(cutOff,0,WACV)
2023       
2024            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Pixel search range '),0,WACV)
2025            pixLimit = wx.ComboBox(parent=G2frame.dataDisplay,value=str(dzero['pixLimit']),choices=['1','2','5','10','15','20'],
2026                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2027            pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
2028            Indx[pixLimit.GetId()] = id
2029            dzeroSizer.Add(pixLimit,0,WACV)               
2030               
2031            dzeroDelete = wx.CheckBox(parent=G2frame.dataDisplay,label='delete?')
2032            dzeroDelete.Bind(wx.EVT_CHECKBOX,OnDeleteDzero)
2033            delIndx.append(dzeroDelete)
2034            dzeroSizer.Add(dzeroDelete,0,WACV)
2035           
2036            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' Strain tensor:')),WACV)
2037            names = ['e11','e12','e22']
2038            for i in range(3):
2039                dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=names[i]),0,WACV)
2040                tensorElem = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(dzero['Emat'][i]),style=wx.TE_READONLY)
2041                tensorElem.SetBackgroundColour(VERY_LIGHT_GREY)
2042                dzeroSizer.Add(tensorElem,0,WACV)
2043            dzeroSizer.Add((5,5),0)             
2044        return dzeroSizer
2045       
2046# patches
2047    if 'Sample load' not in data:
2048        data['Sample load'] = 0.0
2049# end patches
2050   
2051    if G2frame.dataDisplay:
2052        G2frame.dataDisplay.Destroy()
2053    Controls = G2frame.PatternTree.GetItemPyData(
2054        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))       
2055    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.StrStaMenu)
2056    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAppendDzero, id=G2gd.wxID_APPENDDZERO)
2057    G2frame.dataFrame.Bind(wx.EVT_MENU, OnUpdateDzero, id=G2gd.wxID_UPDATEDZERO)
2058    G2frame.dataFrame.Bind(wx.EVT_MENU, OnFitStrSta, id=G2gd.wxID_STRSTAFIT)
2059    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotStrSta, id=G2gd.wxID_STRSTAPLOT)
2060    G2frame.dataFrame.Bind(wx.EVT_MENU, OnFitAllStrSta, id=G2gd.wxID_STRSTAALLFIT)
2061    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyStrSta, id=G2gd.wxID_STRSTACOPY)
2062    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadStrSta, id=G2gd.wxID_STRSTALOAD)
2063    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveStrSta, id=G2gd.wxID_STRSTASAVE)
2064    G2frame.dataFrame.Bind(wx.EVT_MENU, OnStrStaSample, id=G2gd.wxID_STRSTSAMPLE)       
2065    if not G2frame.dataFrame.GetStatusBar():
2066        G2frame.dataFrame.CreateStatusBar()
2067    if G2frame.StrainKey == 'a':    #probably doesn't happen
2068        G2frame.dataFrame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value')
2069    else:
2070        G2frame.dataFrame.GetStatusBar().SetStatusText("To add strain data: On 2D Powder Image, key a:add ring")
2071       
2072    G2frame.dataDisplay = wxscroll.ScrolledPanel(G2frame.dataFrame)
2073    mainSizer = wx.BoxSizer(wx.VERTICAL)
2074    mainSizer.Add((5,10),0)
2075    mainSizer.Add(SamSizer())
2076    mainSizer.Add((5,10),0)
2077    mainSizer.Add(DzeroSizer())
2078   
2079    mainSizer.Layout()   
2080    G2frame.dataDisplay.SetSizer(mainSizer)
2081    G2frame.dataDisplay.SetAutoLayout(1)
2082    G2frame.dataDisplay.SetupScrolling()
2083    Size = mainSizer.Fit(G2frame.dataFrame)
2084    Size[0] += 25
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.