source: trunk/GSASIIimgGUI.py @ 2731

Last change on this file since 2731 was 2731, checked in by vondreele, 7 years ago

add save of stress/strain ring intensities as MRD in txt file
calculate stress/strain ring intensities from 5x5 pixel blocks (was 3x3)
show sig(MRD) instead of var(MRD) in ring intensities

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 158.2 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII - image data display routines
3########### SVN repository information ###################
4# $Date: 2017-03-01 15:08:17 +0000 (Wed, 01 Mar 2017) $
5# $Author: vondreele $
6# $Revision: 2731 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 2731 2017-03-01 15:08:17Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIimgGUI: Image GUI*
12-------------------------
13
14Control image display and processing
15
16'''
17import os
18import copy
19import glob
20import re
21import math
22import sys
23import wx
24import wx.lib.scrolledpanel as wxscroll
25import wx.lib.mixins.listctrl  as  listmix
26import matplotlib as mpl
27import numpy as np
28import GSASIIpath
29GSASIIpath.SetVersionNumber("$Revision: 2731 $")
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        for key in keys:
395            if key not in data:     #uncalibrated!
396                continue
397            File.write(key+':'+str(data[key])+'\n')
398        File.close()
399       
400    def OnSaveMultiControls(event):
401        '''Save controls from multiple images
402        '''
403        imglist = []
404        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
405        while item:
406            name = G2frame.PatternTree.GetItemText(item)
407            if name.startswith('IMG '): 
408                imglist.append(name)               
409            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
410        if not imglist:
411            print('No images!')
412            return
413        dlg = G2G.G2MultiChoiceDialog(G2frame, 'Which images to select?',
414            'Select images', imglist, wx.CHOICEDLG_STYLE)
415        try:
416            if dlg.ShowModal() == wx.ID_OK:
417                treeEntries = [imglist[i] for i in dlg.GetSelections()]
418        finally:
419            dlg.Destroy()
420        if not treeEntries:
421            print('No images selected!')
422            return
423        pth = G2G.GetExportPath(G2frame)
424        dlg = wx.DirDialog(
425            G2frame, 'Select directory for output files',pth,wx.DD_DEFAULT_STYLE)
426        dlg.CenterOnParent()
427        outdir = None
428        try:
429            if dlg.ShowModal() == wx.ID_OK:
430                outdir = dlg.GetPath()
431        finally:
432            dlg.Destroy()
433        if not outdir:
434            print('No directory')
435            return
436        for img in treeEntries:
437            item = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
438            data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
439                G2frame,item,'Image Controls'))
440            Npix,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(item)
441            filename = os.path.join(outdir,
442                                    os.path.splitext(os.path.split(imagefile)[1])[0]
443                                    + '.imctrl')
444            print('writing '+filename)
445            WriteControls(filename,data)
446           
447    def OnLoadControls(event):
448        cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type','Oblique',
449            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
450            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList','setdist',
451            'PolaVal','SampleAbs','dark image','background image']
452        pth = G2G.GetImportPath(G2frame)
453        if not pth: pth = '.'
454        dlg = wx.FileDialog(G2frame, 'Choose image controls file', pth, '', 
455            'image control files (*.imctrl)|*.imctrl',wx.OPEN)
456        try:
457            if dlg.ShowModal() == wx.ID_OK:
458                filename = dlg.GetPath()
459                File = open(filename,'r')
460                save = {}
461                S = File.readline()
462                while S:
463                    if S[0] == '#':
464                        S = File.readline()
465                        continue
466                    [key,val] = S.strip().split(':',1)
467                    if key in ['type','calibrant','binType','SampleShape',]:    #strings
468                        save[key] = val
469                    elif key in ['varyList',]:
470                        save[key] = eval(val)   #dictionary
471                    elif key in ['rotation']:
472                        save[key] = float(val)
473                    elif key in ['center',]:
474                        if ',' in val:
475                            save[key] = eval(val)
476                        else:
477                            vals = val.strip('[] ').split()
478                            save[key] = [float(vals[0]),float(vals[1])] 
479                    elif key in cntlList:
480                        save[key] = eval(val)
481                    S = File.readline()
482                data.update(save)
483                # next line removed. Previous updates tree contents. The next
484                # makes a copy of data, puts it into tree and "disconnects" data
485                # from tree contents (later changes to data are lost!)
486                #G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'),copy.deepcopy(data))
487                File.close()
488        finally:
489            dlg.Destroy()
490        G2frame.ImageZ = GetImageZ(G2frame,data)
491        ResetThresholds()
492        G2plt.PlotExposedImage(G2frame,event=event)
493        wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
494       
495    def OnTransferAngles(event):
496        '''Sets the integration range for the selected Images based on the difference in detector distance
497        '''
498        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
499        if len(Names) == 1:
500            G2frame.ErrorDialog('No images to transfer integration angles to','Need more "IMG"s')
501            return
502        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
503        Names.pop(Names.index(Source))
504        # select targets & do copy
505        extraopts = {"label_1":"Xfer scaled calib d-min", "value_1":False,
506                     "label_2":"Xfer scaled 2-theta min", "value_2":False,
507                     "label_3":"Xfer scaled 2-theta max", "value_3":True,
508                     }
509        dlg = G2G.G2MultiChoiceDialog(G2frame,'Xfer angles','Transfer integration range from '+Source+' to:',
510            Names,extraOpts=extraopts)
511        try:
512            if dlg.ShowModal() == wx.ID_OK:
513                for i in '_1','_2','_3':
514                    if extraopts['value'+i]: break
515                else:
516                    G2G.G2MessageBox(G2frame,'Nothing to do!')
517                    return
518                xferAng = lambda tth,dist1,dist2: atand(dist1 * tand(tth) / dist2)
519                items = dlg.GetSelections()
520                G2frame.EnablePlot = False
521                Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,Source)
522                data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
523                ttmin0,ttmax0 = data['IOtth']
524                dist0 = data['distance']
525                wave0 = data['wavelength']
526                dsp0 = data['calibdmin']
527                print('distance = {:.2f} integration range: [{:.4f}, {:.4f}], calib dmin {:.3f}'
528                            .format(dist0,ttmin0,ttmax0,dsp0))
529                for item in items:
530                    name = Names[item]
531                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
532                    data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
533                    dist1 = data['distance']
534                    if extraopts["value_2"]:
535                        data['IOtth'][0] = xferAng(ttmin0,dist0,dist1)
536                    if extraopts["value_3"]:
537                        data['IOtth'][1] = xferAng(ttmax0,dist0,dist1)
538                    if extraopts["value_1"]:
539                        ang1 = xferAng(2.0*asind(wave0/(2.*dsp0)),dist0,dist1)
540                        data['calibdmin'] = data['wavelength']/(2.*sind(ang1/2.))
541                        print('distance = {:.2f} integration range: [{:.4f}, {:.4f}], calib dmin {:.3f}'
542                            .format(dist1,data['IOtth'][0],data['IOtth'][1],data['calibdmin']))
543                    else:
544                        print('distance = {:.2f} integration range: [{:.4f}, {:.4f}]'
545                            .format(dist1,data['IOtth'][0],data['IOtth'][1]))
546        finally:
547            dlg.Destroy()
548            G2frame.PatternTree.SelectItem(G2frame.PickId)       
549           
550    def OnResetDist(event):
551        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)
552        if dlg.ShowModal() != wx.ID_YES:
553            dlg.Destroy()
554            return
555        dlg.Destroy()
556        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
557        dlg = G2G.G2MultiChoiceDialog(G2frame,'Reset dist','Reset dist to set dist for:',Names)
558        try:
559            if dlg.ShowModal() == wx.ID_OK:
560                items = dlg.GetSelections()
561                for item in items:
562                    name = Names[item]
563                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
564                    Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
565                    Data['distance'] = Data['setdist']
566        finally:
567            dlg.Destroy()
568        wx.CallAfter(UpdateImageControls,G2frame,data,masks)
569           
570# Sizers
571    Indx = {}                                   
572    def ComboSizer():
573
574        def OnDataType(event):
575            data['type'] = typeSel.GetValue()[:4]
576            if 'SASD' in data['type']:
577                data['SampleAbs'][0] = np.exp(-data['SampleAbs'][0]) #switch from muT to trans!
578                if data['binType'] == '2-theta': data['binType'] = 'log(q)'  #switch default bin type
579            elif 'PWDR' in data['type']:
580                data['SampleAbs'][0] = -np.log(data['SampleAbs'][0])  #switch from trans to muT!
581                if data['binType'] == 'log(q)': data['binType'] = '2-theta'  #switch default bin type                 
582            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
583   
584        def OnNewColorBar(event):
585            data['color'] = colSel.GetValue()
586            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
587       
588        def OnAzmthOff(invalid,value,tc):
589            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
590       
591        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
592        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Type of image data: '),0,WACV)
593        typeSel = wx.ComboBox(parent=G2frame.dataDisplay,value=typeDict[data['type']],choices=typeList,
594            style=wx.CB_READONLY|wx.CB_DROPDOWN)
595        typeSel.SetValue(data['type'])
596        typeSel.Bind(wx.EVT_COMBOBOX, OnDataType)
597        comboSizer.Add(typeSel,0,WACV)
598        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Color bar '),0,WACV)
599        colSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['color'],choices=colorList,
600            style=wx.CB_READONLY|wx.CB_DROPDOWN)
601        colSel.Bind(wx.EVT_COMBOBOX, OnNewColorBar)
602        comboSizer.Add(colSel,0,WACV)
603        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Azimuth offset '),0,WACV)
604        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),
605            typeHint=float,OnLeave=OnAzmthOff)
606        comboSizer.Add(azmthOff,0,WACV)
607        return comboSizer
608       
609    def MaxSizer():
610        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
611        for the image, as well as callback routines.
612        '''
613        def OnNewVal(invalid,value,tc):
614            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
615            the value into the data['range'] nested list).
616            This adjusts the slider positions to match the current values
617            '''
618            scaleSel.SetSelection(len(scaleChoices)-1)
619            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
620            if r11 != Range[1][1]:
621                Range[1][1] = r11
622                maxVal.SetValue(int(Range[1][1]))
623            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
624            if r10 != Range[1][0]:
625                Range[1][0] = r10
626                minVal.SetValue(int(Range[1][0]))
627            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
628            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
629            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
630            maxSel.SetValue(sv1)
631            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
632            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
633            minSel.SetValue(sv0)
634            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
635            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
636            if mplOld:
637                Page.canvas.draw()
638            else:
639                Page.canvas.draw_idle()
640           
641        G2frame.prevMaxValue = None   
642        def OnMaxSlider(event):
643            val = maxSel.GetValue()
644            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
645            scaleSel.SetSelection(len(scaleChoices)-1)
646            G2frame.prevMaxValue = val
647            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
648            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
649            maxVal.SetValue(int(0.5+Range[1][1]))
650            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
651            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
652            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
653            minSel.SetValue(sv0)
654            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
655            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
656            if mplOld:
657                Page.canvas.draw()
658            else:
659                Page.canvas.draw_idle()
660           
661        G2frame.prevMinValue = None   
662        def OnMinSlider(event):
663            val = minSel.GetValue()
664            scaleSel.SetSelection(len(scaleChoices)-1)
665            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
666            G2frame.prevMinValue = val
667            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
668            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
669            minVal.SetValue(int(Range[1][0]))
670            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
671            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
672            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
673            maxSel.SetValue(sv1)
674            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
675            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
676            if mplOld:
677                Page.canvas.draw()
678            else:
679                Page.canvas.draw_idle()
680           
681        def OnAutoSet(event):
682            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
683            for the image so that 95% (etc.) of pixels are inside the color map limits.
684            An equal number of pixels are dropped at the minimum and maximum levels.
685            '''
686            try:
687                val = int(event.GetEventObject().GetStringSelection()[:-1])
688                margin = (100-val)/2.
689            except:
690                margin = 0
691                event.GetEventObject().SetSelection(0)
692            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
693            if margin == 0:
694                Range[1] = list(Range[0])
695            else:
696                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
697                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
698            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
699            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
700            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
701            maxSel.SetValue(sv1)
702            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
703            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
704            minSel.SetValue(sv0)
705            minVal.SetValue(int(Range[1][0]))
706            maxVal.SetValue(int(Range[1][1]))
707            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
708            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
709            if mplOld:
710                Page.canvas.draw()
711            else:
712                Page.canvas.draw_idle()
713
714        mplv = mpl.__version__.split('.')
715        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
716        # Plot color scaling uses limits as below:
717        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
718        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
719        maxSizer = wx.GridBagSizer(0,0)
720        r = c = 0
721        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Max intensity'),(r,c))
722        c += 1
723        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
724        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
725        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
726        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
727        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
728        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
729        maxSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv1,size=(300,-1))
730        maxSizer.Add(maxSel,(r,c),flag=wx.EXPAND)
731        maxSizer.AddGrowableCol(c)
732        c += 1
733        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
734        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],1,min=Range[0][0]+1,
735            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
736        maxSizer.Add(maxVal,(r,c))
737        c += 1
738        scaleChoices = ("100%","99%","95%","90%","80%","?")
739        scaleSel = wx.Choice(G2frame.dataDisplay,choices=scaleChoices,size=(-1,-1))
740        if (Range[1][0] == Range[0][0] and
741            Range[1][1] == Range[0][1]):
742            scaleSel.SetSelection(0)
743        else:
744            scaleSel.SetSelection(len(scaleChoices)-1)
745        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
746        maxSizer.Add(scaleSel,(r,c),(2,1),flag=wx.ALIGN_CENTER)
747        c = 0
748        r = 1
749        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min intensity'),(r,c))
750        c += 1
751        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
752        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
753        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
754        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
755        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
756        minSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv0,size=(300,-1))
757        maxSizer.Add(minSel,(r,c),flag=wx.EXPAND|wx.ALL)
758        c += 1
759        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
760        minVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],0,
761            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
762        maxSizer.Add(minVal,(r,c))
763        return maxSizer
764       
765    def CalibCoeffSizer():
766       
767        def OnCalRef(event):
768            Obj = event.GetEventObject()
769            name = Indx[Obj]
770            data['varyList'][name] = Obj.GetValue()
771           
772        calibSizer = wx.FlexGridSizer(0,2,5,5)
773        calibSizer.SetFlexibleDirection(wx.HORIZONTAL)
774        calibSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calibration coefficients'),0,WACV)   
775        calibSizer.Add((5,0),0)
776        Names = ['det-X','det-Y','wave','dist','tilt','phi']
777        if 'PWDR' in data['type']:
778            Names.append('dep') 
779        Parms = {'dist':['Distance',(10,3),data,'distance'],'det-X':['Beam center X',(10,3),data['center'],0],
780            'det-Y':['Beam center Y',(10,3),data['center'],1],'tilt':['Tilt angle',(10,3),data,'tilt'],
781            'phi':['Tilt rotation',(10,2),data,'rotation'],'dep':['Penetration',(10,4),data,'DetDepth'],
782            'wave':['Wavelength',(10,6),data,'wavelength']}
783        for name in Names:
784            calSel = wx.CheckBox(parent=G2frame.dataDisplay,label=Parms[name][0])
785            calibSizer.Add(calSel,0,WACV)
786            calSel.Bind(wx.EVT_CHECKBOX, OnCalRef)
787            calSel.SetValue(data['varyList'][name])
788            Indx[calSel] = name
789            if name == 'wave':
790                calVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Parms[name][2],
791                    Parms[name][3],min=0.01,max=10.,nDig=Parms[name][1],typeHint=float)
792            elif name == 'dep':
793                calVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Parms[name][2],
794                    Parms[name][3],min=0.0,max=0.2,nDig=Parms[name][1],typeHint=float)
795            else:
796                calVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Parms[name][2],
797                    Parms[name][3],nDig=Parms[name][1],typeHint=float)
798            calibSizer.Add(calVal,0,WACV)
799        return calibSizer
800   
801    def IntegrateSizer():
802       
803        def OnNewBinType(event):
804            data['binType'] = binSel.GetValue()
805            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
806       
807        def OnIOtth(invalid,value,tc):
808            Ltth = float(G2frame.InnerTth.GetValue())
809            Utth = float(G2frame.OuterTth.GetValue())
810            if Ltth > Utth:
811                Ltth,Utth = Utth,Ltth
812            if 'Q' in data['binType']:
813                data['IOtth'] = [2.*asind(Ltth*wave/(4.*math.pi)),2.*asind(Utth*wave/(4.*math.pi))]
814            else:
815                data['IOtth'] = [Ltth,Utth]
816            G2frame.InnerTth.SetValue(Ltth)
817            G2frame.OuterTth.SetValue(Utth)
818            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
819       
820        def OnLRazim(invalid,value,tc):
821            Lazm = float(G2frame.Lazim.GetValue())%360.
822            Razm = float(G2frame.Razim.GetValue())%360.
823            if Lazm > Razm:
824                Razm += 360.
825            if data['fullIntegrate']:
826                Razm = Lazm+360.
827            G2frame.Lazim.SetValue(Lazm)
828            G2frame.Razim.SetValue(Razm)
829            data['LRazimuth'] = [Lazm,Razm]
830            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
831               
832        def OnNumOutAzms(invalid,value,tc):
833            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
834       
835        def OnOblique(event):
836            data['Oblique'][1] = not data['Oblique'][1]
837               
838        def OnSampleShape(event):
839            data['SampleShape'] = samShape.GetValue()
840            if 'Cylind' in data['SampleShape']:
841                data['SampleAbs'][0] = 0.0
842            elif 'Fixed' in data['SampleShape']:
843                data['SampleAbs'][0] = 1.0
844            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
845                           
846        def OnSamAbs(event):
847            data['SampleAbs'][1] = not data['SampleAbs'][1]
848                           
849        def OnShowLines(event):
850            data['showLines'] = not data['showLines']
851            G2plt.PlotExposedImage(G2frame,event=event)
852           
853        def OnFullIntegrate(event):
854            Lazm = float(G2frame.Lazim.GetValue())
855            if data['fullIntegrate']:
856                data['fullIntegrate'] = False
857                data['LRazimuth'] = [Lazm,Lazm+20.]
858            else:
859                data['fullIntegrate'] = True
860                data['LRazimuth'] = [Lazm,Lazm+360.]
861            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
862            G2plt.PlotExposedImage(G2frame,event=event)
863           
864        def OnSetDefault(event):
865            if data['setDefault']:
866                G2frame.imageDefault = {}
867                data['setDefault'] = False
868            else:
869                G2frame.imageDefault = copy.copy(data)
870                data['setDefault'] = True
871               
872        def OnCenterAzm(event):
873            data['centerAzm'] = not data['centerAzm']
874            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
875               
876        def OnApplyPola(event):
877            data['PolaVal'][1] = not data['PolaVal'][1]
878               
879        dataSizer = wx.FlexGridSizer(0,2,5,3)
880        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Integration coefficients'),0,WACV)   
881        dataSizer.Add((5,0),0)
882        if 'PWDR' in data['type']:
883            binChoice = ['2-theta','Q']
884        elif 'SASD' in data['type']:
885            binChoice = ['2-theta','Q','log(q)']
886        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Bin style: Constant step bins in'),0,WACV)           
887        binSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['binType'],choices=binChoice,
888            style=wx.CB_READONLY|wx.CB_DROPDOWN)
889        binSel.Bind(wx.EVT_COMBOBOX, OnNewBinType)
890        dataSizer.Add(binSel,0,WACV)
891        binType = '2-theta'
892        if 'q' in data['binType'].lower():
893            binType = 'Q'
894        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Inner/Outer '+binType),0,WACV)           
895        IOtth = data['IOtth'][:]
896        if 'Q' in data['binType']:
897            wave = data['wavelength']
898            IOtth = [4.*math.pi*sind(IOtth[0]/2.)/wave,4.*math.pi*sind(IOtth[1]/2.)/wave]
899        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
900        G2frame.InnerTth = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,IOtth,0,nDig=(8,3,'f'),min=0.001,typeHint=float,OnLeave=OnIOtth)
901        littleSizer.Add(G2frame.InnerTth,0,WACV)
902        G2frame.OuterTth = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,IOtth,1,nDig=(8,3,'f'),min=0.001,typeHint=float,OnLeave=OnIOtth)
903        littleSizer.Add(G2frame.OuterTth,0,WACV)
904        dataSizer.Add(littleSizer,0,)
905        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Start/End azimuth'),0,WACV)
906        LRazim = data['LRazimuth']
907        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
908        G2frame.Lazim = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,LRazim,0,nDig=(6,1,'f'),typeHint=float,OnLeave=OnLRazim)
909        littleSizer.Add(G2frame.Lazim,0,WACV)
910        G2frame.Razim = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,LRazim,1,nDig=(6,1,'f'),typeHint=float,OnLeave=OnLRazim)
911        if data['fullIntegrate']:
912            G2frame.Razim.Enable(False)
913            G2frame.Razim.SetBackgroundColour(VERY_LIGHT_GREY)
914            G2frame.Razim.SetValue(LRazim[0]+360.)
915        littleSizer.Add(G2frame.Razim,0,WACV)
916        dataSizer.Add(littleSizer,0,)
917        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' No. 2-theta/azimuth bins'),0,WACV)
918        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
919        outChan = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'outChannels',typeHint=int,min=10)
920        littleSizer.Add(outChan,0,WACV)
921        outAzim = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'outAzimuths',nDig=(10,4),min=1,typeHint=int,OnLeave=OnNumOutAzms)
922        littleSizer.Add(outAzim,0,WACV)
923        dataSizer.Add(littleSizer,0,)
924        samplechoice = ['Cylinder','Fixed flat plate',]
925        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label='Select sample shape'),0,WACV)
926        samShape = wx.ComboBox(G2frame.dataDisplay,value=data['SampleShape'],choices=samplechoice,
927            style=wx.CB_READONLY|wx.CB_DROPDOWN)
928        samShape.Bind(wx.EVT_COMBOBOX,OnSampleShape)
929        dataSizer.Add(samShape,0,WACV)
930        #SampleShape - cylinder or flat plate choice?
931        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
932        samabs = wx.CheckBox(parent=G2frame.dataDisplay,label='Apply sample absorption?')
933        dataSizer.Add(samabs,0,WACV)
934        samabs.Bind(wx.EVT_CHECKBOX, OnSamAbs)
935        samabs.SetValue(data['SampleAbs'][1])
936        minmax = [0.,2.]
937        if 'Cylind' in data['SampleShape']: #cylinder mu*R; flat plate transmission
938            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='mu*R (0.00-2.0) '),0,WACV)
939        elif 'Fixed' in data['SampleShape']:
940            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='transmission '),0,WACV) #for flat plate
941            minmax = [.05,1.0]
942        samabsVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['SampleAbs'],0,nDig=(10,3),
943            typeHint=float,min=minmax[0],max=minmax[1])
944        littleSizer.Add(samabsVal,0,WACV)
945        dataSizer.Add(littleSizer,0,)       
946        if 'PWDR' in data['type']:
947            littleSizer = wx.BoxSizer(wx.HORIZONTAL)
948            oblique = wx.CheckBox(parent=G2frame.dataDisplay,label='Apply detector absorption?')
949            dataSizer.Add(oblique,0,WACV)
950            oblique.Bind(wx.EVT_CHECKBOX, OnOblique)
951            oblique.SetValue(data['Oblique'][1])
952            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='Value (0.01-0.99)  '),0,WACV)
953            obliqVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['Oblique'],0,nDig=(10,3),typeHint=float,min=0.01,max=0.99)
954            littleSizer.Add(obliqVal,0,WACV)
955            dataSizer.Add(littleSizer,0,)
956        if 'SASD' in data['type']:
957            littleSizer = wx.BoxSizer(wx.HORIZONTAL)
958            setPolariz = wx.CheckBox(parent=G2frame.dataDisplay,label='Apply polarization?')
959            dataSizer.Add(setPolariz,0,WACV)
960            setPolariz.Bind(wx.EVT_CHECKBOX, OnApplyPola)
961            setPolariz.SetValue(data['PolaVal'][1])
962            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='Value (0.001-0.999)  '),0,WACV)
963            polaVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['PolaVal'],0,nDig=(10,3),typeHint=float,min=0.001,max=0.999)
964            littleSizer.Add(polaVal,0,WACV)
965            dataSizer.Add(littleSizer,0,)
966       
967        showLines = wx.CheckBox(parent=G2frame.dataDisplay,label='Show integration limits?')
968        dataSizer.Add(showLines,0,WACV)
969        showLines.Bind(wx.EVT_CHECKBOX, OnShowLines)
970        showLines.SetValue(data['showLines'])
971        fullIntegrate = wx.CheckBox(parent=G2frame.dataDisplay,label='Do full integration?')
972        dataSizer.Add(fullIntegrate,0,WACV)
973        fullIntegrate.Bind(wx.EVT_CHECKBOX, OnFullIntegrate)
974        fullIntegrate.SetValue(data['fullIntegrate'])
975        setDefault = wx.CheckBox(parent=G2frame.dataDisplay,label='Use for all new images?')
976        dataSizer.Add(setDefault,0,WACV)
977        setDefault.Bind(wx.EVT_CHECKBOX, OnSetDefault)
978        setDefault.SetValue(data['setDefault'])
979        centerAzm = wx.CheckBox(parent=G2frame.dataDisplay,label='Azimuth at bin center?')
980        dataSizer.Add(centerAzm,0,WACV)
981        centerAzm.Bind(wx.EVT_CHECKBOX, OnCenterAzm)
982        centerAzm.SetValue(data['centerAzm'])
983        return dataSizer
984       
985    def BackSizer():
986       
987        global oldFlat
988        def OnBackImage(event):
989            data['background image'][0] = backImage.GetValue()
990            G2frame.ImageZ = GetImageZ(G2frame,data)
991            ResetThresholds()
992            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
993           
994        def OnDarkImage(event):
995            data['dark image'][0] = darkImage.GetValue()
996            G2frame.ImageZ = GetImageZ(G2frame,data)
997            ResetThresholds()
998            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
999           
1000        def OnFlatBkg(invalid,value,tc):
1001            global oldFlat
1002            G2frame.ImageZ += int(oldFlat-data['Flat Bkg'])
1003            oldFlat = data['Flat Bkg']
1004            ResetThresholds()
1005            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
1006
1007        def OnMult(invalid,value,tc):
1008            G2frame.ImageZ = GetImageZ(G2frame,data)
1009            ResetThresholds()
1010            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
1011       
1012        backSizer = wx.FlexGridSizer(0,6,5,5)
1013        oldFlat = data.get('Flat Bkg',0.)
1014
1015        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Dark image'),0,WACV)
1016        Choices = ['',]+G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1017        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
1018        Choices.pop(Choices.index(Source))
1019        darkImage = wx.ComboBox(parent=G2frame.dataDisplay,value=data['dark image'][0],choices=Choices,
1020            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1021        darkImage.Bind(wx.EVT_COMBOBOX,OnDarkImage)
1022        backSizer.Add(darkImage)
1023        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' multiplier'),0,WACV)
1024        darkMult = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['dark image'],1,nDig=(10,3),
1025            typeHint=float,OnLeave=OnMult)
1026        backSizer.Add(darkMult,0,WACV)
1027        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Flat Bkg: '),0,WACV)
1028        flatbkg = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Flat Bkg',nDig=(10,0),
1029            typeHint=float,OnLeave=OnFlatBkg)
1030        backSizer.Add(flatbkg,0,WACV)
1031
1032        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Background image'),0,WACV)
1033        backImage = wx.ComboBox(parent=G2frame.dataDisplay,value=data['background image'][0],choices=Choices,
1034            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1035        backImage.Bind(wx.EVT_COMBOBOX,OnBackImage)
1036        backSizer.Add(backImage)
1037        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' multiplier'),0,WACV)
1038        backMult = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['background image'],1,nDig=(10,3),
1039            typeHint=float,OnLeave=OnMult)
1040        backSizer.Add(backMult,0,WACV)
1041        return backSizer
1042                       
1043    def CalibSizer():
1044               
1045        def OnNewCalibrant(event):
1046            data['calibrant'] = calSel.GetValue().strip()
1047            if data['calibrant']:
1048                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBRATE,enable=True)
1049                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMCALIBRATE,enable=True)
1050                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBALL,enable=True)
1051                data['calibskip'] = calFile.Calibrants[data['calibrant']][3]
1052                limits = calFile.Calibrants[data['calibrant']][4]
1053                data['calibdmin'],data['pixLimit'],data['cutoff'] = limits
1054                pixLimit.SetValue(str(limits[1]))
1055                cutOff.SetValue(limits[2])
1056                calibSkip.SetValue(str(data['calibskip']))
1057                G2frame.calibDmin.SetValue(limits[0])
1058            else:
1059                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBRATE,enable=False)
1060                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMCALIBRATE,enable=False)
1061                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBALL,enable=False)
1062               
1063        def OnCalibSkip(event):
1064            data['calibskip'] = int(calibSkip.GetValue())
1065           
1066        def OnPixLimit(event):
1067            data['pixLimit'] = int(pixLimit.GetValue())
1068           
1069        def OnSetRings(event):
1070            data['setRings'] = not data['setRings']
1071            G2plt.PlotExposedImage(G2frame,event=event)
1072   
1073        calibSizer = wx.FlexGridSizer(0,3,5,5)
1074        comboSizer = wx.BoxSizer(wx.HORIZONTAL)   
1075        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calibrant '),0,WACV)
1076        calSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['calibrant'],choices=calList,
1077            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1078        calSel.Bind(wx.EVT_COMBOBOX, OnNewCalibrant)
1079        comboSizer.Add(calSel,0,WACV)
1080        calibSizer.Add(comboSizer,0)
1081       
1082        comboSizer = wx.BoxSizer(wx.HORIZONTAL)   
1083        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calib lines to skip   '),0,WACV)
1084        calibSkip  = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['calibskip']),choices=[str(i) for i in range(25)],
1085            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1086        calibSkip.Bind(wx.EVT_COMBOBOX, OnCalibSkip)
1087        comboSizer.Add(calibSkip,0,WACV)
1088        calibSizer.Add(comboSizer,0)
1089       
1090        comboSizer = wx.BoxSizer(wx.HORIZONTAL)       
1091        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min calib d-spacing '),0,WACV)
1092        G2frame.calibDmin = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'calibdmin',nDig=(10,2),typeHint=float,min=0.25)
1093        comboSizer.Add(G2frame.calibDmin,0,WACV)
1094        calibSizer.Add(comboSizer,0)
1095       
1096        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1097        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min ring I/Ib '),0,WACV)
1098        cutOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'cutoff',nDig=(10,1),typeHint=float,min=0.1)
1099        comboSizer.Add(cutOff,0,WACV)
1100        calibSizer.Add(comboSizer,0)
1101       
1102        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1103        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Pixel search range '),0,WACV)
1104        pixLimit = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['pixLimit']),choices=['1','2','5','10','15','20'],
1105            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1106        pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
1107        comboSizer.Add(pixLimit,0,WACV)
1108        calibSizer.Add(comboSizer,0)
1109       
1110        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1111        setRings = wx.CheckBox(parent=G2frame.dataDisplay,label='Show ring picks?')
1112        comboSizer.Add(setRings,0)
1113        setRings.Bind(wx.EVT_CHECKBOX, OnSetRings)
1114        setRings.SetValue(data['setRings'])
1115        calibSizer.Add(comboSizer,0)
1116        return calibSizer
1117       
1118    def GonioSizer():
1119       
1120        def OnGlobalEdit(event):
1121            Names = []
1122            Items = []
1123            if G2frame.PatternTree.GetCount():
1124                id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1125                while id:
1126                    name = G2frame.PatternTree.GetItemText(id)
1127                    if 'IMG' in name:
1128                        ctrls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,id,'Image Controls'))
1129                        Names.append(name)
1130                        Items.append(ctrls['GonioAngles'])
1131                    id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1132                if len(Names) == 1:
1133                    G2frame.ErrorDialog('Nothing for global editing','There must be more than one "IMG" pattern')
1134                    return
1135                dlg = G2G.G2HistoDataDialog(G2frame,' Edit sample goniometer data:',
1136                    'Edit data',['Omega','Chi','Phi'],['%.2f','%.2f','%.2f'],Names,Items)
1137                try:
1138                    if dlg.ShowModal() == wx.ID_OK:
1139                        id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1140                        while id:
1141                            name = G2frame.PatternTree.GetItemText(id)
1142                            if 'IMG' in name:
1143                                ctrls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,id,'Image Controls'))
1144                                vals = Items[Names.index(name)]
1145                                ctrls['GonioAngles'] = vals
1146                            id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1147                finally:
1148                    dlg.Destroy()
1149                    G2frame.PatternTree.SelectItem(G2frame.PickId)
1150       
1151        gonioSizer = wx.BoxSizer(wx.HORIZONTAL)
1152        names = ['Omega','Chi','Phi']
1153        gonioSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,'Sample goniometer angles: '),0,WACV)
1154        for i,name in enumerate(names):
1155            gonioSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,name),0,WACV)
1156            angle = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['GonioAngles'],i,nDig=(8,2),typeHint=float)
1157            gonioSizer.Add(angle,0,WACV)
1158        globEdit = wx.Button(G2frame.dataDisplay,-1,'Global edit')
1159        globEdit.Bind(wx.EVT_BUTTON,OnGlobalEdit)
1160        gonioSizer.Add(globEdit,0,WACV)
1161        return gonioSizer
1162       
1163# Image Controls main code             
1164                           
1165    #fix for old files:
1166    if 'azmthOff' not in data:
1167        data['azmthOff'] = 0.0
1168    if 'background image' not in data:
1169        data['background image'] = ['',-1.0]
1170    if 'dark image' not in data:
1171        data['dark image'] = ['',-1.0]
1172    if 'centerAzm' not in data:
1173        data['centerAzm'] = False
1174    if 'Oblique' not in data:
1175        data['Oblique'] = [0.5,False]
1176    if 'PolaVal' not in data:
1177        data['PolaVal'] = [0.99,False]
1178    #end fix
1179   
1180    if IntegrateOnly:
1181        OnIntegrate(None)
1182        return
1183   
1184    colorList = sorted([m for m in mpl.cm.datad.keys() if not m.endswith("_r")],key=lambda s: s.lower())
1185    calList = sorted([m for m in calFile.Calibrants.keys()],key=lambda s: s.lower())
1186    typeList = ['PWDR - powder diffraction data','SASD - small angle scattering data',
1187        'REFL - reflectometry data']
1188    if not data.get('type'):                        #patch for old project files
1189        data['type'] = 'PWDR'
1190    typeDict = {'PWDR':typeList[0],'SASD':typeList[1],'REFL':typeList[2]}
1191    if G2frame.dataDisplay:
1192        G2frame.dataDisplay.Destroy()
1193    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.ImageMenu)
1194    if not G2frame.dataFrame.GetStatusBar():
1195        G2frame.dataFrame.CreateStatusBar()
1196    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCalibrate, id=G2gd.wxID_IMCALIBRATE)
1197    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRecalibrate, id=G2gd.wxID_IMRECALIBRATE)
1198    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRecalibAll, id=G2gd.wxID_IMRECALIBALL)
1199    G2frame.dataFrame.Bind(wx.EVT_MENU, OnClearCalib, id=G2gd.wxID_IMCLEARCALIB)
1200    if data.get('calibrant'):
1201        mode = True
1202    else:
1203        mode = False
1204    G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBRATE,enable=mode)
1205    G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMCALIBRATE,enable=mode)
1206    G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBALL,enable=mode)
1207    G2frame.dataFrame.Bind(wx.EVT_MENU, OnIntegrate, id=G2gd.wxID_IMINTEGRATE)
1208    G2frame.dataFrame.Bind(wx.EVT_MENU, OnIntegrateAll, id=G2gd.wxID_INTEGRATEALL)
1209    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyControls, id=G2gd.wxID_IMCOPYCONTROLS)
1210    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopySelected, id=G2gd.wxID_IMCOPYSELECTED)
1211    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveControls, id=G2gd.wxID_IMSAVECONTROLS)
1212    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveMultiControls, id=G2gd.wxID_SAVESELECTEDCONTROLS)
1213    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadControls, id=G2gd.wxID_IMLOADCONTROLS)
1214    G2frame.dataFrame.Bind(wx.EVT_MENU, OnTransferAngles, id=G2gd.wxID_IMXFERCONTROLS)
1215    G2frame.dataFrame.Bind(wx.EVT_MENU, OnResetDist, id=G2gd.wxID_IMRESETDIST)
1216    def OnDestroy(event):
1217        G2frame.autoIntFrame = None
1218    def OnAutoInt(event):
1219        if G2frame.autoIntFrame: # ensure only one open at a time
1220            G2frame.autoIntFrame.Raise()
1221            return
1222        G2frame.autoIntFrame = AutoIntFrame(G2frame,PollTime=10.0)
1223        G2frame.autoIntFrame.Bind(wx.EVT_WINDOW_DESTROY,OnDestroy) # clean up name on window close
1224    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAutoInt, id=G2gd.wxID_IMAUTOINTEG)
1225    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
1226
1227    mainSizer = wx.BoxSizer(wx.VERTICAL)
1228    mainSizer.Add((5,10),0)   
1229    mainSizer.Add(ComboSizer(),0,wx.ALIGN_LEFT)
1230    mainSizer.Add((5,5),0)
1231    Range = data['range'] # allows code to be same in Masks
1232    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1233    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1234   
1235    mainSizer.Add((5,5),0)
1236    DataSizer = wx.FlexGridSizer(0,2,5,0)
1237    DataSizer.Add(CalibCoeffSizer(),0)
1238    DataSizer.Add(IntegrateSizer(),0)       
1239    mainSizer.Add(DataSizer,0)
1240    mainSizer.Add((5,5),0)           
1241    mainSizer.Add(BackSizer(),0)
1242    mainSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calibration controls:'),0,WACV)
1243    mainSizer.Add((5,5),0)
1244    mainSizer.Add(CalibSizer(),0,WACV)
1245    mainSizer.Add((5,5),0)
1246    mainSizer.Add(GonioSizer(),0,WACV)   
1247       
1248    mainSizer.Layout()   
1249    G2frame.dataDisplay.SetSizer(mainSizer)
1250    fitSize = mainSizer.Fit(G2frame.dataFrame)
1251    G2frame.dataFrame.setSizePosLeft(fitSize)
1252    G2frame.dataFrame.SendSizeEvent()
1253   
1254################################################################################
1255##### Masks
1256################################################################################
1257def CleanupMasks(data):
1258    '''If a mask creation is not completed, an empty mask entry is created in the
1259    masks array. This cleans them out. It is called when the masks page is first loaded
1260    and before saving them or after reading them in. This should also probably be done
1261    before they are used for integration.
1262    '''
1263    for key in ['Points','Rings','Arcs','Polygons']:
1264        data[key] = data.get(key,[])
1265        l1 = len(data[key])
1266        data[key] = [i for i in data[key] if len(i)]
1267        l2 = len(data[key])
1268        if GSASIIpath.GetConfigValue('debug') and l1 != l2:
1269            print 'Mask Cleanup:',key,'was',l1,'entries','now',l2
1270   
1271def UpdateMasks(G2frame,data):
1272    '''Shows and handles the controls on the "Masks" data tree entry
1273    '''
1274   
1275    def OnTextMsg(event):
1276        Obj = event.GetEventObject()
1277        Obj.SetToolTipString('Drag this mask on 2D Powder Image with mouse to change ')
1278
1279    def Replot(*args,**kwargs):
1280        wx.CallAfter(G2plt.PlotExposedImage,G2frame)
1281
1282    def onDeleteMask(event):
1283        Obj = event.GetEventObject()
1284        typ = Obj.locationcode.split('+')[1]
1285        num = int(Obj.locationcode.split('+')[2])
1286        del(data[typ][num])
1287        wx.CallAfter(UpdateMasks,G2frame,data)
1288        G2plt.PlotExposedImage(G2frame,event=event)
1289
1290    def onDeleteFrame(event):
1291        data['Frames'] = []
1292        wx.CallAfter(UpdateMasks,G2frame,data)
1293        G2plt.PlotExposedImage(G2frame,event=event)
1294
1295    def OnCopyMask(event):
1296        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1297        if len(Names) == 1:
1298            G2frame.ErrorDialog('Nothing to copy masks to','There must be more than one "IMG" pattern')
1299            return
1300        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
1301        Names.pop(Names.index(Source))
1302        Data = copy.deepcopy(data)
1303        Thresh = Data.pop('Thresholds')     # & remove it as well
1304        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy mask data','Copy masks from '+Source+' to:',Names)
1305        try:
1306            if dlg.ShowModal() == wx.ID_OK:
1307                items = dlg.GetSelections()
1308                for item in items:
1309                    name = Names[item]
1310                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
1311                    MId = G2gd.GetPatternTreeItemId(G2frame,Id,'Masks')
1312                    Mask = G2frame.PatternTree.GetItemPyData(MId)
1313                    Mask.update(copy.deepcopy(Data))
1314                    Mask['Thresholds'][1][0] = Thresh[1][0]  #copy only lower threshold
1315                    G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Masks'),Mask)
1316        finally:
1317            dlg.Destroy()
1318
1319    def OnSaveMask(event):
1320        CleanupMasks(data)
1321        pth = G2G.GetExportPath(G2frame)
1322        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1323            'image mask files (*.immask)|*.immask',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1324        try:
1325            if dlg.ShowModal() == wx.ID_OK:
1326                filename = dlg.GetPath()
1327                filename = os.path.splitext(filename)[0]+'.immask'
1328                File = open(filename,'w')
1329                keys = ['Points','Rings','Arcs','Polygons','Frames','Thresholds']
1330                for key in keys:
1331                    File.write(key+':'+str(data[key])+'\n')
1332                File.close()
1333        finally:
1334            dlg.Destroy()
1335       
1336    def OnLoadMask(event):
1337        if event.Id == G2gd.wxID_MASKLOADNOT:
1338            ignoreThreshold = True
1339        else:
1340            ignoreThreshold = False
1341        pth = G2G.GetImportPath(G2frame)
1342        if not pth: pth = '.'
1343        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1344            'image mask files (*.immask)|*.immask',wx.OPEN)
1345        try:
1346            if dlg.ShowModal() == wx.ID_OK:
1347                filename = dlg.GetPath()
1348                File = open(filename,'r')
1349                save = {}
1350                oldThreshold = data['Thresholds'][0]
1351                S = File.readline()
1352                while S:
1353                    if S[0] == '#':
1354                        S = File.readline()
1355                        continue
1356                    [key,val] = S.strip().split(':',1)
1357                    if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
1358                        if ignoreThreshold and key == 'Thresholds':
1359                            S = File.readline() 
1360                            continue
1361                        save[key] = eval(val)
1362                        if key == 'Thresholds':
1363                            save[key][0] = oldThreshold
1364                            save[key][1][1] = min(oldThreshold[1],save[key][1][1])
1365                    S = File.readline()
1366                File.close()
1367                data.update(save)
1368                CleanupMasks(data)
1369                wx.CallAfter(UpdateMasks,G2frame,data)
1370                G2plt.PlotExposedImage(G2frame,event=event)               
1371        finally:
1372            dlg.Destroy()
1373           
1374    def OnAutoSpotMask(event):
1375        'Do auto search for spot masks'
1376        if wx.MessageDialog(G2frame.dataDisplay,'NB: This will clear any old spot masks','Auto Spot Masks', wx.OK|wx.CANCEL).ShowModal() == wx.ID_OK:
1377            Controls = copy.deepcopy(G2frame.PatternTree.GetItemPyData( 
1378            G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId,'Image Controls')))
1379            Error = G2img.AutoSpotMasks(G2frame.ImageZ,data,Controls)
1380            if not Error is None:
1381                G2frame.ErrorDialog('Auto spot search error',Error)
1382            wx.CallAfter(UpdateMasks,G2frame,data)
1383            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
1384
1385    def OnDeleteSpotMask(event):
1386        data['Points'] = []
1387        wx.CallAfter(UpdateMasks,G2frame,data)
1388        wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)         
1389           
1390    def ToggleSpotMaskMode(event):
1391        G2plt.ToggleMultiSpotMask(G2frame)
1392       
1393    def OnNewArcMask(event):
1394        'Start a new arc mask'
1395        G2frame.MaskKey = 'a'
1396        G2plt.OnStartMask(G2frame)
1397       
1398    def OnNewRingMask(event):
1399        'Start a new ring mask'
1400        G2frame.MaskKey = 'r'
1401        G2plt.OnStartMask(G2frame)
1402       
1403    def OnNewPolyMask(event):
1404        'Start a new polygon mask'
1405        G2frame.MaskKey = 'p'
1406        G2plt.OnStartMask(G2frame)
1407       
1408    def OnNewFrameMask(event):
1409        'Start a new Frame mask'
1410        G2frame.MaskKey = 'f'
1411        G2plt.OnStartMask(G2frame)
1412       
1413    def MaxSizer():
1414        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
1415        for the image, as well as callback routines.
1416        '''
1417        def OnNewVal(invalid,value,tc):
1418            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
1419            the value into the data['range'] nested list).
1420            This adjusts the slider positions to match the current values
1421            '''
1422            scaleSel.SetSelection(len(scaleChoices)-1)
1423            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
1424            if r11 != Range[1][1]:
1425                Range[1][1] = r11
1426                maxVal.SetValue(int(Range[1][1]))
1427            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
1428            if r10 != Range[1][0]:
1429                Range[1][0] = r10
1430                minVal.SetValue(int(Range[1][0]))
1431            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1432            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1433            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1434            maxSel.SetValue(sv1)
1435            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1436            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1437            minSel.SetValue(sv0)
1438            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1439            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1440            if mplOld:
1441                Page.canvas.draw()
1442            else:
1443                Page.canvas.draw_idle()
1444           
1445        G2frame.prevMaxValue = None   
1446        def OnMaxSlider(event):
1447            val = maxSel.GetValue()
1448            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
1449            scaleSel.SetSelection(len(scaleChoices)-1)
1450            G2frame.prevMaxValue = val
1451            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1452            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
1453            maxVal.SetValue(int(0.5+Range[1][1]))
1454            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1455            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
1456            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1457            minSel.SetValue(sv0)
1458            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1459            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1460            if mplOld:
1461                Page.canvas.draw()
1462            else:
1463                Page.canvas.draw_idle()
1464           
1465        G2frame.prevMinValue = None   
1466        def OnMinSlider(event):
1467            val = minSel.GetValue()
1468            scaleSel.SetSelection(len(scaleChoices)-1)
1469            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
1470            G2frame.prevMinValue = val
1471            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1472            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
1473            minVal.SetValue(int(Range[1][0]))
1474            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1475            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1476            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1477            maxSel.SetValue(sv1)
1478            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1479            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1480            if mplOld:
1481                Page.canvas.draw()
1482            else:
1483                Page.canvas.draw_idle()
1484           
1485        def OnAutoSet(event):
1486            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
1487            for the image so that 95% (etc.) of pixels are inside the color map limits.
1488            An equal number of pixels are dropped at the minimum and maximum levels.
1489            '''
1490            try:
1491                val = int(event.GetEventObject().GetStringSelection()[:-1])
1492                margin = (100-val)/2.
1493            except:
1494                margin = 0
1495                event.GetEventObject().SetSelection(0)
1496            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1497            if margin == 0:
1498                Range[1] = list(Range[0])
1499            else:
1500                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
1501                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
1502            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1503            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1504            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1505            maxSel.SetValue(sv1)
1506            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1507            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1508            minSel.SetValue(sv0)
1509            minVal.SetValue(int(Range[1][0]))
1510            maxVal.SetValue(int(Range[1][1]))
1511            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1512            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1513            if mplOld:
1514                Page.canvas.draw()
1515            else:
1516                Page.canvas.draw_idle()
1517
1518        mplv = mpl.__version__.split('.')
1519        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
1520        # Plot color scaling uses limits as below:
1521        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
1522        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
1523        maxSizer = wx.GridBagSizer(0,0)
1524        r = c = 0
1525        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Max intensity'),(r,c))
1526        c += 1
1527        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
1528        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
1529        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
1530        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1531        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1532        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1533        maxSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv1,size=(300,-1))
1534        maxSizer.Add(maxSel,(r,c),flag=wx.EXPAND)
1535        maxSizer.AddGrowableCol(c)
1536        c += 1
1537        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
1538        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],1,min=Range[0][0]+1,
1539            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1540        maxSizer.Add(maxVal,(r,c))
1541        c += 1
1542        scaleChoices = ("100%","99%","95%","90%","80%","?")
1543        scaleSel = wx.Choice(G2frame.dataDisplay,choices=scaleChoices,size=(-1,-1))
1544        if (Range[1][0] == Range[0][0] and
1545            Range[1][1] == Range[0][1]):
1546            scaleSel.SetSelection(0)
1547        else:
1548            scaleSel.SetSelection(len(scaleChoices)-1)
1549        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
1550        maxSizer.Add(scaleSel,(r,c),(2,1),flag=wx.ALIGN_CENTER)
1551        c = 0
1552        r = 1
1553        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min intensity'),(r,c))
1554        c += 1
1555        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
1556        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
1557        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
1558        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1559        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1560        minSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv0,size=(300,-1))
1561        maxSizer.Add(minSel,(r,c),flag=wx.EXPAND|wx.ALL)
1562        c += 1
1563        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
1564        minVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],0,
1565            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1566        maxSizer.Add(minVal,(r,c))
1567        return maxSizer
1568
1569    startScroll = None
1570    if G2frame.dataDisplay:
1571        startScroll = G2frame.dataDisplay.GetScrollPos(wx.VERTICAL) # save scroll position
1572        G2frame.dataDisplay.Destroy()
1573    else:
1574        CleanupMasks(data) # posting page for 1st time; clean out anything unfinished
1575    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.MaskMenu)
1576    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyMask, id=G2gd.wxID_MASKCOPY)
1577    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadMask, id=G2gd.wxID_MASKLOAD)
1578    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadMask, id=G2gd.wxID_MASKLOADNOT)
1579    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveMask, id=G2gd.wxID_MASKSAVE)
1580    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAutoSpotMask, id=G2gd.wxID_FINDSPOTS)
1581    G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteSpotMask, id=G2gd.wxID_DELETESPOTS)
1582    G2frame.dataFrame.Bind(wx.EVT_MENU, ToggleSpotMaskMode, id=G2gd.wxID_NEWMASKSPOT)
1583    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewArcMask, id=G2gd.wxID_NEWMASKARC)
1584    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewRingMask, id=G2gd.wxID_NEWMASKRING)
1585    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewPolyMask, id=G2gd.wxID_NEWMASKPOLY)
1586    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewFrameMask, id=G2gd.wxID_NEWMASKFRAME)
1587    if not G2frame.dataFrame.GetStatusBar():
1588        G2frame.dataFrame.CreateStatusBar()
1589    if G2frame.MaskKey == 'f':
1590        G2frame.dataFrame.GetStatusBar().SetStatusText('Frame mask active - LB pick next point, RB close polygon')
1591    elif G2frame.MaskKey == 'p':
1592        G2frame.dataFrame.GetStatusBar().SetStatusText('Polygon mask active - LB pick next point, RB close polygon')
1593    elif G2frame.MaskKey == 'a':
1594        G2frame.dataFrame.GetStatusBar().SetStatusText('Arc mask active - LB pick arc location')
1595    elif G2frame.MaskKey == 'r':
1596        G2frame.dataFrame.GetStatusBar().SetStatusText('Ring mask active - LB pick ring location')
1597    else:
1598        G2frame.dataFrame.GetStatusBar().SetStatusText("To add mask: press a,r,s,p or f on 2D image for arc/ring/spot/polygon/frame")
1599    G2frame.dataDisplay = wxscroll.ScrolledPanel(G2frame.dataFrame)
1600    mainSizer = wx.BoxSizer(wx.VERTICAL)
1601    mainSizer.Add((5,10),0)
1602
1603    thresh = data['Thresholds']         #min/max intensity range
1604    Spots = data['Points']               #x,y,radius in mm
1605    Rings = data['Rings']               #radius, thickness
1606    Polygons = data['Polygons']         #3+ x,y pairs
1607    if 'Frames' not in data:
1608        data['Frames'] = []
1609    frame = data['Frames']             #3+ x,y pairs
1610    Arcs = data['Arcs']                 #radius, start/end azimuth, thickness
1611
1612    ######################################################################
1613    CId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Image Controls')
1614    controlData = G2frame.PatternTree.GetItemPyData(CId)
1615    Range = controlData['range']
1616    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1617    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1618
1619    littleSizer = wx.FlexGridSizer(0,3,0,5)
1620    littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Lower/Upper limits '),0,WACV)
1621    Text = wx.TextCtrl(G2frame.dataDisplay,value=str(thresh[0][0]),style=wx.TE_READONLY)
1622    littleSizer.Add(Text,0,WACV)
1623    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1624    Text = wx.TextCtrl(G2frame.dataDisplay,value=str(thresh[0][1]),style=wx.TE_READONLY)
1625    littleSizer.Add(Text,0,WACV)
1626    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1627    littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Lower/Upper thresholds '),0,WACV)
1628    lowerThreshold = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=thresh[1],key=0,
1629        min=thresh[0][0],OnLeave=Replot,typeHint=int)
1630    littleSizer.Add(lowerThreshold,0,WACV)
1631    upperThreshold = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=thresh[1],key=1,
1632        max=thresh[0][1],OnLeave=Replot,typeHint=int)
1633    littleSizer.Add(upperThreshold,0,WACV)
1634    mainSizer.Add(littleSizer,0,)
1635    if len(Spots):
1636        lbl = wx.StaticText(parent=G2frame.dataDisplay,label=' Spot masks')
1637        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1638        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1639        littleSizer = wx.FlexGridSizer(0,3,0,5)
1640        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' position, mm'),0,WACV)
1641        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' diameter, mm'),0,WACV)
1642        littleSizer.Add((5,0),0)
1643        for i in range(len(Spots)):
1644            if len(Spots[i]):
1645                x,y,d = Spots[i]
1646                spotText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%.2f,%.2f" % (x,y)),
1647                    style=wx.TE_READONLY)
1648                spotText.SetBackgroundColour(VERY_LIGHT_GREY)
1649                littleSizer.Add(spotText,0,WACV)
1650                spotText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1651                spotDiameter = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=Spots[i],key=2,
1652                    max=100.,OnLeave=Replot,nDig=[8,2])
1653                littleSizer.Add(spotDiameter,0,WACV)
1654                spotDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1655                    locationcode='Delete+Points+'+str(i),handler=onDeleteMask)
1656                littleSizer.Add(spotDelete,0,WACV)
1657        mainSizer.Add(littleSizer,0,)
1658    if Rings:
1659        lbl = wx.StaticText(parent=G2frame.dataDisplay,label=' Ring masks')
1660        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1661        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1662        littleSizer = wx.FlexGridSizer(0,3,0,5)
1663        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' 2-theta,deg'),0,WACV)
1664        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' thickness, deg'),0,WACV)
1665        littleSizer.Add((5,0),0)
1666        for i in range(len(Rings)):
1667            if Rings[i]:
1668                ringText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%.3f" % (Rings[i][0])),
1669                    style=wx.TE_READONLY)
1670                ringText.SetBackgroundColour(VERY_LIGHT_GREY)
1671                ringText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1672                littleSizer.Add(ringText,0,WACV)
1673                ringThick = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=Rings[i],key=1,
1674                    min=0.001,max=1.,OnLeave=Replot,nDig=[8,3])
1675                littleSizer.Add(ringThick,0,WACV)
1676                ringDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1677                    locationcode='Delete+Rings+'+str(i),handler=onDeleteMask)
1678                littleSizer.Add(ringDelete,0,WACV)
1679        mainSizer.Add(littleSizer,0,)
1680    if Arcs:
1681        lbl = wx.StaticText(parent=G2frame.dataDisplay,label=' Arc masks')
1682        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1683        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1684        littleSizer = wx.FlexGridSizer(0,4,0,5)
1685        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' 2-theta,deg'),0,WACV)
1686        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' azimuth, deg'),0,WACV)
1687        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' thickness, deg'),0,WACV)
1688        littleSizer.Add((5,0),0)
1689        for i in range(len(Arcs)):
1690            if Arcs[i]:
1691                tth,azimuth,thick = Arcs[i]
1692                arcText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%.3f" % (tth)),
1693                    style=wx.TE_READONLY)
1694                arcText.SetBackgroundColour(VERY_LIGHT_GREY)
1695                arcText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1696                littleSizer.Add(arcText,0,WACV)
1697                azmText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%d,%d" % (azimuth[0],azimuth[1])),
1698                    style=wx.TE_READONLY)
1699                azmText.SetBackgroundColour(VERY_LIGHT_GREY)
1700                azmText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1701                littleSizer.Add(azmText,0,WACV)
1702                arcThick = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=Arcs[i],key=2,
1703                    min=0.001,max=20.,OnLeave=Replot,nDig=[8,3])
1704                littleSizer.Add(arcThick,0,WACV)
1705                arcDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1706                    locationcode='Delete+Arcs+'+str(i),handler=onDeleteMask)
1707                littleSizer.Add(arcDelete,0,WACV)
1708        mainSizer.Add(littleSizer,0,)
1709    if Polygons:
1710        lbl = wx.StaticText(parent=G2frame.dataDisplay,
1711            label=' Polygon masks (on plot RB vertex drag to move, LB vertex drag to insert)')
1712        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1713        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1714        littleSizer = wx.FlexGridSizer(0,2,0,5)
1715        for i in range(len(Polygons)):
1716            if Polygons[i]:
1717                polyList = []
1718                for x,y in Polygons[i]:
1719                    polyList.append("%.2f, %.2f"%(x,y))
1720                polyText = wx.ComboBox(G2frame.dataDisplay,value=polyList[0],choices=polyList,style=wx.CB_READONLY)
1721                littleSizer.Add(polyText,0,WACV)
1722                polyDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1723                    locationcode='Delete+Polygons+'+str(i),handler=onDeleteMask)
1724                littleSizer.Add(polyDelete,0,WACV)
1725        mainSizer.Add(littleSizer,0,)
1726    if frame:
1727        lbl = wx.StaticText(parent=G2frame.dataDisplay,
1728            label=' Frame mask (on plot RB vertex drag to move, LB vertex drag to insert)')
1729        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1730        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1731        littleSizer = wx.FlexGridSizer(0,2,0,5)
1732        frameList = []
1733        for x,y in frame:
1734            frameList.append("%.2f, %.2f"%(x,y))
1735        frameText = wx.ComboBox(G2frame.dataDisplay,value=frameList[0],choices=frameList,style=wx.CB_READONLY)
1736        littleSizer.Add(frameText,0,WACV)
1737        frameDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1738            locationcode='Delete+Frame',handler=onDeleteFrame)
1739        littleSizer.Add(frameDelete,0,WACV)
1740        mainSizer.Add(littleSizer,0,)
1741    mainSizer.Layout()   
1742    G2frame.dataDisplay.SetSizer(mainSizer)
1743    G2frame.dataDisplay.SetupScrolling()
1744    Size = mainSizer.Fit(G2frame.dataFrame)
1745    Size[0] += 50 # room for scrollbar & status msg
1746    Size[1] = min(Size[1],500)
1747    G2frame.dataFrame.setSizePosLeft(Size)   
1748    if startScroll: # reset scroll to saved position
1749        G2frame.dataDisplay.Scroll(0,startScroll) # set to saved scroll position
1750        wx.Yield()
1751
1752################################################################################
1753##### Stress/Strain
1754################################################################################
1755
1756def UpdateStressStrain(G2frame,data):
1757    '''Shows and handles the controls on the "Stress/Strain"
1758    data tree entry
1759    '''
1760   
1761    def OnAppendDzero(event):
1762        data['d-zero'].append({'Dset':1.0,'Dcalc':0.0,'pixLimit':10,'cutoff':1.0,
1763            'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]],'Emat':[1.0,1.0,1.0]})
1764        UpdateStressStrain(G2frame,data)
1765       
1766    def OnUpdateDzero(event):
1767        for item in data['d-zero']:
1768            if item['Dcalc']:   #skip unrefined ones
1769                item['Dset'] = item['Dcalc']
1770        UpdateStressStrain(G2frame,data)
1771           
1772    def OnCopyStrSta(event):
1773        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1774        if len(Names) == 1:
1775            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
1776            return
1777        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
1778        Names.pop(Names.index(Source))
1779        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy stress/strain controls','Copy controls from '+Source+' to:',Names)
1780        try:
1781            if dlg.ShowModal() == wx.ID_OK:
1782                items = dlg.GetSelections()
1783                for item in items:
1784                    name = Names[item]
1785                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
1786                    CId = G2gd.GetPatternTreeItemId(G2frame,Id,'Stress/Strain')
1787                    oldData = G2frame.PatternTree.GetItemPyData(CId)
1788                    load = oldData.get('Sample load',0.0)
1789                    Data = copy.deepcopy(data)
1790                    Data['Sample load'] = load
1791                    G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Stress/Strain'),Data)
1792        finally:
1793            dlg.Destroy()
1794            G2frame.PatternTree.SelectItem(G2frame.PickId)
1795
1796    def OnLoadStrSta(event):
1797        pth = G2G.GetImportPath(G2frame)
1798        if not pth: pth = '.'
1799        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1800            'image control files (*.strsta)|*.strsta',wx.OPEN)
1801        try:
1802            if dlg.ShowModal() == wx.ID_OK:
1803                filename = dlg.GetPath()
1804                File = open(filename,'r')
1805                S = File.read()
1806                data = eval(S)
1807                Controls = G2frame.PatternTree.GetItemPyData(
1808                    G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1809                G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1810                UpdateStressStrain(G2frame,data)
1811                G2plt.PlotExposedImage(G2frame,event=event)
1812                G2plt.PlotStrain(G2frame,data,newPlot=True)
1813                File.close()
1814        finally:
1815            dlg.Destroy()
1816
1817    def OnSaveStrSta(event):
1818        pth = G2G.GetExportPath(G2frame)
1819        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1820            'image control files (*.strsta)|*.strsta',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1821        try:
1822            if dlg.ShowModal() == wx.ID_OK:
1823                filename = dlg.GetPath()
1824                File = open(filename,'w')
1825                keys = ['Type','Sample phi','Sample z','Sample load']
1826                keys2 = ['Dset','Dcalc','pixLimit','cutoff','Emat']
1827                File.write('{\n\t')
1828                for key in keys:
1829                    if key in 'Type':
1830                        File.write("'"+key+"':'"+data[key]+"',")
1831                    else:
1832                        File.write("'"+key+"':"+str(data[key])+',')
1833                File.write('\n\t'+"'d-zero':[\n")
1834                for data2 in data['d-zero']:
1835                    File.write('\t\t{')
1836                    for key in keys2:
1837                        File.write("'"+key+"':"+str(data2[key])+',')
1838                    File.write("'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]]},\n")
1839                File.write('\t]\n}')
1840                File.close()
1841        finally:
1842            dlg.Destroy()
1843           
1844    def OnStrStaSample(event):
1845        filename = ''
1846        pth = G2G.GetImportPath(G2frame)
1847        if not pth: pth = '.'
1848        dlg = wx.FileDialog(G2frame, 'Choose multihistogram metadata text file', pth, '', 
1849            'metadata file (*.*)|*.*',wx.OPEN)
1850        try:
1851            if dlg.ShowModal() == wx.ID_OK:
1852                filename = dlg.GetPath()
1853                File = open(filename,'r')
1854                S = File.readline()
1855                newItems = []
1856                itemNames = []
1857                Comments = []
1858                while S:
1859                    if S[0] == '#':
1860                        Comments.append(S)
1861                        S = File.readline()
1862                        continue
1863                    S = S.replace(',',' ').replace('\t',' ')
1864                    Stuff = S[:-1].split()
1865                    itemNames.append(Stuff[0])
1866                    newItems.append(Stuff[1:])
1867                    S = File.readline()               
1868                File.close()
1869        finally:
1870            dlg.Destroy()
1871        if not filename:
1872            G2frame.ErrorDialog('Nothing to do','No file selected')
1873            return
1874        dataDict = dict(zip(itemNames,newItems))
1875        ifany = False
1876        Names = [' ','Sample phi','Sample z','Sample load']
1877        dlg = G2G.G2ColumnIDDialog( G2frame,' Choose multihistogram metadata columns:',
1878            'Select columns',Comments,Names,np.array(newItems).T)
1879        try:
1880            if dlg.ShowModal() == wx.ID_OK:
1881                colNames,newData = dlg.GetSelection()
1882                dataDict = dict(zip(itemNames,newData.T))
1883                for item in colNames:
1884                    if item != ' ':
1885                        ifany = True
1886        finally:
1887            dlg.Destroy()
1888        if not ifany:
1889            G2frame.ErrorDialog('Nothing to do','No columns identified')
1890            return
1891        histList = []
1892        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)       
1893        while item:
1894            name = G2frame.PatternTree.GetItemText(item)
1895            if name.startswith('IMG'):
1896                histList.append(name)
1897            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1898        colIds = {}
1899        for i,name in enumerate(colNames):
1900            if name != ' ':
1901                colIds[name] = i
1902        for hist in histList:
1903            name = hist.split()[1]  #this is file name
1904            if name in dataDict:
1905                newItems = {}
1906                for item in colIds:
1907                    newItems[item] = float(dataDict[name][colIds[item]])
1908                Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,hist)
1909                stsrData = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Stress/Strain'))
1910                stsrData.update(newItems)       
1911        UpdateStressStrain(G2frame,data)       
1912   
1913    def OnPlotStrSta(event):
1914        Controls = G2frame.PatternTree.GetItemPyData(
1915            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1916        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1917        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1918        G2plt.PlotExposedImage(G2frame,event=event)
1919        G2frame.G2plotNB.Delete('Ring Intensities')
1920        G2plt.PlotXY(G2frame,RingInt,labelX='Azimuth',
1921            labelY='MRD',newPlot=True,Title='Ring Intensities',
1922            names=Names,lines=True)
1923       
1924    def OnSaveStrRing(event):
1925        Controls = G2frame.PatternTree.GetItemPyData(
1926            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1927        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1928        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1929        pth = G2G.GetExportPath(G2frame)
1930        dlg = wx.FileDialog(G2frame, 'Choose strain ring intensity file', pth, '', 
1931            'ring intensity file (*.txt)|*.txt',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1932        try:
1933            if dlg.ShowModal() == wx.ID_OK:
1934                filename = dlg.GetPath()
1935                File = open(filename,'w')
1936                for i,name in enumerate(Names):
1937                    File.write('%s%s\n'%(' Ring intensity for ',name))
1938                    File.write('%12s %12s\n'%('Azimuth','RMD'))
1939                    for item in RingInt[i].T:
1940                        File.write(' %12.3f %12.3f\n'%(item[0],item[1]))
1941                    File.write('\n')
1942                File.close()
1943        finally:
1944            dlg.Destroy()
1945               
1946               
1947    def OnFitStrSta(event):
1948        Controls = G2frame.PatternTree.GetItemPyData(
1949            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1950        G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1951        print 'Strain fitting finished'
1952        UpdateStressStrain(G2frame,data)
1953        G2plt.PlotExposedImage(G2frame,event=event)
1954        G2plt.PlotStrain(G2frame,data,newPlot=True)
1955       
1956    def OnFitAllStrSta(event):
1957        choices = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1958        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices)
1959        names = []
1960        if dlg.ShowModal() == wx.ID_OK:
1961            for sel in dlg.GetSelections():
1962                names.append(choices[sel])
1963            Id =  G2gd.GetPatternTreeItemId(G2frame,G2frame.root,'Sequential strain fit results')
1964            if Id:
1965                SeqResult = G2frame.PatternTree.GetItemPyData(Id)
1966            else:
1967                SeqResult = {}
1968                Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text='Sequential strain fit results')
1969            SeqResult.update({'SeqPseudoVars':{},'SeqParFitEqList':[]})
1970        else:
1971            dlg.Destroy()
1972            return
1973        dlg.Destroy()
1974        if not names:
1975            return
1976        Reverse = False
1977        CopyForward = False
1978        choice = ['Reverse sequence','Copy from prev.',]
1979        dlg = wx.MultiChoiceDialog(G2frame.dataFrame,'Sequential controls','Select controls',choice)
1980        if dlg.ShowModal() == wx.ID_OK:
1981            for sel in dlg.GetSelections():
1982                if sel:
1983                    CopyForward = True
1984                else:
1985                    Reverse = True
1986        dlg.Destroy()
1987        dlg = wx.ProgressDialog('Sequential IMG Strain fit','Data set name = '+names[0],len(names), 
1988            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT)         
1989        wx.BeginBusyCursor()
1990        goodnames = []
1991        if Reverse:
1992            names.reverse()
1993        try:
1994            for i,name in enumerate(names):
1995                print ' Sequential strain fit for ',name
1996                GoOn = dlg.Update(i,newmsg='Data set name = '+name)[0]
1997                if not GoOn:
1998                    break
1999                sId =  G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
2000                G2frame.Image = sId
2001                Controls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,sId, 'Image Controls'))
2002                StaCtrls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,sId, 'Stress/Strain'))
2003                if not len(StaCtrls['d-zero']):
2004                    continue
2005                goodnames.append(name)
2006                Npix,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(sId)
2007                image = GetImageZ(G2frame,Controls)
2008                sig = []
2009                varyList = []
2010                variables = []
2011                if i and CopyForward:
2012                    for j,ring in enumerate(StaCtrls['d-zero']):
2013                        ring['Emat'] = variables[4*j:4*j+3]
2014                #get results from previous & put in StaCtrls
2015                G2img.FitStrSta(image,StaCtrls,Controls)
2016                G2plt.PlotStrain(G2frame,StaCtrls,newPlot=True)
2017                parmDict = {'Sample load':StaCtrls['Sample load'],}
2018                varyNames = ['e11','e12','e22']
2019                for j,item in enumerate(StaCtrls['d-zero']):
2020                    variables += item['Emat']
2021                    sig += item['Esig']
2022                    varylist = ['%d;%s'%(j,Name) for Name in varyNames]
2023                    varyList += varylist
2024                    parmDict.update(dict(zip(varylist,item['Emat'])))
2025                    parmDict['%d;Dcalc'%(j)] = item['Dcalc']
2026                    parmDict['%d;Ivar'%(j)] = item['Ivar']
2027                    variables.append(item['Ivar'])
2028                    varyList.append('%d;Ivar'%(j))
2029                    sig.append(None)
2030                SeqResult[name] = {'variables':variables,'varyList':varyList,'sig':sig,'Rvals':[],
2031                    'covMatrix':np.eye(len(variables)),'title':name,'parmDict':parmDict}
2032            else:
2033                SeqResult['histNames'] = goodnames
2034                dlg.Destroy()
2035                print ' ***** Sequential strain refinement successful *****'
2036        finally:
2037            wx.EndBusyCursor()   
2038        SeqResult['histNames'] = choices
2039        G2frame.PatternTree.SetItemPyData(Id,SeqResult)
2040        G2frame.PatternTree.SelectItem(Id)
2041        print 'All images fitted'
2042       
2043    def SamSizer():
2044       
2045        def OnStrainType(event):
2046            data['Type'] = strType.GetValue()
2047       
2048        samSizer = wx.BoxSizer(wx.HORIZONTAL)
2049        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Strain type: '),0,WACV)
2050        strType = wx.ComboBox(G2frame.dataDisplay,value=data['Type'],choices=['True','Conventional'],
2051            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2052        strType.SetValue(data['Type'])
2053        strType.Bind(wx.EVT_COMBOBOX, OnStrainType)
2054        samSizer.Add(strType,0,WACV)
2055       
2056        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample phi: '),0,WACV)
2057        samPhi = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample phi',nDig=(10,3),typeHint=float,min=-360.,max=360.)
2058        samSizer.Add(samPhi,0,WACV)
2059        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample delta-z(mm): '),0,WACV)
2060        samZ = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample z',nDig=(10,3),typeHint=float)
2061        samSizer.Add(samZ,0,WACV)
2062        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample load(MPa): '),0,WACV)
2063        samLoad = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample load',
2064                nDig=[8,3],typeHint=float,)
2065        samSizer.Add(samLoad,0,WACV)
2066
2067        return samSizer
2068       
2069    def DzeroSizer():
2070   
2071        def OnDzero(invalid,value,tc):
2072            data['d-zero'] = G2mth.sortArray(data['d-zero'],'Dset',reverse=True)
2073            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[tc.GetId()]],G2frame.ImageZ,Controls)
2074            if len(Ring):
2075                data['d-zero'][Indx[tc.GetId()]].update(R)
2076            else:
2077                G2frame.ErrorDialog('Strain peak selection','WARNING - No points found for this ring selection')
2078               
2079            wx.CallAfter(UpdateStressStrain,G2frame,data)
2080            G2plt.PlotExposedImage(G2frame,event=tc.event,newPlot=False)
2081            G2plt.PlotStrain(G2frame,data,newPlot=True)
2082           
2083        def OnDeleteDzero(event):
2084            Obj = event.GetEventObject()
2085            del(data['d-zero'][delIndx.index(Obj)])
2086            UpdateStressStrain(G2frame,data)
2087            G2plt.PlotExposedImage(G2frame,event=event,newPlot=True)
2088            G2plt.PlotStrain(G2frame,data,newPlot=True)
2089       
2090        def OnCutOff(invalid,value,tc):
2091            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[tc.GetId()]],G2frame.ImageZ,Controls)
2092            G2plt.PlotExposedImage(G2frame,event=tc.event)
2093            G2plt.PlotStrain(G2frame,data,newPlot=True)
2094       
2095        def OnPixLimit(event):
2096            Obj = event.GetEventObject()
2097            data['d-zero'][Indx[Obj.GetId()]]['pixLimit'] = int(Obj.GetValue())
2098            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[Obj.GetId()]],G2frame.ImageZ,Controls)
2099            G2plt.PlotExposedImage(G2frame,event=event)
2100            G2plt.PlotStrain(G2frame,data,newPlot=True)
2101           
2102        Indx = {}
2103        delIndx = []   
2104        dzeroSizer = wx.FlexGridSizer(0,8,5,5)
2105        for id,dzero in enumerate(data['d-zero']):
2106            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' d-zero #%d: '%(id))),0,WACV)
2107            dZero = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['d-zero'][id],'Dset',
2108                min=0.25,max=20.,nDig=(10,5),typeHint=float,OnLeave=OnDzero)
2109            dzeroSizer.Add(dZero,0,WACV)
2110            Indx[dZero.GetId()] = id
2111            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' d-zero ave: %.5f'%(dzero['Dcalc']))),0,WACV)
2112               
2113            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min ring I/Ib '),0,WACV)
2114            cutOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['d-zero'][id],'cutoff',
2115                    min=0.5,max=20.,nDig=(10,1),typeHint=float,OnLeave=OnCutOff)
2116            Indx[cutOff.GetId()] = id
2117            dzeroSizer.Add(cutOff,0,WACV)
2118       
2119            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Pixel search range '),0,WACV)
2120            pixLimit = wx.ComboBox(parent=G2frame.dataDisplay,value=str(dzero['pixLimit']),choices=['1','2','5','10','15','20'],
2121                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2122            pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
2123            Indx[pixLimit.GetId()] = id
2124            dzeroSizer.Add(pixLimit,0,WACV)               
2125               
2126            dzeroDelete = wx.CheckBox(parent=G2frame.dataDisplay,label='delete?')
2127            dzeroDelete.Bind(wx.EVT_CHECKBOX,OnDeleteDzero)
2128            delIndx.append(dzeroDelete)
2129            dzeroSizer.Add(dzeroDelete,0,WACV)
2130           
2131            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' Strain tensor:')),WACV)
2132            names = ['e11','e12','e22']
2133            for i in range(3):
2134                dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=names[i]),0,WACV)
2135                tensorElem = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(dzero['Emat'][i]),style=wx.TE_READONLY)
2136                tensorElem.SetBackgroundColour(VERY_LIGHT_GREY)
2137                dzeroSizer.Add(tensorElem,0,WACV)
2138            dzeroSizer.Add((5,5),0)             
2139        return dzeroSizer
2140       
2141# patches
2142    if 'Sample load' not in data:
2143        data['Sample load'] = 0.0
2144# end patches
2145   
2146    if G2frame.dataDisplay:
2147        G2frame.dataDisplay.Destroy()
2148    Controls = G2frame.PatternTree.GetItemPyData(
2149        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))       
2150    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.StrStaMenu)
2151    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAppendDzero, id=G2gd.wxID_APPENDDZERO)
2152    G2frame.dataFrame.Bind(wx.EVT_MENU, OnUpdateDzero, id=G2gd.wxID_UPDATEDZERO)
2153    G2frame.dataFrame.Bind(wx.EVT_MENU, OnFitStrSta, id=G2gd.wxID_STRSTAFIT)
2154    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotStrSta, id=G2gd.wxID_STRSTAPLOT) 
2155    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveStrRing, id=G2gd.wxID_STRRINGSAVE) 
2156    G2frame.dataFrame.Bind(wx.EVT_MENU, OnFitAllStrSta, id=G2gd.wxID_STRSTAALLFIT)
2157    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyStrSta, id=G2gd.wxID_STRSTACOPY)
2158    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadStrSta, id=G2gd.wxID_STRSTALOAD)
2159    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveStrSta, id=G2gd.wxID_STRSTASAVE)
2160    G2frame.dataFrame.Bind(wx.EVT_MENU, OnStrStaSample, id=G2gd.wxID_STRSTSAMPLE)       
2161    if not G2frame.dataFrame.GetStatusBar():
2162        G2frame.dataFrame.CreateStatusBar()
2163    if G2frame.StrainKey == 'a':    #probably doesn't happen
2164        G2frame.dataFrame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value')
2165    else:
2166        G2frame.dataFrame.GetStatusBar().SetStatusText("To add strain data: On 2D Powder Image, key a:add ring")
2167       
2168    G2frame.dataDisplay = wxscroll.ScrolledPanel(G2frame.dataFrame)
2169    mainSizer = wx.BoxSizer(wx.VERTICAL)
2170    mainSizer.Add((5,10),0)
2171    mainSizer.Add(SamSizer())
2172    mainSizer.Add((5,10),0)
2173    mainSizer.Add(DzeroSizer())
2174   
2175    mainSizer.Layout()   
2176    G2frame.dataDisplay.SetSizer(mainSizer)
2177    G2frame.dataDisplay.SetAutoLayout(1)
2178    G2frame.dataDisplay.SetupScrolling()
2179    Size = mainSizer.Fit(G2frame.dataFrame)
2180    Size[0] += 25
2181    G2frame.dataFrame.setSizePosLeft(Size)   
2182
2183###########################################################################
2184# Autointegration
2185###########################################################################
2186def ReadMask(filename):
2187    'Read a mask (.immask) file'
2188    File = open(filename,'r')
2189    save = {}
2190    S = File.readline()
2191    while S:
2192        if S[0] == '#':
2193            S = File.readline()
2194            continue
2195        [key,val] = S.strip().split(':',1)
2196        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
2197            save[key] = eval(val)
2198        S = File.readline()
2199    File.close()
2200    CleanupMasks(save)
2201    return save
2202
2203def ReadControls(filename):
2204    'read an image controls (.imctrl) file'
2205    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2206            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2207            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2208            'PolaVal','SampleAbs','dark image','background image']
2209    File = open(filename,'r')
2210    save = {}
2211    S = File.readline()
2212    while S:
2213        if S[0] == '#':
2214            S = File.readline()
2215            continue
2216        [key,val] = S.strip().split(':',1)
2217        if key in ['type','calibrant','binType','SampleShape',]:    #strings
2218            save[key] = val
2219        elif key in ['rotation']:
2220            save[key] = float(val)
2221        elif key in ['center',]:
2222            if ',' in val:
2223                save[key] = eval(val)
2224            else:
2225                vals = val.strip('[] ').split()
2226                save[key] = [float(vals[0]),float(vals[1])] 
2227        elif key in cntlList:
2228            save[key] = eval(val)
2229        S = File.readline()
2230    File.close()
2231    return save
2232
2233def Read_imctrl(imctrl_file):
2234    '''Read an image control file and record control parms into a dict, with some simple
2235    type conversions
2236    '''
2237    save = {'filename':imctrl_file}
2238    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
2239    if os.path.exists(immask_file):
2240        save['maskfile'] = immask_file
2241    else:
2242        save['maskfile'] = '(none)'
2243    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2244                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2245                        'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2246                        'PolaVal','SampleAbs','dark image','background image']
2247    File = open(imctrl_file,'r')
2248    fullIntegrate = False
2249    try:
2250        S = File.readline()
2251        while S:
2252            if S[0] == '#':
2253                S = File.readline()
2254                continue
2255            [key,val] = S.strip().split(':',1)
2256            if val.find(':') != -1:
2257                #print 'rejecting ',key,val
2258                S = File.readline()
2259                continue
2260            if key in ['type','calibrant','binType','SampleShape',]:    #strings
2261                save[key] = val
2262            elif key == 'rotation':
2263                save[key] = float(val)
2264            elif key == 'fullIntegrate':
2265                fullIntegrate = eval(val)
2266            elif key == 'LRazimuth':
2267                save['LRazimuth_min'],save['LRazimuth_max'] = eval(val)[0:2]
2268            elif key == 'IOtth':
2269                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
2270            elif key == 'center':
2271                if ',' in val:
2272                    vals = eval(val)
2273                else:
2274                    vals = val.strip('[] ').split()
2275                    vals = [float(vals[0]),float(vals[1])] 
2276                save['center_x'],save['center_y'] = vals[0:2]
2277            elif key in cntlList:
2278                save[key] = eval(val)
2279            S = File.readline()
2280    finally:
2281        File.close()
2282        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0.,0.
2283    return save
2284   
2285class AutoIntFrame(wx.Frame):
2286    '''Creates a wx.Frame window for the Image AutoIntegration.
2287    The intent is that this will be used as a non-modal dialog window.
2288   
2289    Implements a Start button that morphs into a pause and resume button.
2290    This button starts a processing loop that is repeated every
2291    :meth:`PollTime` seconds.
2292
2293    :param wx.Frame G2frame: main GSAS-II frame
2294    :param float PollTime: frequency in seconds to repeat calling the
2295      processing loop. (Default is 3.0 seconds.)
2296    '''
2297
2298    def __init__(self,G2frame,PollTime=60.0):
2299        def OnStart(event):
2300            '''Called when the start button is pressed. Changes button label
2301            to Pause. When Pause is pressed the label changes to Resume.
2302            When either Start or Resume is pressed, the processing loop
2303            is started. When Pause is pressed, the loop is stopped.
2304            '''
2305            # check inputs for errors before starting
2306            #err = ''
2307            #if not any([self.params[fmt] for fmt in self.fmtlist]):
2308            #    err += '\nPlease select at least one output format\n'
2309            #if err:
2310            #    G2G.G2MessageBox(self,err)
2311            #    return
2312            self.Pause = False
2313            # change button label
2314            if self.btnstart.GetLabel() != 'Pause':
2315                self.btnstart.SetLabel('Pause')
2316                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
2317                if self.timer.IsRunning(): self.timer.Stop()
2318                self.PreventReEntryTimer = False
2319                if self.StartLoop():
2320                    G2G.G2MessageBox(self,'Error in setting up integration. See console')
2321                    return
2322                self.OnTimerLoop(None) # run once immediately
2323                if not self.Pause:
2324                    # no pause, so start timer to check for new files
2325                    self.timer.Start(int(1000*PollTime),oneShot=False)
2326                    return
2327            # we will get to this point if Paused
2328            self.OnPause()
2329           
2330        def OnReset(event):
2331            '''Called when Reset button is pressed. This stops the
2332            processing loop and resets the list of integrated files so
2333            all images can be reintegrated.
2334            '''
2335            self.btnstart.SetLabel('Restart')
2336            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
2337            if self.timer.IsRunning(): self.timer.Stop()
2338            self.Reset = True
2339            self.Pause = True
2340           
2341        def OnQuit(event):
2342            '''Stop the processing loop and close the Frame
2343            '''
2344            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
2345            wx.CallAfter(self.Destroy)
2346           
2347        def OnBrowse(event):
2348            '''Responds when the Browse button is pressed to load a file.
2349            The routine determines which button was pressed and gets the
2350            appropriate file type and loads it into the appropriate place
2351            in the dict.
2352            '''
2353            if btn3 == event.GetEventObject():
2354                dlg = wx.DirDialog(
2355                    self, 'Select directory for output files',
2356                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
2357                dlg.CenterOnParent()
2358                try:
2359                    if dlg.ShowModal() == wx.ID_OK:
2360                        self.params['outdir'] = dlg.GetPath()
2361                        fInp3.SetValue(self.params['outdir'])
2362                finally:
2363                    dlg.Destroy()
2364                return
2365            elif btn4 == event.GetEventObject():
2366                msg = ''
2367                pth = G2G.GetExportPath(G2frame)
2368                dlg = wx.FileDialog(
2369                    self, 'Select a PDF parameter file',
2370                    pth, self.params['pdfprm'], 
2371                    "PDF controls file (*.pdfprm)|*.pdfprm",
2372                    wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
2373                dlg.CenterOnParent()
2374                try:
2375                    if dlg.ShowModal() == wx.ID_OK:
2376                        self.params['pdfprm'] = dlg.GetPath()
2377                        fInp4.SetValue(self.params['pdfprm'])
2378                        scanPDFprm()
2379                        msg = self.checkPDFprm(True)
2380                finally:
2381                    dlg.Destroy()
2382                if 'Error' in msg:
2383                    print(msg)
2384                    lbl = 'PDFPRM error'
2385                else:
2386                    msg = 'Information from file {}\n\n{}'.format(self.params['pdfprm'],msg)
2387                    lbl = 'PDFPRM information'
2388                G2G.G2MessageBox(self,msg,lbl)
2389                return
2390               
2391        def OnRadioSelect(event):
2392            '''Respond to a radiobutton selection and when in table
2393            mode, get distance-dependent parameters from user.
2394            '''
2395            self.Evaluator = None
2396            if self.useTable.GetValue():
2397                dlg = None
2398                try:
2399                    dlg = IntegParmTable(self) # create the dialog
2400                    dlg.CenterOnParent()
2401                    if dlg.ShowModal() == wx.ID_OK:
2402                        self.ImgTblParms = dlg.parms
2403                        self.IMfileList = dlg.IMfileList
2404                        self.Evaluator = DefineEvaluator(dlg)
2405                        self.params['Mode'] = 'table'
2406                        self.editTable.Enable(True)
2407                    else:
2408                        self.useActive.SetValue(True)
2409                finally:
2410                    if dlg: dlg.Destroy()
2411            elif self.useActive.GetValue():
2412                self.params['Mode'] = 'active'
2413                self.imageBase = G2frame.Image
2414                self.useActive.SetLabel("Active Image: "+
2415                        G2frame.PatternTree.GetItemText(self.imageBase))
2416                self.editTable.Enable(False)
2417            else:
2418                print('unexpected mode in OnRadioSelect')
2419
2420        def OnEditTable(event):
2421            '''Called to edit the distance-dependent parameter look-up table.
2422            Should be called only when table is defined and active.
2423            '''
2424            dlg = None
2425            try:
2426                dlg = IntegParmTable(self,self.ImgTblParms,self.IMfileList)
2427                dlg.CenterOnParent()
2428                if dlg.ShowModal() == wx.ID_OK:
2429                    self.ImgTblParms = dlg.parms
2430                    self.IMfileList = dlg.IMfileList
2431                    self.Evaluator = DefineEvaluator(dlg)
2432                    self.params['Mode'] = 'table'
2433                    self.editTable.Enable(True)
2434                else:
2435                    self.useActive.SetValue(True)
2436                    self.params['Mode'] = 'active'
2437                    self.imageBase = G2frame.Image
2438                    self.useActive.SetLabel("Active Image: "+
2439                            G2frame.PatternTree.GetItemText(self.imageBase))
2440                    self.editTable.Enable(False)
2441            finally:
2442                if dlg: dlg.Destroy()
2443               
2444        def showPDFctrls(event):
2445            '''Called to show or hide AutoPDF widgets. Note that fInp4 must be included in the
2446            sizer layout with .Show(True) before .Show(False) will work properly.
2447            '''
2448            fInp4.Enable(self.params['ComputePDF'])
2449            fInp4.Show(self.params['ComputePDF'])
2450            fInp4a.Enable(self.params['ComputePDF'])
2451            btn4.Enable(self.params['ComputePDF'])
2452            cOpt.Enable(self.params['ComputePDF'])
2453            if self.params['ComputePDF']:
2454                lbl4.SetForegroundColour("black")
2455                lbl4a.SetForegroundColour("black")
2456            else:
2457                lbl4.SetForegroundColour("gray")
2458                lbl4a.SetForegroundColour("gray")
2459                                   
2460        def scanPDFprm(**kw):
2461            fInp4.invalid = not os.path.exists(fInp4.GetValue())
2462            fInp4._IndicateValidity()
2463               
2464        ##################################################
2465        # beginning of __init__ processing
2466        ##################################################
2467        self.G2frame = G2frame
2468        self.ImgTblParms = None
2469        self.IMfileList = None
2470        self.Evaluator = None
2471        self.params = {}
2472        self.Reset = False
2473        self.Pause = False
2474        self.PreventReEntryShowMatch = False
2475        self.PreventReEntryTimer = False
2476        self.params['IMGfile'] = ''
2477        self.params['MaskFile'] = ''
2478        self.params['IgnoreMask'] = True
2479        self.fmtlist = G2IO.ExportPowderList(G2frame)
2480        self.timer = wx.Timer()
2481        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
2482        self.imageBase = G2frame.Image
2483        self.params['ComputePDF'] = False
2484        self.params['pdfDmax'] = 0.0
2485        self.params['pdfprm'] = ''
2486        self.params['optPDF'] = True
2487        self.pdfControls = {}
2488
2489        G2frame.PatternTree.GetSelection()
2490        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(self.imageBase)       
2491        self.imagedir,fileroot = os.path.split(imagefile)
2492        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2493        self.params['outdir'] = os.path.abspath(self.imagedir)
2494        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2495                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2496        self.Status = self.CreateStatusBar()
2497        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2498        mnpnl = wx.Panel(self)
2499        mnsizer = wx.BoxSizer(wx.VERTICAL)
2500        # box for integration controls & masks input
2501        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2502        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2503        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2504        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2505                            style = wx.RB_GROUP)
2506        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2507        self.useActive.SetLabel("Active Image: "+
2508                                G2frame.PatternTree.GetItemText(self.imageBase))
2509        lblsizr.Add(self.useActive,1,wx.EXPAND,1)
2510        self.useActive.SetValue(True)
2511        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2512        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2513        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2514        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2515        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2516        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2517        self.editTable.Enable(False)
2518        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2519        # bind button and deactivate be default
2520        lblsizr.Add(minisizer)
2521        mnsizer.Add(lblsizr,1,wx.EXPAND,1)
2522
2523        # file filter stuff
2524        sizer = wx.BoxSizer(wx.HORIZONTAL)
2525        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Image filter'))
2526        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',OnLeave=self.ShowMatchingFiles)
2527        sizer.Add(flterInp)
2528        mnsizer.Add(sizer,0,wx.ALIGN_RIGHT,1)
2529        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2530        mnsizer.Add(self.ListBox,0,wx.EXPAND,1)
2531        self.ShowMatchingFiles(self.params['filter'])
2532        # box for output selections
2533        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2534        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2535        sizer = wx.BoxSizer(wx.HORIZONTAL)
2536        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2537        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2538        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2539        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2540        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2541        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2542        lblsizr.Add(sizer,0,wx.EXPAND)
2543        sizer = wx.BoxSizer(wx.HORIZONTAL)
2544        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
2545        for dfmt in self.fmtlist:
2546            fmt = dfmt[1:]
2547            self.params[fmt] = False
2548            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2549            sizer.Add(btn)
2550        lblsizr.Add(sizer)
2551        sizer = wx.BoxSizer(wx.HORIZONTAL)
2552        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2553        self.params['SeparateDir'] = False
2554        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2555        lblsizr.Add(sizer)
2556        sizer = wx.BoxSizer(wx.HORIZONTAL)
2557        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
2558        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2559        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2560        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2561        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2562        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2563        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
2564        sizer.Add(cOpt)
2565        lblsizr.Add(sizer,0)
2566        sizer = wx.BoxSizer(wx.HORIZONTAL)
2567        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2568        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2569        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2570                                     OnLeave=scanPDFprm)
2571        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2572        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2573        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2574        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2575        lblsizr.Add(sizer,0,wx.EXPAND)
2576        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2577        # buttons on bottom
2578        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2579        sizer = wx.BoxSizer(wx.HORIZONTAL)
2580        sizer.Add((20,-1))
2581        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2582        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2583        sizer.Add(self.btnstart)
2584        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2585        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2586        sizer.Add(self.btnreset)
2587        sizer.Add((20,-1),wx.EXPAND,1)
2588        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
2589        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
2590        sizer.Add(self.btnclose)
2591        sizer.Add((20,-1))
2592        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
2593        # finish up window
2594        mnpnl.SetSizer(mnsizer)
2595        OnRadioSelect(None) # disable widgets
2596        mnsizer.Fit(self)
2597        self.CenterOnParent()
2598        self.Show()
2599        showPDFctrls(None)
2600
2601    def checkPDFprm(self,ShowContents=False):
2602        '''Read in the PDF (.pdfprm) parameter file and check for problems.
2603        If ShowContents is True, a formatted text version of some of the file
2604        contents is returned. If errors are found, the return string will contain
2605        the string "Error:" at least once.
2606        '''
2607        self.pdfControls = {}
2608        msg = ''
2609        File = None
2610        try:
2611            File = open(self.params['pdfprm'],'r')
2612            S = File.readline()
2613            while S:
2614                if '#' in S:
2615                    S = File.readline()
2616                    continue
2617                key,val = S.split(':',1)
2618                try:
2619                    self.pdfControls[key] = eval(val)
2620                except:
2621                    self.pdfControls[key] = val
2622                S = File.readline()
2623        except Exception as err:
2624            msg += 'PDF Processing Error: error with open or read of {}'.format(self.params['pdfprm'])
2625            if GSASIIpath.GetConfigValue('debug'):
2626                print(msg)
2627                print(err)
2628            self.pdfControls = {}
2629            return msg
2630        finally:
2631            if File: File.close()
2632        formula = ''
2633        for el in self.pdfControls['ElList']:
2634            if self.pdfControls['ElList'][el]['FormulaNo'] <= 0: continue
2635            if formula: formula += ' '
2636            formula += '{}({:.1f})'.format(el,self.pdfControls['ElList'][el]['FormulaNo'])
2637        if not formula:
2638            msg += 'Error: no chemical formula in file'
2639        for key in ['Sample Bkg.','Container','Container Bkg.']:
2640            if key not in self.pdfControls:
2641                if msg: msg += '\n'
2642                msg += 'Error: missing key in self.pdfControls: '+key
2643                continue
2644        if msg or not ShowContents: return msg  # stop on error
2645        msg += 'Default formula: '+formula+'\n'
2646        for key in ['Sample Bkg.','Container','Container Bkg.']:
2647            name = self.pdfControls[key]['Name']
2648            mult = self.pdfControls[key].get('Mult',0.0)
2649            if not name: continue
2650            msg += '\n{}: {:.2f} * "{}"'.format(key,mult,name)
2651            if not G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,name):
2652                msg += ' *** missing ***'
2653        return msg
2654   
2655    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
2656        G2frame = self.G2frame
2657        if invalid: return
2658        msg = ''
2659        if self.PreventReEntryShowMatch: return
2660        self.PreventReEntryShowMatch = True
2661        imageFileList = []
2662        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
2663            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2664            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2665            if imagefile not in imageFileList: imageFileList.append(imagefile)
2666            if img not in G2frame.IntegratedList:
2667                if msg: msg += '\n'
2668                msg += '  ' + img
2669        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
2670        msg1 = ""
2671        try:
2672            imageList = sorted(
2673                glob.glob(os.path.join(self.imagedir,value)))
2674            if not imageList:
2675                msg1 = 'Warning: No files match search string '+os.path.join(self.imagedir,value)
2676            else:
2677                for fil in imageList:
2678                    if fil not in imageFileList: msg1 += '\n  '+fil
2679                if msg1:
2680                    msg += 'Files to integrate from '+os.path.join(self.imagedir,value)+msg1
2681                else:
2682                    msg += 'All files integrated'
2683        except IndexError:
2684            msg += 'Error searching for files named '+os.path.join(self.imagedir,value)
2685        self.ListBox.Clear()
2686        self.ListBox.AppendItems(msg.split('\n'))
2687        self.PreventReEntryShowMatch = False
2688        return
2689       
2690    def OnPause(self):
2691        '''Respond to Pause, changes text on button/Status line, if needed
2692        Stops timer
2693        self.Pause should already be True
2694        '''
2695        if self.timer.IsRunning(): self.timer.Stop()
2696        if self.btnstart.GetLabel() == 'Restart':
2697            return
2698        if self.btnstart.GetLabel() != 'Resume':
2699            print('\nPausing autointegration\n')
2700            self.btnstart.SetLabel('Resume')
2701            self.Status.SetStatusText(
2702                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
2703        self.Pause = True
2704           
2705    def IntegrateImage(self,img):
2706        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
2707        are placed in G2frame.IntgOutList
2708        '''
2709        G2frame = self.G2frame
2710        imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2711        G2frame.Image = imgId
2712        G2frame.PickId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls')
2713        # do integration
2714        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2715        masks = G2frame.PatternTree.GetItemPyData(
2716            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
2717        data = G2frame.PatternTree.GetItemPyData(G2frame.PickId)
2718        # simulate a Image Controls press, since that is where the
2719        # integration is hidden
2720        UpdateImageControls(G2frame,data,masks,IntegrateOnly=True)
2721        G2frame.IntegratedList.append(img) # note this as integrated
2722        # split name and control number
2723        s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
2724        namepre = s[0]
2725        if len(s) > 1:
2726            namenum = s[1]
2727        else:
2728            namenum = ''
2729        for Id in G2frame.IntgOutList: # loop over newly created PDWR entry(ies)
2730            # save the created PWDR tree names so that a reset can delete them
2731            G2frame.Image = Id
2732            treename = G2frame.PatternTree.GetItemText(Id)
2733            G2frame.AutointPWDRnames.append(treename)
2734            # write out the images in the selected formats
2735            Sdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Sample Parameters'))
2736            # determine the name for the current file
2737            fileroot = namepre
2738            if len(G2frame.IntgOutList) > 1:
2739                fileroot += "_AZM"
2740                if 'Azimuth' in Sdata:
2741                    fileroot += str(int(10*Sdata['Azimuth']))
2742                fileroot += "_" 
2743            fileroot += namenum
2744            # loop over selected formats
2745            for dfmt in self.fmtlist:
2746                if not self.params[dfmt[1:]]: continue
2747                if self.params['SeparateDir']:
2748                    subdir = dfmt[1:]
2749                else:
2750                    subdir = ''
2751                fil = os.path.join(self.params['outdir'],subdir,fileroot)
2752                print('writing file '+fil+dfmt)
2753                G2IO.ExportPowder(G2frame,treename,fil,dfmt)
2754               
2755    def EnableButtons(self,flag):
2756        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
2757        '''
2758        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
2759        # on windows
2760        if sys.platform != "win32":
2761            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
2762        self.btnstart.SetLabel('Pause')
2763        wx.Yield()
2764               
2765    def ResetFromTable(self,dist):
2766        '''Sets integration parameters based on values from
2767        the lookup table
2768        '''
2769        #dist = self.controlsDict['distance']
2770        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
2771        #if GSASIIpath.GetConfigValue('debug'):
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['distance'])
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 = {'distance':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 = ('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 = ('Det 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            for key in self.ParmList:
3186                try:
3187                    float(tmpDict[key][0])
3188                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3189                except ValueError:
3190                    parms.append(tmpDict[key])
3191                except IndexError:
3192                    print('No valid image control entries read')
3193                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3194                    return [[]],[]
3195            return parms,fileList
3196        # option 2, read in a list of files
3197        for file in files: # read all files; place in dict by distance
3198            imgDict = Read_imctrl(file)
3199            tmpDict[imgDict.get('distance')] = imgDict
3200        parms = [[] for key in self.ParmList]
3201        fileList = []
3202        for d in sorted(tmpDict):
3203            fileList.append(tmpDict[d].get('filename'))
3204            if d is None: continue
3205            if d < 0: continue
3206            for i,key in enumerate(self.ParmList):
3207                val = tmpDict[d].get(key)
3208                try:
3209                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3210                except:
3211                    val = str(val)
3212                parms[i].append(val)
3213        return parms,fileList
3214   
3215    def ReadImageParmTable(self):
3216        '''Reads possibly edited values from the ListCtrl table and returns a list
3217        of values for each column.
3218        '''
3219        rows = self.list.GetItemCount()
3220        cols = self.list.GetColumnCount()
3221        parms = []
3222        for c in range(cols):
3223            parms.append([])
3224            for r in range(rows):
3225                parms[c].append(self.list.GetItem(r,c).GetText())
3226        return parms
3227
3228    def _onClose(self,event):
3229        'Called when Cancel button is pressed'
3230        self.EndModal(wx.ID_CANCEL)
3231       
3232    def _onSave(self,event):
3233        'Called when save button is pressed; creates a .imtbl file'
3234        fil = ''
3235        if self.G2frame.GSASprojectfile:
3236            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3237        dir,f = os.path.split(fil)
3238        pth = G2G.GetExportPath(self.G2frame)
3239        try:
3240            dlg = wx.FileDialog(self, 'Save table data as',
3241                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3242                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3243            dlg.CenterOnParent()
3244            if dlg.ShowModal() != wx.ID_OK: return
3245            fil = dlg.GetPath()
3246            fil = os.path.splitext(fil)[0]+'.imtbl'
3247        finally:
3248            dlg.Destroy()       
3249        parms = self.ReadImageParmTable()
3250        print('Writing image parameter table as '+fil)
3251        fp = open(fil,'w')
3252        for c in range(len(parms)-1):
3253            lbl = self.ParmList[c]
3254            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3255        lbl = self.ParmList[c+1]
3256        fp.write(lbl+': '+str(parms[c+1])+'\n')
3257        lbl = 'filenames'
3258        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3259        fp.close()
3260   
3261class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3262    '''Creates a custom ListCtrl for editing Image Integration parameters
3263    '''
3264    def __init__(self, parent, ID, pos=wx.DefaultPosition,
3265                 size=(1000,200), style=0):
3266        self.parent=parent
3267        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3268        listmix.ListCtrlAutoWidthMixin.__init__(self)
3269        listmix.TextEditMixin.__init__(self)
3270        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3271        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3272    def FillList(self,parms):
3273        'Places the current parms into the table'
3274        self.ClearAll()
3275        self.rowlen = len(self.parent.ParmList)
3276        for i,lbl in enumerate(self.parent.HeaderList):
3277            self.InsertColumn(i, lbl)
3278        for r,d in enumerate(parms[0]):
3279            if float(d) < 0: continue
3280            index = self.InsertStringItem(sys.maxint, d)
3281            for j in range(1,len(parms)):
3282                self.SetStringItem(index, j, parms[j][r])
3283        for i,lbl in enumerate(self.parent.ParmList):
3284            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3285
3286    def OnDouble(self,evt):
3287        'respond to a double-click'
3288        self.CloseEditor()
3289        fil = '(none)'
3290        pth = G2G.GetImportPath(self.parent.G2frame)
3291        if not pth: pth = '.'
3292        try:
3293            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3294                                style=wx.OPEN,
3295                                wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3296            dlg.CenterOnParent()
3297            if dlg.ShowModal() == wx.ID_OK:
3298                fil = dlg.GetPath()
3299        finally:
3300            dlg.Destroy()
3301        if os.path.splitext(fil)[1] != '.imctrl':
3302            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3303            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3304        else:
3305            # insert or overwrite an instrument parameter set
3306            if not os.path.exists(fil):
3307                print('Does not exist: '+fil)
3308                return
3309            imgDict = Read_imctrl(fil)
3310            dist = imgDict['distance']
3311            parms = self.parent.ReadImageParmTable()
3312            x = np.array([float(i) for i in parms[0]])
3313            closest = abs(x-dist).argmin()
3314            closeX = x[closest]
3315            # fix IMfileList
3316            for c,lbl in enumerate(self.parent.ParmList):
3317                try:
3318                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3319                except ValueError:
3320                    vali = imgDict[lbl]
3321                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3322                    parms[c][closest] = vali
3323                elif dist > closeX: # insert after
3324                    parms[c].insert(closest+1,vali)
3325                else:
3326                    parms[c].insert(closest,vali)
3327            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3328                self.parent.IMfileList[closest] = fil
3329            elif dist > closeX: # insert after
3330                self.parent.IMfileList.insert(closest+1,fil)
3331            else:
3332                self.parent.IMfileList.insert(closest,fil)
3333            self.FillList(parms)
3334# Autointegration end
3335###########################################################################
Note: See TracBrowser for help on using the repository browser.