source: trunk/GSASIIimgGUI.py @ 2742

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

Key on autointegration on setdist

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