source: trunk/GSASIIimgGUI.py @ 2798

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

avoid crash if DefaultAutoScaleNames? isn't in config.py

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