source: trunk/GSASIIimgGUI.py @ 2789

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

fix GetImageZ to correctly treat background/dark images.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 160.7 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII - image data display routines
3########### SVN repository information ###################
4# $Date: 2017-04-18 17:47:27 +0000 (Tue, 18 Apr 2017) $
5# $Author: vondreele $
6# $Revision: 2789 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 2789 2017-04-18 17:47:27Z 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: 2789 $")
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        for comment in Comments:
2504            if '=' in comment:
2505                name,val = comment.split('=',1) 
2506                if name in DefaultAutoScaleNames:
2507                    try:
2508                        self.AutoScales[name] = float(val)
2509                        if name == self.AutoScaleName:
2510                            self.Scale[0] = float(val)
2511                    except ValueError:
2512                        continue
2513        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2514        self.params['outdir'] = os.path.abspath(self.imagedir)
2515        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2516                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2517        self.Status = self.CreateStatusBar()
2518        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2519        mnpnl = wx.Panel(self)
2520        mnsizer = wx.BoxSizer(wx.VERTICAL)
2521        # box for integration controls & masks input
2522        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2523        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2524        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2525        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2526                            style = wx.RB_GROUP)
2527        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2528        self.useActive.SetLabel("Active Image: "+
2529                                G2frame.PatternTree.GetItemText(self.imageBase))
2530        lblsizr.Add(self.useActive,1,wx.EXPAND,1)
2531        self.useActive.SetValue(True)
2532        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2533        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2534        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2535        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2536        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2537        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2538        self.editTable.Enable(False)
2539        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2540        # bind button and deactivate be default
2541        lblsizr.Add(minisizer)
2542        mnsizer.Add(lblsizr,1,wx.EXPAND,1)
2543
2544        # file filter stuff
2545        sizer = wx.BoxSizer(wx.HORIZONTAL)
2546        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Image filter'))
2547        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',OnLeave=self.ShowMatchingFiles)
2548        sizer.Add(flterInp)
2549        mnsizer.Add(sizer,0,wx.ALIGN_RIGHT,1)
2550        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2551        mnsizer.Add(self.ListBox,0,wx.EXPAND,1)
2552        self.ShowMatchingFiles(self.params['filter'])
2553        # box for output selections
2554        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2555        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2556        sizer = wx.BoxSizer(wx.HORIZONTAL)
2557        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2558        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2559        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2560        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2561        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2562        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2563        lblsizr.Add(sizer,0,wx.EXPAND)
2564        sizer = wx.BoxSizer(wx.HORIZONTAL)
2565        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
2566        for dfmt in self.fmtlist:
2567            fmt = dfmt[1:]
2568            self.params[fmt] = False
2569            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2570            sizer.Add(btn)
2571        lblsizr.Add(sizer)
2572        sizer = wx.BoxSizer(wx.HORIZONTAL)
2573        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2574        self.params['SeparateDir'] = False
2575        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2576        lblsizr.Add(sizer)
2577        if self.AutoScales:
2578            sizer = wx.BoxSizer(wx.HORIZONTAL)
2579            autoscale = wx.CheckBox(mnpnl,label='Do autoscaling with:')
2580            autoscale.Bind(wx.EVT_CHECKBOX,OnAutoScale)
2581            sizer.Add(autoscale,0,WACV)
2582            scalename = wx.ComboBox(mnpnl,value=self.AutoScaleName,choices=self.AutoScales.keys(),
2583                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2584            scalename.Bind(wx.EVT_COMBOBOX,OnAutoScaleName)
2585            sizer.Add(scalename,0,WACV)
2586            sizer.Add(wx.StaticText(mnpnl,label=' to '),0,WACV)
2587            scaleval = G2G.ValidatedTxtCtrl(mnpnl,self.Scale,0,nDig=(10,2),min=1.)
2588            sizer.Add(scaleval,0,WACV)
2589            lblsizr.Add(sizer,0)
2590        #ToDO: Autonormalize, parm name?, scaling value?
2591        sizer = wx.BoxSizer(wx.HORIZONTAL)
2592        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
2593        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2594        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2595        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2596        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2597        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2598        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
2599        sizer.Add(cOpt)
2600        lblsizr.Add(sizer,0)
2601        sizer = wx.BoxSizer(wx.HORIZONTAL)
2602        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2603        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2604        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2605                                     OnLeave=scanPDFprm)
2606        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2607        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2608        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2609        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2610        lblsizr.Add(sizer,0,wx.EXPAND)
2611        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2612        # buttons on bottom
2613        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2614        sizer = wx.BoxSizer(wx.HORIZONTAL)
2615        sizer.Add((20,-1))
2616        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2617        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2618        sizer.Add(self.btnstart)
2619        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2620        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2621        sizer.Add(self.btnreset)
2622        sizer.Add((20,-1),wx.EXPAND,1)
2623        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
2624        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
2625        sizer.Add(self.btnclose)
2626        sizer.Add((20,-1))
2627        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
2628        # finish up window
2629        mnpnl.SetSizer(mnsizer)
2630        OnRadioSelect(None) # disable widgets
2631        mnsizer.Fit(self)
2632        self.CenterOnParent()
2633        self.Show()
2634        showPDFctrls(None)
2635
2636    def checkPDFprm(self,ShowContents=False):
2637        '''Read in the PDF (.pdfprm) parameter file and check for problems.
2638        If ShowContents is True, a formatted text version of some of the file
2639        contents is returned. If errors are found, the return string will contain
2640        the string "Error:" at least once.
2641        '''
2642        self.pdfControls = {}
2643        msg = ''
2644        File = None
2645        try:
2646            File = open(self.params['pdfprm'],'r')
2647            S = File.readline()
2648            while S:
2649                if '#' in S:
2650                    S = File.readline()
2651                    continue
2652                key,val = S.split(':',1)
2653                try:
2654                    self.pdfControls[key] = eval(val)
2655                except:
2656                    self.pdfControls[key] = val
2657                S = File.readline()
2658        except Exception as err:
2659            msg += 'PDF Processing Error: error with open or read of {}'.format(self.params['pdfprm'])
2660            if GSASIIpath.GetConfigValue('debug'):
2661                print(msg)
2662                print(err)
2663            self.pdfControls = {}
2664            return msg
2665        finally:
2666            if File: File.close()
2667        formula = ''
2668        for el in self.pdfControls['ElList']:
2669            if self.pdfControls['ElList'][el]['FormulaNo'] <= 0: continue
2670            if formula: formula += ' '
2671            formula += '{}({:.1f})'.format(el,self.pdfControls['ElList'][el]['FormulaNo'])
2672        if not formula:
2673            msg += 'Error: no chemical formula in file'
2674        for key in ['Sample Bkg.','Container','Container Bkg.']:
2675            if key not in self.pdfControls:
2676                if msg: msg += '\n'
2677                msg += 'Error: missing key in self.pdfControls: '+key
2678                continue
2679        if msg or not ShowContents: return msg  # stop on error
2680        msg += 'Default formula: '+formula+'\n'
2681        for key in ['Sample Bkg.','Container','Container Bkg.']:
2682            name = self.pdfControls[key]['Name']
2683            mult = self.pdfControls[key].get('Mult',0.0)
2684            if not name: continue
2685            msg += '\n{}: {:.2f} * "{}"'.format(key,mult,name)
2686            if not G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,name):
2687                msg += ' *** missing ***'
2688        return msg
2689   
2690    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
2691        G2frame = self.G2frame
2692        if invalid: return
2693        msg = ''
2694        if self.PreventReEntryShowMatch: return
2695        self.PreventReEntryShowMatch = True
2696        imageFileList = []
2697        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
2698            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2699            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2700            if imagefile not in imageFileList: imageFileList.append(imagefile)
2701            if img not in G2frame.IntegratedList:
2702                if msg: msg += '\n'
2703                msg += '  ' + img
2704        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
2705        msg1 = ""
2706        try:
2707            imageList = sorted(
2708                glob.glob(os.path.join(self.imagedir,value)))
2709            if not imageList:
2710                msg1 = 'Warning: No files match search string '+os.path.join(self.imagedir,value)
2711            else:
2712                for fil in imageList:
2713                    if fil not in imageFileList: msg1 += '\n  '+fil
2714                if msg1:
2715                    msg += 'Files to integrate from '+os.path.join(self.imagedir,value)+msg1
2716                else:
2717                    msg += 'No files found to read'
2718        except IndexError:
2719            msg += 'Error searching for files named '+os.path.join(self.imagedir,value)
2720        self.ListBox.Clear()
2721        self.ListBox.AppendItems(msg.split('\n'))
2722        self.PreventReEntryShowMatch = False
2723        return
2724       
2725    def OnPause(self):
2726        '''Respond to Pause, changes text on button/Status line, if needed
2727        Stops timer
2728        self.Pause should already be True
2729        '''
2730        if self.timer.IsRunning(): self.timer.Stop()
2731        if self.btnstart.GetLabel() == 'Restart':
2732            return
2733        if self.btnstart.GetLabel() != 'Resume':
2734            print('\nPausing autointegration\n')
2735            self.btnstart.SetLabel('Resume')
2736            self.Status.SetStatusText(
2737                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
2738        self.Pause = True
2739           
2740    def IntegrateImage(self,img):
2741        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
2742        are placed in G2frame.IntgOutList
2743        '''
2744        G2frame = self.G2frame
2745        imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2746        G2frame.Image = imgId
2747        G2frame.PickId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls')
2748        # do integration
2749        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2750        if self.AutoScale:
2751            Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2752                G2frame,imgId, 'Comments'))
2753            for comment in Comments:
2754                if '=' in comment:
2755                    name,val = comment.split('=',1) 
2756                    if name == self.AutoScaleName:
2757                        val = float(val)
2758                        if val > 0.:
2759                            Scale = self.Scale[0]/val
2760                        break
2761        masks = G2frame.PatternTree.GetItemPyData(
2762            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
2763        data = G2frame.PatternTree.GetItemPyData(G2frame.PickId)
2764        # simulate a Image Controls press, since that is where the
2765        # integration is hidden
2766        UpdateImageControls(G2frame,data,masks,IntegrateOnly=True)
2767        G2frame.IntegratedList.append(img) # note this as integrated
2768        # split name and control number
2769        s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
2770        namepre = s[0]
2771        if len(s) > 1:
2772            namenum = s[1]
2773        else:
2774            namenum = ''
2775        for Id in G2frame.IntgOutList: # loop over newly created PDWR entry(ies)
2776            # save the created PWDR tree names so that a reset can delete them
2777            G2frame.Image = Id
2778            treename = G2frame.PatternTree.GetItemText(Id)
2779            G2frame.AutointPWDRnames.append(treename)
2780            # write out the images in the selected formats
2781            Sdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Sample Parameters'))
2782            if self.AutoScale:
2783                print 'Rescale by %.4f'%(Scale)
2784                y,w = G2frame.PatternTree.GetItemPyData(Id)[1][1:3]
2785                y *= Scale
2786                w /= Scale**2
2787            # determine the name for the current file
2788            fileroot = namepre
2789            if len(G2frame.IntgOutList) > 1:
2790                fileroot += "_AZM"
2791                if 'Azimuth' in Sdata:
2792                    fileroot += str(int(10*Sdata['Azimuth']))
2793                fileroot += "_" 
2794            fileroot += namenum
2795            # loop over selected formats
2796            for dfmt in self.fmtlist:
2797                if not self.params[dfmt[1:]]: continue
2798                if self.params['SeparateDir']:
2799                    subdir = dfmt[1:]
2800                else:
2801                    subdir = ''
2802                fil = os.path.join(self.params['outdir'],subdir,fileroot)
2803                print('writing file '+fil+dfmt)
2804                G2IO.ExportPowder(G2frame,treename,fil,dfmt)
2805               
2806    def EnableButtons(self,flag):
2807        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
2808        '''
2809        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
2810        # on windows
2811        if sys.platform != "win32":
2812            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
2813        self.btnstart.SetLabel('Pause')
2814        wx.Yield()
2815               
2816    def ResetFromTable(self,dist):
2817        '''Sets integration parameters based on values from
2818        the lookup table
2819        '''
2820        #dist = self.controlsDict['distance']
2821        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
2822        if GSASIIpath.GetConfigValue('debug'):
2823            print 'interpolated values: ',interpDict
2824        self.ImageControls = ReadControls(imgctrl)
2825        self.ImageControls.update(interpDict)
2826        self.ImageControls['showLines'] = True
2827        self.ImageControls['ring'] = []
2828        self.ImageControls['rings'] = []
2829        self.ImageControls['ellipses'] = []
2830        self.ImageControls['setDefault'] = False
2831        for i in 'range','size','GonioAngles':
2832            if i in self.ImageControls:
2833                del self.ImageControls[i]
2834        # load copy of Image Masks
2835        if immask:
2836            self.ImageMasks = ReadMask(immask)
2837            if list(self.ImageMasks['Thresholds'][0]) == self.ImageMasks['Thresholds'][1]:     #avoid copy of unchanged thresholds
2838                del self.ImageMasks['Thresholds']
2839        else:
2840            self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
2841       
2842    def StartLoop(self):
2843        '''Prepare to start autointegration timer loop.
2844        Save current Image params for use in future integrations
2845        also label the window so users understand what is being used
2846        '''
2847        print('\nStarting new autointegration\n')
2848        G2frame = self.G2frame
2849        # show current IMG base
2850        if self.params['Mode'] != 'table':
2851            self.useActive.SetLabel("Active Image: "+
2852                                    G2frame.PatternTree.GetItemText(self.imageBase))
2853            # load copy of Image Controls from current image and clean up
2854            # items that should not be copied
2855            self.ImageControls = copy.deepcopy(
2856                G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2857                    G2frame,self.imageBase, 'Image Controls')))
2858            self.ImageControls['showLines'] = True
2859            self.ImageControls['ring'] = []
2860            self.ImageControls['rings'] = []
2861            self.ImageControls['ellipses'] = []
2862            self.ImageControls['setDefault'] = False
2863            del self.ImageControls['range']
2864            del self.ImageControls['size']
2865            del self.ImageControls['GonioAngles']
2866            # load copy of Image Masks, keep thresholds
2867            self.ImageMasks = copy.deepcopy(
2868                G2frame.PatternTree.GetItemPyData(
2869                    G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Masks')))
2870            self.Thresholds = self.ImageMasks['Thresholds'][:]
2871            if list(self.Thresholds[0]) == self.Thresholds[1]:     #avoid copy of unchanged thresholds
2872                del self.ImageMasks['Thresholds']   
2873        # make sure all output directories exist
2874        if self.params['SeparateDir']:
2875            for dfmt in self.fmtlist:
2876                if not self.params[dfmt[1:]]: continue
2877                dir = os.path.join(self.params['outdir'],dfmt[1:])
2878                if not os.path.exists(dir): os.makedirs(dir)
2879        else:
2880            if not os.path.exists(self.params['outdir']):
2881                os.makedirs(self.params['outdir'])
2882        if self.Reset: # special things to do after Reset has been pressed
2883            self.G2frame.IntegratedList = []
2884           
2885            if self.params['Mode'] != 'table': # reset controls and masks for all IMG items in tree to master
2886                for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
2887                    # update controls from master
2888                    controlsDict = G2frame.PatternTree.GetItemPyData(
2889                        G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Image Controls'))
2890                    controlsDict.update(self.ImageControls)
2891                    # update masks from master
2892                    ImageMasks = G2frame.PatternTree.GetItemPyData(
2893                        G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Masks'))
2894                    ImageMasks.update(self.ImageMasks)
2895            # delete all PWDR items created after last Start was pressed
2896            idlist = []
2897            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
2898            while item:
2899                itemName = G2frame.PatternTree.GetItemText(item)
2900                if itemName in G2frame.AutointPWDRnames:
2901                    idlist.append(item)
2902                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
2903            for item in idlist:
2904                G2frame.PatternTree.Delete(item)
2905            wx.Yield()
2906            self.Reset = False
2907        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
2908        G2frame.AutointPDFnames = [] # list of created PWDR tree item names
2909        # check that AutoPDF input is OK, offer chance to use alternate PDWRs if referenced ones
2910        # are not present
2911        if self.params['ComputePDF']:
2912            msg = self.checkPDFprm()
2913            if 'Error:' in msg:
2914                print(msg)
2915                return True
2916            fileList = []
2917            id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
2918            while id:
2919                name = G2frame.PatternTree.GetItemText(id)
2920                if name.startswith('PWDR '): fileList.append(name)
2921                id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
2922            if not fileList:
2923                print(msg)
2924                print('No PWDR entries to select')
2925                return True
2926            for key in ['Sample Bkg.','Container','Container Bkg.']:
2927                name = self.pdfControls[key]['Name']
2928                if not name: continue
2929                if not G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name):
2930                    indx = G2G.ItemSelector(fileList, self, header='Select PDWR item',
2931                                    title='Select a PDWR tree item for '+key+'\n(or cancel to quit)')
2932                    if indx is None:
2933                        print('No PWDR entry selected for '+key)
2934                        return True
2935                    self.pdfControls[key]['Name'] = fileList[indx]
2936        return False
2937               
2938    def OnTimerLoop(self,event):
2939        '''A method that is called every :meth:`PollTime` seconds that is
2940        used to check for new files and process them. Integrates new images.
2941        Also optionally sets up and computes PDF.
2942        This is called only after the "Start" button is pressed (then its label reads "Pause").
2943        '''
2944        def AutoIntegrateImage(imgId):
2945            '''Integrates an image that has been read into the data tree and updates the
2946            AutoInt window.
2947            '''
2948            img = G2frame.PatternTree.GetItemText(imgId)
2949            controlsDict = G2frame.PatternTree.GetItemPyData(
2950                G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
2951            ImageMasks = G2frame.PatternTree.GetItemPyData(
2952                G2gd.GetPatternTreeItemId(G2frame,imgId, 'Masks'))
2953            if self.params['Mode'] == 'table': # look up parameter values from table
2954                self.ResetFromTable(controlsDict['setdist'])
2955            # update controls from master
2956            controlsDict.update(self.ImageControls)
2957            # update masks from master w/o Thresholds
2958            ImageMasks.update(self.ImageMasks)
2959            self.EnableButtons(False)
2960            try:
2961                self.IntegrateImage(img)
2962            finally:
2963                self.EnableButtons(True)
2964            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
2965            wx.Yield()
2966            self.ShowMatchingFiles(self.params['filter'])
2967            wx.Yield()
2968           
2969        def AutoComputePDF(imgId):
2970            '''Computes a PDF for a PWDR data tree tree item
2971            '''
2972            for pwdr in G2frame.AutointPWDRnames[:]:
2973                if not pwdr.startswith('PWDR '): continue
2974                if pwdr in G2frame.AutointPDFnames: continue
2975                PWid = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,pwdr)
2976                controlsDict = G2frame.PatternTree.GetItemPyData(
2977                    G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
2978                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
2979                    print('Skipping PDF for '+pwdr+' due to detector position')
2980                    continue
2981                # Setup PDF
2982                Data = G2frame.PatternTree.GetItemPyData(PWid)[1]
2983                pwdrMin = np.min(Data[1])
2984                Parms = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2985                    G2frame,PWid,'Instrument Parameters'))[0]
2986                fullLimits = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2987                    G2frame,PWid,'Limits'))[0]
2988                if 'C' in Parms['Type'][0]:
2989                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
2990                else:
2991                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
2992                Qlimits = [0.9*qMax,qMax]
2993
2994                item = pwdr
2995                Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2996                    G2frame,imgId, 'Comments'))
2997                ElList = {}
2998                sumnum = 1.0
2999                for item in Comments:           #grab chemical formula from Comments, if there
3000                    if 'formula' in item[:15].lower():
3001                        formula = item.split('=')[1].split()
3002                        elems = formula[::2]
3003                        nums = formula[1::2]
3004                        formula = zip(elems,nums)
3005                        sumnum = 0.
3006                        for [elem,num] in formula:
3007                            ElData = G2elem.GetElInfo(elem,Parms)
3008                            ElData['FormulaNo'] = float(num)
3009                            sumnum += float(num)
3010                            ElList[elem] = ElData
3011                PDFnames = G2gd.GetPatternTreeDataNames(G2frame,['PDF ',])
3012                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits,sumnum,pwdrMin,PDFnames)
3013                if not PDFid: continue
3014                PDFdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
3015                    G2frame,PDFid, 'PDF Controls'))
3016                PDFdata.update(self.pdfControls)
3017                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
3018                PDFdata['Sample']['Name'] = pwdr
3019                # compute PDF
3020                wx.Yield()
3021                G2pdG.computePDF(G2frame,PDFdata)
3022                wx.Yield()
3023                G2frame.PatternId = PDFid
3024                G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3025                if self.params['optPDF']:
3026                    G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
3027                    wx.Yield()
3028                    G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3029                G2frame.AutointPDFnames.append(pwdr)
3030                # save names of PDF entry to be deleted later if needed
3031                G2frame.AutointPWDRnames.append(G2frame.PatternTree.GetItemText(PDFid))
3032           
3033        G2frame = self.G2frame
3034        try:
3035            self.currImageList = sorted(
3036                glob.glob(os.path.join(self.imagedir,self.params['filter'])))
3037            self.ShowMatchingFiles(self.params['filter'])
3038        except IndexError:
3039            self.currImageList = []
3040            return
3041
3042        if self.PreventReEntryTimer: return
3043        self.PreventReEntryTimer = True
3044        imageFileList = []
3045        # integrate the images that have already been read in, but
3046        # have not yet been processed           
3047        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
3048            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
3049            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
3050            # Create a list of image files that have been read in
3051            if imagefile not in imageFileList: imageFileList.append(imagefile)
3052            # skip if already integrated
3053            if img in G2frame.IntegratedList: continue
3054            AutoIntegrateImage(imgId)
3055            #self.Pause |= G2frame.PauseIntegration
3056            #if self.Pause:
3057            #    self.OnPause()
3058            #    self.PreventReEntryTimer = False
3059            #    return
3060            if self.pdfControls: AutoComputePDF(imgId)
3061            self.Pause |= G2frame.PauseIntegration
3062            if self.Pause:
3063                self.OnPause()
3064                self.PreventReEntryTimer = False
3065                return
3066
3067        # loop over image files matching glob, reading in any new ones
3068        for newImage in self.currImageList:
3069            if newImage in imageFileList or self.Pause: continue # already read?
3070            for imgId in G2IO.ReadImages(G2frame,newImage):
3071                AutoIntegrateImage(imgId)           
3072                #self.Pause |= G2frame.PauseIntegration
3073                #if self.Pause:
3074                #    self.OnPause()
3075                #    self.PreventReEntryTimer = False
3076                #    return
3077                if self.pdfControls: AutoComputePDF(imgId)
3078                self.Pause |= G2frame.PauseIntegration
3079                if self.Pause:
3080                    self.OnPause()
3081                    self.PreventReEntryTimer = False
3082                    return
3083        if GSASIIpath.GetConfigValue('debug'):
3084            import datetime
3085            print ("Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
3086        self.PreventReEntryTimer = False
3087
3088def DefineEvaluator(dlg):
3089    '''Creates a function that provides interpolated values for a given distance value
3090    '''
3091    def Evaluator(dist):
3092        '''Interpolate image parameters for a supplied distance value
3093
3094        :param float dist: distance to use for interpolation
3095        :returns: a list with 3 items:
3096
3097          * a dict with parameter values,
3098          * the closest imctrl and
3099          * the closest maskfile (or None)
3100        '''           
3101        x = np.array([float(i) for i in parms[0]])
3102        closest = abs(x-dist).argmin()
3103        D = {'setdist':dist}
3104        imctfile = IMfileList[closest]
3105        if parms[-1][closest].lower() != '(none)':
3106            maskfile = parms[-1][closest]
3107        else:
3108            maskfile = None
3109        for c in range(1,cols-1):
3110            lbl = ParmList[c]
3111            if lbl in nonInterpVars:
3112                if lbl in ['outChannels',]:
3113                    D[lbl] = int(float(parms[c][closest]))
3114                else:
3115                    D[lbl] = float(parms[c][closest])
3116            else:
3117                y = np.array([float(i) for i in parms[c]])
3118                D[lbl] = np.interp(dist,x,y)
3119        # full integration when angular range is 0
3120        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
3121        # conversion for paired values
3122        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
3123            r = a.split('_')[0]
3124            D[r] = [D[a],D[b]]
3125            if r in ['LRazimuth',]:
3126                D[r] = [int(D[a]),int(D[b])]
3127            del D[a]
3128            del D[b]
3129        return D,imctfile,maskfile
3130    # save local copies of values needed in Evaluator
3131    parms = dlg.ReadImageParmTable()
3132    IMfileList = dlg.IMfileList
3133    cols = dlg.list.GetColumnCount()
3134    ParmList = dlg.ParmList
3135    nonInterpVars = dlg.nonInterpVars
3136    return Evaluator
3137
3138class IntegParmTable(wx.Dialog):
3139    '''Creates a dialog window with a table of integration parameters.
3140    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
3141    In this case, :func:`DefineEvaluator` should be called to obtain a function that
3142    creates a dictionary with interpolated parameter values.
3143    '''
3144    ParmList = ('setdist','distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
3145            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
3146            'maskfile',
3147            )
3148    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
3149                     'outChannels')  # values in this list are taken from nearest rather than interpolated
3150    HeaderList = ('Set Dist','Calib Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
3151            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
3152            'Mask File',
3153            )
3154    def __init__(self,parent,parms=None,IMfileList=None):
3155        self.G2frame = parent.G2frame
3156        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
3157        if parms:
3158            self.parms = parms # list of values by column
3159            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
3160        else:
3161            self.parms = [] # list of values by column
3162            self.IMfileList = [] # list of .imctrl file names for each entry in table
3163            files = []
3164            try:
3165                pth = G2G.GetImportPath(self.G2frame)
3166                if not pth: pth = '.'
3167                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3168                    style=wx.OPEN| wx.MULTIPLE,
3169                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3170                dlg.CenterOnParent()
3171                if dlg.ShowModal() == wx.ID_OK:
3172                    files = dlg.GetPaths()
3173                    self.parms,self.IMfileList = self.ReadFiles(files)
3174            finally:
3175                dlg.Destroy()
3176            if not files:
3177                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3178                return
3179        mainSizer = wx.BoxSizer(wx.VERTICAL)
3180        self.list = ImgIntLstCtrl(self, wx.ID_ANY,
3181                      style=wx.LC_REPORT
3182                          | wx.BORDER_SUNKEN
3183                         #| wx.BORDER_NONE
3184                         )
3185        mainSizer.Add(self.list,1,wx.EXPAND,1)
3186        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3187        btn = wx.Button(self, wx.ID_OK)
3188        btnsizer.Add(btn)
3189        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3190        btn.Bind(wx.EVT_BUTTON,self._onSave)
3191        btnsizer.Add(btn)
3192        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3193        btn.Bind(wx.EVT_BUTTON,self._onClose)
3194        btnsizer.Add(btn)
3195        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3196        self.SetSizer(mainSizer)
3197        self.list.FillList(self.parms)
3198        mainSizer.Layout()
3199        mainSizer.Fit(self)
3200       
3201    def ReadFiles(self,files):
3202        '''Reads a list of .imctrl files or a single .imtbl file
3203        '''
3204        tmpDict = {}
3205        if not files: return
3206        # option 1, a dump from a previous save
3207        if os.path.splitext(files[0])[1] == '.imtbl':
3208            fp = open(files[0],'r')
3209            S = fp.readline()
3210            while S:
3211                if S[0] != '#':
3212                    [key,val] = S[:-1].split(':',1)
3213                    tmpDict[key] = eval(val)
3214                S = fp.readline()
3215            fp.close()
3216            # delete entries where files do not exist
3217            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3218            if m1:
3219                print('\nimctrl file not found:')
3220                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3221            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3222            if m2:
3223                print('\nmask file not found')
3224                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3225            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3226            if m3:
3227                print('\nDropping entries due to negative distance: '+str(m3))
3228            m = sorted(set(m1 + m2 + m3))
3229            m.reverse()
3230            for c in m:
3231                for key in tmpDict:
3232                    del tmpDict[key][c]
3233            fileList = tmpDict.get('filenames','[]')
3234            parms = []
3235            if 'setdist' not in tmpDict:
3236                print(u'Old file, recreate before using: {}'.format(files[0]))
3237                return [[]],[]
3238            for key in self.ParmList:
3239                try:
3240                    float(tmpDict[key][0])
3241                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3242                except ValueError:
3243                    parms.append(tmpDict[key])
3244                except IndexError:
3245                    print('No valid image control entries read')
3246                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3247                    return [[]],[]
3248            return parms,fileList
3249        # option 2, read in a list of files
3250        for file in files: # read all files; place in dict by distance
3251            imgDict = Read_imctrl(file)
3252            dist = imgDict.get('setdist')
3253            if dist is None:
3254                print('Skipping old file, redo: {}'.format(file))
3255            tmpDict[dist] = imgDict
3256        parms = [[] for key in self.ParmList]
3257        fileList = []
3258        for d in sorted(tmpDict):
3259            fileList.append(tmpDict[d].get('filename'))
3260            if d is None: continue
3261            if d < 0: continue
3262            for i,key in enumerate(self.ParmList):
3263                val = tmpDict[d].get(key)
3264                try:
3265                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3266                except:
3267                    val = str(val)
3268                parms[i].append(val)
3269        return parms,fileList
3270   
3271    def ReadImageParmTable(self):
3272        '''Reads possibly edited values from the ListCtrl table and returns a list
3273        of values for each column.
3274        '''
3275        rows = self.list.GetItemCount()
3276        cols = self.list.GetColumnCount()
3277        parms = []
3278        for c in range(cols):
3279            parms.append([])
3280            for r in range(rows):
3281                parms[c].append(self.list.GetItem(r,c).GetText())
3282        return parms
3283
3284    def _onClose(self,event):
3285        'Called when Cancel button is pressed'
3286        self.EndModal(wx.ID_CANCEL)
3287       
3288    def _onSave(self,event):
3289        'Called when save button is pressed; creates a .imtbl file'
3290        fil = ''
3291        if self.G2frame.GSASprojectfile:
3292            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3293        dir,f = os.path.split(fil)
3294        pth = G2G.GetExportPath(self.G2frame)
3295        try:
3296            dlg = wx.FileDialog(self, 'Save table data as',
3297                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3298                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3299            dlg.CenterOnParent()
3300            if dlg.ShowModal() != wx.ID_OK: return
3301            fil = dlg.GetPath()
3302            fil = os.path.splitext(fil)[0]+'.imtbl'
3303        finally:
3304            dlg.Destroy()       
3305        parms = self.ReadImageParmTable()
3306        print('Writing image parameter table as '+fil)
3307        fp = open(fil,'w')
3308        for c in range(len(parms)-1):
3309            lbl = self.ParmList[c]
3310            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3311        lbl = self.ParmList[c+1]
3312        fp.write(lbl+': '+str(parms[c+1])+'\n')
3313        lbl = 'filenames'
3314        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3315        fp.close()
3316   
3317class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3318    '''Creates a custom ListCtrl for editing Image Integration parameters
3319    '''
3320    def __init__(self, parent, ID, pos=wx.DefaultPosition,
3321                 size=(1000,200), style=0):
3322        self.parent=parent
3323        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3324        listmix.ListCtrlAutoWidthMixin.__init__(self)
3325        listmix.TextEditMixin.__init__(self)
3326        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3327        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3328    def FillList(self,parms):
3329        'Places the current parms into the table'
3330        self.ClearAll()
3331        self.rowlen = len(self.parent.ParmList)
3332        for i,lbl in enumerate(self.parent.HeaderList):
3333            self.InsertColumn(i, lbl)
3334        for r,d in enumerate(parms[0]):
3335            if d is None: continue
3336            if float(d) < 0: continue
3337            index = self.InsertStringItem(sys.maxint, d)
3338            for j in range(1,len(parms)):
3339                self.SetStringItem(index, j, parms[j][r])
3340        for i,lbl in enumerate(self.parent.ParmList):
3341            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3342
3343    def OnDouble(self,evt):
3344        'respond to a double-click'
3345        self.CloseEditor()
3346        fil = '(none)'
3347        pth = G2G.GetImportPath(self.parent.G2frame)
3348        if not pth: pth = '.'
3349        try:
3350            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3351                                style=wx.OPEN,
3352                                wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3353            dlg.CenterOnParent()
3354            if dlg.ShowModal() == wx.ID_OK:
3355                fil = dlg.GetPath()
3356        finally:
3357            dlg.Destroy()
3358        if os.path.splitext(fil)[1] != '.imctrl':
3359            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3360            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3361        else:
3362            # insert or overwrite an instrument parameter set
3363            if not os.path.exists(fil):
3364                print('Does not exist: '+fil)
3365                return
3366            imgDict = Read_imctrl(fil)
3367            dist = imgDict['distance']
3368            parms = self.parent.ReadImageParmTable()
3369            x = np.array([float(i) for i in parms[0]])
3370            closest = abs(x-dist).argmin()
3371            closeX = x[closest]
3372            # fix IMfileList
3373            for c,lbl in enumerate(self.parent.ParmList):
3374                try:
3375                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3376                except ValueError:
3377                    vali = imgDict[lbl]
3378                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3379                    parms[c][closest] = vali
3380                elif dist > closeX: # insert after
3381                    parms[c].insert(closest+1,vali)
3382                else:
3383                    parms[c].insert(closest,vali)
3384            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3385                self.parent.IMfileList[closest] = fil
3386            elif dist > closeX: # insert after
3387                self.parent.IMfileList.insert(closest+1,fil)
3388            else:
3389                self.parent.IMfileList.insert(closest,fil)
3390            self.FillList(parms)
3391# Autointegration end
3392###########################################################################
Note: See TracBrowser for help on using the repository browser.