source: trunk/GSASIIimgGUI.py @ 3039

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

fixes to 2D strain fitting & remove no longer used code

  • 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-09-02 12:50:17 +0000 (Sat, 02 Sep 2017) $
5# $Author: vondreele $
6# $Revision: 3039 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 3039 2017-09-02 12:50:17Z 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: 3039 $")
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['setdist'])
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                save['LRazimuth_min'],save['LRazimuth_max'] = eval(val)[0:2]
2215            elif key == 'IOtth':
2216                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
2217            elif key == 'center':
2218                if ',' in val:
2219                    vals = eval(val)
2220                else:
2221                    vals = val.strip('[] ').split()
2222                    vals = [float(vals[0]),float(vals[1])] 
2223                save['center_x'],save['center_y'] = vals[0:2]
2224            elif key in cntlList:
2225                save[key] = eval(val)
2226            S = File.readline()
2227    finally:
2228        File.close()
2229        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0.,0.
2230    return save
2231   
2232class AutoIntFrame(wx.Frame):
2233    '''Creates a wx.Frame window for the Image AutoIntegration.
2234    The intent is that this will be used as a non-modal dialog window.
2235   
2236    Implements a Start button that morphs into a pause and resume button.
2237    This button starts a processing loop that is repeated every
2238    :meth:`PollTime` seconds.
2239
2240    :param wx.Frame G2frame: main GSAS-II frame
2241    :param float PollTime: frequency in seconds to repeat calling the
2242      processing loop. (Default is 30.0 seconds.)
2243    '''
2244    def __init__(self,G2frame,PollTime=30.0):
2245        def OnStart(event):
2246            '''Called when the start button is pressed. Changes button label
2247            to Pause. When Pause is pressed the label changes to Resume.
2248            When either Start or Resume is pressed, the processing loop
2249            is started. When Pause is pressed, the loop is stopped.
2250            '''
2251            # check inputs for errors before starting
2252            #err = ''
2253            #if not any([self.params[fmt] for fmt in self.fmtlist]):
2254            #    err += '\nPlease select at least one output format\n'
2255            #if err:
2256            #    G2G.G2MessageBox(self,err)
2257            #    return
2258            self.Pause = False
2259            # change button label
2260            if self.btnstart.GetLabel() != 'Pause':
2261                self.btnstart.SetLabel('Pause')
2262                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
2263                if self.timer.IsRunning(): self.timer.Stop()
2264                self.PreventReEntryTimer = False
2265                if self.StartLoop():
2266                    G2G.G2MessageBox(self,'Error in setting up integration. See console')
2267                    return
2268                self.OnTimerLoop(None) # run once immediately
2269                if not self.Pause:
2270                    # no pause, so start timer to check for new files
2271                    self.timer.Start(int(1000*PollTime),oneShot=False)
2272                    return
2273            # we will get to this point if Paused
2274            self.OnPause()
2275           
2276        def OnReset(event):
2277            '''Called when Reset button is pressed. This stops the
2278            processing loop and resets the list of integrated files so
2279            all images can be reintegrated.
2280            '''
2281            self.btnstart.SetLabel('Restart')
2282            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
2283            if self.timer.IsRunning(): self.timer.Stop()
2284            self.Reset = True
2285            self.Pause = True
2286           
2287        def OnQuit(event):
2288            '''Stop the processing loop and close the Frame
2289            '''
2290            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
2291            wx.CallAfter(self.Destroy)
2292           
2293        def OnBrowse(event):
2294            '''Responds when the Browse button is pressed to load a file.
2295            The routine determines which button was pressed and gets the
2296            appropriate file type and loads it into the appropriate place
2297            in the dict.
2298            '''
2299            if btn3 == event.GetEventObject():
2300                dlg = wx.DirDialog(
2301                    self, 'Select directory for output files',
2302                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
2303                dlg.CenterOnParent()
2304                try:
2305                    if dlg.ShowModal() == wx.ID_OK:
2306                        self.params['outdir'] = dlg.GetPath()
2307                        fInp3.SetValue(self.params['outdir'])
2308                finally:
2309                    dlg.Destroy()
2310                return
2311            elif btn4 == event.GetEventObject():
2312                msg = ''
2313                pth = G2G.GetExportPath(G2frame)
2314                dlg = wx.FileDialog(
2315                    self, 'Select a PDF parameter file',
2316                    pth, self.params['pdfprm'], 
2317                    "PDF controls file (*.pdfprm)|*.pdfprm",
2318                    wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
2319                dlg.CenterOnParent()
2320                try:
2321                    if dlg.ShowModal() == wx.ID_OK:
2322                        self.params['pdfprm'] = dlg.GetPath()
2323                        fInp4.SetValue(self.params['pdfprm'])
2324                        scanPDFprm()
2325                        msg = self.checkPDFprm(True)
2326                finally:
2327                    dlg.Destroy()
2328                if 'Error' in msg:
2329                    print(msg)
2330                    lbl = 'PDFPRM error'
2331                else:
2332                    msg = 'Information from file {}\n\n{}'.format(self.params['pdfprm'],msg)
2333                    lbl = 'PDFPRM information'
2334                G2G.G2MessageBox(self,msg,lbl)
2335                return
2336               
2337        def OnRadioSelect(event):
2338            '''Respond to a radiobutton selection and when in table
2339            mode, get distance-dependent parameters from user.
2340            '''
2341            self.Evaluator = None
2342            if self.useTable.GetValue():
2343                dlg = None
2344                try:
2345                    dlg = IntegParmTable(self) # create the dialog
2346                    dlg.CenterOnParent()
2347                    if dlg.ShowModal() == wx.ID_OK:
2348                        self.ImgTblParms = dlg.parms
2349                        self.IMfileList = dlg.IMfileList
2350                        self.Evaluator = DefineEvaluator(dlg)
2351                        self.params['Mode'] = 'table'
2352                        self.editTable.Enable(True)
2353                    else:
2354                        self.useActive.SetValue(True)
2355                finally:
2356                    if dlg: dlg.Destroy()
2357            elif self.useActive.GetValue():
2358                self.params['Mode'] = 'active'
2359                self.imageBase = G2frame.Image
2360                self.useActive.SetLabel("Active Image: "+
2361                        G2frame.GPXtree.GetItemText(self.imageBase))
2362                self.editTable.Enable(False)
2363            else:
2364                print('unexpected mode in OnRadioSelect')
2365
2366        def OnEditTable(event):
2367            '''Called to edit the distance-dependent parameter look-up table.
2368            Should be called only when table is defined and active.
2369            '''
2370            dlg = None
2371            try:
2372                dlg = IntegParmTable(self,self.ImgTblParms,self.IMfileList)
2373                dlg.CenterOnParent()
2374                if dlg.ShowModal() == wx.ID_OK:
2375                    self.ImgTblParms = dlg.parms
2376                    self.IMfileList = dlg.IMfileList
2377                    self.Evaluator = DefineEvaluator(dlg)
2378                    self.params['Mode'] = 'table'
2379                    self.editTable.Enable(True)
2380                else:
2381                    self.useActive.SetValue(True)
2382                    self.params['Mode'] = 'active'
2383                    self.imageBase = G2frame.Image
2384                    self.useActive.SetLabel("Active Image: "+
2385                            G2frame.GPXtree.GetItemText(self.imageBase))
2386                    self.editTable.Enable(False)
2387            finally:
2388                if dlg: dlg.Destroy()
2389               
2390        def showPDFctrls(event):
2391            '''Called to show or hide AutoPDF widgets. Note that fInp4 must be included in the
2392            sizer layout with .Show(True) before .Show(False) will work properly.
2393            '''
2394            fInp4.Enable(self.params['ComputePDF'])
2395            fInp4.Show(self.params['ComputePDF'])
2396            fInp4a.Enable(self.params['ComputePDF'])
2397            btn4.Enable(self.params['ComputePDF'])
2398            cOpt.Enable(self.params['ComputePDF'])
2399            if self.params['ComputePDF']:
2400                lbl4.SetForegroundColour("black")
2401                lbl4a.SetForegroundColour("black")
2402            else:
2403                lbl4.SetForegroundColour("gray")
2404                lbl4a.SetForegroundColour("gray")
2405                                   
2406        def scanPDFprm(**kw):
2407            fInp4.invalid = not os.path.exists(fInp4.GetValue())
2408            fInp4._IndicateValidity()
2409           
2410        def OnAutoScale(event):
2411            self.AutoScale = autoscale.GetValue()
2412           
2413        def OnAutoScaleName(event):
2414            self.AutoScaleName = scalename.GetValue()
2415            self.Scale[0] = self.AutoScales[self.AutoScaleName]
2416            scaleval.SetValue(self.Scale[0])
2417               
2418        ##################################################
2419        # beginning of __init__ processing
2420        ##################################################
2421        self.G2frame = G2frame
2422        self.ImgTblParms = None
2423        self.IMfileList = None
2424        self.Evaluator = None
2425        self.params = {}
2426        self.Reset = False
2427        self.Pause = False
2428        self.PreventReEntryShowMatch = False
2429        self.PreventReEntryTimer = False
2430        self.params['IMGfile'] = ''
2431        self.params['MaskFile'] = ''
2432        self.params['IgnoreMask'] = True
2433        self.fmtlist = G2IO.ExportPowderList(G2frame)
2434        self.timer = wx.Timer()
2435        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
2436        self.imageBase = G2frame.Image
2437        self.params['ComputePDF'] = False
2438        self.params['pdfDmax'] = 0.0
2439        self.params['pdfprm'] = ''
2440        self.params['optPDF'] = True
2441        self.pdfControls = {}
2442        self.AutoScale = False
2443        self.Scale = [1.0,]
2444
2445        G2frame.GPXtree.GetSelection()
2446        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(self.imageBase) 
2447        self.imagedir,fileroot = os.path.split(imagefile)
2448        Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2449            G2frame,self.imageBase, 'Comments'))
2450        DefaultAutoScaleNames = GSASIIpath.GetConfigValue('Autoscale_ParmNames')
2451        self.AutoScaleName = GSASIIpath.GetConfigValue('DefaultAutoScale')
2452        self.AutoScales = {}
2453        if DefaultAutoScaleNames is not None:
2454            for comment in Comments:
2455                if '=' in comment:
2456                    name,val = comment.split('=',1) 
2457                    if name in DefaultAutoScaleNames:
2458                        try:
2459                            self.AutoScales[name] = float(val)
2460                            if name == self.AutoScaleName:
2461                                self.Scale[0] = float(val)
2462                        except ValueError:
2463                            continue
2464        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2465        self.params['outdir'] = os.path.abspath(self.imagedir)
2466        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2467                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2468        self.Status = self.CreateStatusBar()
2469        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2470        mnpnl = wx.Panel(self)
2471        mnsizer = wx.BoxSizer(wx.VERTICAL)
2472        # box for integration controls & masks input
2473        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2474        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2475        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2476        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2477                            style = wx.RB_GROUP)
2478        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2479        self.useActive.SetLabel("Active Image: "+
2480                                G2frame.GPXtree.GetItemText(self.imageBase))
2481        lblsizr.Add(self.useActive,1,wx.EXPAND,1)
2482        self.useActive.SetValue(True)
2483        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2484        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2485        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2486        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2487        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2488        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2489        self.editTable.Enable(False)
2490        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2491        # bind button and deactivate be default
2492        lblsizr.Add(minisizer)
2493        mnsizer.Add(lblsizr,1,wx.EXPAND,1)
2494
2495        # file filter stuff
2496        sizer = wx.BoxSizer(wx.HORIZONTAL)
2497        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Image filter'))
2498        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',OnLeave=self.ShowMatchingFiles)
2499        sizer.Add(flterInp)
2500        mnsizer.Add(sizer,0,wx.ALIGN_RIGHT,1)
2501        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2502        mnsizer.Add(self.ListBox,0,wx.EXPAND,1)
2503        self.ShowMatchingFiles(self.params['filter'])
2504        # box for output selections
2505        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2506        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2507        sizer = wx.BoxSizer(wx.HORIZONTAL)
2508        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2509        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2510        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2511        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2512        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2513        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2514        lblsizr.Add(sizer,0,wx.EXPAND)
2515        sizer = wx.BoxSizer(wx.HORIZONTAL)
2516        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
2517        for dfmt in self.fmtlist:
2518            fmt = dfmt[1:]
2519            self.params[fmt] = False
2520            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2521            sizer.Add(btn)
2522        lblsizr.Add(sizer)
2523        sizer = wx.BoxSizer(wx.HORIZONTAL)
2524        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2525        self.params['SeparateDir'] = False
2526        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2527        lblsizr.Add(sizer)
2528        if self.AutoScales:
2529            sizer = wx.BoxSizer(wx.HORIZONTAL)
2530            autoscale = wx.CheckBox(mnpnl,label='Do autoscaling with:')
2531            autoscale.Bind(wx.EVT_CHECKBOX,OnAutoScale)
2532            sizer.Add(autoscale,0,WACV)
2533            scalename = wx.ComboBox(mnpnl,value=self.AutoScaleName,choices=self.AutoScales.keys(),
2534                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2535            scalename.Bind(wx.EVT_COMBOBOX,OnAutoScaleName)
2536            sizer.Add(scalename,0,WACV)
2537            sizer.Add(wx.StaticText(mnpnl,label=' to '),0,WACV)
2538            scaleval = G2G.ValidatedTxtCtrl(mnpnl,self.Scale,0,nDig=(10,2),min=1.)
2539            sizer.Add(scaleval,0,WACV)
2540            lblsizr.Add(sizer,0)
2541        #ToDO: Autonormalize, parm name?, scaling value?
2542        sizer = wx.BoxSizer(wx.HORIZONTAL)
2543        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
2544        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2545        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2546        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2547        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2548        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2549        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
2550        sizer.Add(cOpt)
2551        lblsizr.Add(sizer,0)
2552        sizer = wx.BoxSizer(wx.HORIZONTAL)
2553        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2554        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2555        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2556                                     OnLeave=scanPDFprm)
2557        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2558        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2559        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2560        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2561        lblsizr.Add(sizer,0,wx.EXPAND)
2562        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2563        # buttons on bottom
2564        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2565        sizer = wx.BoxSizer(wx.HORIZONTAL)
2566        sizer.Add((20,-1))
2567        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2568        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2569        sizer.Add(self.btnstart)
2570        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2571        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2572        sizer.Add(self.btnreset)
2573        sizer.Add((20,-1),wx.EXPAND,1)
2574        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
2575        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
2576        sizer.Add(self.btnclose)
2577        sizer.Add((20,-1))
2578        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
2579        # finish up window
2580        mnpnl.SetSizer(mnsizer)
2581        OnRadioSelect(None) # disable widgets
2582        mnsizer.Fit(self)
2583        self.CenterOnParent()
2584        self.Show()
2585        showPDFctrls(None)
2586
2587    def checkPDFprm(self,ShowContents=False):
2588        '''Read in the PDF (.pdfprm) parameter file and check for problems.
2589        If ShowContents is True, a formatted text version of some of the file
2590        contents is returned. If errors are found, the return string will contain
2591        the string "Error:" at least once.
2592        '''
2593        self.pdfControls = {}
2594        msg = ''
2595        File = None
2596        try:
2597            File = open(self.params['pdfprm'],'r')
2598            S = File.readline()
2599            while S:
2600                if '#' in S:
2601                    S = File.readline()
2602                    continue
2603                key,val = S.split(':',1)
2604                try:
2605                    self.pdfControls[key] = eval(val)
2606                except:
2607                    self.pdfControls[key] = val
2608                S = File.readline()
2609        except Exception as err:
2610            msg += 'PDF Processing Error: error with open or read of {}'.format(self.params['pdfprm'])
2611            if GSASIIpath.GetConfigValue('debug'):
2612                print(msg)
2613                print(err)
2614            self.pdfControls = {}
2615            return msg
2616        finally:
2617            if File: File.close()
2618        formula = ''
2619        for el in self.pdfControls['ElList']:
2620            if self.pdfControls['ElList'][el]['FormulaNo'] <= 0: continue
2621            if formula: formula += ' '
2622            formula += '{}({:.1f})'.format(el,self.pdfControls['ElList'][el]['FormulaNo'])
2623        if not formula:
2624            msg += 'Error: no chemical formula in file'
2625        for key in ['Sample Bkg.','Container','Container Bkg.']:
2626            if key not in self.pdfControls:
2627                if msg: msg += '\n'
2628                msg += 'Error: missing key in self.pdfControls: '+key
2629                continue
2630        if msg or not ShowContents: return msg  # stop on error
2631        msg += 'Default formula: '+formula+'\n'
2632        for key in ['Sample Bkg.','Container','Container Bkg.']:
2633            name = self.pdfControls[key]['Name']
2634            mult = self.pdfControls[key].get('Mult',0.0)
2635            if not name: continue
2636            msg += '\n{}: {:.2f} * "{}"'.format(key,mult,name)
2637            if not G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,name):
2638                msg += ' *** missing ***'
2639        return msg
2640   
2641    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
2642        G2frame = self.G2frame
2643        if invalid: return
2644        msg = ''
2645        if self.PreventReEntryShowMatch: return
2646        self.PreventReEntryShowMatch = True
2647        imageFileList = []
2648        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
2649            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
2650            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
2651            if imagefile not in imageFileList: imageFileList.append(imagefile)
2652            if img not in G2frame.IntegratedList:
2653                if msg: msg += '\n'
2654                msg += '  ' + img
2655        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
2656        msg1 = ""
2657        try:
2658            imageList = sorted(
2659                glob.glob(os.path.join(self.imagedir,value)))
2660            if not imageList:
2661                msg1 = 'Warning: No files match search string '+os.path.join(self.imagedir,value)
2662            else:
2663                for fil in imageList:
2664                    if fil not in imageFileList: msg1 += '\n  '+fil
2665                if msg1:
2666                    msg += 'Files to integrate from '+os.path.join(self.imagedir,value)+msg1
2667                else:
2668                    msg += 'No files found to read'
2669        except IndexError:
2670            msg += 'Error searching for files named '+os.path.join(self.imagedir,value)
2671        self.ListBox.Clear()
2672        self.ListBox.AppendItems(msg.split('\n'))
2673        self.PreventReEntryShowMatch = False
2674        return
2675       
2676    def OnPause(self):
2677        '''Respond to Pause, changes text on button/Status line, if needed
2678        Stops timer
2679        self.Pause should already be True
2680        '''
2681        if self.timer.IsRunning(): self.timer.Stop()
2682        if self.btnstart.GetLabel() == 'Restart':
2683            return
2684        if self.btnstart.GetLabel() != 'Resume':
2685            print('\nPausing autointegration\n')
2686            self.btnstart.SetLabel('Resume')
2687            self.Status.SetStatusText(
2688                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
2689        self.Pause = True
2690           
2691    def IntegrateImage(self,img):
2692        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
2693        are placed in G2frame.IntgOutList
2694        '''
2695        G2frame = self.G2frame
2696        imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
2697        G2frame.Image = imgId
2698        G2frame.PickId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls')
2699        # do integration
2700        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
2701        if self.AutoScale:
2702            Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2703                G2frame,imgId, 'Comments'))
2704            for comment in Comments:
2705                if '=' in comment:
2706                    name,val = comment.split('=',1) 
2707                    if name == self.AutoScaleName:
2708                        val = float(val)
2709                        if val > 0.:
2710                            Scale = self.Scale[0]/val
2711                        break
2712        masks = G2frame.GPXtree.GetItemPyData(
2713            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Masks'))
2714        data = G2frame.GPXtree.GetItemPyData(G2frame.PickId)
2715        # simulate a Image Controls press, since that is where the
2716        # integration is hidden
2717        UpdateImageControls(G2frame,data,masks,IntegrateOnly=True)
2718        G2frame.IntegratedList.append(img) # note this as integrated
2719        # split name and control number
2720        s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
2721        namepre = s[0]
2722        if len(s) > 1:
2723            namenum = s[1]
2724        else:
2725            namenum = ''
2726        for Id in G2frame.IntgOutList: # loop over newly created PDWR entry(ies)
2727            # save the created PWDR tree names so that a reset can delete them
2728            G2frame.Image = Id
2729            treename = G2frame.GPXtree.GetItemText(Id)
2730            G2frame.AutointPWDRnames.append(treename)
2731            # write out the images in the selected formats
2732            Sdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Sample Parameters'))
2733            if self.AutoScale:
2734                print 'Rescale by %.4f'%(Scale)
2735                y,w = G2frame.GPXtree.GetItemPyData(Id)[1][1:3]
2736                y *= Scale
2737                w /= Scale**2
2738            # determine the name for the current file
2739            fileroot = namepre
2740            if len(G2frame.IntgOutList) > 1:
2741                fileroot += "_AZM"
2742                if 'Azimuth' in Sdata:
2743                    fileroot += str(int(10*Sdata['Azimuth']))
2744                fileroot += "_" 
2745            fileroot += namenum
2746            # loop over selected formats
2747            for dfmt in self.fmtlist:
2748                if not self.params[dfmt[1:]]: continue
2749                if self.params['SeparateDir']:
2750                    subdir = dfmt[1:]
2751                else:
2752                    subdir = ''
2753                fil = os.path.join(self.params['outdir'],subdir,fileroot)
2754                print('writing file '+fil+dfmt)
2755                G2IO.ExportPowder(G2frame,treename,fil,dfmt)
2756               
2757    def EnableButtons(self,flag):
2758        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
2759        '''
2760        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
2761        # on windows
2762        if sys.platform != "win32":
2763            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
2764        self.btnstart.SetLabel('Pause')
2765        wx.Yield()
2766               
2767    def ResetFromTable(self,dist):
2768        '''Sets integration parameters based on values from
2769        the lookup table
2770        '''
2771        #dist = self.controlsDict['distance']
2772        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
2773        if GSASIIpath.GetConfigValue('debug'):
2774            print 'interpolated values: ',interpDict
2775        self.ImageControls = ReadControls(imgctrl)
2776        self.ImageControls.update(interpDict)
2777        self.ImageControls['showLines'] = True
2778        self.ImageControls['ring'] = []
2779        self.ImageControls['rings'] = []
2780        self.ImageControls['ellipses'] = []
2781        self.ImageControls['setDefault'] = False
2782        for i in 'range','size','GonioAngles':
2783            if i in self.ImageControls:
2784                del self.ImageControls[i]
2785        # load copy of Image Masks
2786        if immask:
2787            self.ImageMasks = ReadMask(immask)
2788            if list(self.ImageMasks['Thresholds'][0]) == self.ImageMasks['Thresholds'][1]:     #avoid copy of unchanged thresholds
2789                del self.ImageMasks['Thresholds']
2790        else:
2791            self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
2792       
2793    def StartLoop(self):
2794        '''Prepare to start autointegration timer loop.
2795        Save current Image params for use in future integrations
2796        also label the window so users understand what is being used
2797        '''
2798        print('\nStarting new autointegration\n')
2799        G2frame = self.G2frame
2800        # show current IMG base
2801        if self.params['Mode'] != 'table':
2802            self.useActive.SetLabel("Active Image: "+
2803                                    G2frame.GPXtree.GetItemText(self.imageBase))
2804            # load copy of Image Controls from current image and clean up
2805            # items that should not be copied
2806            self.ImageControls = copy.deepcopy(
2807                G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2808                    G2frame,self.imageBase, 'Image Controls')))
2809            self.ImageControls['showLines'] = True
2810            self.ImageControls['ring'] = []
2811            self.ImageControls['rings'] = []
2812            self.ImageControls['ellipses'] = []
2813            self.ImageControls['setDefault'] = False
2814            del self.ImageControls['range']
2815            del self.ImageControls['size']
2816            del self.ImageControls['GonioAngles']
2817            # load copy of Image Masks, keep thresholds
2818            self.ImageMasks = copy.deepcopy(
2819                G2frame.GPXtree.GetItemPyData(
2820                    G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks')))
2821            self.Thresholds = self.ImageMasks['Thresholds'][:]
2822            if list(self.Thresholds[0]) == self.Thresholds[1]:     #avoid copy of unchanged thresholds
2823                del self.ImageMasks['Thresholds']   
2824        # make sure all output directories exist
2825        if self.params['SeparateDir']:
2826            for dfmt in self.fmtlist:
2827                if not self.params[dfmt[1:]]: continue
2828                dir = os.path.join(self.params['outdir'],dfmt[1:])
2829                if not os.path.exists(dir): os.makedirs(dir)
2830        else:
2831            if not os.path.exists(self.params['outdir']):
2832                os.makedirs(self.params['outdir'])
2833        if self.Reset: # special things to do after Reset has been pressed
2834            self.G2frame.IntegratedList = []
2835           
2836            if self.params['Mode'] != 'table': # reset controls and masks for all IMG items in tree to master
2837                for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
2838                    # update controls from master
2839                    controlsDict = G2frame.GPXtree.GetItemPyData(
2840                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Image Controls'))
2841                    controlsDict.update(self.ImageControls)
2842                    # update masks from master
2843                    ImageMasks = G2frame.GPXtree.GetItemPyData(
2844                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks'))
2845                    ImageMasks.update(self.ImageMasks)
2846            # delete all PWDR items created after last Start was pressed
2847            idlist = []
2848            item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
2849            while item:
2850                itemName = G2frame.GPXtree.GetItemText(item)
2851                if itemName in G2frame.AutointPWDRnames:
2852                    idlist.append(item)
2853                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
2854            for item in idlist:
2855                G2frame.GPXtree.Delete(item)
2856            wx.Yield()
2857            self.Reset = False
2858        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
2859        G2frame.AutointPDFnames = [] # list of created PWDR tree item names
2860        # check that AutoPDF input is OK, offer chance to use alternate PDWRs if referenced ones
2861        # are not present
2862        if self.params['ComputePDF']:
2863            msg = self.checkPDFprm()
2864            if 'Error:' in msg:
2865                print(msg)
2866                return True
2867            fileList = []
2868            id, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
2869            while id:
2870                name = G2frame.GPXtree.GetItemText(id)
2871                if name.startswith('PWDR '): fileList.append(name)
2872                id, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
2873            if not fileList:
2874                print(msg)
2875                print('No PWDR entries to select')
2876                return True
2877            for key in ['Sample Bkg.','Container','Container Bkg.']:
2878                name = self.pdfControls[key]['Name']
2879                if not name: continue
2880                if not G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name):
2881                    indx = G2G.ItemSelector(fileList, self, header='Select PDWR item',
2882                                    title='Select a PDWR tree item for '+key+'\n(or cancel to quit)')
2883                    if indx is None:
2884                        print('No PWDR entry selected for '+key)
2885                        return True
2886                    self.pdfControls[key]['Name'] = fileList[indx]
2887        return False
2888               
2889    def OnTimerLoop(self,event):
2890        '''A method that is called every :meth:`PollTime` seconds that is
2891        used to check for new files and process them. Integrates new images.
2892        Also optionally sets up and computes PDF.
2893        This is called only after the "Start" button is pressed (then its label reads "Pause").
2894        '''
2895        def AutoIntegrateImage(imgId):
2896            '''Integrates an image that has been read into the data tree and updates the
2897            AutoInt window.
2898            '''
2899            img = G2frame.GPXtree.GetItemText(imgId)
2900            controlsDict = G2frame.GPXtree.GetItemPyData(
2901                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
2902            ImageMasks = G2frame.GPXtree.GetItemPyData(
2903                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Masks'))
2904            if self.params['Mode'] == 'table': # look up parameter values from table
2905                self.ResetFromTable(controlsDict['setdist'])
2906            # update controls from master
2907            controlsDict.update(self.ImageControls)
2908            # update masks from master w/o Thresholds
2909            ImageMasks.update(self.ImageMasks)
2910            self.EnableButtons(False)
2911            try:
2912                self.IntegrateImage(img)
2913            finally:
2914                self.EnableButtons(True)
2915            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
2916            wx.Yield()
2917            self.ShowMatchingFiles(self.params['filter'])
2918            wx.Yield()
2919           
2920        def AutoComputePDF(imgId):
2921            '''Computes a PDF for a PWDR data tree tree item
2922            '''
2923            for pwdr in G2frame.AutointPWDRnames[:]:
2924                if not pwdr.startswith('PWDR '): continue
2925                if pwdr in G2frame.AutointPDFnames: continue
2926                PWid = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,pwdr)
2927                controlsDict = G2frame.GPXtree.GetItemPyData(
2928                    G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
2929                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
2930                    print('Skipping PDF for '+pwdr+' due to detector position')
2931                    continue
2932                # Setup PDF
2933                Data = G2frame.GPXtree.GetItemPyData(PWid)[1]
2934                pwdrMin = np.min(Data[1])
2935                Parms = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2936                    G2frame,PWid,'Instrument Parameters'))[0]
2937                fullLimits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2938                    G2frame,PWid,'Limits'))[0]
2939                if 'C' in Parms['Type'][0]:
2940                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
2941                else:
2942                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
2943                Qlimits = [0.9*qMax,qMax]
2944
2945                item = pwdr
2946                Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2947                    G2frame,imgId, 'Comments'))
2948                ElList = {}
2949                sumnum = 1.0
2950                for item in Comments:           #grab chemical formula from Comments, if there
2951                    if 'formula' in item[:15].lower():
2952                        formula = item.split('=')[1].split()
2953                        elems = formula[::2]
2954                        nums = formula[1::2]
2955                        formula = zip(elems,nums)
2956                        sumnum = 0.
2957                        for [elem,num] in formula:
2958                            ElData = G2elem.GetElInfo(elem,Parms)
2959                            ElData['FormulaNo'] = float(num)
2960                            sumnum += float(num)
2961                            ElList[elem] = ElData
2962                PDFnames = G2gd.GetGPXtreeDataNames(G2frame,['PDF ',])
2963                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits,sumnum,pwdrMin,PDFnames)
2964                if not PDFid: continue
2965                PDFdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2966                    G2frame,PDFid, 'PDF Controls'))
2967                PDFdata.update(self.pdfControls)
2968                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
2969                PDFdata['Sample']['Name'] = pwdr
2970                # compute PDF
2971                wx.Yield()
2972                G2pdG.computePDF(G2frame,PDFdata)
2973                wx.Yield()
2974                G2frame.PatternId = PDFid
2975                G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
2976                if self.params['optPDF']:
2977                    G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
2978                    wx.Yield()
2979                    G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
2980                G2frame.AutointPDFnames.append(pwdr)
2981                # save names of PDF entry to be deleted later if needed
2982                G2frame.AutointPWDRnames.append(G2frame.GPXtree.GetItemText(PDFid))
2983           
2984        G2frame = self.G2frame
2985        try:
2986            self.currImageList = sorted(
2987                glob.glob(os.path.join(self.imagedir,self.params['filter'])))
2988            self.ShowMatchingFiles(self.params['filter'])
2989        except IndexError:
2990            self.currImageList = []
2991            return
2992
2993        if self.PreventReEntryTimer: return
2994        self.PreventReEntryTimer = True
2995        imageFileList = []
2996        # integrate the images that have already been read in, but
2997        # have not yet been processed           
2998        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
2999            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
3000            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
3001            # Create a list of image files that have been read in
3002            if imagefile not in imageFileList: imageFileList.append(imagefile)
3003            # skip if already integrated
3004            if img in G2frame.IntegratedList: continue
3005            AutoIntegrateImage(imgId)
3006            #self.Pause |= G2frame.PauseIntegration
3007            #if self.Pause:
3008            #    self.OnPause()
3009            #    self.PreventReEntryTimer = False
3010            #    return
3011            if self.pdfControls: AutoComputePDF(imgId)
3012            self.Pause |= G2frame.PauseIntegration
3013            if self.Pause:
3014                self.OnPause()
3015                self.PreventReEntryTimer = False
3016                return
3017
3018        # loop over image files matching glob, reading in any new ones
3019        for newImage in self.currImageList:
3020            if newImage in imageFileList or self.Pause: continue # already read?
3021            for imgId in G2IO.ReadImages(G2frame,newImage):
3022                AutoIntegrateImage(imgId)           
3023                #self.Pause |= G2frame.PauseIntegration
3024                #if self.Pause:
3025                #    self.OnPause()
3026                #    self.PreventReEntryTimer = False
3027                #    return
3028                if self.pdfControls: AutoComputePDF(imgId)
3029                self.Pause |= G2frame.PauseIntegration
3030                if self.Pause:
3031                    self.OnPause()
3032                    self.PreventReEntryTimer = False
3033                    return
3034        if GSASIIpath.GetConfigValue('debug'):
3035            import datetime
3036            print ("Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
3037        self.PreventReEntryTimer = False
3038
3039def DefineEvaluator(dlg):
3040    '''Creates a function that provides interpolated values for a given distance value
3041    '''
3042    def Evaluator(dist):
3043        '''Interpolate image parameters for a supplied distance value
3044
3045        :param float dist: distance to use for interpolation
3046        :returns: a list with 3 items:
3047
3048          * a dict with parameter values,
3049          * the closest imctrl and
3050          * the closest maskfile (or None)
3051        '''           
3052        x = np.array([float(i) for i in parms[0]])
3053        closest = abs(x-dist).argmin()
3054        D = {'setdist':dist}
3055        imctfile = IMfileList[closest]
3056        if parms[-1][closest].lower() != '(none)':
3057            maskfile = parms[-1][closest]
3058        else:
3059            maskfile = None
3060        for c in range(1,cols-1):
3061            lbl = ParmList[c]
3062            if lbl in nonInterpVars:
3063                if lbl in ['outChannels',]:
3064                    D[lbl] = int(float(parms[c][closest]))
3065                else:
3066                    D[lbl] = float(parms[c][closest])
3067            else:
3068                y = np.array([float(i) for i in parms[c]])
3069                D[lbl] = np.interp(dist,x,y)
3070        # full integration when angular range is 0
3071        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
3072        # conversion for paired values
3073        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
3074            r = a.split('_')[0]
3075            D[r] = [D[a],D[b]]
3076            if r in ['LRazimuth',]:
3077                D[r] = [int(D[a]),int(D[b])]
3078            del D[a]
3079            del D[b]
3080        return D,imctfile,maskfile
3081    # save local copies of values needed in Evaluator
3082    parms = dlg.ReadImageParmTable()
3083    IMfileList = dlg.IMfileList
3084    cols = dlg.list.GetColumnCount()
3085    ParmList = dlg.ParmList
3086    nonInterpVars = dlg.nonInterpVars
3087    return Evaluator
3088
3089class IntegParmTable(wx.Dialog):
3090    '''Creates a dialog window with a table of integration parameters.
3091    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
3092    In this case, :func:`DefineEvaluator` should be called to obtain a function that
3093    creates a dictionary with interpolated parameter values.
3094    '''
3095    ParmList = ('setdist','distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
3096            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
3097            'maskfile',
3098            )
3099    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
3100                     'outChannels')  # values in this list are taken from nearest rather than interpolated
3101    HeaderList = ('Set Dist','Calib Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
3102            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
3103            'Mask File',
3104            )
3105    def __init__(self,parent,parms=None,IMfileList=None):
3106        self.G2frame = parent.G2frame
3107        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
3108        if parms:
3109            self.parms = parms # list of values by column
3110            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
3111        else:
3112            self.parms = [] # list of values by column
3113            self.IMfileList = [] # list of .imctrl file names for each entry in table
3114            files = []
3115            try:
3116                pth = G2G.GetImportPath(self.G2frame)
3117                if not pth: pth = '.'
3118                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3119                    style=wx.OPEN| wx.MULTIPLE,
3120                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3121                dlg.CenterOnParent()
3122                if dlg.ShowModal() == wx.ID_OK:
3123                    files = dlg.GetPaths()
3124                    self.parms,self.IMfileList = self.ReadFiles(files)
3125            finally:
3126                dlg.Destroy()
3127            if not files:
3128                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3129                return
3130        mainSizer = self.G2frame.dataWindow.GetSizer()
3131        self.list = ImgIntLstCtrl(self, wx.ID_ANY,
3132                      style=wx.LC_REPORT
3133                          | wx.BORDER_SUNKEN
3134                         #| wx.BORDER_NONE
3135                         )
3136        mainSizer.Add(self.list,1,wx.EXPAND,1)
3137        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3138        btn = wx.Button(self, wx.ID_OK)
3139        btnsizer.Add(btn)
3140        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3141        btn.Bind(wx.EVT_BUTTON,self._onSave)
3142        btnsizer.Add(btn)
3143        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3144        btn.Bind(wx.EVT_BUTTON,self._onClose)
3145        btnsizer.Add(btn)
3146        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3147        self.SetSizer(mainSizer)
3148        self.list.FillList(self.parms)
3149       
3150    def ReadFiles(self,files):
3151        '''Reads a list of .imctrl files or a single .imtbl file
3152        '''
3153        tmpDict = {}
3154        if not files: return
3155        # option 1, a dump from a previous save
3156        if os.path.splitext(files[0])[1] == '.imtbl':
3157            fp = open(files[0],'r')
3158            S = fp.readline()
3159            while S:
3160                if S[0] != '#':
3161                    [key,val] = S[:-1].split(':',1)
3162                    tmpDict[key] = eval(val)
3163                S = fp.readline()
3164            fp.close()
3165            # delete entries where files do not exist
3166            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3167            if m1:
3168                print('\nimctrl file not found:')
3169                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3170            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3171            if m2:
3172                print('\nmask file not found')
3173                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3174            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3175            if m3:
3176                print('\nDropping entries due to negative distance: '+str(m3))
3177            m = sorted(set(m1 + m2 + m3))
3178            m.reverse()
3179            for c in m:
3180                for key in tmpDict:
3181                    del tmpDict[key][c]
3182            fileList = tmpDict.get('filenames','[]')
3183            parms = []
3184            if 'setdist' not in tmpDict:
3185                print(u'Old file, recreate before using: {}'.format(files[0]))
3186                return [[]],[]
3187            for key in self.ParmList:
3188                try:
3189                    float(tmpDict[key][0])
3190                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3191                except ValueError:
3192                    parms.append(tmpDict[key])
3193                except IndexError:
3194                    print('No valid image control entries read')
3195                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3196                    return [[]],[]
3197            return parms,fileList
3198        # option 2, read in a list of files
3199        for file in files: # read all files; place in dict by distance
3200            imgDict = Read_imctrl(file)
3201            dist = imgDict.get('setdist')
3202            if dist is None:
3203                print('Skipping old file, redo: {}'.format(file))
3204            tmpDict[dist] = imgDict
3205        parms = [[] for key in self.ParmList]
3206        fileList = []
3207        for d in sorted(tmpDict):
3208            fileList.append(tmpDict[d].get('filename'))
3209            if d is None: continue
3210            if d < 0: continue
3211            for i,key in enumerate(self.ParmList):
3212                val = tmpDict[d].get(key)
3213                try:
3214                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3215                except:
3216                    val = str(val)
3217                parms[i].append(val)
3218        return parms,fileList
3219   
3220    def ReadImageParmTable(self):
3221        '''Reads possibly edited values from the ListCtrl table and returns a list
3222        of values for each column.
3223        '''
3224        rows = self.list.GetItemCount()
3225        cols = self.list.GetColumnCount()
3226        parms = []
3227        for c in range(cols):
3228            parms.append([])
3229            for r in range(rows):
3230                parms[c].append(self.list.GetItem(r,c).GetText())
3231        return parms
3232
3233    def _onClose(self,event):
3234        'Called when Cancel button is pressed'
3235        self.EndModal(wx.ID_CANCEL)
3236       
3237    def _onSave(self,event):
3238        'Called when save button is pressed; creates a .imtbl file'
3239        fil = ''
3240        if self.G2frame.GSASprojectfile:
3241            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3242        dir,f = os.path.split(fil)
3243        pth = G2G.GetExportPath(self.G2frame)
3244        try:
3245            dlg = wx.FileDialog(self, 'Save table data as',
3246                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3247                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3248            dlg.CenterOnParent()
3249            if dlg.ShowModal() != wx.ID_OK: return
3250            fil = dlg.GetPath()
3251            fil = os.path.splitext(fil)[0]+'.imtbl'
3252        finally:
3253            dlg.Destroy()       
3254        parms = self.ReadImageParmTable()
3255        print('Writing image parameter table as '+fil)
3256        fp = open(fil,'w')
3257        for c in range(len(parms)-1):
3258            lbl = self.ParmList[c]
3259            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3260        lbl = self.ParmList[c+1]
3261        fp.write(lbl+': '+str(parms[c+1])+'\n')
3262        lbl = 'filenames'
3263        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3264        fp.close()
3265   
3266class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3267    '''Creates a custom ListCtrl for editing Image Integration parameters
3268    '''
3269    def __init__(self, parent, ID, pos=wx.DefaultPosition,
3270                 size=(1000,200), style=0):
3271        self.parent=parent
3272        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3273        listmix.ListCtrlAutoWidthMixin.__init__(self)
3274        listmix.TextEditMixin.__init__(self)
3275        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3276        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3277    def FillList(self,parms):
3278        'Places the current parms into the table'
3279        self.ClearAll()
3280        self.rowlen = len(self.parent.ParmList)
3281        for i,lbl in enumerate(self.parent.HeaderList):
3282            self.InsertColumn(i, lbl)
3283        for r,d in enumerate(parms[0]):
3284            if d is None: continue
3285            if float(d) < 0: continue
3286            index = self.InsertStringItem(sys.maxint, d)
3287            for j in range(1,len(parms)):
3288                self.SetStringItem(index, j, parms[j][r])
3289        for i,lbl in enumerate(self.parent.ParmList):
3290            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3291
3292    def OnDouble(self,evt):
3293        'respond to a double-click'
3294        self.CloseEditor()
3295        fil = '(none)'
3296        pth = G2G.GetImportPath(self.parent.G2frame)
3297        if not pth: pth = '.'
3298        try:
3299            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3300                                style=wx.OPEN,
3301                                wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3302            dlg.CenterOnParent()
3303            if dlg.ShowModal() == wx.ID_OK:
3304                fil = dlg.GetPath()
3305        finally:
3306            dlg.Destroy()
3307        if os.path.splitext(fil)[1] != '.imctrl':
3308            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3309            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3310        else:
3311            # insert or overwrite an instrument parameter set
3312            if not os.path.exists(fil):
3313                print('Does not exist: '+fil)
3314                return
3315            imgDict = Read_imctrl(fil)
3316            dist = imgDict['distance']
3317            parms = self.parent.ReadImageParmTable()
3318            x = np.array([float(i) for i in parms[0]])
3319            closest = abs(x-dist).argmin()
3320            closeX = x[closest]
3321            # fix IMfileList
3322            for c,lbl in enumerate(self.parent.ParmList):
3323                try:
3324                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3325                except ValueError:
3326                    vali = imgDict[lbl]
3327                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3328                    parms[c][closest] = vali
3329                elif dist > closeX: # insert after
3330                    parms[c].insert(closest+1,vali)
3331                else:
3332                    parms[c].insert(closest,vali)
3333            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3334                self.parent.IMfileList[closest] = fil
3335            elif dist > closeX: # insert after
3336                self.parent.IMfileList.insert(closest+1,fil)
3337            else:
3338                self.parent.IMfileList.insert(closest,fil)
3339            self.FillList(parms)
3340# Autointegration end
3341###########################################################################
Note: See TracBrowser for help on using the repository browser.