source: trunk/GSASIIimgGUI.py @ 2676

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

refactor UpdatePDFGrid to break up into blocks of sizers

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