source: trunk/GSASIIimgGUI.py @ 3186

Last change on this file since 3186 was 3186, checked in by vondreele, 4 years ago

revisions to integrate images so that masks are reused if not changed in integrate all & auto integrate

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