source: trunk/GSASIIimgGUI.py @ 2674

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

prevent delete if PWDR if corresponding PDF exists
use PickElements? instead of PickElement? - allows multiple selection, blacks out ones already picked & can unpick a pick
add delete spots option to masks

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 151.7 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII - image data display routines
3########### SVN repository information ###################
4# $Date: 2017-01-30 20:25:48 +0000 (Mon, 30 Jan 2017) $
5# $Author: vondreele $
6# $Revision: 2674 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 2674 2017-01-30 20:25:48Z 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: 2674 $")
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,\nLB 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            if self.params['ComputePDF']:
2362                lbl4.SetForegroundColour("black")
2363                lbl4a.SetForegroundColour("black")
2364            else:
2365                lbl4.SetForegroundColour("gray")
2366                lbl4a.SetForegroundColour("gray")
2367                                   
2368        def scanPDFprm(**kw):
2369            fInp4.invalid = not os.path.exists(fInp4.GetValue())
2370            fInp4._IndicateValidity()
2371               
2372        ##################################################
2373        # beginning of __init__ processing
2374        ##################################################
2375        self.G2frame = G2frame
2376        self.ImgTblParms = None
2377        self.IMfileList = None
2378        self.Evaluator = None
2379        self.params = {}
2380        self.Reset = False
2381        self.Pause = False
2382        self.PreventReEntryShowMatch = False
2383        self.PreventReEntryTimer = False
2384        self.params['IMGfile'] = ''
2385        self.params['MaskFile'] = ''
2386        self.params['IgnoreMask'] = True
2387        self.fmtlist = G2IO.ExportPowderList(G2frame)
2388        self.timer = wx.Timer()
2389        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
2390        self.imageBase = G2frame.Image
2391        self.params['ComputePDF'] = False
2392        self.params['pdfDmax'] = 0.0
2393        self.params['pdfprm'] = ''
2394        self.pdfControls = {}
2395
2396        G2frame.PatternTree.GetSelection()
2397        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(self.imageBase)       
2398        self.imagedir,fileroot = os.path.split(imagefile)
2399        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2400        self.params['outdir'] = os.path.abspath(self.imagedir)
2401        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2402                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2403        self.Status = self.CreateStatusBar()
2404        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2405        mnpnl = wx.Panel(self)
2406        mnsizer = wx.BoxSizer(wx.VERTICAL)
2407        # box for integration controls & masks input
2408        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2409        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2410        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2411        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2412                            style = wx.RB_GROUP)
2413        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2414        self.useActive.SetLabel("Active Image: "+
2415                                G2frame.PatternTree.GetItemText(self.imageBase))
2416        lblsizr.Add(self.useActive,1,wx.EXPAND,1)
2417        self.useActive.SetValue(True)
2418        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2419        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2420        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2421        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2422        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2423        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2424        self.editTable.Enable(False)
2425        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2426        # bind button and deactivate be default
2427        lblsizr.Add(minisizer)
2428        mnsizer.Add(lblsizr,1,wx.EXPAND,1)
2429
2430        # file filter stuff
2431        sizer = wx.BoxSizer(wx.HORIZONTAL)
2432        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Image filter'))
2433        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',OnLeave=self.ShowMatchingFiles)
2434        sizer.Add(flterInp)
2435        mnsizer.Add(sizer,0,wx.ALIGN_RIGHT,1)
2436        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2437        mnsizer.Add(self.ListBox,0,wx.EXPAND,1)
2438        self.ShowMatchingFiles(self.params['filter'])
2439        # box for output selections
2440        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2441        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2442        sizer = wx.BoxSizer(wx.HORIZONTAL)
2443        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2444        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2445        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2446        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2447        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2448        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2449        lblsizr.Add(sizer,0,wx.EXPAND)
2450        sizer = wx.BoxSizer(wx.HORIZONTAL)
2451        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
2452        for dfmt in self.fmtlist:
2453            fmt = dfmt[1:]
2454            self.params[fmt] = False
2455            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2456            sizer.Add(btn)
2457        lblsizr.Add(sizer)
2458        sizer = wx.BoxSizer(wx.HORIZONTAL)
2459        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2460        self.params['SeparateDir'] = False
2461        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2462        lblsizr.Add(sizer)
2463        sizer = wx.BoxSizer(wx.HORIZONTAL)
2464        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF'),0,wx.ALIGN_CENTER_VERTICAL)
2465        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2466        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2467        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2468        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2469        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2470        lblsizr.Add(sizer,0)
2471        sizer = wx.BoxSizer(wx.HORIZONTAL)
2472        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2473        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2474        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2475                                     OnLeave=scanPDFprm)
2476        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2477        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2478        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2479        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2480        lblsizr.Add(sizer,0,wx.EXPAND)
2481        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2482        # buttons on bottom
2483        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2484        sizer = wx.BoxSizer(wx.HORIZONTAL)
2485        sizer.Add((20,-1))
2486        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2487        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2488        sizer.Add(self.btnstart)
2489        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2490        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2491        sizer.Add(self.btnreset)
2492        sizer.Add((20,-1),wx.EXPAND,1)
2493        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
2494        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
2495        sizer.Add(self.btnclose)
2496        sizer.Add((20,-1))
2497        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
2498        # finish up window
2499        mnpnl.SetSizer(mnsizer)
2500        OnRadioSelect(None) # disable widgets
2501        mnsizer.Fit(self)
2502        self.CenterOnParent()
2503        self.Show()
2504        showPDFctrls(None)
2505
2506    def checkPDFprm(self):
2507        '''Read in the PDF (.pdfprm) parameter file and check for problems
2508        '''
2509        self.pdfControls = {}
2510        msg = ''
2511        File = None
2512        try:
2513            File = open(self.params['pdfprm'],'r')
2514            S = File.readline()
2515            while S:
2516                if '#' in S:
2517                    S = File.readline()
2518                    continue
2519                key,val = S.split(':',1)
2520                try:
2521                    self.pdfControls[key] = eval(val)
2522                except:
2523                    self.pdfControls[key] = val
2524                S = File.readline()
2525        except Exception as err:
2526            msg += 'PDF processing error: Error with open or read of {}'.format(self.params['pdfprm'])
2527            if GSASIIpath.GetConfigValue('debug'):
2528                print(msg)
2529                print(err)
2530            self.pdfControls = {}
2531            return msg
2532        finally:
2533            if File: File.close()
2534        # may want to offer a substitution when not found
2535        for key in ['Sample Bkg.','Container','Container Bkg.']:
2536            if key not in self.pdfControls:
2537                if msg: msg += '\n'
2538                msg += 'Error, missing key in self.pdfControls: '+key
2539                continue
2540            name = self.pdfControls[key]['Name']
2541            if not name: continue
2542            if not G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,name):
2543                if msg: msg += '\n'
2544                msg += 'Warning, tree item for PDF '+key+' not present:\n  '+name
2545        return msg
2546   
2547    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
2548        G2frame = self.G2frame
2549        if invalid: return
2550        msg = ''
2551        if self.PreventReEntryShowMatch: return
2552        self.PreventReEntryShowMatch = True
2553        imageFileList = []
2554        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
2555            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2556            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2557            if imagefile not in imageFileList: imageFileList.append(imagefile)
2558            if img not in G2frame.IntegratedList:
2559                if msg: msg += '\n'
2560                msg += '  ' + img
2561        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
2562        msg1 = ""
2563        try:
2564            imageList = sorted(
2565                glob.glob(os.path.join(self.imagedir,value)))
2566            if not imageList:
2567                msg1 = 'Warning: No files match search string '+os.path.join(self.imagedir,value)
2568            else:
2569                for fil in imageList:
2570                    if fil not in imageFileList: msg1 += '\n  '+fil
2571                if msg1:
2572                    msg += 'Files to integrate from '+os.path.join(self.imagedir,value)+msg1
2573                else:
2574                    msg += 'All files integrated'
2575        except IndexError:
2576            msg += 'Error searching for files named '+os.path.join(self.imagedir,value)
2577        self.ListBox.Clear()
2578        self.ListBox.AppendItems(msg.split('\n'))
2579        self.PreventReEntryShowMatch = False
2580        return
2581       
2582    def OnPause(self):
2583        '''Respond to Pause, changes text on button/Status line, if needed
2584        Stops timer
2585        self.Pause should already be True
2586        '''
2587        if self.timer.IsRunning(): self.timer.Stop()
2588        if self.btnstart.GetLabel() == 'Restart':
2589            return
2590        if self.btnstart.GetLabel() != 'Resume':
2591            print('\nPausing autointegration\n')
2592            self.btnstart.SetLabel('Resume')
2593            self.Status.SetStatusText(
2594                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
2595        self.Pause = True
2596           
2597    def IntegrateImage(self,img):
2598        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
2599        are placed in G2frame.IntgOutList
2600        '''
2601        G2frame = self.G2frame
2602        imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2603        G2frame.Image = imgId
2604        G2frame.PickId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls')
2605        # do integration
2606        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2607        masks = G2frame.PatternTree.GetItemPyData(
2608            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
2609        data = G2frame.PatternTree.GetItemPyData(G2frame.PickId)
2610        # simulate a Image Controls press, since that is where the
2611        # integration is hidden
2612        UpdateImageControls(G2frame,data,masks,IntegrateOnly=True)
2613        G2frame.IntegratedList.append(img) # note this as integrated
2614        # split name and control number
2615        s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
2616        namepre = s[0]
2617        if len(s) > 1:
2618            namenum = s[1]
2619        else:
2620            namenum = ''
2621        for Id in G2frame.IntgOutList: # loop over newly created PDWR entry(ies)
2622            # save the created PWDR tree names so that a reset can delete them
2623            G2frame.Image = Id
2624            treename = G2frame.PatternTree.GetItemText(Id)
2625            G2frame.AutointPWDRnames.append(treename)
2626            # write out the images in the selected formats
2627            Sdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Sample Parameters'))
2628            # determine the name for the current file
2629            fileroot = namepre
2630            if len(G2frame.IntgOutList) > 1:
2631                fileroot += "_AZM"
2632                if 'Azimuth' in Sdata:
2633                    fileroot += str(int(10*Sdata['Azimuth']))
2634                fileroot += "_" 
2635            fileroot += namenum
2636            # loop over selected formats
2637            for dfmt in self.fmtlist:
2638                if not self.params[dfmt[1:]]: continue
2639                if self.params['SeparateDir']:
2640                    subdir = dfmt[1:]
2641                else:
2642                    subdir = ''
2643                fil = os.path.join(self.params['outdir'],subdir,fileroot)
2644                print('writing file '+fil+dfmt)
2645                G2IO.ExportPowder(G2frame,treename,fil,dfmt)
2646               
2647    def EnableButtons(self,flag):
2648        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
2649        '''
2650        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
2651        # on windows
2652        if sys.platform != "win32":
2653            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
2654        self.btnstart.SetLabel('Pause')
2655        wx.Yield()
2656               
2657    def ResetFromTable(self,dist):
2658        '''Sets integration parameters based on values from
2659        the lookup table
2660        '''
2661        #dist = self.controlsDict['distance']
2662        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
2663        #if GSASIIpath.GetConfigValue('debug'):
2664        if GSASIIpath.GetConfigValue('debug'):
2665            print 'interpolated values: ',interpDict
2666        self.ImageControls = ReadControls(imgctrl)
2667        self.ImageControls.update(interpDict)
2668        self.ImageControls['showLines'] = True
2669        self.ImageControls['ring'] = []
2670        self.ImageControls['rings'] = []
2671        self.ImageControls['ellipses'] = []
2672        self.ImageControls['setDefault'] = False
2673        for i in 'range','size','GonioAngles':
2674            if i in self.ImageControls:
2675                del self.ImageControls[i]
2676        # load copy of Image Masks
2677        if immask:
2678            self.ImageMasks = ReadMask(immask)
2679            if list(self.ImageMasks['Thresholds'][0]) == self.ImageMasks['Thresholds'][1]:     #avoid copy of unchanged thresholds
2680                del self.ImageMasks['Thresholds']
2681        else:
2682            self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
2683       
2684    def StartLoop(self):
2685        '''Prepare to start autointegration timer loop.
2686        Save current Image params for use in future integrations
2687        also label the window so users understand what is being used
2688        '''
2689        print('\nStarting new autointegration\n')
2690        G2frame = self.G2frame
2691        # show current IMG base
2692        if self.params['Mode'] != 'table':
2693            self.useActive.SetLabel("Active Image: "+
2694                                    G2frame.PatternTree.GetItemText(self.imageBase))
2695            # load copy of Image Controls from current image and clean up
2696            # items that should not be copied
2697            self.ImageControls = copy.deepcopy(
2698                G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2699                    G2frame,self.imageBase, 'Image Controls')))
2700            self.ImageControls['showLines'] = True
2701            self.ImageControls['ring'] = []
2702            self.ImageControls['rings'] = []
2703            self.ImageControls['ellipses'] = []
2704            self.ImageControls['setDefault'] = False
2705            del self.ImageControls['range']
2706            del self.ImageControls['size']
2707            del self.ImageControls['GonioAngles']
2708            # load copy of Image Masks, keep thresholds
2709            self.ImageMasks = copy.deepcopy(
2710                G2frame.PatternTree.GetItemPyData(
2711                    G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Masks')))
2712            self.Thresholds = self.ImageMasks['Thresholds'][:]
2713            if list(self.Thresholds[0]) == self.Thresholds[1]:     #avoid copy of unchanged thresholds
2714                del self.ImageMasks['Thresholds']   
2715        # make sure all output directories exist
2716        if self.params['SeparateDir']:
2717            for dfmt in self.fmtlist:
2718                if not self.params[dfmt[1:]]: continue
2719                dir = os.path.join(self.params['outdir'],dfmt[1:])
2720                if not os.path.exists(dir): os.makedirs(dir)
2721        else:
2722            if not os.path.exists(self.params['outdir']):
2723                os.makedirs(self.params['outdir'])
2724        if self.Reset: # special things to do after Reset has been pressed
2725            self.G2frame.IntegratedList = []
2726           
2727            if self.params['Mode'] != 'table': # reset controls and masks for all IMG items in tree to master
2728                for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
2729                    # update controls from master
2730                    controlsDict = G2frame.PatternTree.GetItemPyData(
2731                        G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Image Controls'))
2732                    controlsDict.update(self.ImageControls)
2733                    # update masks from master
2734                    ImageMasks = G2frame.PatternTree.GetItemPyData(
2735                        G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Masks'))
2736                    ImageMasks.update(self.ImageMasks)
2737            # delete all PWDR items created after last Start was pressed
2738            idlist = []
2739            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
2740            while item:
2741                itemName = G2frame.PatternTree.GetItemText(item)
2742                if itemName in G2frame.AutointPWDRnames:
2743                    idlist.append(item)
2744                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
2745            for item in idlist:
2746                G2frame.PatternTree.Delete(item)
2747            wx.Yield()
2748            self.Reset = False
2749        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
2750        G2frame.AutointPDFnames = [] # list of created PWDR tree item names
2751        # check that AutoPDF input is OK, offer chance to use alternate PDWRs if referenced ones
2752        # are not present
2753        if self.params['ComputePDF']:
2754            msg = self.checkPDFprm()
2755            if 'Error' in msg:
2756                print(msg)
2757                return True
2758            elif msg:
2759                fileList = []
2760                id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
2761                while id:
2762                    name = G2frame.PatternTree.GetItemText(id)
2763                    if name.startswith('PWDR '):
2764                        fileList.append(name)
2765                    id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
2766                if not fileList:
2767                    print(msg)
2768                    print('No PWDR entries to select')
2769                    return True
2770                for key in ['Sample Bkg.','Container','Container Bkg.']:
2771                    name = self.pdfControls[key]['Name']
2772                    if not name: continue
2773                    if not G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name):
2774                        indx = G2G.ItemSelector(fileList, self, header='Select PDWR item',
2775                                    title='Select a PDWR tree item for '+key+'\n(or cancel to quit)')
2776                        if indx is None:
2777                            print('No PWDR entry selected for '+key)
2778                            return True
2779                        self.pdfControls[key]['Name'] = fileList[indx]
2780        return False
2781               
2782    def OnTimerLoop(self,event):
2783        '''A method that is called every :meth:`PollTime` seconds that is
2784        used to check for new files and process them. Integrates new images.
2785        Also optionally sets up and computes PDF.
2786        This is called only after the "Start" button is pressed (then its label reads "Pause").
2787        '''
2788        def AutoIntegrateImage(imgId):
2789            '''Integrates an image that has been read into the data tree and updates the
2790            AutoInt window.
2791            '''
2792            img = G2frame.PatternTree.GetItemText(imgId)
2793            controlsDict = G2frame.PatternTree.GetItemPyData(
2794                G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
2795            ImageMasks = G2frame.PatternTree.GetItemPyData(
2796                G2gd.GetPatternTreeItemId(G2frame,imgId, 'Masks'))
2797            if self.params['Mode'] == 'table': # look up parameter values from table
2798                self.ResetFromTable(controlsDict['distance'])
2799            # update controls from master
2800            controlsDict.update(self.ImageControls)
2801            # update masks from master w/o Thresholds
2802            ImageMasks.update(self.ImageMasks)
2803            self.EnableButtons(False)
2804            try:
2805                self.IntegrateImage(img)
2806            finally:
2807                self.EnableButtons(True)
2808            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
2809            wx.Yield()
2810            self.ShowMatchingFiles(self.params['filter'])
2811            wx.Yield()
2812           
2813        def AutoComputePDF(imgId):
2814            '''Computes a PDF for a PWDR data tree tree item
2815            '''
2816            for pwdr in G2frame.AutointPWDRnames[:]:
2817                if not pwdr.startswith('PWDR '): continue
2818                if pwdr in G2frame.AutointPDFnames: continue
2819                PWid = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,pwdr)
2820                controlsDict = G2frame.PatternTree.GetItemPyData(
2821                    G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
2822                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
2823                    print('Skipping PDF for '+pwdr+' due to detector position')
2824                    continue
2825                # Setup PDF
2826                Parms = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2827                    G2frame,PWid,'Instrument Parameters'))[0]
2828                fullLimits = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2829                    G2frame,PWid,'Limits'))[0]
2830                if 'C' in Parms['Type'][0]:
2831                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
2832                else:
2833                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
2834                Qlimits = [0.9*qMax,qMax]
2835
2836                item = pwdr
2837                Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2838                    G2frame,imgId, 'Comments'))
2839                ElList = {}
2840                for item in Comments:           #grab chemical formula from Comments, if there
2841                    if 'formula' in item[:15].lower():
2842                        formula = item.split('=')[1].split()
2843                        elems = formula[::2]
2844                        nums = formula[1::2]
2845                        formula = zip(elems,nums)
2846                        for [elem,num] in formula:
2847                            ElData = G2elem.GetElInfo(elem,Parms)
2848                            ElData['FormulaNo'] = float(num)
2849                            ElList[elem] = ElData
2850                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits)
2851                PDFdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2852                    G2frame,PDFid, 'PDF Controls'))
2853                PDFdata.update(self.pdfControls)
2854                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
2855                PDFdata['Sample']['Name'] = pwdr
2856                # compute PDF
2857                wx.Yield()
2858                G2pdG.computePDF(G2frame,PDFdata)
2859                wx.Yield()
2860                G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
2861                wx.Yield()
2862                G2frame.AutointPDFnames.append(pwdr)
2863                # save names of PDF entry to be deleted later if needed
2864                G2frame.AutointPWDRnames.append(G2frame.PatternTree.GetItemText(PDFid))
2865           
2866        G2frame = self.G2frame
2867        try:
2868            self.currImageList = sorted(
2869                glob.glob(os.path.join(self.imagedir,self.params['filter'])))
2870            self.ShowMatchingFiles(self.params['filter'])
2871        except IndexError:
2872            self.currImageList = []
2873            return
2874
2875        if self.PreventReEntryTimer: return
2876        self.PreventReEntryTimer = True
2877        imageFileList = []
2878        # integrate the images that have already been read in, but
2879        # have not yet been processed           
2880        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
2881            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2882            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2883            # Create a list of image files that have been read in
2884            if imagefile not in imageFileList: imageFileList.append(imagefile)
2885            # skip if already integrated
2886            if img in G2frame.IntegratedList: continue
2887            AutoIntegrateImage(imgId)
2888            #self.Pause |= G2frame.PauseIntegration
2889            #if self.Pause:
2890            #    self.OnPause()
2891            #    self.PreventReEntryTimer = False
2892            #    return
2893            if self.pdfControls: AutoComputePDF(imgId)
2894            self.Pause |= G2frame.PauseIntegration
2895            if self.Pause:
2896                self.OnPause()
2897                self.PreventReEntryTimer = False
2898                return
2899
2900        # loop over image files matching glob, reading in any new ones
2901        for newImage in self.currImageList:
2902            if newImage in imageFileList or self.Pause: continue # already read?
2903            for imgId in G2IO.ReadImages(G2frame,newImage):
2904                AutoIntegrateImage(imgId)           
2905                #self.Pause |= G2frame.PauseIntegration
2906                #if self.Pause:
2907                #    self.OnPause()
2908                #    self.PreventReEntryTimer = False
2909                #    return
2910                if self.pdfControls: AutoComputePDF(imgId)
2911                self.Pause |= G2frame.PauseIntegration
2912                if self.Pause:
2913                    self.OnPause()
2914                    self.PreventReEntryTimer = False
2915                    return
2916        if GSASIIpath.GetConfigValue('debug'):
2917            import datetime
2918            print ("Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
2919        self.PreventReEntryTimer = False
2920
2921def DefineEvaluator(dlg):
2922    '''Creates a function that provides interpolated values for a given distance value
2923    '''
2924    def Evaluator(dist):
2925        '''Interpolate image parameters for a supplied distance value
2926
2927        :param float dist: distance to use for interpolation
2928        :returns: a list with 3 items:
2929
2930          * a dict with parameter values,
2931          * the closest imctrl and
2932          * the closest maskfile (or None)
2933        '''           
2934        x = np.array([float(i) for i in parms[0]])
2935        closest = abs(x-dist).argmin()
2936        D = {'distance':dist}
2937        imctfile = IMfileList[closest]
2938        if parms[-1][closest].lower() != '(none)':
2939            maskfile = parms[-1][closest]
2940        else:
2941            maskfile = None
2942        for c in range(1,cols-1):
2943            lbl = ParmList[c]
2944            if lbl in nonInterpVars:
2945                if lbl in ['outChannels',]:
2946                    D[lbl] = int(float(parms[c][closest]))
2947                else:
2948                    D[lbl] = float(parms[c][closest])
2949            else:
2950                y = np.array([float(i) for i in parms[c]])
2951                D[lbl] = np.interp(dist,x,y)
2952        # full integration when angular range is 0
2953        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
2954        # conversion for paired values
2955        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
2956            r = a.split('_')[0]
2957            D[r] = [D[a],D[b]]
2958            if r in ['LRazimuth',]:
2959                D[r] = [int(D[a]),int(D[b])]
2960            del D[a]
2961            del D[b]
2962        return D,imctfile,maskfile
2963    # save local copies of values needed in Evaluator
2964    parms = dlg.ReadImageParmTable()
2965    IMfileList = dlg.IMfileList
2966    cols = dlg.list.GetColumnCount()
2967    ParmList = dlg.ParmList
2968    nonInterpVars = dlg.nonInterpVars
2969    return Evaluator
2970
2971class IntegParmTable(wx.Dialog):
2972    '''Creates a dialog window with a table of integration parameters.
2973    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
2974    In this case, :func:`DefineEvaluator` should be called to obtain a function that
2975    creates a dictionary with interpolated parameter values.
2976    '''
2977    ParmList = ('distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
2978            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
2979            'maskfile',
2980            )
2981    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
2982                     'outChannels')  # values in this list are taken from nearest rather than interpolated
2983    HeaderList = ('Det Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
2984            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
2985            'Mask File',
2986            )
2987    def __init__(self,parent,parms=None,IMfileList=None):
2988        self.G2frame = parent.G2frame
2989        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
2990        if parms:
2991            self.parms = parms # list of values by column
2992            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
2993        else:
2994            self.parms = [] # list of values by column
2995            self.IMfileList = [] # list of .imctrl file names for each entry in table
2996            files = []
2997            try:
2998                pth = G2G.GetImportPath(self.G2frame)
2999                if not pth: pth = '.'
3000                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3001                    style=wx.OPEN| wx.MULTIPLE,
3002                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3003                dlg.CenterOnParent()
3004                if dlg.ShowModal() == wx.ID_OK:
3005                    files = dlg.GetPaths()
3006                    self.parms,self.IMfileList = self.ReadFiles(files)
3007            finally:
3008                dlg.Destroy()
3009            if not files:
3010                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3011                return
3012        mainSizer = wx.BoxSizer(wx.VERTICAL)
3013        self.list = ImgIntLstCtrl(self, wx.ID_ANY,
3014                      style=wx.LC_REPORT
3015                          | wx.BORDER_SUNKEN
3016                         #| wx.BORDER_NONE
3017                         )
3018        mainSizer.Add(self.list,1,wx.EXPAND,1)
3019        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3020        btn = wx.Button(self, wx.ID_OK)
3021        btnsizer.Add(btn)
3022        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3023        btn.Bind(wx.EVT_BUTTON,self._onSave)
3024        btnsizer.Add(btn)
3025        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3026        btn.Bind(wx.EVT_BUTTON,self._onClose)
3027        btnsizer.Add(btn)
3028        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3029        self.SetSizer(mainSizer)
3030        self.list.FillList(self.parms)
3031        mainSizer.Layout()
3032        mainSizer.Fit(self)
3033       
3034    def ReadFiles(self,files):
3035        '''Reads a list of .imctrl files or a single .imtbl file
3036        '''
3037        tmpDict = {}
3038        if not files: return
3039        # option 1, a dump from a previous save
3040        if os.path.splitext(files[0])[1] == '.imtbl':
3041            fp = open(files[0],'r')
3042            S = fp.readline()
3043            while S:
3044                if S[0] != '#':
3045                    [key,val] = S[:-1].split(':',1)
3046                    tmpDict[key] = eval(val)
3047                S = fp.readline()
3048            fp.close()
3049            # delete entries where files do not exist
3050            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3051            if m1:
3052                print('\nimctrl file not found:')
3053                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3054            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3055            if m2:
3056                print('\nmask file not found')
3057                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3058            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3059            if m3:
3060                print('\nDropping entries due to negative distance: '+str(m3))
3061            m = sorted(set(m1 + m2 + m3))
3062            m.reverse()
3063            for c in m:
3064                for key in tmpDict:
3065                    del tmpDict[key][c]
3066            fileList = tmpDict.get('filenames','[]')
3067            parms = []
3068            for key in self.ParmList:
3069                try:
3070                    float(tmpDict[key][0])
3071                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3072                except ValueError:
3073                    parms.append(tmpDict[key])
3074                except IndexError:
3075                    print('No valid image control entries read')
3076                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3077                    return [[]],[]
3078            return parms,fileList
3079        # option 2, read in a list of files
3080        for file in files: # read all files; place in dict by distance
3081            imgDict = Read_imctrl(file)
3082            tmpDict[imgDict.get('distance')] = imgDict
3083        parms = [[] for key in self.ParmList]
3084        fileList = []
3085        for d in sorted(tmpDict):
3086            fileList.append(tmpDict[d].get('filename'))
3087            if d is None: continue
3088            if d < 0: continue
3089            for i,key in enumerate(self.ParmList):
3090                val = tmpDict[d].get(key)
3091                try:
3092                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3093                except:
3094                    val = str(val)
3095                parms[i].append(val)
3096        return parms,fileList
3097   
3098    def ReadImageParmTable(self):
3099        '''Reads possibly edited values from the ListCtrl table and returns a list
3100        of values for each column.
3101        '''
3102        rows = self.list.GetItemCount()
3103        cols = self.list.GetColumnCount()
3104        parms = []
3105        for c in range(cols):
3106            parms.append([])
3107            for r in range(rows):
3108                parms[c].append(self.list.GetItem(r,c).GetText())
3109        return parms
3110
3111    def _onClose(self,event):
3112        'Called when Cancel button is pressed'
3113        self.EndModal(wx.ID_CANCEL)
3114       
3115    def _onSave(self,event):
3116        'Called when save button is pressed; creates a .imtbl file'
3117        fil = ''
3118        if self.G2frame.GSASprojectfile:
3119            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3120        dir,f = os.path.split(fil)
3121        pth = G2G.GetExportPath(self.G2frame)
3122        try:
3123            dlg = wx.FileDialog(self, 'Save table data as',
3124                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3125                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3126            dlg.CenterOnParent()
3127            if dlg.ShowModal() != wx.ID_OK: return
3128            fil = dlg.GetPath()
3129            fil = os.path.splitext(fil)[0]+'.imtbl'
3130        finally:
3131            dlg.Destroy()       
3132        parms = self.ReadImageParmTable()
3133        print('Writing image parameter table as '+fil)
3134        fp = open(fil,'w')
3135        for c in range(len(parms)-1):
3136            lbl = self.ParmList[c]
3137            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3138        lbl = self.ParmList[c+1]
3139        fp.write(lbl+': '+str(parms[c+1])+'\n')
3140        lbl = 'filenames'
3141        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3142        fp.close()
3143   
3144class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3145    '''Creates a custom ListCtrl for editing Image Integration parameters
3146    '''
3147    def __init__(self, parent, ID, pos=wx.DefaultPosition,
3148                 size=(1000,200), style=0):
3149        self.parent=parent
3150        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3151        listmix.ListCtrlAutoWidthMixin.__init__(self)
3152        listmix.TextEditMixin.__init__(self)
3153        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3154        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3155    def FillList(self,parms):
3156        'Places the current parms into the table'
3157        self.ClearAll()
3158        self.rowlen = len(self.parent.ParmList)
3159        for i,lbl in enumerate(self.parent.HeaderList):
3160            self.InsertColumn(i, lbl)
3161        for r,d in enumerate(parms[0]):
3162            if float(d) < 0: continue
3163            index = self.InsertStringItem(sys.maxint, d)
3164            for j in range(1,len(parms)):
3165                self.SetStringItem(index, j, parms[j][r])
3166        for i,lbl in enumerate(self.parent.ParmList):
3167            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3168
3169    def OnDouble(self,evt):
3170        'respond to a double-click'
3171        self.CloseEditor()
3172        fil = '(none)'
3173        pth = G2G.GetImportPath(self.parent.G2frame)
3174        if not pth: pth = '.'
3175        try:
3176            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3177                                style=wx.OPEN,
3178                                wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3179            dlg.CenterOnParent()
3180            if dlg.ShowModal() == wx.ID_OK:
3181                fil = dlg.GetPath()
3182        finally:
3183            dlg.Destroy()
3184        if os.path.splitext(fil)[1] != '.imctrl':
3185            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3186            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3187        else:
3188            # insert or overwrite an instrument parameter set
3189            if not os.path.exists(fil):
3190                print('Does not exist: '+fil)
3191                return
3192            imgDict = Read_imctrl(fil)
3193            dist = imgDict['distance']
3194            parms = self.parent.ReadImageParmTable()
3195            x = np.array([float(i) for i in parms[0]])
3196            closest = abs(x-dist).argmin()
3197            closeX = x[closest]
3198            # fix IMfileList
3199            for c,lbl in enumerate(self.parent.ParmList):
3200                try:
3201                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3202                except ValueError:
3203                    vali = imgDict[lbl]
3204                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3205                    parms[c][closest] = vali
3206                elif dist > closeX: # insert after
3207                    parms[c].insert(closest+1,vali)
3208                else:
3209                    parms[c].insert(closest,vali)
3210            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3211                self.parent.IMfileList[closest] = fil
3212            elif dist > closeX: # insert after
3213                self.parent.IMfileList.insert(closest+1,fil)
3214            else:
3215                self.parent.IMfileList.insert(closest,fil)
3216            self.FillList(parms)
3217# Autointegration end
3218###########################################################################
Note: See TracBrowser for help on using the repository browser.