source: trunk/GSASIIimgGUI.py @ 2697

Last change on this file since 2697 was 2697, checked in by vondreele, 5 years ago

put colList for seqTable in G2frame it has persistence when plot is revised
put SeqPseudoVars? & SeqParFitEqList? in data (SeqResult?) so are local to each type of sequential result. Remove from Controls except for seqRefine (this may change).
Use parmDict instead of indepVarDict in AddNewParFitEqList? - allows more choice of parameters for functions
clear both SeqPseudoVars? & SeqParFitEqList? for a new sequential fit

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