source: trunk/GSASIIimgGUI.py @ 2719

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

clean up code formatting
fix delete tree entry problem with names
add polarization to Image data window

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