source: trunk/GSASIIimgGUI.py @ 3193

Last change on this file since 3193 was 3193, checked in by toby, 4 years ago

better error msg w/o calibrant; search comments when Sample Parameters FreePrm? label is changed

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