source: branch/2frame/GSASIIimgGUI.py @ 2900

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

reorg for GUI separation pretty much complete (GSASIIIO.py still needs some thought)

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