source: trunk/GSASIIimgGUI.py @ 2682

Last change on this file since 2682 was 2682, checked in by toby, 5 years ago

fix EditTable? bug

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