source: trunk/GSASIIimgGUI.py @ 2671

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

Set calib d-min as option for xfer angles; fix trig error

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