source: trunk/GSASIIimgGUI.py @ 3124

Last change on this file since 3124 was 3124, checked in by vondreele, 4 years ago

fixes for AutoIntegrate? from imgctrl table

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