source: trunk/GSASIIimgGUI.py @ 2670

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

Add PDF computation to autointegrate

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