source: trunk/GSASIIimgGUI.py @ 2788

Last change on this file since 2788 was 2788, checked in by vondreele, 6 years ago

make I/Ib floats in ImageCalibrants?.py
fix issue for SASD seq. refinement
fix some issues with SASD image integrations

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