source: trunk/GSASIIimgGUI.py @ 2590

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

Redo color scaling after discussion w/RBVD

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